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