1f0319968116e97b42d25a0eb8c42c0e5a0d7f8a
[reactos.git] / reactos / dll / win32 / wininet / ftp.c
1 /*
2 * WININET - Ftp implementation
3 *
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
8 *
9 * Ulrich Czekalla
10 * Noureddine Jemmali
11 *
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 */
29
30 #include "config.h"
31 #include "wine/port.h"
32
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
36
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 # include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 # include <sys/ioctl.h>
54 #endif
55 #include <time.h>
56 #include <assert.h>
57
58 #include "windef.h"
59 #include "winbase.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "wininet.h"
63 #include "winnls.h"
64 #include "winerror.h"
65 #include "winreg.h"
66 #include "winternl.h"
67 #include "shlwapi.h"
68
69 #include "wine/debug.h"
70 #include "internet.h"
71
72 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
73
74 typedef struct _ftp_session_t ftp_session_t;
75
76 typedef struct
77 {
78 object_header_t hdr;
79 ftp_session_t *lpFtpSession;
80 BOOL session_deleted;
81 int nDataSocket;
82 WCHAR *cache_file;
83 HANDLE cache_file_handle;
84 } ftp_file_t;
85
86 struct _ftp_session_t
87 {
88 object_header_t hdr;
89 appinfo_t *lpAppInfo;
90 int sndSocket;
91 int lstnSocket;
92 int pasvSocket; /* data socket connected by us in case of passive FTP */
93 ftp_file_t *download_in_progress;
94 struct sockaddr_in socketAddress;
95 struct sockaddr_in lstnSocketAddress;
96 LPWSTR servername;
97 INTERNET_PORT serverport;
98 LPWSTR lpszPassword;
99 LPWSTR lpszUserName;
100 };
101
102 typedef struct
103 {
104 BOOL bIsDirectory;
105 LPWSTR lpszName;
106 DWORD nSize;
107 SYSTEMTIME tmLastModified;
108 unsigned short permissions;
109 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
110
111 typedef struct
112 {
113 object_header_t hdr;
114 ftp_session_t *lpFtpSession;
115 DWORD index;
116 DWORD size;
117 LPFILEPROPERTIESW lpafp;
118 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
119
120 #define DATA_PACKET_SIZE 0x2000
121 #define szCRLF "\r\n"
122 #define MAX_BACKLOG 5
123
124 /* Testing shows that Windows only accepts dwFlags where the last
125 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
126 */
127 #define FTP_CONDITION_MASK 0x0007
128
129 typedef enum {
130 /* FTP commands with arguments. */
131 FTP_CMD_ACCT,
132 FTP_CMD_CWD,
133 FTP_CMD_DELE,
134 FTP_CMD_MKD,
135 FTP_CMD_PASS,
136 FTP_CMD_PORT,
137 FTP_CMD_RETR,
138 FTP_CMD_RMD,
139 FTP_CMD_RNFR,
140 FTP_CMD_RNTO,
141 FTP_CMD_STOR,
142 FTP_CMD_TYPE,
143 FTP_CMD_USER,
144 FTP_CMD_SIZE,
145
146 /* FTP commands without arguments. */
147 FTP_CMD_ABOR,
148 FTP_CMD_LIST,
149 FTP_CMD_NLST,
150 FTP_CMD_PASV,
151 FTP_CMD_PWD,
152 FTP_CMD_QUIT,
153 } FTP_COMMAND;
154
155 static const CHAR *const szFtpCommands[] = {
156 "ACCT",
157 "CWD",
158 "DELE",
159 "MKD",
160 "PASS",
161 "PORT",
162 "RETR",
163 "RMD",
164 "RNFR",
165 "RNTO",
166 "STOR",
167 "TYPE",
168 "USER",
169 "SIZE",
170 "ABOR",
171 "LIST",
172 "NLST",
173 "PASV",
174 "PWD",
175 "QUIT",
176 };
177
178 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
179 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
180
181 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
182 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
183 static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
184 static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
185 static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
186 static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
187 static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
188 static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
189 static BOOL FTP_InitListenSocket(ftp_session_t*);
190 static BOOL FTP_ConnectToHost(ftp_session_t*);
191 static BOOL FTP_SendPassword(ftp_session_t*);
192 static BOOL FTP_SendAccount(ftp_session_t*);
193 static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
194 static BOOL FTP_SendPort(ftp_session_t*);
195 static BOOL FTP_DoPassive(ftp_session_t*);
196 static BOOL FTP_SendPortOrPasv(ftp_session_t*);
197 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
198 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
199 static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
200 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
201 static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
202 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
203 static DWORD FTP_SetResponseError(DWORD dwResponse);
204 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
205 static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
206 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
207 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
209 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
210 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
211 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
212 LPDWORD lpdwCurrentDirectory);
213 static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
214 static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
215 static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
216 static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
217 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
218 DWORD_PTR dwContext);
219
220 /* A temporary helper until we get rid of INTERNET_GetLastError calls */
221 static BOOL res_to_le(DWORD res)
222 {
223 if(res != ERROR_SUCCESS)
224 INTERNET_SetLastError(res);
225 return res == ERROR_SUCCESS;
226 }
227
228 /***********************************************************************
229 * FtpPutFileA (WININET.@)
230 *
231 * Uploads a file to the FTP server
232 *
233 * RETURNS
234 * TRUE on success
235 * FALSE on failure
236 *
237 */
238 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
239 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
240 {
241 LPWSTR lpwzLocalFile;
242 LPWSTR lpwzNewRemoteFile;
243 BOOL ret;
244
245 lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
246 lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
247 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
248 dwFlags, dwContext);
249 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
250 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
251 return ret;
252 }
253
254 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
255 {
256 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
257 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
258
259 TRACE("%p\n", lpwfs);
260
261 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
262 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
263
264 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
265 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
266 }
267
268 /***********************************************************************
269 * FtpPutFileW (WININET.@)
270 *
271 * Uploads a file to the FTP server
272 *
273 * RETURNS
274 * TRUE on success
275 * FALSE on failure
276 *
277 */
278 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
279 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
280 {
281 ftp_session_t *lpwfs;
282 appinfo_t *hIC = NULL;
283 BOOL r = FALSE;
284
285 if (!lpszLocalFile || !lpszNewRemoteFile)
286 {
287 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
288 return FALSE;
289 }
290
291 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
292 if (!lpwfs)
293 {
294 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
295 return FALSE;
296 }
297
298 if (WH_HFTPSESSION != lpwfs->hdr.htype)
299 {
300 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
301 goto lend;
302 }
303
304 if (lpwfs->download_in_progress != NULL)
305 {
306 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
307 goto lend;
308 }
309
310 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
311 {
312 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
313 goto lend;
314 }
315
316 hIC = lpwfs->lpAppInfo;
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
318 {
319 WORKREQUEST workRequest;
320 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
321
322 workRequest.asyncproc = AsyncFtpPutFileProc;
323 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
324 req->lpszLocalFile = heap_strdupW(lpszLocalFile);
325 req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
326 req->dwFlags = dwFlags;
327 req->dwContext = dwContext;
328
329 r = res_to_le(INTERNET_AsyncCall(&workRequest));
330 }
331 else
332 {
333 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
334 lpszNewRemoteFile, dwFlags, dwContext);
335 }
336
337 lend:
338 WININET_Release( &lpwfs->hdr );
339
340 return r;
341 }
342
343 /***********************************************************************
344 * FTP_FtpPutFileW (Internal)
345 *
346 * Uploads a file to the FTP server
347 *
348 * RETURNS
349 * TRUE on success
350 * FALSE on failure
351 *
352 */
353 static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
354 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
355 {
356 HANDLE hFile;
357 BOOL bSuccess = FALSE;
358 appinfo_t *hIC = NULL;
359 INT nResCode;
360
361 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
362
363 /* Clear any error information */
364 INTERNET_SetLastError(0);
365
366 /* Open file to be uploaded */
367 if (INVALID_HANDLE_VALUE ==
368 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
369 /* Let CreateFile set the appropriate error */
370 return FALSE;
371
372 hIC = lpwfs->lpAppInfo;
373
374 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
375
376 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
377 {
378 INT nDataSocket;
379
380 /* Get data socket to server */
381 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
382 {
383 FTP_SendData(lpwfs, nDataSocket, hFile);
384 closesocket(nDataSocket);
385 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
386 if (nResCode)
387 {
388 if (nResCode == 226)
389 bSuccess = TRUE;
390 else
391 FTP_SetResponseError(nResCode);
392 }
393 }
394 }
395
396 if (lpwfs->lstnSocket != -1)
397 {
398 closesocket(lpwfs->lstnSocket);
399 lpwfs->lstnSocket = -1;
400 }
401
402 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
403 {
404 INTERNET_ASYNC_RESULT iar;
405
406 iar.dwResult = (DWORD)bSuccess;
407 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
408 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
409 &iar, sizeof(INTERNET_ASYNC_RESULT));
410 }
411
412 CloseHandle(hFile);
413
414 return bSuccess;
415 }
416
417
418 /***********************************************************************
419 * FtpSetCurrentDirectoryA (WININET.@)
420 *
421 * Change the working directory on the FTP server
422 *
423 * RETURNS
424 * TRUE on success
425 * FALSE on failure
426 *
427 */
428 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
429 {
430 LPWSTR lpwzDirectory;
431 BOOL ret;
432
433 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
434 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
435 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
436 return ret;
437 }
438
439
440 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
441 {
442 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
443 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
444
445 TRACE("%p\n", lpwfs);
446
447 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
448 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
449 }
450
451 /***********************************************************************
452 * FtpSetCurrentDirectoryW (WININET.@)
453 *
454 * Change the working directory on the FTP server
455 *
456 * RETURNS
457 * TRUE on success
458 * FALSE on failure
459 *
460 */
461 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
462 {
463 ftp_session_t *lpwfs = NULL;
464 appinfo_t *hIC = NULL;
465 BOOL r = FALSE;
466
467 if (!lpszDirectory)
468 {
469 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
470 goto lend;
471 }
472
473 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
474 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
475 {
476 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
477 goto lend;
478 }
479
480 if (lpwfs->download_in_progress != NULL)
481 {
482 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
483 goto lend;
484 }
485
486 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
487
488 hIC = lpwfs->lpAppInfo;
489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
490 {
491 WORKREQUEST workRequest;
492 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
493
494 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
495 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
496 req = &workRequest.u.FtpSetCurrentDirectoryW;
497 req->lpszDirectory = heap_strdupW(lpszDirectory);
498
499 r = res_to_le(INTERNET_AsyncCall(&workRequest));
500 }
501 else
502 {
503 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
504 }
505
506 lend:
507 if( lpwfs )
508 WININET_Release( &lpwfs->hdr );
509
510 return r;
511 }
512
513
514 /***********************************************************************
515 * FTP_FtpSetCurrentDirectoryW (Internal)
516 *
517 * Change the working directory on the FTP server
518 *
519 * RETURNS
520 * TRUE on success
521 * FALSE on failure
522 *
523 */
524 static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
525 {
526 INT nResCode;
527 appinfo_t *hIC = NULL;
528 DWORD bSuccess = FALSE;
529
530 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
531
532 /* Clear any error information */
533 INTERNET_SetLastError(0);
534
535 hIC = lpwfs->lpAppInfo;
536 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
537 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
538 goto lend;
539
540 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
541
542 if (nResCode)
543 {
544 if (nResCode == 250)
545 bSuccess = TRUE;
546 else
547 FTP_SetResponseError(nResCode);
548 }
549
550 lend:
551 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
552 {
553 INTERNET_ASYNC_RESULT iar;
554
555 iar.dwResult = bSuccess;
556 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
557 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
558 &iar, sizeof(INTERNET_ASYNC_RESULT));
559 }
560 return bSuccess;
561 }
562
563
564 /***********************************************************************
565 * FtpCreateDirectoryA (WININET.@)
566 *
567 * Create new directory on the FTP server
568 *
569 * RETURNS
570 * TRUE on success
571 * FALSE on failure
572 *
573 */
574 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
575 {
576 LPWSTR lpwzDirectory;
577 BOOL ret;
578
579 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
580 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
581 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
582 return ret;
583 }
584
585
586 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
587 {
588 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
589 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
590
591 TRACE(" %p\n", lpwfs);
592
593 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
594 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
595 }
596
597 /***********************************************************************
598 * FtpCreateDirectoryW (WININET.@)
599 *
600 * Create new directory on the FTP server
601 *
602 * RETURNS
603 * TRUE on success
604 * FALSE on failure
605 *
606 */
607 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
608 {
609 ftp_session_t *lpwfs;
610 appinfo_t *hIC = NULL;
611 BOOL r = FALSE;
612
613 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
614 if (!lpwfs)
615 {
616 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
617 return FALSE;
618 }
619
620 if (WH_HFTPSESSION != lpwfs->hdr.htype)
621 {
622 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
623 goto lend;
624 }
625
626 if (lpwfs->download_in_progress != NULL)
627 {
628 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
629 goto lend;
630 }
631
632 if (!lpszDirectory)
633 {
634 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
635 goto lend;
636 }
637
638 hIC = lpwfs->lpAppInfo;
639 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
640 {
641 WORKREQUEST workRequest;
642 struct WORKREQ_FTPCREATEDIRECTORYW *req;
643
644 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
645 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
646 req = &workRequest.u.FtpCreateDirectoryW;
647 req->lpszDirectory = heap_strdupW(lpszDirectory);
648
649 r = res_to_le(INTERNET_AsyncCall(&workRequest));
650 }
651 else
652 {
653 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
654 }
655 lend:
656 WININET_Release( &lpwfs->hdr );
657
658 return r;
659 }
660
661
662 /***********************************************************************
663 * FTP_FtpCreateDirectoryW (Internal)
664 *
665 * Create new directory on the FTP server
666 *
667 * RETURNS
668 * TRUE on success
669 * FALSE on failure
670 *
671 */
672 static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
673 {
674 INT nResCode;
675 BOOL bSuccess = FALSE;
676 appinfo_t *hIC = NULL;
677
678 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
679
680 /* Clear any error information */
681 INTERNET_SetLastError(0);
682
683 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
684 goto lend;
685
686 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
687 if (nResCode)
688 {
689 if (nResCode == 257)
690 bSuccess = TRUE;
691 else
692 FTP_SetResponseError(nResCode);
693 }
694
695 lend:
696 hIC = lpwfs->lpAppInfo;
697 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
698 {
699 INTERNET_ASYNC_RESULT iar;
700
701 iar.dwResult = (DWORD)bSuccess;
702 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
703 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
704 &iar, sizeof(INTERNET_ASYNC_RESULT));
705 }
706
707 return bSuccess;
708 }
709
710 /***********************************************************************
711 * FtpFindFirstFileA (WININET.@)
712 *
713 * Search the specified directory
714 *
715 * RETURNS
716 * HINTERNET on success
717 * NULL on failure
718 *
719 */
720 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
721 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
722 {
723 LPWSTR lpwzSearchFile;
724 WIN32_FIND_DATAW wfd;
725 LPWIN32_FIND_DATAW lpFindFileDataW;
726 HINTERNET ret;
727
728 lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
729 lpFindFileDataW = lpFindFileData?&wfd:NULL;
730 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
731 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
732
733 if (ret && lpFindFileData)
734 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
735
736 return ret;
737 }
738
739
740 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
741 {
742 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
743 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
744
745 TRACE("%p\n", lpwfs);
746
747 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
748 req->lpFindFileData, req->dwFlags, req->dwContext);
749 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
750 }
751
752 /***********************************************************************
753 * FtpFindFirstFileW (WININET.@)
754 *
755 * Search the specified directory
756 *
757 * RETURNS
758 * HINTERNET on success
759 * NULL on failure
760 *
761 */
762 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
763 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
764 {
765 ftp_session_t *lpwfs;
766 appinfo_t *hIC = NULL;
767 HINTERNET r = NULL;
768
769 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
770 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
771 {
772 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
773 goto lend;
774 }
775
776 if (lpwfs->download_in_progress != NULL)
777 {
778 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
779 goto lend;
780 }
781
782 hIC = lpwfs->lpAppInfo;
783 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
784 {
785 WORKREQUEST workRequest;
786 struct WORKREQ_FTPFINDFIRSTFILEW *req;
787
788 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
789 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
790 req = &workRequest.u.FtpFindFirstFileW;
791 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
792 req->lpFindFileData = lpFindFileData;
793 req->dwFlags = dwFlags;
794 req->dwContext= dwContext;
795
796 INTERNET_AsyncCall(&workRequest);
797 r = NULL;
798 }
799 else
800 {
801 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
802 dwFlags, dwContext);
803 }
804 lend:
805 if( lpwfs )
806 WININET_Release( &lpwfs->hdr );
807
808 return r;
809 }
810
811
812 /***********************************************************************
813 * FTP_FtpFindFirstFileW (Internal)
814 *
815 * Search the specified directory
816 *
817 * RETURNS
818 * HINTERNET on success
819 * NULL on failure
820 *
821 */
822 static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
823 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
824 {
825 INT nResCode;
826 appinfo_t *hIC = NULL;
827 HINTERNET hFindNext = NULL;
828
829 TRACE("\n");
830
831 /* Clear any error information */
832 INTERNET_SetLastError(0);
833
834 if (!FTP_InitListenSocket(lpwfs))
835 goto lend;
836
837 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
838 goto lend;
839
840 if (!FTP_SendPortOrPasv(lpwfs))
841 goto lend;
842
843 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
844 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
845 goto lend;
846
847 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
848 if (nResCode)
849 {
850 if (nResCode == 125 || nResCode == 150)
851 {
852 INT nDataSocket;
853
854 /* Get data socket to server */
855 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
856 {
857 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
858 closesocket(nDataSocket);
859 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
860 if (nResCode != 226 && nResCode != 250)
861 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
862 }
863 }
864 else
865 FTP_SetResponseError(nResCode);
866 }
867
868 lend:
869 if (lpwfs->lstnSocket != -1)
870 {
871 closesocket(lpwfs->lstnSocket);
872 lpwfs->lstnSocket = -1;
873 }
874
875 hIC = lpwfs->lpAppInfo;
876 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
877 {
878 INTERNET_ASYNC_RESULT iar;
879
880 if (hFindNext)
881 {
882 iar.dwResult = (DWORD_PTR)hFindNext;
883 iar.dwError = ERROR_SUCCESS;
884 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
885 &iar, sizeof(INTERNET_ASYNC_RESULT));
886 }
887
888 iar.dwResult = (DWORD_PTR)hFindNext;
889 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
890 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
891 &iar, sizeof(INTERNET_ASYNC_RESULT));
892 }
893
894 return hFindNext;
895 }
896
897
898 /***********************************************************************
899 * FtpGetCurrentDirectoryA (WININET.@)
900 *
901 * Retrieves the current directory
902 *
903 * RETURNS
904 * TRUE on success
905 * FALSE on failure
906 *
907 */
908 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
909 LPDWORD lpdwCurrentDirectory)
910 {
911 WCHAR *dir = NULL;
912 DWORD len;
913 BOOL ret;
914
915 if(lpdwCurrentDirectory) {
916 len = *lpdwCurrentDirectory;
917 if(lpszCurrentDirectory)
918 {
919 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
920 if (NULL == dir)
921 {
922 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
923 return FALSE;
924 }
925 }
926 }
927 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
928
929 if (ret && lpszCurrentDirectory)
930 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
931
932 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
933 HeapFree(GetProcessHeap(), 0, dir);
934 return ret;
935 }
936
937
938 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
939 {
940 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
941 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
942
943 TRACE("%p\n", lpwfs);
944
945 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
946 }
947
948 /***********************************************************************
949 * FtpGetCurrentDirectoryW (WININET.@)
950 *
951 * Retrieves the current directory
952 *
953 * RETURNS
954 * TRUE on success
955 * FALSE on failure
956 *
957 */
958 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
959 LPDWORD lpdwCurrentDirectory)
960 {
961 ftp_session_t *lpwfs;
962 appinfo_t *hIC = NULL;
963 BOOL r = FALSE;
964
965 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
966
967 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
968 if (NULL == lpwfs)
969 {
970 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
971 goto lend;
972 }
973
974 if (WH_HFTPSESSION != lpwfs->hdr.htype)
975 {
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 goto lend;
978 }
979
980 if (!lpdwCurrentDirectory)
981 {
982 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
983 goto lend;
984 }
985
986 if (lpszCurrentDirectory == NULL)
987 {
988 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
989 goto lend;
990 }
991
992 if (lpwfs->download_in_progress != NULL)
993 {
994 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
995 goto lend;
996 }
997
998 hIC = lpwfs->lpAppInfo;
999 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1000 {
1001 WORKREQUEST workRequest;
1002 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
1003
1004 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
1005 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1006 req = &workRequest.u.FtpGetCurrentDirectoryW;
1007 req->lpszDirectory = lpszCurrentDirectory;
1008 req->lpdwDirectory = lpdwCurrentDirectory;
1009
1010 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1011 }
1012 else
1013 {
1014 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
1015 lpdwCurrentDirectory);
1016 }
1017
1018 lend:
1019 if( lpwfs )
1020 WININET_Release( &lpwfs->hdr );
1021
1022 return r;
1023 }
1024
1025
1026 /***********************************************************************
1027 * FTP_FtpGetCurrentDirectoryW (Internal)
1028 *
1029 * Retrieves the current directory
1030 *
1031 * RETURNS
1032 * TRUE on success
1033 * FALSE on failure
1034 *
1035 */
1036 static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
1037 LPDWORD lpdwCurrentDirectory)
1038 {
1039 INT nResCode;
1040 appinfo_t *hIC = NULL;
1041 DWORD bSuccess = FALSE;
1042
1043 /* Clear any error information */
1044 INTERNET_SetLastError(0);
1045
1046 hIC = lpwfs->lpAppInfo;
1047 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1048 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1049 goto lend;
1050
1051 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1052 if (nResCode)
1053 {
1054 if (nResCode == 257) /* Extract directory name */
1055 {
1056 DWORD firstpos, lastpos, len;
1057 LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
1058
1059 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1060 {
1061 if ('"' == lpszResponseBuffer[lastpos])
1062 {
1063 if (!firstpos)
1064 firstpos = lastpos;
1065 else
1066 break;
1067 }
1068 }
1069 len = lastpos - firstpos;
1070 if (*lpdwCurrentDirectory >= len)
1071 {
1072 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1073 lpszCurrentDirectory[len - 1] = 0;
1074 *lpdwCurrentDirectory = len;
1075 bSuccess = TRUE;
1076 }
1077 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1078
1079 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1080 }
1081 else
1082 FTP_SetResponseError(nResCode);
1083 }
1084
1085 lend:
1086 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1087 {
1088 INTERNET_ASYNC_RESULT iar;
1089
1090 iar.dwResult = bSuccess;
1091 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1092 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1093 &iar, sizeof(INTERNET_ASYNC_RESULT));
1094 }
1095
1096 return bSuccess;
1097 }
1098
1099
1100 /***********************************************************************
1101 * FTPFILE_Destroy(internal)
1102 *
1103 * Closes the file transfer handle. This also 'cleans' the data queue of
1104 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1105 *
1106 */
1107 static void FTPFILE_Destroy(object_header_t *hdr)
1108 {
1109 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1110 ftp_session_t *lpwfs = lpwh->lpFtpSession;
1111 INT nResCode;
1112
1113 TRACE("\n");
1114
1115 if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
1116 CloseHandle(lpwh->cache_file_handle);
1117
1118 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1119
1120 if (!lpwh->session_deleted)
1121 lpwfs->download_in_progress = NULL;
1122
1123 if (lpwh->nDataSocket != -1)
1124 closesocket(lpwh->nDataSocket);
1125
1126 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1127 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1128
1129 WININET_Release(&lpwh->lpFtpSession->hdr);
1130
1131 HeapFree(GetProcessHeap(), 0, lpwh);
1132 }
1133
1134 static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1135 {
1136 switch(option) {
1137 case INTERNET_OPTION_HANDLE_TYPE:
1138 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
1139
1140 if (*size < sizeof(ULONG))
1141 return ERROR_INSUFFICIENT_BUFFER;
1142
1143 *size = sizeof(DWORD);
1144 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
1145 return ERROR_SUCCESS;
1146 case INTERNET_OPTION_DATAFILE_NAME:
1147 {
1148 DWORD required;
1149 ftp_file_t *file = (ftp_file_t *)hdr;
1150
1151 TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
1152
1153 if (!file->cache_file)
1154 {
1155 *size = 0;
1156 return ERROR_INTERNET_ITEM_NOT_FOUND;
1157 }
1158 if (unicode)
1159 {
1160 required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
1161 if (*size < required)
1162 return ERROR_INSUFFICIENT_BUFFER;
1163
1164 *size = required;
1165 memcpy(buffer, file->cache_file, *size);
1166 return ERROR_SUCCESS;
1167 }
1168 else
1169 {
1170 required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
1171 if (required > *size)
1172 return ERROR_INSUFFICIENT_BUFFER;
1173
1174 *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
1175 return ERROR_SUCCESS;
1176 }
1177 }
1178 }
1179 return INET_QueryOption(option, buffer, size, unicode);
1180 }
1181
1182 static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
1183 {
1184 ftp_file_t *file = (ftp_file_t*)hdr;
1185 int res;
1186 DWORD error;
1187
1188 if (file->nDataSocket == -1)
1189 return ERROR_INTERNET_DISCONNECTED;
1190
1191 /* FIXME: FTP should use NETCON_ stuff */
1192 res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
1193 *read = res>0 ? res : 0;
1194
1195 error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
1196 if (error == ERROR_SUCCESS && file->cache_file)
1197 {
1198 DWORD bytes_written;
1199
1200 if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
1201 WARN("WriteFile failed: %u\n", GetLastError());
1202 }
1203 return error;
1204 }
1205
1206 static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
1207 DWORD flags, DWORD_PTR context)
1208 {
1209 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1210 }
1211
1212 static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
1213 DWORD flags, DWORD_PTR context)
1214 {
1215 return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
1216 }
1217
1218 static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
1219 {
1220 ftp_file_t *lpwh = (ftp_file_t*) hdr;
1221 int res;
1222
1223 res = send(lpwh->nDataSocket, buffer, size, 0);
1224
1225 *written = res>0 ? res : 0;
1226 return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
1227 }
1228
1229 static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
1230 {
1231 INTERNET_ASYNC_RESULT iar;
1232 BYTE buffer[4096];
1233 int available;
1234
1235 TRACE("%p\n", file);
1236
1237 available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
1238
1239 if(available != -1) {
1240 iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
1241 iar.dwError = first_notif ? 0 : available;
1242 }else {
1243 iar.dwResult = 0;
1244 iar.dwError = INTERNET_GetLastError();
1245 }
1246
1247 INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1248 sizeof(INTERNET_ASYNC_RESULT));
1249 }
1250
1251 static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
1252 {
1253 ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
1254
1255 FTP_ReceiveRequestData(file, FALSE);
1256 }
1257
1258 static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
1259 {
1260 ftp_file_t *file = (ftp_file_t*) hdr;
1261 int retval, unread = 0;
1262
1263 TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
1264
1265 #ifdef FIONREAD
1266 retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
1267 if (!retval)
1268 TRACE("%d bytes of queued, but unread data\n", unread);
1269 #else
1270 FIXME("FIONREAD not available\n");
1271 #endif
1272
1273 *available = unread;
1274
1275 if(!unread) {
1276 BYTE byte;
1277
1278 *available = 0;
1279
1280 retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
1281 if(retval > 0) {
1282 WORKREQUEST workRequest;
1283
1284 *available = 0;
1285 workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
1286 workRequest.hdr = WININET_AddRef( &file->hdr );
1287
1288 INTERNET_AsyncCall(&workRequest);
1289
1290 return ERROR_IO_PENDING;
1291 }
1292 }
1293
1294 return ERROR_SUCCESS;
1295 }
1296
1297
1298 static const object_vtbl_t FTPFILEVtbl = {
1299 FTPFILE_Destroy,
1300 NULL,
1301 FTPFILE_QueryOption,
1302 NULL,
1303 FTPFILE_ReadFile,
1304 FTPFILE_ReadFileExA,
1305 FTPFILE_ReadFileExW,
1306 FTPFILE_WriteFile,
1307 FTPFILE_QueryDataAvailable,
1308 NULL
1309 };
1310
1311 /***********************************************************************
1312 * FTP_FtpOpenFileW (Internal)
1313 *
1314 * Open a remote file for writing or reading
1315 *
1316 * RETURNS
1317 * HINTERNET handle on success
1318 * NULL on failure
1319 *
1320 */
1321 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1322 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1323 DWORD_PTR dwContext)
1324 {
1325 INT nDataSocket;
1326 BOOL bSuccess = FALSE;
1327 ftp_file_t *lpwh = NULL;
1328 appinfo_t *hIC = NULL;
1329 HINTERNET handle = NULL;
1330
1331 TRACE("\n");
1332
1333 /* Clear any error information */
1334 INTERNET_SetLastError(0);
1335
1336 if (GENERIC_READ == fdwAccess)
1337 {
1338 /* Set up socket to retrieve data */
1339 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1340 }
1341 else if (GENERIC_WRITE == fdwAccess)
1342 {
1343 /* Set up socket to send data */
1344 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1345 }
1346
1347 /* Get data socket to server */
1348 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1349 {
1350 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
1351 lpwh->hdr.htype = WH_HFILE;
1352 lpwh->hdr.vtbl = &FTPFILEVtbl;
1353 lpwh->hdr.dwFlags = dwFlags;
1354 lpwh->hdr.dwContext = dwContext;
1355 lpwh->hdr.dwInternalFlags = 0;
1356 lpwh->hdr.refs = 1;
1357 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1358 lpwh->nDataSocket = nDataSocket;
1359 lpwh->cache_file = NULL;
1360 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1361 lpwh->session_deleted = FALSE;
1362
1363 WININET_AddRef( &lpwfs->hdr );
1364 lpwh->lpFtpSession = lpwfs;
1365 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1366
1367 handle = WININET_AllocHandle( &lpwh->hdr );
1368 if( !handle )
1369 goto lend;
1370
1371 /* Indicate that a download is currently in progress */
1372 lpwfs->download_in_progress = lpwh;
1373 }
1374
1375 if (lpwfs->lstnSocket != -1)
1376 {
1377 closesocket(lpwfs->lstnSocket);
1378 lpwfs->lstnSocket = -1;
1379 }
1380
1381 if (bSuccess && fdwAccess == GENERIC_READ)
1382 {
1383 WCHAR filename[MAX_PATH + 1];
1384 URL_COMPONENTSW uc;
1385 DWORD len;
1386
1387 memset(&uc, 0, sizeof(uc));
1388 uc.dwStructSize = sizeof(uc);
1389 uc.nScheme = INTERNET_SCHEME_FTP;
1390 uc.lpszHostName = lpwfs->servername;
1391 uc.nPort = lpwfs->serverport;
1392 uc.lpszUserName = lpwfs->lpszUserName;
1393 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1394
1395 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1396 {
1397 WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1398
1399 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1400 {
1401 lpwh->cache_file = heap_strdupW(filename);
1402 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1403 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1404 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1405 {
1406 WARN("Could not create cache file: %u\n", GetLastError());
1407 HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
1408 lpwh->cache_file = NULL;
1409 }
1410 }
1411 HeapFree(GetProcessHeap(), 0, url);
1412 }
1413 HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
1414 }
1415
1416 hIC = lpwfs->lpAppInfo;
1417 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1418 {
1419 INTERNET_ASYNC_RESULT iar;
1420
1421 if (lpwh)
1422 {
1423 iar.dwResult = (DWORD_PTR)handle;
1424 iar.dwError = ERROR_SUCCESS;
1425 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1426 &iar, sizeof(INTERNET_ASYNC_RESULT));
1427 }
1428
1429 if(bSuccess) {
1430 FTP_ReceiveRequestData(lpwh, TRUE);
1431 }else {
1432 iar.dwResult = 0;
1433 iar.dwError = INTERNET_GetLastError();
1434 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1435 &iar, sizeof(INTERNET_ASYNC_RESULT));
1436 }
1437 }
1438
1439 lend:
1440 if( lpwh )
1441 WININET_Release( &lpwh->hdr );
1442
1443 return handle;
1444 }
1445
1446
1447 /***********************************************************************
1448 * FtpOpenFileA (WININET.@)
1449 *
1450 * Open a remote file for writing or reading
1451 *
1452 * RETURNS
1453 * HINTERNET handle on success
1454 * NULL on failure
1455 *
1456 */
1457 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1458 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1459 DWORD_PTR dwContext)
1460 {
1461 LPWSTR lpwzFileName;
1462 HINTERNET ret;
1463
1464 lpwzFileName = heap_strdupAtoW(lpszFileName);
1465 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1466 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1467 return ret;
1468 }
1469
1470
1471 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
1472 {
1473 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
1474 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1475
1476 TRACE("%p\n", lpwfs);
1477
1478 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1479 req->dwAccess, req->dwFlags, req->dwContext);
1480 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1481 }
1482
1483 /***********************************************************************
1484 * FtpOpenFileW (WININET.@)
1485 *
1486 * Open a remote file for writing or reading
1487 *
1488 * RETURNS
1489 * HINTERNET handle on success
1490 * NULL on failure
1491 *
1492 */
1493 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1494 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1495 DWORD_PTR dwContext)
1496 {
1497 ftp_session_t *lpwfs;
1498 appinfo_t *hIC = NULL;
1499 HINTERNET r = NULL;
1500
1501 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1502 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1503
1504 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1505 if (!lpwfs)
1506 {
1507 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1508 return FALSE;
1509 }
1510
1511 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1512 {
1513 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1514 goto lend;
1515 }
1516
1517 if ((!lpszFileName) ||
1518 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1519 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1520 {
1521 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1522 goto lend;
1523 }
1524
1525 if (lpwfs->download_in_progress != NULL)
1526 {
1527 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1528 goto lend;
1529 }
1530
1531 hIC = lpwfs->lpAppInfo;
1532 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1533 {
1534 WORKREQUEST workRequest;
1535 struct WORKREQ_FTPOPENFILEW *req;
1536
1537 workRequest.asyncproc = AsyncFtpOpenFileProc;
1538 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1539 req = &workRequest.u.FtpOpenFileW;
1540 req->lpszFilename = heap_strdupW(lpszFileName);
1541 req->dwAccess = fdwAccess;
1542 req->dwFlags = dwFlags;
1543 req->dwContext = dwContext;
1544
1545 INTERNET_AsyncCall(&workRequest);
1546 r = NULL;
1547 }
1548 else
1549 {
1550 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1551 }
1552
1553 lend:
1554 WININET_Release( &lpwfs->hdr );
1555
1556 return r;
1557 }
1558
1559
1560 /***********************************************************************
1561 * FtpGetFileA (WININET.@)
1562 *
1563 * Retrieve file from the FTP server
1564 *
1565 * RETURNS
1566 * TRUE on success
1567 * FALSE on failure
1568 *
1569 */
1570 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1571 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1572 DWORD_PTR dwContext)
1573 {
1574 LPWSTR lpwzRemoteFile;
1575 LPWSTR lpwzNewFile;
1576 BOOL ret;
1577
1578 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1579 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1580 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1581 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1582 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1583 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1584 return ret;
1585 }
1586
1587
1588 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1589 {
1590 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1591 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1592
1593 TRACE("%p\n", lpwfs);
1594
1595 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1596 req->lpszNewFile, req->fFailIfExists,
1597 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1598 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1599 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1600 }
1601
1602
1603 /***********************************************************************
1604 * FtpGetFileW (WININET.@)
1605 *
1606 * Retrieve file from the FTP server
1607 *
1608 * RETURNS
1609 * TRUE on success
1610 * FALSE on failure
1611 *
1612 */
1613 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1614 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1615 DWORD_PTR dwContext)
1616 {
1617 ftp_session_t *lpwfs;
1618 appinfo_t *hIC = NULL;
1619 BOOL r = FALSE;
1620
1621 if (!lpszRemoteFile || !lpszNewFile)
1622 {
1623 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1624 return FALSE;
1625 }
1626
1627 lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
1628 if (!lpwfs)
1629 {
1630 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1631 return FALSE;
1632 }
1633
1634 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1635 {
1636 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1637 goto lend;
1638 }
1639
1640 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1641 {
1642 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1643 goto lend;
1644 }
1645
1646 if (lpwfs->download_in_progress != NULL)
1647 {
1648 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1649 goto lend;
1650 }
1651
1652 hIC = lpwfs->lpAppInfo;
1653 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1654 {
1655 WORKREQUEST workRequest;
1656 struct WORKREQ_FTPGETFILEW *req;
1657
1658 workRequest.asyncproc = AsyncFtpGetFileProc;
1659 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1660 req = &workRequest.u.FtpGetFileW;
1661 req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
1662 req->lpszNewFile = heap_strdupW(lpszNewFile);
1663 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1664 req->fFailIfExists = fFailIfExists;
1665 req->dwFlags = dwInternetFlags;
1666 req->dwContext = dwContext;
1667
1668 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1669 }
1670 else
1671 {
1672 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1673 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1674 }
1675
1676 lend:
1677 WININET_Release( &lpwfs->hdr );
1678
1679 return r;
1680 }
1681
1682
1683 /***********************************************************************
1684 * FTP_FtpGetFileW (Internal)
1685 *
1686 * Retrieve file from the FTP server
1687 *
1688 * RETURNS
1689 * TRUE on success
1690 * FALSE on failure
1691 *
1692 */
1693 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1694 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1695 DWORD_PTR dwContext)
1696 {
1697 BOOL bSuccess = FALSE;
1698 HANDLE hFile;
1699 appinfo_t *hIC = NULL;
1700
1701 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1702
1703 /* Clear any error information */
1704 INTERNET_SetLastError(0);
1705
1706 /* Ensure we can write to lpszNewfile by opening it */
1707 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1708 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1709 if (INVALID_HANDLE_VALUE == hFile)
1710 return FALSE;
1711
1712 /* Set up socket to retrieve data */
1713 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1714 {
1715 INT nDataSocket;
1716
1717 /* Get data socket to server */
1718 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1719 {
1720 INT nResCode;
1721
1722 /* Receive data */
1723 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1724 closesocket(nDataSocket);
1725
1726 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1727 if (nResCode)
1728 {
1729 if (nResCode == 226)
1730 bSuccess = TRUE;
1731 else
1732 FTP_SetResponseError(nResCode);
1733 }
1734 }
1735 }
1736
1737 if (lpwfs->lstnSocket != -1)
1738 {
1739 closesocket(lpwfs->lstnSocket);
1740 lpwfs->lstnSocket = -1;
1741 }
1742
1743 CloseHandle(hFile);
1744
1745 hIC = lpwfs->lpAppInfo;
1746 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1747 {
1748 INTERNET_ASYNC_RESULT iar;
1749
1750 iar.dwResult = (DWORD)bSuccess;
1751 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1752 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1753 &iar, sizeof(INTERNET_ASYNC_RESULT));
1754 }
1755
1756 if (!bSuccess) DeleteFileW(lpszNewFile);
1757 return bSuccess;
1758 }
1759
1760 /***********************************************************************
1761 * FtpGetFileSize (WININET.@)
1762 */
1763 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1764 {
1765 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1766
1767 if (lpdwFileSizeHigh)
1768 *lpdwFileSizeHigh = 0;
1769
1770 return 0;
1771 }
1772
1773 /***********************************************************************
1774 * FtpDeleteFileA (WININET.@)
1775 *
1776 * Delete a file on the ftp server
1777 *
1778 * RETURNS
1779 * TRUE on success
1780 * FALSE on failure
1781 *
1782 */
1783 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1784 {
1785 LPWSTR lpwzFileName;
1786 BOOL ret;
1787
1788 lpwzFileName = heap_strdupAtoW(lpszFileName);
1789 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1790 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1791 return ret;
1792 }
1793
1794 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1795 {
1796 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1797 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1798
1799 TRACE("%p\n", lpwfs);
1800
1801 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1802 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1803 }
1804
1805 /***********************************************************************
1806 * FtpDeleteFileW (WININET.@)
1807 *
1808 * Delete a file on the ftp server
1809 *
1810 * RETURNS
1811 * TRUE on success
1812 * FALSE on failure
1813 *
1814 */
1815 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1816 {
1817 ftp_session_t *lpwfs;
1818 appinfo_t *hIC = NULL;
1819 BOOL r = FALSE;
1820
1821 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1822 if (!lpwfs)
1823 {
1824 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1825 return FALSE;
1826 }
1827
1828 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1829 {
1830 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1831 goto lend;
1832 }
1833
1834 if (lpwfs->download_in_progress != NULL)
1835 {
1836 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1837 goto lend;
1838 }
1839
1840 if (!lpszFileName)
1841 {
1842 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1843 goto lend;
1844 }
1845
1846 hIC = lpwfs->lpAppInfo;
1847 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1848 {
1849 WORKREQUEST workRequest;
1850 struct WORKREQ_FTPDELETEFILEW *req;
1851
1852 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1853 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1854 req = &workRequest.u.FtpDeleteFileW;
1855 req->lpszFilename = heap_strdupW(lpszFileName);
1856
1857 r = res_to_le(INTERNET_AsyncCall(&workRequest));
1858 }
1859 else
1860 {
1861 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1862 }
1863
1864 lend:
1865 WININET_Release( &lpwfs->hdr );
1866
1867 return r;
1868 }
1869
1870 /***********************************************************************
1871 * FTP_FtpDeleteFileW (Internal)
1872 *
1873 * Delete a file on the ftp server
1874 *
1875 * RETURNS
1876 * TRUE on success
1877 * FALSE on failure
1878 *
1879 */
1880 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1881 {
1882 INT nResCode;
1883 BOOL bSuccess = FALSE;
1884 appinfo_t *hIC = NULL;
1885
1886 TRACE("%p\n", lpwfs);
1887
1888 /* Clear any error information */
1889 INTERNET_SetLastError(0);
1890
1891 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1892 goto lend;
1893
1894 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1895 if (nResCode)
1896 {
1897 if (nResCode == 250)
1898 bSuccess = TRUE;
1899 else
1900 FTP_SetResponseError(nResCode);
1901 }
1902 lend:
1903 hIC = lpwfs->lpAppInfo;
1904 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1905 {
1906 INTERNET_ASYNC_RESULT iar;
1907
1908 iar.dwResult = (DWORD)bSuccess;
1909 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1910 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1911 &iar, sizeof(INTERNET_ASYNC_RESULT));
1912 }
1913
1914 return bSuccess;
1915 }
1916
1917
1918 /***********************************************************************
1919 * FtpRemoveDirectoryA (WININET.@)
1920 *
1921 * Remove a directory on the ftp server
1922 *
1923 * RETURNS
1924 * TRUE on success
1925 * FALSE on failure
1926 *
1927 */
1928 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1929 {
1930 LPWSTR lpwzDirectory;
1931 BOOL ret;
1932
1933 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1934 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1935 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1936 return ret;
1937 }
1938
1939 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1940 {
1941 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1942 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
1943
1944 TRACE("%p\n", lpwfs);
1945
1946 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1947 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1948 }
1949
1950 /***********************************************************************
1951 * FtpRemoveDirectoryW (WININET.@)
1952 *
1953 * Remove a directory on the ftp server
1954 *
1955 * RETURNS
1956 * TRUE on success
1957 * FALSE on failure
1958 *
1959 */
1960 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1961 {
1962 ftp_session_t *lpwfs;
1963 appinfo_t *hIC = NULL;
1964 BOOL r = FALSE;
1965
1966 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
1967 if (!lpwfs)
1968 {
1969 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1970 return FALSE;
1971 }
1972
1973 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1974 {
1975 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1976 goto lend;
1977 }
1978
1979 if (lpwfs->download_in_progress != NULL)
1980 {
1981 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1982 goto lend;
1983 }
1984
1985 if (!lpszDirectory)
1986 {
1987 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1988 goto lend;
1989 }
1990
1991 hIC = lpwfs->lpAppInfo;
1992 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1993 {
1994 WORKREQUEST workRequest;
1995 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1996
1997 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1998 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1999 req = &workRequest.u.FtpRemoveDirectoryW;
2000 req->lpszDirectory = heap_strdupW(lpszDirectory);
2001
2002 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2003 }
2004 else
2005 {
2006 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
2007 }
2008
2009 lend:
2010 WININET_Release( &lpwfs->hdr );
2011
2012 return r;
2013 }
2014
2015 /***********************************************************************
2016 * FTP_FtpRemoveDirectoryW (Internal)
2017 *
2018 * Remove a directory on the ftp server
2019 *
2020 * RETURNS
2021 * TRUE on success
2022 * FALSE on failure
2023 *
2024 */
2025 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
2026 {
2027 INT nResCode;
2028 BOOL bSuccess = FALSE;
2029 appinfo_t *hIC = NULL;
2030
2031 TRACE("\n");
2032
2033 /* Clear any error information */
2034 INTERNET_SetLastError(0);
2035
2036 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
2037 goto lend;
2038
2039 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2040 if (nResCode)
2041 {
2042 if (nResCode == 250)
2043 bSuccess = TRUE;
2044 else
2045 FTP_SetResponseError(nResCode);
2046 }
2047
2048 lend:
2049 hIC = lpwfs->lpAppInfo;
2050 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2051 {
2052 INTERNET_ASYNC_RESULT iar;
2053
2054 iar.dwResult = (DWORD)bSuccess;
2055 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2056 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2057 &iar, sizeof(INTERNET_ASYNC_RESULT));
2058 }
2059
2060 return bSuccess;
2061 }
2062
2063
2064 /***********************************************************************
2065 * FtpRenameFileA (WININET.@)
2066 *
2067 * Rename a file on the ftp server
2068 *
2069 * RETURNS
2070 * TRUE on success
2071 * FALSE on failure
2072 *
2073 */
2074 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2075 {
2076 LPWSTR lpwzSrc;
2077 LPWSTR lpwzDest;
2078 BOOL ret;
2079
2080 lpwzSrc = heap_strdupAtoW(lpszSrc);
2081 lpwzDest = heap_strdupAtoW(lpszDest);
2082 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2083 HeapFree(GetProcessHeap(), 0, lpwzSrc);
2084 HeapFree(GetProcessHeap(), 0, lpwzDest);
2085 return ret;
2086 }
2087
2088 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
2089 {
2090 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
2091 ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
2092
2093 TRACE("%p\n", lpwfs);
2094
2095 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
2096 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2097 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2098 }
2099
2100 /***********************************************************************
2101 * FtpRenameFileW (WININET.@)
2102 *
2103 * Rename a file on the ftp server
2104 *
2105 * RETURNS
2106 * TRUE on success
2107 * FALSE on failure
2108 *
2109 */
2110 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2111 {
2112 ftp_session_t *lpwfs;
2113 appinfo_t *hIC = NULL;
2114 BOOL r = FALSE;
2115
2116 lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
2117 if (!lpwfs)
2118 {
2119 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2120 return FALSE;
2121 }
2122
2123 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2124 {
2125 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2126 goto lend;
2127 }
2128
2129 if (lpwfs->download_in_progress != NULL)
2130 {
2131 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2132 goto lend;
2133 }
2134
2135 if (!lpszSrc || !lpszDest)
2136 {
2137 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2138 goto lend;
2139 }
2140
2141 hIC = lpwfs->lpAppInfo;
2142 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2143 {
2144 WORKREQUEST workRequest;
2145 struct WORKREQ_FTPRENAMEFILEW *req;
2146
2147 workRequest.asyncproc = AsyncFtpRenameFileProc;
2148 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
2149 req = &workRequest.u.FtpRenameFileW;
2150 req->lpszSrcFile = heap_strdupW(lpszSrc);
2151 req->lpszDestFile = heap_strdupW(lpszDest);
2152
2153 r = res_to_le(INTERNET_AsyncCall(&workRequest));
2154 }
2155 else
2156 {
2157 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2158 }
2159
2160 lend:
2161 WININET_Release( &lpwfs->hdr );
2162
2163 return r;
2164 }
2165
2166 /***********************************************************************
2167 * FTP_FtpRenameFileW (Internal)
2168 *
2169 * Rename a file on the ftp server
2170 *
2171 * RETURNS
2172 * TRUE on success
2173 * FALSE on failure
2174 *
2175 */
2176 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2177 {
2178 INT nResCode;
2179 BOOL bSuccess = FALSE;
2180 appinfo_t *hIC = NULL;
2181
2182 TRACE("\n");
2183
2184 /* Clear any error information */
2185 INTERNET_SetLastError(0);
2186
2187 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2188 goto lend;
2189
2190 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2191 if (nResCode == 350)
2192 {
2193 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2194 goto lend;
2195
2196 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2197 }
2198
2199 if (nResCode == 250)
2200 bSuccess = TRUE;
2201 else
2202 FTP_SetResponseError(nResCode);
2203
2204 lend:
2205 hIC = lpwfs->lpAppInfo;
2206 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2207 {
2208 INTERNET_ASYNC_RESULT iar;
2209
2210 iar.dwResult = (DWORD)bSuccess;
2211 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2212 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2213 &iar, sizeof(INTERNET_ASYNC_RESULT));
2214 }
2215
2216 return bSuccess;
2217 }
2218
2219 /***********************************************************************
2220 * FtpCommandA (WININET.@)
2221 */
2222 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2223 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2224 {
2225 BOOL r;
2226 WCHAR *cmdW;
2227
2228 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2229 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2230
2231 if (fExpectResponse)
2232 {
2233 FIXME("data connection not supported\n");
2234 return FALSE;
2235 }
2236
2237 if (!lpszCommand || !lpszCommand[0])
2238 {
2239 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2240 return FALSE;
2241 }
2242
2243 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2244 {
2245 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2246 return FALSE;
2247 }
2248
2249 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2250
2251 HeapFree(GetProcessHeap(), 0, cmdW);
2252 return r;
2253 }
2254
2255 /***********************************************************************
2256 * FtpCommandW (WININET.@)
2257 */
2258 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2259 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2260 {
2261 BOOL r = FALSE;
2262 ftp_session_t *lpwfs;
2263 LPSTR cmd = NULL;
2264 DWORD len, nBytesSent= 0;
2265 INT nResCode, nRC = 0;
2266
2267 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2268 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2269
2270 if (!lpszCommand || !lpszCommand[0])
2271 {
2272 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2273 return FALSE;
2274 }
2275
2276 if (fExpectResponse)
2277 {
2278 FIXME("data connection not supported\n");
2279 return FALSE;
2280 }
2281
2282 lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
2283 if (!lpwfs)
2284 {
2285 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2286 return FALSE;
2287 }
2288
2289 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2290 {
2291 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2292 goto lend;
2293 }
2294
2295 if (lpwfs->download_in_progress != NULL)
2296 {
2297 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2298 goto lend;
2299 }
2300
2301 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2302 if ((cmd = HeapAlloc(GetProcessHeap(), 0, len )))
2303 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2304 else
2305 {
2306 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2307 goto lend;
2308 }
2309
2310 strcat(cmd, szCRLF);
2311 len--;
2312
2313 TRACE("Sending (%s) len(%d)\n", cmd, len);
2314 while ((nBytesSent < len) && (nRC != -1))
2315 {
2316 nRC = send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2317 if (nRC != -1)
2318 {
2319 nBytesSent += nRC;
2320 TRACE("Sent %d bytes\n", nRC);
2321 }
2322 }
2323
2324 if (nBytesSent)
2325 {
2326 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2327 if (nResCode > 0 && nResCode < 400)
2328 r = TRUE;
2329 else
2330 FTP_SetResponseError(nResCode);
2331 }
2332
2333 lend:
2334 WININET_Release( &lpwfs->hdr );
2335 HeapFree(GetProcessHeap(), 0, cmd);
2336 return r;
2337 }
2338
2339
2340 /***********************************************************************
2341 * FTPSESSION_Destroy (internal)
2342 *
2343 * Deallocate session handle
2344 */
2345 static void FTPSESSION_Destroy(object_header_t *hdr)
2346 {
2347 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2348
2349 TRACE("\n");
2350
2351 WININET_Release(&lpwfs->lpAppInfo->hdr);
2352
2353 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2354 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2355 HeapFree(GetProcessHeap(), 0, lpwfs->servername);
2356 HeapFree(GetProcessHeap(), 0, lpwfs);
2357 }
2358
2359 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2360 {
2361 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2362
2363 TRACE("\n");
2364
2365 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2366 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2367
2368 if (lpwfs->download_in_progress != NULL)
2369 lpwfs->download_in_progress->session_deleted = TRUE;
2370
2371 if (lpwfs->sndSocket != -1)
2372 closesocket(lpwfs->sndSocket);
2373
2374 if (lpwfs->lstnSocket != -1)
2375 closesocket(lpwfs->lstnSocket);
2376
2377 if (lpwfs->pasvSocket != -1)
2378 closesocket(lpwfs->pasvSocket);
2379
2380 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2381 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2382 }
2383
2384 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2385 {
2386 switch(option) {
2387 case INTERNET_OPTION_HANDLE_TYPE:
2388 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2389
2390 if (*size < sizeof(ULONG))
2391 return ERROR_INSUFFICIENT_BUFFER;
2392
2393 *size = sizeof(DWORD);
2394 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2395 return ERROR_SUCCESS;
2396 }
2397
2398 return INET_QueryOption(option, buffer, size, unicode);
2399 }
2400
2401 static const object_vtbl_t FTPSESSIONVtbl = {
2402 FTPSESSION_Destroy,
2403 FTPSESSION_CloseConnection,
2404 FTPSESSION_QueryOption,
2405 NULL,
2406 NULL,
2407 NULL,
2408 NULL,
2409 NULL,
2410 NULL
2411 };
2412
2413
2414 /***********************************************************************
2415 * FTP_Connect (internal)
2416 *
2417 * Connect to a ftp server
2418 *
2419 * RETURNS
2420 * HINTERNET a session handle on success
2421 * NULL on failure
2422 *
2423 * NOTES:
2424 *
2425 * Windows uses 'anonymous' as the username, when given a NULL username
2426 * and a NULL password. The password is first looked up in:
2427 *
2428 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2429 *
2430 * If this entry is not present it uses the current username as the password.
2431 *
2432 */
2433
2434 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2435 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2436 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2437 DWORD dwInternalFlags)
2438 {
2439 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2440 'M','i','c','r','o','s','o','f','t','\\',
2441 'W','i','n','d','o','w','s','\\',
2442 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2443 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2444 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2445 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2446 static const WCHAR szEmpty[] = {'\0'};
2447 struct sockaddr_in socketAddr;
2448 INT nsocket = -1;
2449 UINT sock_namelen;
2450 BOOL bSuccess = FALSE;
2451 ftp_session_t *lpwfs = NULL;
2452 HINTERNET handle = NULL;
2453 char szaddr[INET_ADDRSTRLEN];
2454
2455 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2456 hIC, debugstr_w(lpszServerName),
2457 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2458
2459 assert( hIC->hdr.htype == WH_HINIT );
2460
2461 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2462 {
2463 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2464 goto lerror;
2465 }
2466
2467 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
2468 if (NULL == lpwfs)
2469 {
2470 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2471 goto lerror;
2472 }
2473
2474 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2475 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2476 else
2477 lpwfs->serverport = nServerPort;
2478
2479 lpwfs->hdr.htype = WH_HFTPSESSION;
2480 lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
2481 lpwfs->hdr.dwFlags = dwFlags;
2482 lpwfs->hdr.dwContext = dwContext;
2483 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
2484 lpwfs->hdr.refs = 1;
2485 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
2486 lpwfs->download_in_progress = NULL;
2487 lpwfs->sndSocket = -1;
2488 lpwfs->lstnSocket = -1;
2489 lpwfs->pasvSocket = -1;
2490
2491 WININET_AddRef( &hIC->hdr );
2492 lpwfs->lpAppInfo = hIC;
2493 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2494
2495 handle = WININET_AllocHandle( &lpwfs->hdr );
2496 if( !handle )
2497 {
2498 ERR("Failed to alloc handle\n");
2499 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2500 goto lerror;
2501 }
2502
2503 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
2504 if(strchrW(hIC->lpszProxy, ' '))
2505 FIXME("Several proxies not implemented.\n");
2506 if(hIC->lpszProxyBypass)
2507 FIXME("Proxy bypass is ignored.\n");
2508 }
2509 if (!lpszUserName || !strlenW(lpszUserName)) {
2510 HKEY key;
2511 WCHAR szPassword[MAX_PATH];
2512 DWORD len = sizeof(szPassword);
2513
2514 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2515
2516 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2517 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2518 /* Nothing in the registry, get the username and use that as the password */
2519 if (!GetUserNameW(szPassword, &len)) {
2520 /* Should never get here, but use an empty password as failsafe */
2521 strcpyW(szPassword, szEmpty);
2522 }
2523 }
2524 RegCloseKey(key);
2525
2526 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2527 lpwfs->lpszPassword = heap_strdupW(szPassword);
2528 }
2529 else {
2530 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2531 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2532 }
2533 lpwfs->servername = heap_strdupW(lpszServerName);
2534
2535 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2536 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2537 {
2538 INTERNET_ASYNC_RESULT iar;
2539
2540 iar.dwResult = (DWORD_PTR)handle;
2541 iar.dwError = ERROR_SUCCESS;
2542
2543 SendAsyncCallback(&hIC->hdr, dwContext,
2544 INTERNET_STATUS_HANDLE_CREATED, &iar,
2545 sizeof(INTERNET_ASYNC_RESULT));
2546 }
2547
2548 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2549 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2550
2551 sock_namelen = sizeof(socketAddr);
2552 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2553 {
2554 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2555 goto lerror;
2556 }
2557
2558 if (socketAddr.sin_family != AF_INET)
2559 {
2560 WARN("unsupported address family %d\n", socketAddr.sin_family);
2561 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2562 goto lerror;
2563 }
2564
2565 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2566 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2567 szaddr, strlen(szaddr)+1);
2568
2569 nsocket = socket(AF_INET,SOCK_STREAM,0);
2570 if (nsocket == -1)
2571 {
2572 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2573 goto lerror;
2574 }
2575
2576 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2577 szaddr, strlen(szaddr)+1);
2578
2579 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2580 {
2581 ERR("Unable to connect (%s)\n", strerror(errno));
2582 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2583 closesocket(nsocket);
2584 }
2585 else
2586 {
2587 TRACE("Connected to server\n");
2588 lpwfs->sndSocket = nsocket;
2589 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2590 szaddr, strlen(szaddr)+1);
2591
2592 sock_namelen = sizeof(lpwfs->socketAddress);
2593 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2594
2595 if (FTP_ConnectToHost(lpwfs))
2596 {
2597 TRACE("Successfully logged into server\n");
2598 bSuccess = TRUE;
2599 }
2600 }
2601
2602 lerror:
2603 if (lpwfs) WININET_Release( &lpwfs->hdr );
2604
2605 if (!bSuccess && handle)
2606 {
2607 WININET_FreeHandle( handle );
2608 handle = NULL;
2609 }
2610
2611 return handle;
2612 }
2613
2614
2615 /***********************************************************************
2616 * FTP_ConnectToHost (internal)
2617 *
2618 * Connect to a ftp server
2619 *
2620 * RETURNS
2621 * TRUE on success
2622 * NULL on failure
2623 *
2624 */
2625 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2626 {
2627 INT nResCode;
2628 BOOL bSuccess = FALSE;
2629
2630 TRACE("\n");
2631 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2632
2633 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2634 goto lend;
2635
2636 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2637 if (nResCode)
2638 {
2639 /* Login successful... */
2640 if (nResCode == 230)
2641 bSuccess = TRUE;
2642 /* User name okay, need password... */
2643 else if (nResCode == 331)
2644 bSuccess = FTP_SendPassword(lpwfs);
2645 /* Need account for login... */
2646 else if (nResCode == 332)
2647 bSuccess = FTP_SendAccount(lpwfs);
2648 else
2649 FTP_SetResponseError(nResCode);
2650 }
2651
2652 TRACE("Returning %d\n", bSuccess);
2653 lend:
2654 return bSuccess;
2655 }
2656
2657
2658 /***********************************************************************
2659 * FTP_SendCommandA (internal)
2660 *
2661 * Send command to server
2662 *
2663 * RETURNS
2664 * TRUE on success
2665 * NULL on failure
2666 *
2667 */
2668 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2669 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2670 {
2671 DWORD len;
2672 CHAR *buf;
2673 DWORD nBytesSent = 0;
2674 int nRC = 0;
2675 DWORD dwParamLen;
2676
2677 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2678
2679 if (lpfnStatusCB)
2680 {
2681 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2682 }
2683
2684 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2685 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2686 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2687 {
2688 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2689 return FALSE;
2690 }
2691 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2692 dwParamLen ? lpszParam : "", szCRLF);
2693
2694 TRACE("Sending (%s) len(%d)\n", buf, len);
2695 while((nBytesSent < len) && (nRC != -1))
2696 {
2697 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2698 nBytesSent += nRC;
2699 }
2700
2701 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2702
2703 if (lpfnStatusCB)
2704 {
2705 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2706 &nBytesSent, sizeof(DWORD));
2707 }
2708
2709 TRACE("Sent %d bytes\n", nBytesSent);
2710 return (nRC != -1);
2711 }
2712
2713 /***********************************************************************
2714 * FTP_SendCommand (internal)
2715 *
2716 * Send command to server
2717 *
2718 * RETURNS
2719 * TRUE on success
2720 * NULL on failure
2721 *
2722 */
2723 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2724 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2725 {
2726 BOOL ret;
2727 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2728 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2729 HeapFree(GetProcessHeap(), 0, lpszParamA);
2730 return ret;
2731 }
2732
2733 /***********************************************************************
2734 * FTP_ReceiveResponse (internal)
2735 *
2736 * Receive response from server
2737 *
2738 * RETURNS
2739 * Reply code on success
2740 * 0 on failure
2741 *
2742 */
2743 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2744 {
2745 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2746 DWORD nRecv;
2747 INT rc = 0;
2748 char firstprefix[5];
2749 BOOL multiline = FALSE;
2750
2751 TRACE("socket(%d)\n", lpwfs->sndSocket);
2752
2753 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2754
2755 while(1)
2756 {
2757 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2758 goto lerror;
2759
2760 if (nRecv >= 3)
2761 {
2762 if(!multiline)
2763 {
2764 if(lpszResponse[3] != '-')
2765 break;
2766 else
2767 { /* Start of multiline response. Loop until we get "nnn " */
2768 multiline = TRUE;
2769 memcpy(firstprefix, lpszResponse, 3);
2770 firstprefix[3] = ' ';
2771 firstprefix[4] = '\0';
2772 }
2773 }
2774 else
2775 {
2776 if(!memcmp(firstprefix, lpszResponse, 4))
2777 break;
2778 }
2779 }
2780 }
2781
2782 if (nRecv >= 3)
2783 {
2784 rc = atoi(lpszResponse);
2785
2786 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2787 &nRecv, sizeof(DWORD));
2788 }
2789
2790 lerror:
2791 TRACE("return %d\n", rc);
2792 return rc;
2793 }
2794
2795
2796 /***********************************************************************
2797 * FTP_SendPassword (internal)
2798 *
2799 * Send password to ftp server
2800 *
2801 * RETURNS
2802 * TRUE on success
2803 * NULL on failure
2804 *
2805 */
2806 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2807 {
2808 INT nResCode;
2809 BOOL bSuccess = FALSE;
2810
2811 TRACE("\n");
2812 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2813 goto lend;
2814
2815 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2816 if (nResCode)
2817 {
2818 TRACE("Received reply code %d\n", nResCode);
2819 /* Login successful... */
2820 if (nResCode == 230)
2821 bSuccess = TRUE;
2822 /* Command not implemented, superfluous at the server site... */
2823 /* Need account for login... */
2824 else if (nResCode == 332)
2825 bSuccess = FTP_SendAccount(lpwfs);
2826 else
2827 FTP_SetResponseError(nResCode);
2828 }
2829
2830 lend:
2831 TRACE("Returning %d\n", bSuccess);
2832 return bSuccess;
2833 }
2834
2835
2836 /***********************************************************************
2837 * FTP_SendAccount (internal)
2838 *
2839 *
2840 *
2841 * RETURNS
2842 * TRUE on success
2843 * FALSE on failure
2844 *
2845 */
2846 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2847 {
2848 INT nResCode;
2849 BOOL bSuccess = FALSE;
2850
2851 TRACE("\n");
2852 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2853 goto lend;
2854
2855 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2856 if (nResCode)
2857 bSuccess = TRUE;
2858 else
2859 FTP_SetResponseError(nResCode);
2860
2861 lend:
2862 return bSuccess;
2863 }
2864
2865
2866 /***********************************************************************
2867 * FTP_SendStore (internal)
2868 *
2869 * Send request to upload file to ftp server
2870 *
2871 * RETURNS
2872 * TRUE on success
2873 * FALSE on failure
2874 *
2875 */
2876 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2877 {
2878 INT nResCode;
2879 BOOL bSuccess = FALSE;
2880
2881 TRACE("\n");
2882 if (!FTP_InitListenSocket(lpwfs))
2883 goto lend;
2884
2885 if (!FTP_SendType(lpwfs, dwType))
2886 goto lend;
2887
2888 if (!FTP_SendPortOrPasv(lpwfs))
2889 goto lend;
2890
2891 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2892 goto lend;
2893 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2894 if (nResCode)
2895 {
2896 if (nResCode == 150 || nResCode == 125)
2897 bSuccess = TRUE;
2898 else
2899 FTP_SetResponseError(nResCode);
2900 }
2901
2902 lend:
2903 if (!bSuccess && lpwfs->lstnSocket != -1)
2904 {
2905 closesocket(lpwfs->lstnSocket);
2906 lpwfs->lstnSocket = -1;
2907 }
2908
2909 return bSuccess;
2910 }
2911
2912
2913 /***********************************************************************
2914 * FTP_InitListenSocket (internal)
2915 *
2916 * Create a socket to listen for server response
2917 *
2918 * RETURNS
2919 * TRUE on success
2920 * FALSE on failure
2921 *
2922 */
2923 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2924 {
2925 BOOL bSuccess = FALSE;
2926 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2927
2928 TRACE("\n");
2929
2930 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2931 if (lpwfs->lstnSocket == -1)
2932 {
2933 TRACE("Unable to create listening socket\n");
2934 goto lend;
2935 }
2936
2937 /* We obtain our ip addr from the name of the command channel socket */
2938 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2939
2940 /* and get the system to assign us a port */
2941 lpwfs->lstnSocketAddress.sin_port = htons(0);
2942
2943 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2944 {
2945 TRACE("Unable to bind socket\n");
2946 goto lend;
2947 }
2948
2949 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2950 {
2951 TRACE("listen failed\n");
2952 goto lend;
2953 }
2954
2955 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2956 bSuccess = TRUE;
2957
2958 lend:
2959 if (!bSuccess && lpwfs->lstnSocket != -1)
2960 {
2961 closesocket(lpwfs->lstnSocket);
2962 lpwfs->lstnSocket = -1;
2963 }
2964
2965 return bSuccess;
2966 }
2967
2968
2969 /***********************************************************************
2970 * FTP_SendType (internal)
2971 *
2972 * Tell server type of data being transferred
2973 *
2974 * RETURNS
2975 * TRUE on success
2976 * FALSE on failure
2977 *
2978 * W98SE doesn't cache the type that's currently set
2979 * (i.e. it sends it always),
2980 * so we probably don't want to do that either.
2981 */
2982 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2983 {
2984 INT nResCode;
2985 WCHAR type[] = { 'I','\0' };
2986 BOOL bSuccess = FALSE;
2987
2988 TRACE("\n");
2989 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2990 type[0] = 'A';
2991
2992 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2993 goto lend;
2994
2995 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2996 if (nResCode)
2997 {
2998 if (nResCode == 2)
2999 bSuccess = TRUE;
3000 else
3001 FTP_SetResponseError(nResCode);
3002 }
3003
3004 lend:
3005 return bSuccess;
3006 }
3007
3008
3009 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
3010 /***********************************************************************
3011 * FTP_GetFileSize (internal)
3012 *
3013 * Retrieves from the server the size of the given file
3014 *
3015 * RETURNS
3016 * TRUE on success
3017 * FALSE on failure
3018 *
3019 */
3020 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
3021 {
3022 INT nResCode;
3023 BOOL bSuccess = FALSE;
3024
3025 TRACE("\n");
3026
3027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
3028 goto lend;
3029
3030 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3031 if (nResCode)
3032 {
3033 if (nResCode == 213) {
3034 /* Now parses the output to get the actual file size */
3035 int i;
3036 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3037
3038 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
3039 if (lpszResponseBuffer[i] == '\0') return FALSE;
3040 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
3041
3042 bSuccess = TRUE;
3043 } else {
3044 FTP_SetResponseError(nResCode);
3045 }
3046 }
3047
3048 lend:
3049 return bSuccess;
3050 }
3051 #endif
3052
3053
3054 /***********************************************************************
3055 * FTP_SendPort (internal)
3056 *
3057 * Tell server which port to use
3058 *
3059 * RETURNS
3060 * TRUE on success
3061 * FALSE on failure
3062 *
3063 */
3064 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3065 {
3066 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3067 INT nResCode;
3068 WCHAR szIPAddress[64];
3069 BOOL bSuccess = FALSE;
3070 TRACE("\n");
3071
3072 sprintfW(szIPAddress, szIPFormat,
3073 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3074 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3075 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3076 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3077 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3078 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3079
3080 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3081 goto lend;
3082
3083 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3084 if (nResCode)
3085 {
3086 if (nResCode == 200)
3087 bSuccess = TRUE;
3088 else
3089 FTP_SetResponseError(nResCode);
3090 }
3091
3092 lend:
3093 return bSuccess;
3094 }
3095
3096
3097 /***********************************************************************
3098 * FTP_DoPassive (internal)
3099 *
3100 * Tell server that we want to do passive transfers
3101 * and connect data socket
3102 *
3103 * RETURNS
3104 * TRUE on success
3105 * FALSE on failure
3106 *
3107 */
3108 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3109 {
3110 INT nResCode;
3111 BOOL bSuccess = FALSE;
3112
3113 TRACE("\n");
3114 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3115 goto lend;
3116
3117 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3118 if (nResCode)
3119 {
3120 if (nResCode == 227)
3121 {
3122 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3123 LPSTR p;
3124 int f[6];
3125 int i;
3126 char *pAddr, *pPort;
3127 INT nsocket = -1;
3128 struct sockaddr_in dataSocketAddress;
3129
3130 p = lpszResponseBuffer+4; /* skip status code */
3131 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3132
3133 if (*p == '\0')
3134 {
3135 ERR("no address found in response, aborting\n");
3136 goto lend;
3137 }
3138
3139 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3140 &f[4], &f[5]) != 6)
3141 {
3142 ERR("unknown response address format '%s', aborting\n", p);
3143 goto lend;
3144 }
3145 for (i=0; i < 6; i++)
3146 f[i] = f[i] & 0xff;
3147
3148 dataSocketAddress = lpwfs->socketAddress;
3149 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3150 pPort = (char *)&(dataSocketAddress.sin_port);
3151 pAddr[0] = f[0];
3152 pAddr[1] = f[1];
3153 pAddr[2] = f[2];
3154 pAddr[3] = f[3];
3155 pPort[0] = f[4];
3156 pPort[1] = f[5];
3157
3158 nsocket = socket(AF_INET,SOCK_STREAM,0);
3159 if (nsocket == -1)
3160 goto lend;
3161
3162 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3163 {
3164 ERR("can't connect passive FTP data port.\n");
3165 closesocket(nsocket);
3166 goto lend;
3167 }
3168 lpwfs->pasvSocket = nsocket;
3169 bSuccess = TRUE;
3170 }
3171 else
3172 FTP_SetResponseError(nResCode);
3173 }
3174
3175 lend:
3176 return bSuccess;
3177 }
3178
3179
3180 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3181 {
3182 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3183 {
3184 if (!FTP_DoPassive(lpwfs))
3185 return FALSE;
3186 }
3187 else
3188 {
3189 if (!FTP_SendPort(lpwfs))
3190 return FALSE;
3191 }
3192 return TRUE;
3193 }
3194
3195
3196 /***********************************************************************
3197 * FTP_GetDataSocket (internal)
3198 *
3199 * Either accepts an incoming data socket connection from the server
3200 * or just returns the already opened socket after a PASV command
3201 * in case of passive FTP.
3202 *
3203 *
3204 * RETURNS
3205 * TRUE on success
3206 * FALSE on failure
3207 *
3208 */
3209 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3210 {
3211 struct sockaddr_in saddr;
3212 socklen_t addrlen = sizeof(struct sockaddr);
3213
3214 TRACE("\n");
3215 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3216 {
3217 *nDataSocket = lpwfs->pasvSocket;
3218 lpwfs->pasvSocket = -1;
3219 }
3220 else
3221 {
3222 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3223 closesocket(lpwfs->lstnSocket);
3224 lpwfs->lstnSocket = -1;
3225 }
3226 return *nDataSocket != -1;
3227 }
3228
3229
3230 /***********************************************************************
3231 * FTP_SendData (internal)
3232 *
3233 * Send data to the server
3234 *
3235 * RETURNS
3236 * TRUE on success
3237 * FALSE on failure
3238 *
3239 */
3240 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3241 {
3242 BY_HANDLE_FILE_INFORMATION fi;
3243 DWORD nBytesRead = 0;
3244 DWORD nBytesSent = 0;
3245 DWORD nTotalSent = 0;
3246 DWORD nBytesToSend, nLen;
3247 int nRC = 1;
3248 time_t s_long_time, e_long_time;
3249 LONG nSeconds;
3250 CHAR *lpszBuffer;
3251
3252 TRACE("\n");
3253 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3254
3255 /* Get the size of the file. */
3256 GetFileInformationByHandle(hFile, &fi);
3257 time(&s_long_time);
3258
3259 do
3260 {
3261 nBytesToSend = nBytesRead - nBytesSent;
3262
3263 if (nBytesToSend <= 0)
3264 {
3265 /* Read data from file. */
3266 nBytesSent = 0;
3267 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3268 ERR("Failed reading from file\n");
3269
3270 if (nBytesRead > 0)
3271 nBytesToSend = nBytesRead;
3272 else
3273 break;
3274 }
3275
3276 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3277 DATA_PACKET_SIZE : nBytesToSend;
3278 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
3279
3280 if (nRC != -1)
3281 {
3282 nBytesSent += nRC;
3283 nTotalSent += nRC;
3284 }
3285
3286 /* Do some computation to display the status. */
3287 time(&e_long_time);
3288 nSeconds = e_long_time - s_long_time;
3289 if( nSeconds / 60 > 0 )
3290 {
3291 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3292 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3293 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3294 }
3295 else
3296 {
3297 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3298 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3299 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3300 }
3301 } while (nRC != -1);
3302
3303 TRACE("file transfer complete!\n");
3304
3305 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3306
3307 return nTotalSent;
3308 }
3309
3310
3311 /***********************************************************************
3312 * FTP_SendRetrieve (internal)
3313 *
3314 * Send request to retrieve a file
3315 *
3316 * RETURNS
3317 * Number of bytes to be received on success
3318 * 0 on failure
3319 *
3320 */
3321 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3322 {
3323 INT nResCode;
3324 BOOL ret;
3325
3326 TRACE("\n");
3327 if (!(ret = FTP_InitListenSocket(lpwfs)))
3328 goto lend;
3329
3330 if (!(ret = FTP_SendType(lpwfs, dwType)))
3331 goto lend;
3332
3333 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3334 goto lend;
3335
3336 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3337 goto lend;
3338
3339 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3340 if ((nResCode != 125) && (nResCode != 150)) {
3341 /* That means that we got an error getting the file. */
3342 FTP_SetResponseError(nResCode);
3343 ret = FALSE;
3344 }
3345
3346 lend:
3347 if (!ret && lpwfs->lstnSocket != -1)
3348 {
3349 closesocket(lpwfs->lstnSocket);
3350 lpwfs->lstnSocket = -1;
3351 }
3352
3353 return ret;
3354 }
3355
3356
3357 /***********************************************************************
3358 * FTP_RetrieveData (internal)
3359 *
3360 * Retrieve data from server
3361 *
3362 * RETURNS
3363 * TRUE on success
3364 * FALSE on failure
3365 *
3366 */
3367 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3368 {
3369 DWORD nBytesWritten;
3370 INT nRC = 0;
3371 CHAR *lpszBuffer;
3372
3373 TRACE("\n");
3374
3375 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
3376 if (NULL == lpszBuffer)
3377 {
3378 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3379 return FALSE;
3380 }
3381
3382 while (nRC != -1)
3383 {
3384 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3385 if (nRC != -1)
3386 {
3387 /* other side closed socket. */
3388 if (nRC == 0)
3389 goto recv_end;
3390 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3391 }
3392 }
3393
3394 TRACE("Data transfer complete\n");
3395
3396 recv_end:
3397 HeapFree(GetProcessHeap(), 0, lpszBuffer);
3398
3399 return (nRC != -1);
3400 }
3401
3402 /***********************************************************************
3403 * FTPFINDNEXT_Destroy (internal)
3404 *
3405 * Deallocate session handle
3406 */
3407 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3408 {
3409 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3410 DWORD i;
3411
3412 TRACE("\n");
3413
3414 WININET_Release(&lpwfn->lpFtpSession->hdr);
3415
3416 for (i = 0; i < lpwfn->size; i++)
3417 {
3418 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
3419 }
3420
3421 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
3422 HeapFree(GetProcessHeap(), 0, lpwfn);
3423 }
3424
3425 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3426 {
3427 WIN32_FIND_DATAW *find_data = data;
3428 DWORD res = ERROR_SUCCESS;
3429
3430 TRACE("index(%d) size(%d)\n", find->index, find->size);
3431
3432 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3433
3434 if (find->index < find->size) {
3435 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3436 find->index++;
3437
3438 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3439 }else {
3440 res = ERROR_NO_MORE_FILES;
3441 }
3442
3443 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3444 {
3445 INTERNET_ASYNC_RESULT iar;
3446
3447 iar.dwResult = (res == ERROR_SUCCESS);
3448 iar.dwError = res;
3449
3450 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3451 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3452 sizeof(INTERNET_ASYNC_RESULT));
3453 }
3454
3455 return res;
3456 }
3457
3458 static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
3459 {
3460 struct WORKREQ_FTPFINDNEXTW *req = &workRequest->u.FtpFindNextW;
3461
3462 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
3463 }
3464
3465 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3466 {
3467 switch(option) {
3468 case INTERNET_OPTION_HANDLE_TYPE:
3469 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3470
3471 if (*size < sizeof(ULONG))
3472 return ERROR_INSUFFICIENT_BUFFER;
3473
3474 *size = sizeof(DWORD);
3475 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3476 return ERROR_SUCCESS;
3477 }
3478
3479 return INET_QueryOption(option, buffer, size, unicode);
3480 }
3481
3482 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3483 {
3484 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3485
3486 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3487 {
3488 WORKREQUEST workRequest;
3489 struct WORKREQ_FTPFINDNEXTW *req;
3490
3491 workRequest.asyncproc = FTPFINDNEXT_AsyncFindNextFileProc;
3492 workRequest.hdr = WININET_AddRef( &find->hdr );
3493 req = &workRequest.u.FtpFindNextW;
3494 req->lpFindFileData = data;
3495
3496 INTERNET_AsyncCall(&workRequest);
3497
3498 return ERROR_SUCCESS;
3499 }
3500
3501 return FTPFINDNEXT_FindNextFileProc(find, data);
3502 }
3503
3504 static const object_vtbl_t FTPFINDNEXTVtbl = {
3505 FTPFINDNEXT_Destroy,
3506 NULL,
3507 FTPFINDNEXT_QueryOption,
3508 NULL,
3509 NULL,
3510 NULL,
3511 NULL,
3512 NULL,
3513 NULL,
3514 FTPFINDNEXT_FindNextFileW
3515 };
3516
3517 /***********************************************************************
3518 * FTP_ReceiveFileList (internal)
3519 *
3520 * Read file list from server
3521 *
3522 * RETURNS
3523 * Handle to file list on success
3524 * NULL on failure
3525 *
3526 */
3527 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3528 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3529 {
3530 DWORD dwSize = 0;
3531 LPFILEPROPERTIESW lpafp = NULL;
3532 LPWININETFTPFINDNEXTW lpwfn = NULL;
3533 HINTERNET handle = 0;
3534
3535 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3536
3537 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3538 {
3539 if(lpFindFileData)
3540 FTP_ConvertFileProp(lpafp, lpFindFileData);
3541
3542 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3543 if (lpwfn)
3544 {
3545 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3546 lpwfn->hdr.vtbl = &FTPFINDNEXTVtbl;
3547 lpwfn->hdr.dwContext = dwContext;
3548 lpwfn->hdr.refs = 1;
3549 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3550 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3551 lpwfn->size = dwSize;
3552 lpwfn->lpafp = lpafp;
3553
3554 WININET_AddRef( &lpwfs->hdr );
3555 lpwfn->lpFtpSession = lpwfs;
3556 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3557
3558 handle = WININET_AllocHandle( &lpwfn->hdr );
3559 }
3560 }
3561
3562 if( lpwfn )
3563 WININET_Release( &lpwfn->hdr );
3564
3565 TRACE("Matched %d files\n", dwSize);
3566 return handle;
3567 }
3568
3569
3570 /***********************************************************************
3571 * FTP_ConvertFileProp (internal)
3572 *
3573 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3574 *
3575 * RETURNS
3576 * TRUE on success
3577 * FALSE on failure
3578 *
3579 */
3580 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3581 {
3582 BOOL bSuccess = FALSE;
3583
3584 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3585
3586 if (lpafp)
3587 {
3588 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3589 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3590 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3591
3592 /* Not all fields are filled in */
3593 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3594 lpFindFileData->nFileSizeLow = lpafp->nSize;
3595
3596 if (lpafp->bIsDirectory)
3597 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3598
3599 if (lpafp->lpszName)
3600 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3601
3602 bSuccess = TRUE;
3603 }
3604
3605 return bSuccess;
3606 }
3607
3608 /***********************************************************************
3609 * FTP_ParseNextFile (internal)
3610 *
3611 * Parse the next line in file listing
3612 *
3613 * RETURNS
3614 * TRUE on success
3615 * FALSE on failure
3616 */
3617 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3618 {
3619 static const char szSpace[] = " \t";
3620 DWORD nBufLen;
3621 char *pszLine;
3622 char *pszToken;
3623 char *pszTmp;
3624 BOOL found = FALSE;
3625 int i;
3626
3627 lpfp->lpszName = NULL;
3628 do {
3629 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3630 return FALSE;
3631
3632 pszToken = strtok(pszLine, szSpace);
3633 /* ls format
3634 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3635 *
3636 * For instance:
3637 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3638 */
3639 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3640 if(!FTP_ParsePermission(pszToken, lpfp))
3641 lpfp->bIsDirectory = FALSE;
3642 for(i=0; i<=3; i++) {
3643 if(!(pszToken = strtok(NULL, szSpace)))
3644 break;
3645 }
3646 if(!pszToken) continue;
3647 if(lpfp->bIsDirectory) {
3648 TRACE("Is directory\n");
3649 lpfp->nSize = 0;
3650 }
3651 else {
3652 TRACE("Size: %s\n", pszToken);
3653 lpfp->nSize = atol(pszToken);
3654 }
3655
3656 lpfp->tmLastModified.wSecond = 0;
3657 lpfp->tmLastModified.wMinute = 0;
3658 lpfp->tmLastModified.wHour = 0;
3659 lpfp->tmLastModified.wDay = 0;
3660 lpfp->tmLastModified.wMonth = 0;
3661 lpfp->tmLastModified.wYear = 0;
3662
3663 /* Determine month */
3664 pszToken = strtok(NULL, szSpace);
3665 if(!pszToken) continue;
3666 if(strlen(pszToken) >= 3) {
3667 pszToken[3] = 0;
3668 if((pszTmp = StrStrIA(szMonths, pszToken)))
3669 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3670 }
3671 /* Determine day */
3672 pszToken = strtok(NULL, szSpace);
3673 if(!pszToken) continue;
3674 lpfp->tmLastModified.wDay = atoi(pszToken);
3675 /* Determine time or year */
3676 pszToken = strtok(NULL, szSpace);
3677 if(!pszToken) continue;
3678 if((pszTmp = strchr(pszToken, ':'))) {
3679 SYSTEMTIME curr_time;
3680 *pszTmp = 0;
3681 pszTmp++;
3682 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3683 lpfp->tmLastModified.wHour = atoi(pszToken);
3684 GetLocalTime( &curr_time );
3685 lpfp->tmLastModified.wYear = curr_time.wYear;
3686 }
3687 else {
3688 lpfp->tmLastModified.wYear = atoi(pszToken);
3689 lpfp->tmLastModified.wHour = 12;
3690 }
3691 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3692 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3693 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3694
3695 pszToken = strtok(NULL, szSpace);
3696 if(!pszToken) continue;
3697 lpfp->lpszName = heap_strdupAtoW(pszToken);
3698 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3699 }
3700 /* NT way of parsing ... :
3701
3702 07-13-03 08:55PM <DIR> sakpatch
3703 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3704 */
3705 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3706 int mon, mday, year, hour, min;
3707 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3708
3709 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3710 lpfp->tmLastModified.wDay = mday;
3711 lpfp->tmLastModified.wMonth = mon;
3712 lpfp->tmLastModified.wYear = year;
3713
3714 /* Hacky and bad Y2K protection :-) */
3715 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3716
3717 pszToken = strtok(NULL, szSpace);
3718 if(!pszToken) continue;
3719 sscanf(pszToken, "%d:%d", &hour, &min);
3720 lpfp->tmLastModified.wHour = hour;
3721 lpfp->tmLastModified.wMinute = min;
3722 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3723 lpfp->tmLastModified.wHour += 12;
3724 }
3725 lpfp->tmLastModified.wSecond = 0;
3726
3727 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3728 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3729 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3730
3731 pszToken = strtok(NULL, szSpace);
3732 if(!pszToken) continue;
3733 if(!strcasecmp(pszToken, "<DIR>")) {
3734 lpfp->bIsDirectory = TRUE;
3735 lpfp->nSize = 0;
3736 TRACE("Is directory\n");
3737 }
3738 else {
3739 lpfp->bIsDirectory = FALSE;
3740 lpfp->nSize = atol(pszToken);
3741 TRACE("Size: %d\n", lpfp->nSize);
3742 }
3743
3744 pszToken = strtok(NULL, szSpace);
3745 if(!pszToken) continue;
3746 lpfp->lpszName = heap_strdupAtoW(pszToken);
3747 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3748 }
3749 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3750 else if(pszToken[0] == '+') {
3751 FIXME("EPLF Format not implemented\n");
3752 }
3753
3754 if(lpfp->lpszName) {
3755 if((lpszSearchFile == NULL) ||
3756 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3757 found = TRUE;
3758 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3759 }
3760 else {
3761 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3762 lpfp->lpszName = NULL;
3763 }
3764 }
3765 } while(!found);
3766 return TRUE;
3767 }
3768
3769 /***********************************************************************
3770 * FTP_ParseDirectory (internal)
3771 *
3772 * Parse string of directory information
3773 *
3774 * RETURNS
3775 * TRUE on success
3776 * FALSE on failure
3777 */
3778 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3779 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3780 {
3781 BOOL bSuccess = TRUE;
3782 INT sizeFilePropArray = 500;/*20; */
3783 INT indexFilePropArray = -1;
3784
3785 TRACE("\n");
3786
3787 /* Allocate initial file properties array */
3788 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3789 if (!*lpafp)
3790 return FALSE;
3791
3792 do {
3793 if (indexFilePropArray+1 >= sizeFilePropArray)
3794 {
3795 LPFILEPROPERTIESW tmpafp;
3796
3797 sizeFilePropArray *= 2;
3798 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3799 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3800 if (NULL == tmpafp)
3801 {
3802 bSuccess = FALSE;
3803 break;
3804 }
3805
3806 *lpafp = tmpafp;
3807 }
3808 indexFilePropArray++;
3809 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3810
3811 if (bSuccess && indexFilePropArray)
3812 {
3813 if (indexFilePropArray < sizeFilePropArray - 1)
3814 {
3815 LPFILEPROPERTIESW tmpafp;
3816
3817 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3818 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3819 if (NULL != tmpafp)
3820 *lpafp = tmpafp;
3821 }
3822 *dwfp = indexFilePropArray;
3823 }
3824 else
3825 {
3826 HeapFree(GetProcessHeap(), 0, *lpafp);
3827 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3828 bSuccess = FALSE;
3829 }
3830
3831 return bSuccess;
3832 }
3833
3834
3835 /***********************************************************************
3836 * FTP_ParsePermission (internal)
3837 *
3838 * Parse permission string of directory information
3839 *
3840 * RETURNS
3841 * TRUE on success
3842 * FALSE on failure
3843 *
3844 */
3845 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3846 {
3847 BOOL bSuccess = TRUE;
3848 unsigned short nPermission = 0;
3849 INT nPos = 1;
3850 INT nLast = 9;
3851
3852 TRACE("\n");
3853 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3854 {
3855 bSuccess = FALSE;
3856 return bSuccess;
3857 }
3858
3859 lpfp->bIsDirectory = (*lpszPermission == 'd');
3860 do
3861 {
3862 switch (nPos)
3863 {
3864 case 1:
3865 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3866 break;
3867 case 2:
3868 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3869 break;
3870 case 3:
3871 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3872 break;
3873 case 4:
3874 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3875 break;
3876 case 5:
3877 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3878 break;
3879 case 6:
3880 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3881 break;
3882 case 7:
3883 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3884 break;
3885 case 8:
3886 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3887 break;
3888 case 9:
3889 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3890 break;
3891 }
3892 nPos++;
3893 }while (nPos <= nLast);
3894
3895 lpfp->permissions = nPermission;
3896 return bSuccess;
3897 }
3898
3899
3900 /***********************************************************************
3901 * FTP_SetResponseError (internal)
3902 *
3903 * Set the appropriate error code for a given response from the server
3904 *
3905 * RETURNS
3906 *
3907 */
3908 static DWORD FTP_SetResponseError(DWORD dwResponse)
3909 {
3910 DWORD dwCode = 0;
3911
3912 switch(dwResponse)
3913 {
3914 case 425: /* Cannot open data connection. */
3915 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3916 break;
3917
3918 case 426: /* Connection closed, transer aborted. */
3919 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3920 break;
3921
3922 case 530: /* Not logged in. Login incorrect. */
3923 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3924 break;
3925
3926 case 421: /* Service not available - Server may be shutting down. */
3927 case 450: /* File action not taken. File may be busy. */
3928 case 451: /* Action aborted. Server error. */
3929 case 452: /* Action not taken. Insufficient storage space on server. */
3930 case 500: /* Syntax error. Command unrecognized. */
3931 case 501: /* Syntax error. Error in parameters or arguments. */
3932 case 502: /* Command not implemented. */
3933 case 503: /* Bad sequence of commands. */
3934 case 504: /* Command not implemented for that parameter. */
3935 case 532: /* Need account for storing files */
3936 case 550: /* File action not taken. File not found or no access. */
3937 case 551: /* Requested action aborted. Page type unknown */
3938 case 552: /* Action aborted. Exceeded storage allocation */
3939 case 553: /* Action not taken. File name not allowed. */
3940
3941 default:
3942 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3943 break;
3944 }
3945
3946 INTERNET_SetLastError(dwCode);
3947 return dwCode;
3948 }