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