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