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