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