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