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