* Sync up to trunk head (r64377).
[reactos.git] / 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 BOOL 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 BOOL 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 = sock_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 = sock_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 = sock_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 = sock_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 static DWORD FTPFILE_LockRequestFile(object_header_t *hdr, req_file_t **ret)
1253 {
1254 ftp_file_t *file = (ftp_file_t*)hdr;
1255 FIXME("%p\n", file);
1256 return ERROR_NOT_SUPPORTED;
1257 }
1258
1259 static const object_vtbl_t FTPFILEVtbl = {
1260 FTPFILE_Destroy,
1261 NULL,
1262 FTPFILE_QueryOption,
1263 INET_SetOption,
1264 FTPFILE_ReadFile,
1265 FTPFILE_ReadFileEx,
1266 FTPFILE_WriteFile,
1267 FTPFILE_QueryDataAvailable,
1268 NULL,
1269 FTPFILE_LockRequestFile
1270 };
1271
1272 /***********************************************************************
1273 * FTP_FtpOpenFileW (Internal)
1274 *
1275 * Open a remote file for writing or reading
1276 *
1277 * RETURNS
1278 * HINTERNET handle on success
1279 * NULL on failure
1280 *
1281 */
1282 static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
1283 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1284 DWORD_PTR dwContext)
1285 {
1286 INT nDataSocket;
1287 BOOL bSuccess = FALSE;
1288 ftp_file_t *lpwh = NULL;
1289 appinfo_t *hIC = NULL;
1290
1291 TRACE("\n");
1292
1293 /* Clear any error information */
1294 INTERNET_SetLastError(0);
1295
1296 if (GENERIC_READ == fdwAccess)
1297 {
1298 /* Set up socket to retrieve data */
1299 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1300 }
1301 else if (GENERIC_WRITE == fdwAccess)
1302 {
1303 /* Set up socket to send data */
1304 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1305 }
1306
1307 /* Get data socket to server */
1308 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1309 {
1310 lpwh = alloc_object(&lpwfs->hdr, &FTPFILEVtbl, sizeof(ftp_file_t));
1311 lpwh->hdr.htype = WH_HFILE;
1312 lpwh->hdr.dwFlags = dwFlags;
1313 lpwh->hdr.dwContext = dwContext;
1314 lpwh->nDataSocket = nDataSocket;
1315 lpwh->cache_file = NULL;
1316 lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
1317 lpwh->session_deleted = FALSE;
1318
1319 WININET_AddRef( &lpwfs->hdr );
1320 lpwh->lpFtpSession = lpwfs;
1321 list_add_head( &lpwfs->hdr.children, &lpwh->hdr.entry );
1322
1323 /* Indicate that a download is currently in progress */
1324 lpwfs->download_in_progress = lpwh;
1325 }
1326
1327 if (lpwfs->lstnSocket != -1)
1328 {
1329 closesocket(lpwfs->lstnSocket);
1330 lpwfs->lstnSocket = -1;
1331 }
1332
1333 if (bSuccess && fdwAccess == GENERIC_READ)
1334 {
1335 WCHAR filename[MAX_PATH + 1];
1336 URL_COMPONENTSW uc;
1337 DWORD len;
1338
1339 memset(&uc, 0, sizeof(uc));
1340 uc.dwStructSize = sizeof(uc);
1341 uc.nScheme = INTERNET_SCHEME_FTP;
1342 uc.lpszHostName = lpwfs->servername;
1343 uc.nPort = lpwfs->serverport;
1344 uc.lpszUserName = lpwfs->lpszUserName;
1345 uc.lpszUrlPath = heap_strdupW(lpszFileName);
1346
1347 if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1348 {
1349 WCHAR *url = heap_alloc(len * sizeof(WCHAR));
1350
1351 if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
1352 {
1353 lpwh->cache_file = heap_strdupW(filename);
1354 lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
1355 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1356 if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
1357 {
1358 WARN("Could not create cache file: %u\n", GetLastError());
1359 heap_free(lpwh->cache_file);
1360 lpwh->cache_file = NULL;
1361 }
1362 }
1363 heap_free(url);
1364 }
1365 heap_free(uc.lpszUrlPath);
1366 }
1367
1368 hIC = lpwfs->lpAppInfo;
1369 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1370 {
1371 INTERNET_ASYNC_RESULT iar;
1372
1373 if (lpwh)
1374 {
1375 iar.dwResult = (DWORD_PTR)lpwh->hdr.hInternet;
1376 iar.dwError = ERROR_SUCCESS;
1377 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1378 &iar, sizeof(INTERNET_ASYNC_RESULT));
1379 }
1380
1381 if(bSuccess) {
1382 FTP_ReceiveRequestData(lpwh, TRUE);
1383 }else {
1384 iar.dwResult = 0;
1385 iar.dwError = INTERNET_GetLastError();
1386 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1387 &iar, sizeof(INTERNET_ASYNC_RESULT));
1388 }
1389 }
1390
1391 if(!bSuccess)
1392 return FALSE;
1393
1394 return lpwh->hdr.hInternet;
1395 }
1396
1397
1398 /***********************************************************************
1399 * FtpOpenFileA (WININET.@)
1400 *
1401 * Open a remote file for writing or reading
1402 *
1403 * RETURNS
1404 * HINTERNET handle on success
1405 * NULL on failure
1406 *
1407 */
1408 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
1409 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1410 DWORD_PTR dwContext)
1411 {
1412 LPWSTR lpwzFileName;
1413 HINTERNET ret;
1414
1415 lpwzFileName = heap_strdupAtoW(lpszFileName);
1416 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
1417 heap_free(lpwzFileName);
1418 return ret;
1419 }
1420
1421 typedef struct {
1422 task_header_t hdr;
1423 WCHAR *file_name;
1424 DWORD access;
1425 DWORD flags;
1426 DWORD_PTR context;
1427 } open_file_task_t;
1428
1429 static void AsyncFtpOpenFileProc(task_header_t *hdr)
1430 {
1431 open_file_task_t *task = (open_file_task_t*)hdr;
1432 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1433
1434 TRACE("%p\n", session);
1435
1436 FTP_FtpOpenFileW(session, task->file_name, task->access, task->flags, task->context);
1437 heap_free(task->file_name);
1438 }
1439
1440 /***********************************************************************
1441 * FtpOpenFileW (WININET.@)
1442 *
1443 * Open a remote file for writing or reading
1444 *
1445 * RETURNS
1446 * HINTERNET handle on success
1447 * NULL on failure
1448 *
1449 */
1450 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1451 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1452 DWORD_PTR dwContext)
1453 {
1454 ftp_session_t *lpwfs;
1455 appinfo_t *hIC = NULL;
1456 HINTERNET r = NULL;
1457
1458 TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
1459 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1460
1461 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1462 if (!lpwfs)
1463 {
1464 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1465 return FALSE;
1466 }
1467
1468 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1469 {
1470 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1471 goto lend;
1472 }
1473
1474 if ((!lpszFileName) ||
1475 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1476 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1477 {
1478 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1479 goto lend;
1480 }
1481
1482 if (lpwfs->download_in_progress != NULL)
1483 {
1484 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1485 goto lend;
1486 }
1487
1488 hIC = lpwfs->lpAppInfo;
1489 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1490 {
1491 open_file_task_t *task;
1492
1493 task = alloc_async_task(&lpwfs->hdr, AsyncFtpOpenFileProc, sizeof(*task));
1494 task->file_name = heap_strdupW(lpszFileName);
1495 task->access = fdwAccess;
1496 task->flags = dwFlags;
1497 task->context = dwContext;
1498
1499 INTERNET_AsyncCall(&task->hdr);
1500 r = NULL;
1501 }
1502 else
1503 {
1504 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1505 }
1506
1507 lend:
1508 WININET_Release( &lpwfs->hdr );
1509
1510 return r;
1511 }
1512
1513
1514 /***********************************************************************
1515 * FtpGetFileA (WININET.@)
1516 *
1517 * Retrieve file from the FTP server
1518 *
1519 * RETURNS
1520 * TRUE on success
1521 * FALSE on failure
1522 *
1523 */
1524 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1525 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1526 DWORD_PTR dwContext)
1527 {
1528 LPWSTR lpwzRemoteFile;
1529 LPWSTR lpwzNewFile;
1530 BOOL ret;
1531
1532 lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
1533 lpwzNewFile = heap_strdupAtoW(lpszNewFile);
1534 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1535 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1536 heap_free(lpwzRemoteFile);
1537 heap_free(lpwzNewFile);
1538 return ret;
1539 }
1540
1541 typedef struct {
1542 task_header_t hdr;
1543 WCHAR *remote_file;
1544 WCHAR *new_file;
1545 BOOL fail_if_exists;
1546 DWORD local_attr;
1547 DWORD flags;
1548 DWORD_PTR context;
1549 } get_file_task_t;
1550
1551 static void AsyncFtpGetFileProc(task_header_t *hdr)
1552 {
1553 get_file_task_t *task = (get_file_task_t*)hdr;
1554 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1555
1556 TRACE("%p\n", session);
1557
1558 FTP_FtpGetFileW(session, task->remote_file, task->new_file, task->fail_if_exists,
1559 task->local_attr, task->flags, task->context);
1560 heap_free(task->remote_file);
1561 heap_free(task->new_file);
1562 }
1563
1564
1565 /***********************************************************************
1566 * FtpGetFileW (WININET.@)
1567 *
1568 * Retrieve file from the FTP server
1569 *
1570 * RETURNS
1571 * TRUE on success
1572 * FALSE on failure
1573 *
1574 */
1575 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1576 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1577 DWORD_PTR dwContext)
1578 {
1579 ftp_session_t *lpwfs;
1580 appinfo_t *hIC = NULL;
1581 BOOL r = FALSE;
1582
1583 if (!lpszRemoteFile || !lpszNewFile)
1584 {
1585 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1586 return FALSE;
1587 }
1588
1589 lpwfs = (ftp_session_t*) get_handle_object( hInternet );
1590 if (!lpwfs)
1591 {
1592 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1593 return FALSE;
1594 }
1595
1596 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1597 {
1598 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1599 goto lend;
1600 }
1601
1602 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1603 {
1604 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1605 goto lend;
1606 }
1607
1608 if (lpwfs->download_in_progress != NULL)
1609 {
1610 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1611 goto lend;
1612 }
1613
1614 hIC = lpwfs->lpAppInfo;
1615 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1616 {
1617 get_file_task_t *task;
1618
1619 task = alloc_async_task(&lpwfs->hdr, AsyncFtpGetFileProc, sizeof(*task));
1620 task->remote_file = heap_strdupW(lpszRemoteFile);
1621 task->new_file = heap_strdupW(lpszNewFile);
1622 task->local_attr = dwLocalFlagsAttribute;
1623 task->fail_if_exists = fFailIfExists;
1624 task->flags = dwInternetFlags;
1625 task->context = dwContext;
1626
1627 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1628 }
1629 else
1630 {
1631 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1632 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1633 }
1634
1635 lend:
1636 WININET_Release( &lpwfs->hdr );
1637
1638 return r;
1639 }
1640
1641
1642 /***********************************************************************
1643 * FTP_FtpGetFileW (Internal)
1644 *
1645 * Retrieve file from the FTP server
1646 *
1647 * RETURNS
1648 * TRUE on success
1649 * FALSE on failure
1650 *
1651 */
1652 static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1653 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1654 DWORD_PTR dwContext)
1655 {
1656 BOOL bSuccess = FALSE;
1657 HANDLE hFile;
1658 appinfo_t *hIC = NULL;
1659
1660 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1661
1662 /* Clear any error information */
1663 INTERNET_SetLastError(0);
1664
1665 /* Ensure we can write to lpszNewfile by opening it */
1666 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1667 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1668 if (INVALID_HANDLE_VALUE == hFile)
1669 return FALSE;
1670
1671 /* Set up socket to retrieve data */
1672 if (FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags))
1673 {
1674 INT nDataSocket;
1675
1676 /* Get data socket to server */
1677 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1678 {
1679 INT nResCode;
1680
1681 /* Receive data */
1682 FTP_RetrieveFileData(lpwfs, nDataSocket, hFile);
1683 closesocket(nDataSocket);
1684
1685 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1686 if (nResCode)
1687 {
1688 if (nResCode == 226)
1689 bSuccess = TRUE;
1690 else
1691 FTP_SetResponseError(nResCode);
1692 }
1693 }
1694 }
1695
1696 if (lpwfs->lstnSocket != -1)
1697 {
1698 closesocket(lpwfs->lstnSocket);
1699 lpwfs->lstnSocket = -1;
1700 }
1701
1702 CloseHandle(hFile);
1703
1704 hIC = lpwfs->lpAppInfo;
1705 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1706 {
1707 INTERNET_ASYNC_RESULT iar;
1708
1709 iar.dwResult = (DWORD)bSuccess;
1710 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1711 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1712 &iar, sizeof(INTERNET_ASYNC_RESULT));
1713 }
1714
1715 if (!bSuccess) DeleteFileW(lpszNewFile);
1716 return bSuccess;
1717 }
1718
1719 /***********************************************************************
1720 * FtpGetFileSize (WININET.@)
1721 */
1722 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1723 {
1724 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1725
1726 if (lpdwFileSizeHigh)
1727 *lpdwFileSizeHigh = 0;
1728
1729 return 0;
1730 }
1731
1732 /***********************************************************************
1733 * FtpDeleteFileA (WININET.@)
1734 *
1735 * Delete a file on the ftp server
1736 *
1737 * RETURNS
1738 * TRUE on success
1739 * FALSE on failure
1740 *
1741 */
1742 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1743 {
1744 LPWSTR lpwzFileName;
1745 BOOL ret;
1746
1747 lpwzFileName = heap_strdupAtoW(lpszFileName);
1748 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1749 heap_free(lpwzFileName);
1750 return ret;
1751 }
1752
1753 typedef struct {
1754 task_header_t hdr;
1755 WCHAR *file_name;
1756 } delete_file_task_t;
1757
1758 static void AsyncFtpDeleteFileProc(task_header_t *hdr)
1759 {
1760 delete_file_task_t *task = (delete_file_task_t*)hdr;
1761 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1762
1763 TRACE("%p\n", session);
1764
1765 FTP_FtpDeleteFileW(session, task->file_name);
1766 heap_free(task->file_name);
1767 }
1768
1769 /***********************************************************************
1770 * FtpDeleteFileW (WININET.@)
1771 *
1772 * Delete a file on the ftp server
1773 *
1774 * RETURNS
1775 * TRUE on success
1776 * FALSE on failure
1777 *
1778 */
1779 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1780 {
1781 ftp_session_t *lpwfs;
1782 appinfo_t *hIC = NULL;
1783 BOOL r = FALSE;
1784
1785 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1786 if (!lpwfs)
1787 {
1788 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1789 return FALSE;
1790 }
1791
1792 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1793 {
1794 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1795 goto lend;
1796 }
1797
1798 if (lpwfs->download_in_progress != NULL)
1799 {
1800 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1801 goto lend;
1802 }
1803
1804 if (!lpszFileName)
1805 {
1806 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1807 goto lend;
1808 }
1809
1810 hIC = lpwfs->lpAppInfo;
1811 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1812 {
1813 delete_file_task_t *task;
1814
1815 task = alloc_async_task(&lpwfs->hdr, AsyncFtpDeleteFileProc, sizeof(*task));
1816 task->file_name = heap_strdupW(lpszFileName);
1817
1818 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1819 }
1820 else
1821 {
1822 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1823 }
1824
1825 lend:
1826 WININET_Release( &lpwfs->hdr );
1827
1828 return r;
1829 }
1830
1831 /***********************************************************************
1832 * FTP_FtpDeleteFileW (Internal)
1833 *
1834 * Delete a file on the ftp server
1835 *
1836 * RETURNS
1837 * TRUE on success
1838 * FALSE on failure
1839 *
1840 */
1841 BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
1842 {
1843 INT nResCode;
1844 BOOL bSuccess = FALSE;
1845 appinfo_t *hIC = NULL;
1846
1847 TRACE("%p\n", lpwfs);
1848
1849 /* Clear any error information */
1850 INTERNET_SetLastError(0);
1851
1852 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1853 goto lend;
1854
1855 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1856 if (nResCode)
1857 {
1858 if (nResCode == 250)
1859 bSuccess = TRUE;
1860 else
1861 FTP_SetResponseError(nResCode);
1862 }
1863 lend:
1864 hIC = lpwfs->lpAppInfo;
1865 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1866 {
1867 INTERNET_ASYNC_RESULT iar;
1868
1869 iar.dwResult = (DWORD)bSuccess;
1870 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1871 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1872 &iar, sizeof(INTERNET_ASYNC_RESULT));
1873 }
1874
1875 return bSuccess;
1876 }
1877
1878
1879 /***********************************************************************
1880 * FtpRemoveDirectoryA (WININET.@)
1881 *
1882 * Remove a directory on the ftp server
1883 *
1884 * RETURNS
1885 * TRUE on success
1886 * FALSE on failure
1887 *
1888 */
1889 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1890 {
1891 LPWSTR lpwzDirectory;
1892 BOOL ret;
1893
1894 lpwzDirectory = heap_strdupAtoW(lpszDirectory);
1895 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1896 heap_free(lpwzDirectory);
1897 return ret;
1898 }
1899
1900 static void AsyncFtpRemoveDirectoryProc(task_header_t *hdr)
1901 {
1902 directory_task_t *task = (directory_task_t*)hdr;
1903 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
1904
1905 TRACE("%p\n", session);
1906
1907 FTP_FtpRemoveDirectoryW(session, task->directory);
1908 heap_free(task->directory);
1909 }
1910
1911 /***********************************************************************
1912 * FtpRemoveDirectoryW (WININET.@)
1913 *
1914 * Remove a directory on the ftp server
1915 *
1916 * RETURNS
1917 * TRUE on success
1918 * FALSE on failure
1919 *
1920 */
1921 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1922 {
1923 ftp_session_t *lpwfs;
1924 appinfo_t *hIC = NULL;
1925 BOOL r = FALSE;
1926
1927 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
1928 if (!lpwfs)
1929 {
1930 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1931 return FALSE;
1932 }
1933
1934 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1935 {
1936 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1937 goto lend;
1938 }
1939
1940 if (lpwfs->download_in_progress != NULL)
1941 {
1942 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1943 goto lend;
1944 }
1945
1946 if (!lpszDirectory)
1947 {
1948 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1949 goto lend;
1950 }
1951
1952 hIC = lpwfs->lpAppInfo;
1953 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1954 {
1955 directory_task_t *task;
1956
1957 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRemoveDirectoryProc, sizeof(*task));
1958 task->directory = heap_strdupW(lpszDirectory);
1959
1960 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
1961 }
1962 else
1963 {
1964 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1965 }
1966
1967 lend:
1968 WININET_Release( &lpwfs->hdr );
1969
1970 return r;
1971 }
1972
1973 /***********************************************************************
1974 * FTP_FtpRemoveDirectoryW (Internal)
1975 *
1976 * Remove a directory on the ftp server
1977 *
1978 * RETURNS
1979 * TRUE on success
1980 * FALSE on failure
1981 *
1982 */
1983 BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
1984 {
1985 INT nResCode;
1986 BOOL bSuccess = FALSE;
1987 appinfo_t *hIC = NULL;
1988
1989 TRACE("\n");
1990
1991 /* Clear any error information */
1992 INTERNET_SetLastError(0);
1993
1994 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1995 goto lend;
1996
1997 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1998 if (nResCode)
1999 {
2000 if (nResCode == 250)
2001 bSuccess = TRUE;
2002 else
2003 FTP_SetResponseError(nResCode);
2004 }
2005
2006 lend:
2007 hIC = lpwfs->lpAppInfo;
2008 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2009 {
2010 INTERNET_ASYNC_RESULT iar;
2011
2012 iar.dwResult = (DWORD)bSuccess;
2013 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2014 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2015 &iar, sizeof(INTERNET_ASYNC_RESULT));
2016 }
2017
2018 return bSuccess;
2019 }
2020
2021
2022 /***********************************************************************
2023 * FtpRenameFileA (WININET.@)
2024 *
2025 * Rename a file on the ftp server
2026 *
2027 * RETURNS
2028 * TRUE on success
2029 * FALSE on failure
2030 *
2031 */
2032 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
2033 {
2034 LPWSTR lpwzSrc;
2035 LPWSTR lpwzDest;
2036 BOOL ret;
2037
2038 lpwzSrc = heap_strdupAtoW(lpszSrc);
2039 lpwzDest = heap_strdupAtoW(lpszDest);
2040 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
2041 heap_free(lpwzSrc);
2042 heap_free(lpwzDest);
2043 return ret;
2044 }
2045
2046 typedef struct {
2047 task_header_t hdr;
2048 WCHAR *src_file;
2049 WCHAR *dst_file;
2050 } rename_file_task_t;
2051
2052 static void AsyncFtpRenameFileProc(task_header_t *hdr)
2053 {
2054 rename_file_task_t *task = (rename_file_task_t*)hdr;
2055 ftp_session_t *session = (ftp_session_t*)task->hdr.hdr;
2056
2057 TRACE("%p\n", session);
2058
2059 FTP_FtpRenameFileW(session, task->src_file, task->dst_file);
2060 heap_free(task->src_file);
2061 heap_free(task->dst_file);
2062 }
2063
2064 /***********************************************************************
2065 * FtpRenameFileW (WININET.@)
2066 *
2067 * Rename a file on the ftp server
2068 *
2069 * RETURNS
2070 * TRUE on success
2071 * FALSE on failure
2072 *
2073 */
2074 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2075 {
2076 ftp_session_t *lpwfs;
2077 appinfo_t *hIC = NULL;
2078 BOOL r = FALSE;
2079
2080 lpwfs = (ftp_session_t*) get_handle_object( hFtpSession );
2081 if (!lpwfs)
2082 {
2083 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2084 return FALSE;
2085 }
2086
2087 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2088 {
2089 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2090 goto lend;
2091 }
2092
2093 if (lpwfs->download_in_progress != NULL)
2094 {
2095 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2096 goto lend;
2097 }
2098
2099 if (!lpszSrc || !lpszDest)
2100 {
2101 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2102 goto lend;
2103 }
2104
2105 hIC = lpwfs->lpAppInfo;
2106 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2107 {
2108 rename_file_task_t *task;
2109
2110 task = alloc_async_task(&lpwfs->hdr, AsyncFtpRenameFileProc, sizeof(*task));
2111 task->src_file = heap_strdupW(lpszSrc);
2112 task->dst_file = heap_strdupW(lpszDest);
2113
2114 r = res_to_le(INTERNET_AsyncCall(&task->hdr));
2115 }
2116 else
2117 {
2118 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
2119 }
2120
2121 lend:
2122 WININET_Release( &lpwfs->hdr );
2123
2124 return r;
2125 }
2126
2127 /***********************************************************************
2128 * FTP_FtpRenameFileW (Internal)
2129 *
2130 * Rename a file on the ftp server
2131 *
2132 * RETURNS
2133 * TRUE on success
2134 * FALSE on failure
2135 *
2136 */
2137 BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
2138 {
2139 INT nResCode;
2140 BOOL bSuccess = FALSE;
2141 appinfo_t *hIC = NULL;
2142
2143 TRACE("\n");
2144
2145 /* Clear any error information */
2146 INTERNET_SetLastError(0);
2147
2148 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
2149 goto lend;
2150
2151 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2152 if (nResCode == 350)
2153 {
2154 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
2155 goto lend;
2156
2157 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2158 }
2159
2160 if (nResCode == 250)
2161 bSuccess = TRUE;
2162 else
2163 FTP_SetResponseError(nResCode);
2164
2165 lend:
2166 hIC = lpwfs->lpAppInfo;
2167 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2168 {
2169 INTERNET_ASYNC_RESULT iar;
2170
2171 iar.dwResult = (DWORD)bSuccess;
2172 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2173 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2174 &iar, sizeof(INTERNET_ASYNC_RESULT));
2175 }
2176
2177 return bSuccess;
2178 }
2179
2180 /***********************************************************************
2181 * FtpCommandA (WININET.@)
2182 */
2183 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2184 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2185 {
2186 BOOL r;
2187 WCHAR *cmdW;
2188
2189 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2190 debugstr_a(lpszCommand), dwContext, phFtpCommand);
2191
2192 if (fExpectResponse)
2193 {
2194 FIXME("data connection not supported\n");
2195 return FALSE;
2196 }
2197
2198 if (!lpszCommand || !lpszCommand[0])
2199 {
2200 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2201 return FALSE;
2202 }
2203
2204 if (!(cmdW = heap_strdupAtoW(lpszCommand)))
2205 {
2206 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2207 return FALSE;
2208 }
2209
2210 r = FtpCommandW(hConnect, fExpectResponse, dwFlags, cmdW, dwContext, phFtpCommand);
2211
2212 heap_free(cmdW);
2213 return r;
2214 }
2215
2216 /***********************************************************************
2217 * FtpCommandW (WININET.@)
2218 */
2219 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
2220 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
2221 {
2222 BOOL r = FALSE;
2223 ftp_session_t *lpwfs;
2224 LPSTR cmd = NULL;
2225 DWORD len, nBytesSent= 0;
2226 INT nResCode, nRC = 0;
2227
2228 TRACE("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
2229 debugstr_w(lpszCommand), dwContext, phFtpCommand);
2230
2231 if (!lpszCommand || !lpszCommand[0])
2232 {
2233 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2234 return FALSE;
2235 }
2236
2237 if (fExpectResponse)
2238 {
2239 FIXME("data connection not supported\n");
2240 return FALSE;
2241 }
2242
2243 lpwfs = (ftp_session_t*) get_handle_object( hConnect );
2244 if (!lpwfs)
2245 {
2246 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2247 return FALSE;
2248 }
2249
2250 if (WH_HFTPSESSION != lpwfs->hdr.htype)
2251 {
2252 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2253 goto lend;
2254 }
2255
2256 if (lpwfs->download_in_progress != NULL)
2257 {
2258 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2259 goto lend;
2260 }
2261
2262 len = WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, NULL, 0, NULL, NULL) + strlen(szCRLF);
2263 if ((cmd = heap_alloc(len)))
2264 WideCharToMultiByte(CP_ACP, 0, lpszCommand, -1, cmd, len, NULL, NULL);
2265 else
2266 {
2267 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2268 goto lend;
2269 }
2270
2271 strcat(cmd, szCRLF);
2272 len--;
2273
2274 TRACE("Sending (%s) len(%d)\n", cmd, len);
2275 while ((nBytesSent < len) && (nRC != -1))
2276 {
2277 nRC = sock_send(lpwfs->sndSocket, cmd + nBytesSent, len - nBytesSent, 0);
2278 if (nRC != -1)
2279 {
2280 nBytesSent += nRC;
2281 TRACE("Sent %d bytes\n", nRC);
2282 }
2283 }
2284
2285 if (nBytesSent)
2286 {
2287 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2288 if (nResCode > 0 && nResCode < 400)
2289 r = TRUE;
2290 else
2291 FTP_SetResponseError(nResCode);
2292 }
2293
2294 lend:
2295 WININET_Release( &lpwfs->hdr );
2296 heap_free( cmd );
2297 return r;
2298 }
2299
2300
2301 /***********************************************************************
2302 * FTPSESSION_Destroy (internal)
2303 *
2304 * Deallocate session handle
2305 */
2306 static void FTPSESSION_Destroy(object_header_t *hdr)
2307 {
2308 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2309
2310 TRACE("\n");
2311
2312 WININET_Release(&lpwfs->lpAppInfo->hdr);
2313
2314 heap_free(lpwfs->lpszPassword);
2315 heap_free(lpwfs->lpszUserName);
2316 heap_free(lpwfs->servername);
2317 }
2318
2319 static void FTPSESSION_CloseConnection(object_header_t *hdr)
2320 {
2321 ftp_session_t *lpwfs = (ftp_session_t*) hdr;
2322
2323 TRACE("\n");
2324
2325 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2326 INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
2327
2328 if (lpwfs->download_in_progress != NULL)
2329 lpwfs->download_in_progress->session_deleted = TRUE;
2330
2331 if (lpwfs->sndSocket != -1)
2332 closesocket(lpwfs->sndSocket);
2333
2334 if (lpwfs->lstnSocket != -1)
2335 closesocket(lpwfs->lstnSocket);
2336
2337 if (lpwfs->pasvSocket != -1)
2338 closesocket(lpwfs->pasvSocket);
2339
2340 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext,
2341 INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
2342 }
2343
2344 static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2345 {
2346 switch(option) {
2347 case INTERNET_OPTION_HANDLE_TYPE:
2348 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
2349
2350 if (*size < sizeof(ULONG))
2351 return ERROR_INSUFFICIENT_BUFFER;
2352
2353 *size = sizeof(DWORD);
2354 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_CONNECT_FTP;
2355 return ERROR_SUCCESS;
2356 }
2357
2358 return INET_QueryOption(hdr, option, buffer, size, unicode);
2359 }
2360
2361 static const object_vtbl_t FTPSESSIONVtbl = {
2362 FTPSESSION_Destroy,
2363 FTPSESSION_CloseConnection,
2364 FTPSESSION_QueryOption,
2365 INET_SetOption,
2366 NULL,
2367 NULL,
2368 NULL,
2369 NULL,
2370 NULL
2371 };
2372
2373
2374 /***********************************************************************
2375 * FTP_Connect (internal)
2376 *
2377 * Connect to a ftp server
2378 *
2379 * RETURNS
2380 * HINTERNET a session handle on success
2381 * NULL on failure
2382 *
2383 * NOTES:
2384 *
2385 * Windows uses 'anonymous' as the username, when given a NULL username
2386 * and a NULL password. The password is first looked up in:
2387 *
2388 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
2389 *
2390 * If this entry is not present it uses the current username as the password.
2391 *
2392 */
2393
2394 HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
2395 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
2396 LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
2397 DWORD dwInternalFlags)
2398 {
2399 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
2400 'M','i','c','r','o','s','o','f','t','\\',
2401 'W','i','n','d','o','w','s','\\',
2402 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2403 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
2404 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
2405 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
2406 static const WCHAR szEmpty[] = {'\0'};
2407 struct sockaddr_in socketAddr;
2408 INT nsocket = -1;
2409 socklen_t sock_namelen;
2410 BOOL bSuccess = FALSE;
2411 ftp_session_t *lpwfs = NULL;
2412 char szaddr[INET_ADDRSTRLEN];
2413
2414 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
2415 hIC, debugstr_w(lpszServerName),
2416 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
2417
2418 assert( hIC->hdr.htype == WH_HINIT );
2419
2420 if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
2421 {
2422 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2423 return NULL;
2424 }
2425
2426 lpwfs = alloc_object(&hIC->hdr, &FTPSESSIONVtbl, sizeof(ftp_session_t));
2427 if (NULL == lpwfs)
2428 {
2429 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2430 return NULL;
2431 }
2432
2433 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
2434 lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
2435 else
2436 lpwfs->serverport = nServerPort;
2437
2438 lpwfs->hdr.htype = WH_HFTPSESSION;
2439 lpwfs->hdr.dwFlags = dwFlags;
2440 lpwfs->hdr.dwContext = dwContext;
2441 lpwfs->hdr.dwInternalFlags |= dwInternalFlags;
2442 lpwfs->download_in_progress = NULL;
2443 lpwfs->sndSocket = -1;
2444 lpwfs->lstnSocket = -1;
2445 lpwfs->pasvSocket = -1;
2446
2447 WININET_AddRef( &hIC->hdr );
2448 lpwfs->lpAppInfo = hIC;
2449 list_add_head( &hIC->hdr.children, &lpwfs->hdr.entry );
2450
2451 if(hIC->proxy && hIC->accessType == INTERNET_OPEN_TYPE_PROXY) {
2452 if(strchrW(hIC->proxy, ' '))
2453 FIXME("Several proxies not implemented.\n");
2454 if(hIC->proxyBypass)
2455 FIXME("Proxy bypass is ignored.\n");
2456 }
2457 if (!lpszUserName || !strlenW(lpszUserName)) {
2458 HKEY key;
2459 WCHAR szPassword[MAX_PATH];
2460 DWORD len = sizeof(szPassword);
2461
2462 lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
2463
2464 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
2465 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
2466 /* Nothing in the registry, get the username and use that as the password */
2467 if (!GetUserNameW(szPassword, &len)) {
2468 /* Should never get here, but use an empty password as failsafe */
2469 strcpyW(szPassword, szEmpty);
2470 }
2471 }
2472 RegCloseKey(key);
2473
2474 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
2475 lpwfs->lpszPassword = heap_strdupW(szPassword);
2476 }
2477 else {
2478 lpwfs->lpszUserName = heap_strdupW(lpszUserName);
2479 lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
2480 }
2481 lpwfs->servername = heap_strdupW(lpszServerName);
2482
2483 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
2484 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
2485 {
2486 INTERNET_ASYNC_RESULT iar;
2487
2488 iar.dwResult = (DWORD_PTR)lpwfs->hdr.hInternet;
2489 iar.dwError = ERROR_SUCCESS;
2490
2491 SendAsyncCallback(&hIC->hdr, dwContext,
2492 INTERNET_STATUS_HANDLE_CREATED, &iar,
2493 sizeof(INTERNET_ASYNC_RESULT));
2494 }
2495
2496 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
2497 (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
2498
2499 sock_namelen = sizeof(socketAddr);
2500 if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
2501 {
2502 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
2503 goto lerror;
2504 }
2505
2506 if (socketAddr.sin_family != AF_INET)
2507 {
2508 WARN("unsupported address family %d\n", socketAddr.sin_family);
2509 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2510 goto lerror;
2511 }
2512
2513 inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
2514 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
2515 szaddr, strlen(szaddr)+1);
2516
2517 nsocket = socket(AF_INET,SOCK_STREAM,0);
2518 if (nsocket == -1)
2519 {
2520 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2521 goto lerror;
2522 }
2523
2524 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
2525 szaddr, strlen(szaddr)+1);
2526
2527 if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
2528 {
2529 ERR("Unable to connect (%s)\n", strerror(errno));
2530 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
2531 closesocket(nsocket);
2532 }
2533 else
2534 {
2535 TRACE("Connected to server\n");
2536 lpwfs->sndSocket = nsocket;
2537 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
2538 szaddr, strlen(szaddr)+1);
2539
2540 sock_namelen = sizeof(lpwfs->socketAddress);
2541 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2542
2543 if (FTP_ConnectToHost(lpwfs))
2544 {
2545 TRACE("Successfully logged into server\n");
2546 bSuccess = TRUE;
2547 }
2548 }
2549
2550 lerror:
2551 if (!bSuccess)
2552 {
2553 if(lpwfs)
2554 WININET_Release( &lpwfs->hdr );
2555 return NULL;
2556 }
2557
2558 return lpwfs->hdr.hInternet;
2559 }
2560
2561
2562 /***********************************************************************
2563 * FTP_ConnectToHost (internal)
2564 *
2565 * Connect to a ftp server
2566 *
2567 * RETURNS
2568 * TRUE on success
2569 * NULL on failure
2570 *
2571 */
2572 static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
2573 {
2574 INT nResCode;
2575 BOOL bSuccess = FALSE;
2576
2577 TRACE("\n");
2578 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2579
2580 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2581 goto lend;
2582
2583 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2584 if (nResCode)
2585 {
2586 /* Login successful... */
2587 if (nResCode == 230)
2588 bSuccess = TRUE;
2589 /* User name okay, need password... */
2590 else if (nResCode == 331)
2591 bSuccess = FTP_SendPassword(lpwfs);
2592 /* Need account for login... */
2593 else if (nResCode == 332)
2594 bSuccess = FTP_SendAccount(lpwfs);
2595 else
2596 FTP_SetResponseError(nResCode);
2597 }
2598
2599 TRACE("Returning %d\n", bSuccess);
2600 lend:
2601 return bSuccess;
2602 }
2603
2604
2605 /***********************************************************************
2606 * FTP_SendCommandA (internal)
2607 *
2608 * Send command to server
2609 *
2610 * RETURNS
2611 * TRUE on success
2612 * NULL on failure
2613 *
2614 */
2615 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2616 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2617 {
2618 DWORD len;
2619 CHAR *buf;
2620 DWORD nBytesSent = 0;
2621 int nRC = 0;
2622 DWORD dwParamLen;
2623
2624 TRACE("%d: (%s) %d\n", ftpCmd, debugstr_a(lpszParam), nSocket);
2625
2626 if (lpfnStatusCB)
2627 {
2628 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2629 }
2630
2631 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2632 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2633 if (NULL == (buf = heap_alloc(len+1)))
2634 {
2635 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2636 return FALSE;
2637 }
2638 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2639 dwParamLen ? lpszParam : "", szCRLF);
2640
2641 TRACE("Sending (%s) len(%d)\n", buf, len);
2642 while((nBytesSent < len) && (nRC != -1))
2643 {
2644 nRC = sock_send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2645 nBytesSent += nRC;
2646 }
2647 heap_free(buf);
2648
2649 if (lpfnStatusCB)
2650 {
2651 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2652 &nBytesSent, sizeof(DWORD));
2653 }
2654
2655 TRACE("Sent %d bytes\n", nBytesSent);
2656 return (nRC != -1);
2657 }
2658
2659 /***********************************************************************
2660 * FTP_SendCommand (internal)
2661 *
2662 * Send command to server
2663 *
2664 * RETURNS
2665 * TRUE on success
2666 * NULL on failure
2667 *
2668 */
2669 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2670 INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
2671 {
2672 BOOL ret;
2673 LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
2674 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2675 heap_free(lpszParamA);
2676 return ret;
2677 }
2678
2679 /***********************************************************************
2680 * FTP_ReceiveResponse (internal)
2681 *
2682 * Receive response from server
2683 *
2684 * RETURNS
2685 * Reply code on success
2686 * 0 on failure
2687 *
2688 */
2689 INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
2690 {
2691 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2692 DWORD nRecv;
2693 INT rc = 0;
2694 char firstprefix[5];
2695 BOOL multiline = FALSE;
2696
2697 TRACE("socket(%d)\n", lpwfs->sndSocket);
2698
2699 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2700
2701 while(1)
2702 {
2703 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2704 goto lerror;
2705
2706 if (nRecv >= 3)
2707 {
2708 if(!multiline)
2709 {
2710 if(lpszResponse[3] != '-')
2711 break;
2712 else
2713 { /* Start of multiline response. Loop until we get "nnn " */
2714 multiline = TRUE;
2715 memcpy(firstprefix, lpszResponse, 3);
2716 firstprefix[3] = ' ';
2717 firstprefix[4] = '\0';
2718 }
2719 }
2720 else
2721 {
2722 if(!memcmp(firstprefix, lpszResponse, 4))
2723 break;
2724 }
2725 }
2726 }
2727
2728 if (nRecv >= 3)
2729 {
2730 rc = atoi(lpszResponse);
2731
2732 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2733 &nRecv, sizeof(DWORD));
2734 }
2735
2736 lerror:
2737 TRACE("return %d\n", rc);
2738 return rc;
2739 }
2740
2741
2742 /***********************************************************************
2743 * FTP_SendPassword (internal)
2744 *
2745 * Send password to ftp server
2746 *
2747 * RETURNS
2748 * TRUE on success
2749 * NULL on failure
2750 *
2751 */
2752 static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
2753 {
2754 INT nResCode;
2755 BOOL bSuccess = FALSE;
2756
2757 TRACE("\n");
2758 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2759 goto lend;
2760
2761 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2762 if (nResCode)
2763 {
2764 TRACE("Received reply code %d\n", nResCode);
2765 /* Login successful... */
2766 if (nResCode == 230)
2767 bSuccess = TRUE;
2768 /* Command not implemented, superfluous at the server site... */
2769 /* Need account for login... */
2770 else if (nResCode == 332)
2771 bSuccess = FTP_SendAccount(lpwfs);
2772 else
2773 FTP_SetResponseError(nResCode);
2774 }
2775
2776 lend:
2777 TRACE("Returning %d\n", bSuccess);
2778 return bSuccess;
2779 }
2780
2781
2782 /***********************************************************************
2783 * FTP_SendAccount (internal)
2784 *
2785 *
2786 *
2787 * RETURNS
2788 * TRUE on success
2789 * FALSE on failure
2790 *
2791 */
2792 static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
2793 {
2794 INT nResCode;
2795 BOOL bSuccess = FALSE;
2796
2797 TRACE("\n");
2798 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2799 goto lend;
2800
2801 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2802 if (nResCode)
2803 bSuccess = TRUE;
2804 else
2805 FTP_SetResponseError(nResCode);
2806
2807 lend:
2808 return bSuccess;
2809 }
2810
2811
2812 /***********************************************************************
2813 * FTP_SendStore (internal)
2814 *
2815 * Send request to upload file to ftp server
2816 *
2817 * RETURNS
2818 * TRUE on success
2819 * FALSE on failure
2820 *
2821 */
2822 static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2823 {
2824 INT nResCode;
2825 BOOL bSuccess = FALSE;
2826
2827 TRACE("\n");
2828 if (!FTP_InitListenSocket(lpwfs))
2829 goto lend;
2830
2831 if (!FTP_SendType(lpwfs, dwType))
2832 goto lend;
2833
2834 if (!FTP_SendPortOrPasv(lpwfs))
2835 goto lend;
2836
2837 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2838 goto lend;
2839 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2840 if (nResCode)
2841 {
2842 if (nResCode == 150 || nResCode == 125)
2843 bSuccess = TRUE;
2844 else
2845 FTP_SetResponseError(nResCode);
2846 }
2847
2848 lend:
2849 if (!bSuccess && lpwfs->lstnSocket != -1)
2850 {
2851 closesocket(lpwfs->lstnSocket);
2852 lpwfs->lstnSocket = -1;
2853 }
2854
2855 return bSuccess;
2856 }
2857
2858
2859 /***********************************************************************
2860 * FTP_InitListenSocket (internal)
2861 *
2862 * Create a socket to listen for server response
2863 *
2864 * RETURNS
2865 * TRUE on success
2866 * FALSE on failure
2867 *
2868 */
2869 static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
2870 {
2871 BOOL bSuccess = FALSE;
2872 socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
2873
2874 TRACE("\n");
2875
2876 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2877 if (lpwfs->lstnSocket == -1)
2878 {
2879 TRACE("Unable to create listening socket\n");
2880 goto lend;
2881 }
2882
2883 /* We obtain our ip addr from the name of the command channel socket */
2884 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2885
2886 /* and get the system to assign us a port */
2887 lpwfs->lstnSocketAddress.sin_port = htons(0);
2888
2889 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
2890 {
2891 TRACE("Unable to bind socket\n");
2892 goto lend;
2893 }
2894
2895 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2896 {
2897 TRACE("listen failed\n");
2898 goto lend;
2899 }
2900
2901 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2902 bSuccess = TRUE;
2903
2904 lend:
2905 if (!bSuccess && lpwfs->lstnSocket != -1)
2906 {
2907 closesocket(lpwfs->lstnSocket);
2908 lpwfs->lstnSocket = -1;
2909 }
2910
2911 return bSuccess;
2912 }
2913
2914
2915 /***********************************************************************
2916 * FTP_SendType (internal)
2917 *
2918 * Tell server type of data being transferred
2919 *
2920 * RETURNS
2921 * TRUE on success
2922 * FALSE on failure
2923 *
2924 * W98SE doesn't cache the type that's currently set
2925 * (i.e. it sends it always),
2926 * so we probably don't want to do that either.
2927 */
2928 static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
2929 {
2930 INT nResCode;
2931 WCHAR type[] = { 'I','\0' };
2932 BOOL bSuccess = FALSE;
2933
2934 TRACE("\n");
2935 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2936 type[0] = 'A';
2937
2938 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2939 goto lend;
2940
2941 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2942 if (nResCode)
2943 {
2944 if (nResCode == 2)
2945 bSuccess = TRUE;
2946 else
2947 FTP_SetResponseError(nResCode);
2948 }
2949
2950 lend:
2951 return bSuccess;
2952 }
2953
2954
2955 #if 0 /* FIXME: should probably be used for FtpGetFileSize */
2956 /***********************************************************************
2957 * FTP_GetFileSize (internal)
2958 *
2959 * Retrieves from the server the size of the given file
2960 *
2961 * RETURNS
2962 * TRUE on success
2963 * FALSE on failure
2964 *
2965 */
2966 static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2967 {
2968 INT nResCode;
2969 BOOL bSuccess = FALSE;
2970
2971 TRACE("\n");
2972
2973 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2974 goto lend;
2975
2976 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2977 if (nResCode)
2978 {
2979 if (nResCode == 213) {
2980 /* Now parses the output to get the actual file size */
2981 int i;
2982 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2983
2984 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2985 if (lpszResponseBuffer[i] == '\0') return FALSE;
2986 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2987
2988 bSuccess = TRUE;
2989 } else {
2990 FTP_SetResponseError(nResCode);
2991 }
2992 }
2993
2994 lend:
2995 return bSuccess;
2996 }
2997 #endif
2998
2999
3000 /***********************************************************************
3001 * FTP_SendPort (internal)
3002 *
3003 * Tell server which port to use
3004 *
3005 * RETURNS
3006 * TRUE on success
3007 * FALSE on failure
3008 *
3009 */
3010 static BOOL FTP_SendPort(ftp_session_t *lpwfs)
3011 {
3012 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
3013 INT nResCode;
3014 WCHAR szIPAddress[64];
3015 BOOL bSuccess = FALSE;
3016 TRACE("\n");
3017
3018 sprintfW(szIPAddress, szIPFormat,
3019 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
3020 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
3021 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
3022 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
3023 lpwfs->lstnSocketAddress.sin_port & 0xFF,
3024 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
3025
3026 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
3027 goto lend;
3028
3029 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3030 if (nResCode)
3031 {
3032 if (nResCode == 200)
3033 bSuccess = TRUE;
3034 else
3035 FTP_SetResponseError(nResCode);
3036 }
3037
3038 lend:
3039 return bSuccess;
3040 }
3041
3042
3043 /***********************************************************************
3044 * FTP_DoPassive (internal)
3045 *
3046 * Tell server that we want to do passive transfers
3047 * and connect data socket
3048 *
3049 * RETURNS
3050 * TRUE on success
3051 * FALSE on failure
3052 *
3053 */
3054 static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
3055 {
3056 INT nResCode;
3057 BOOL bSuccess = FALSE;
3058
3059 TRACE("\n");
3060 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
3061 goto lend;
3062
3063 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3064 if (nResCode)
3065 {
3066 if (nResCode == 227)
3067 {
3068 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
3069 LPSTR p;
3070 int f[6];
3071 int i;
3072 char *pAddr, *pPort;
3073 INT nsocket = -1;
3074 struct sockaddr_in dataSocketAddress;
3075
3076 p = lpszResponseBuffer+4; /* skip status code */
3077 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
3078
3079 if (*p == '\0')
3080 {
3081 ERR("no address found in response, aborting\n");
3082 goto lend;
3083 }
3084
3085 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
3086 &f[4], &f[5]) != 6)
3087 {
3088 ERR("unknown response address format '%s', aborting\n", p);
3089 goto lend;
3090 }
3091 for (i=0; i < 6; i++)
3092 f[i] = f[i] & 0xff;
3093
3094 dataSocketAddress = lpwfs->socketAddress;
3095 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
3096 pPort = (char *)&(dataSocketAddress.sin_port);
3097 pAddr[0] = f[0];
3098 pAddr[1] = f[1];
3099 pAddr[2] = f[2];
3100 pAddr[3] = f[3];
3101 pPort[0] = f[4];
3102 pPort[1] = f[5];
3103
3104 nsocket = socket(AF_INET,SOCK_STREAM,0);
3105 if (nsocket == -1)
3106 goto lend;
3107
3108 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
3109 {
3110 ERR("can't connect passive FTP data port.\n");
3111 closesocket(nsocket);
3112 goto lend;
3113 }
3114 lpwfs->pasvSocket = nsocket;
3115 bSuccess = TRUE;
3116 }
3117 else
3118 FTP_SetResponseError(nResCode);
3119 }
3120
3121 lend:
3122 return bSuccess;
3123 }
3124
3125
3126 static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
3127 {
3128 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3129 {
3130 if (!FTP_DoPassive(lpwfs))
3131 return FALSE;
3132 }
3133 else
3134 {
3135 if (!FTP_SendPort(lpwfs))
3136 return FALSE;
3137 }
3138 return TRUE;
3139 }
3140
3141
3142 /***********************************************************************
3143 * FTP_GetDataSocket (internal)
3144 *
3145 * Either accepts an incoming data socket connection from the server
3146 * or just returns the already opened socket after a PASV command
3147 * in case of passive FTP.
3148 *
3149 *
3150 * RETURNS
3151 * TRUE on success
3152 * FALSE on failure
3153 *
3154 */
3155 static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
3156 {
3157 struct sockaddr_in saddr;
3158 socklen_t addrlen = sizeof(struct sockaddr);
3159
3160 TRACE("\n");
3161 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
3162 {
3163 *nDataSocket = lpwfs->pasvSocket;
3164 lpwfs->pasvSocket = -1;
3165 }
3166 else
3167 {
3168 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
3169 closesocket(lpwfs->lstnSocket);
3170 lpwfs->lstnSocket = -1;
3171 }
3172 return *nDataSocket != -1;
3173 }
3174
3175
3176 /***********************************************************************
3177 * FTP_SendData (internal)
3178 *
3179 * Send data to the server
3180 *
3181 * RETURNS
3182 * TRUE on success
3183 * FALSE on failure
3184 *
3185 */
3186 static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3187 {
3188 BY_HANDLE_FILE_INFORMATION fi;
3189 DWORD nBytesRead = 0;
3190 DWORD nBytesSent = 0;
3191 DWORD nTotalSent = 0;
3192 DWORD nBytesToSend, nLen;
3193 int nRC = 1;
3194 time_t s_long_time, e_long_time;
3195 LONG nSeconds;
3196 CHAR *lpszBuffer;
3197
3198 TRACE("\n");
3199 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3200
3201 /* Get the size of the file. */
3202 GetFileInformationByHandle(hFile, &fi);
3203 time(&s_long_time);
3204
3205 do
3206 {
3207 nBytesToSend = nBytesRead - nBytesSent;
3208
3209 if (nBytesToSend <= 0)
3210 {
3211 /* Read data from file. */
3212 nBytesSent = 0;
3213 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
3214 ERR("Failed reading from file\n");
3215
3216 if (nBytesRead > 0)
3217 nBytesToSend = nBytesRead;
3218 else
3219 break;
3220 }
3221
3222 nLen = DATA_PACKET_SIZE < nBytesToSend ?
3223 DATA_PACKET_SIZE : nBytesToSend;
3224 nRC = sock_send(nDataSocket, lpszBuffer, nLen, 0);
3225
3226 if (nRC != -1)
3227 {
3228 nBytesSent += nRC;
3229 nTotalSent += nRC;
3230 }
3231
3232 /* Do some computation to display the status. */
3233 time(&e_long_time);
3234 nSeconds = e_long_time - s_long_time;
3235 if( nSeconds / 60 > 0 )
3236 {
3237 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
3238 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
3239 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
3240 }
3241 else
3242 {
3243 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
3244 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
3245 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
3246 }
3247 } while (nRC != -1);
3248
3249 TRACE("file transfer complete!\n");
3250
3251 heap_free(lpszBuffer);
3252 return nTotalSent;
3253 }
3254
3255
3256 /***********************************************************************
3257 * FTP_SendRetrieve (internal)
3258 *
3259 * Send request to retrieve a file
3260 *
3261 * RETURNS
3262 * Number of bytes to be received on success
3263 * 0 on failure
3264 *
3265 */
3266 static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
3267 {
3268 INT nResCode;
3269 BOOL ret;
3270
3271 TRACE("\n");
3272 if (!(ret = FTP_InitListenSocket(lpwfs)))
3273 goto lend;
3274
3275 if (!(ret = FTP_SendType(lpwfs, dwType)))
3276 goto lend;
3277
3278 if (!(ret = FTP_SendPortOrPasv(lpwfs)))
3279 goto lend;
3280
3281 if (!(ret = FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)))
3282 goto lend;
3283
3284 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
3285 if ((nResCode != 125) && (nResCode != 150)) {
3286 /* That means that we got an error getting the file. */
3287 FTP_SetResponseError(nResCode);
3288 ret = FALSE;
3289 }
3290
3291 lend:
3292 if (!ret && lpwfs->lstnSocket != -1)
3293 {
3294 closesocket(lpwfs->lstnSocket);
3295 lpwfs->lstnSocket = -1;
3296 }
3297
3298 return ret;
3299 }
3300
3301
3302 /***********************************************************************
3303 * FTP_RetrieveData (internal)
3304 *
3305 * Retrieve data from server
3306 *
3307 * RETURNS
3308 * TRUE on success
3309 * FALSE on failure
3310 *
3311 */
3312 static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
3313 {
3314 DWORD nBytesWritten;
3315 INT nRC = 0;
3316 CHAR *lpszBuffer;
3317
3318 TRACE("\n");
3319
3320 lpszBuffer = heap_alloc_zero(sizeof(CHAR)*DATA_PACKET_SIZE);
3321 if (NULL == lpszBuffer)
3322 {
3323 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
3324 return FALSE;
3325 }
3326
3327 while (nRC != -1)
3328 {
3329 nRC = sock_recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
3330 if (nRC != -1)
3331 {
3332 /* other side closed socket. */
3333 if (nRC == 0)
3334 goto recv_end;
3335 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
3336 }
3337 }
3338
3339 TRACE("Data transfer complete\n");
3340
3341 recv_end:
3342 heap_free(lpszBuffer);
3343 return (nRC != -1);
3344 }
3345
3346 /***********************************************************************
3347 * FTPFINDNEXT_Destroy (internal)
3348 *
3349 * Deallocate session handle
3350 */
3351 static void FTPFINDNEXT_Destroy(object_header_t *hdr)
3352 {
3353 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
3354 DWORD i;
3355
3356 TRACE("\n");
3357
3358 WININET_Release(&lpwfn->lpFtpSession->hdr);
3359
3360 for (i = 0; i < lpwfn->size; i++)
3361 {
3362 heap_free(lpwfn->lpafp[i].lpszName);
3363 }
3364 heap_free(lpwfn->lpafp);
3365 }
3366
3367 static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
3368 {
3369 WIN32_FIND_DATAW *find_data = data;
3370 DWORD res = ERROR_SUCCESS;
3371
3372 TRACE("index(%d) size(%d)\n", find->index, find->size);
3373
3374 ZeroMemory(find_data, sizeof(WIN32_FIND_DATAW));
3375
3376 if (find->index < find->size) {
3377 FTP_ConvertFileProp(&find->lpafp[find->index], find_data);
3378 find->index++;
3379
3380 TRACE("Name: %s\nSize: %d\n", debugstr_w(find_data->cFileName), find_data->nFileSizeLow);
3381 }else {
3382 res = ERROR_NO_MORE_FILES;
3383 }
3384
3385 if (find->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3386 {
3387 INTERNET_ASYNC_RESULT iar;
3388
3389 iar.dwResult = (res == ERROR_SUCCESS);
3390 iar.dwError = res;
3391
3392 INTERNET_SendCallback(&find->hdr, find->hdr.dwContext,
3393 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
3394 sizeof(INTERNET_ASYNC_RESULT));
3395 }
3396
3397 return res;
3398 }
3399
3400 typedef struct {
3401 task_header_t hdr;
3402 WIN32_FIND_DATAW *find_data;
3403 } find_next_task_t;
3404
3405 static void FTPFINDNEXT_AsyncFindNextFileProc(task_header_t *hdr)
3406 {
3407 find_next_task_t *task = (find_next_task_t*)hdr;
3408
3409 FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)task->hdr.hdr, task->find_data);
3410 }
3411
3412 static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
3413 {
3414 switch(option) {
3415 case INTERNET_OPTION_HANDLE_TYPE:
3416 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
3417
3418 if (*size < sizeof(ULONG))
3419 return ERROR_INSUFFICIENT_BUFFER;
3420
3421 *size = sizeof(DWORD);
3422 *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FIND;
3423 return ERROR_SUCCESS;
3424 }
3425
3426 return INET_QueryOption(hdr, option, buffer, size, unicode);
3427 }
3428
3429 static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
3430 {
3431 WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
3432
3433 if (find->lpFtpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
3434 {
3435 find_next_task_t *task;
3436
3437 task = alloc_async_task(&find->hdr, FTPFINDNEXT_AsyncFindNextFileProc, sizeof(*task));
3438 task->find_data = data;
3439
3440 INTERNET_AsyncCall(&task->hdr);
3441 return ERROR_SUCCESS;
3442 }
3443
3444 return FTPFINDNEXT_FindNextFileProc(find, data);
3445 }
3446
3447 static const object_vtbl_t FTPFINDNEXTVtbl = {
3448 FTPFINDNEXT_Destroy,
3449 NULL,
3450 FTPFINDNEXT_QueryOption,
3451 INET_SetOption,
3452 NULL,
3453 NULL,
3454 NULL,
3455 NULL,
3456 FTPFINDNEXT_FindNextFileW
3457 };
3458
3459 /***********************************************************************
3460 * FTP_ReceiveFileList (internal)
3461 *
3462 * Read file list from server
3463 *
3464 * RETURNS
3465 * Handle to file list on success
3466 * NULL on failure
3467 *
3468 */
3469 static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3470 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
3471 {
3472 DWORD dwSize = 0;
3473 LPFILEPROPERTIESW lpafp = NULL;
3474 LPWININETFTPFINDNEXTW lpwfn = NULL;
3475
3476 TRACE("(%p,%d,%s,%p,%08lx)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
3477
3478 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3479 {
3480 if(lpFindFileData)
3481 FTP_ConvertFileProp(lpafp, lpFindFileData);
3482
3483 lpwfn = alloc_object(&lpwfs->hdr, &FTPFINDNEXTVtbl, sizeof(WININETFTPFINDNEXTW));
3484 if (lpwfn)
3485 {
3486 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3487 lpwfn->hdr.dwContext = dwContext;
3488 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3489 lpwfn->size = dwSize;
3490 lpwfn->lpafp = lpafp;
3491
3492 WININET_AddRef( &lpwfs->hdr );
3493 lpwfn->lpFtpSession = lpwfs;
3494 list_add_head( &lpwfs->hdr.children, &lpwfn->hdr.entry );
3495 }
3496 }
3497
3498 TRACE("Matched %d files\n", dwSize);
3499 return lpwfn ? lpwfn->hdr.hInternet : NULL;
3500 }
3501
3502
3503 /***********************************************************************
3504 * FTP_ConvertFileProp (internal)
3505 *
3506 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3507 *
3508 * RETURNS
3509 * TRUE on success
3510 * FALSE on failure
3511 *
3512 */
3513 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3514 {
3515 BOOL bSuccess = FALSE;
3516
3517 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3518
3519 if (lpafp)
3520 {
3521 SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
3522 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3523 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3524
3525 /* Not all fields are filled in */
3526 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3527 lpFindFileData->nFileSizeLow = lpafp->nSize;
3528
3529 if (lpafp->bIsDirectory)
3530 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3531
3532 if (lpafp->lpszName)
3533 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3534
3535 bSuccess = TRUE;
3536 }
3537
3538 return bSuccess;
3539 }
3540
3541 /***********************************************************************
3542 * FTP_ParseNextFile (internal)
3543 *
3544 * Parse the next line in file listing
3545 *
3546 * RETURNS
3547 * TRUE on success
3548 * FALSE on failure
3549 */
3550 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3551 {
3552 static const char szSpace[] = " \t";
3553 DWORD nBufLen;
3554 char *pszLine;
3555 char *pszToken;
3556 char *pszTmp;
3557 BOOL found = FALSE;
3558 int i;
3559
3560 lpfp->lpszName = NULL;
3561 do {
3562 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3563 return FALSE;
3564
3565 pszToken = strtok(pszLine, szSpace);
3566 /* ls format
3567 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3568 *
3569 * For instance:
3570 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3571 */
3572 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3573 if(!FTP_ParsePermission(pszToken, lpfp))
3574 lpfp->bIsDirectory = FALSE;
3575 for(i=0; i<=3; i++) {
3576 if(!(pszToken = strtok(NULL, szSpace)))
3577 break;
3578 }
3579 if(!pszToken) continue;
3580 if(lpfp->bIsDirectory) {
3581 TRACE("Is directory\n");
3582 lpfp->nSize = 0;
3583 }
3584 else {
3585 TRACE("Size: %s\n", pszToken);
3586 lpfp->nSize = atol(pszToken);
3587 }
3588
3589 lpfp->tmLastModified.wSecond = 0;
3590 lpfp->tmLastModified.wMinute = 0;
3591 lpfp->tmLastModified.wHour = 0;
3592 lpfp->tmLastModified.wDay = 0;
3593 lpfp->tmLastModified.wMonth = 0;
3594 lpfp->tmLastModified.wYear = 0;
3595
3596 /* Determine month */
3597 pszToken = strtok(NULL, szSpace);
3598 if(!pszToken) continue;
3599 if(strlen(pszToken) >= 3) {
3600 pszToken[3] = 0;
3601 if((pszTmp = StrStrIA(szMonths, pszToken)))
3602 lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
3603 }
3604 /* Determine day */
3605 pszToken = strtok(NULL, szSpace);
3606 if(!pszToken) continue;
3607 lpfp->tmLastModified.wDay = atoi(pszToken);
3608 /* Determine time or year */
3609 pszToken = strtok(NULL, szSpace);
3610 if(!pszToken) continue;
3611 if((pszTmp = strchr(pszToken, ':'))) {
3612 SYSTEMTIME curr_time;
3613 *pszTmp = 0;
3614 pszTmp++;
3615 lpfp->tmLastModified.wMinute = atoi(pszTmp);
3616 lpfp->tmLastModified.wHour = atoi(pszToken);
3617 GetLocalTime( &curr_time );
3618 lpfp->tmLastModified.wYear = curr_time.wYear;
3619 }
3620 else {
3621 lpfp->tmLastModified.wYear = atoi(pszToken);
3622 lpfp->tmLastModified.wHour = 12;
3623 }
3624 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3625 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3626 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3627
3628 pszToken = strtok(NULL, szSpace);
3629 if(!pszToken) continue;
3630 lpfp->lpszName = heap_strdupAtoW(pszToken);
3631 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3632 }
3633 /* NT way of parsing ... :
3634
3635 07-13-03 08:55PM <DIR> sakpatch
3636 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3637 */
3638 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3639 int mon, mday, year, hour, min;
3640 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3641
3642 sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
3643 lpfp->tmLastModified.wDay = mday;
3644 lpfp->tmLastModified.wMonth = mon;
3645 lpfp->tmLastModified.wYear = year;
3646
3647 /* Hacky and bad Y2K protection :-) */
3648 if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
3649
3650 pszToken = strtok(NULL, szSpace);
3651 if(!pszToken) continue;
3652 sscanf(pszToken, "%d:%d", &hour, &min);
3653 lpfp->tmLastModified.wHour = hour;
3654 lpfp->tmLastModified.wMinute = min;
3655 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3656 lpfp->tmLastModified.wHour += 12;
3657 }
3658 lpfp->tmLastModified.wSecond = 0;
3659
3660 TRACE("Mod time: %02d:%02d:%02d %04d/%02d/%02d\n",
3661 lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
3662 lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
3663
3664 pszToken = strtok(NULL, szSpace);
3665 if(!pszToken) continue;
3666 if(!strcasecmp(pszToken, "<DIR>")) {
3667 lpfp->bIsDirectory = TRUE;
3668 lpfp->nSize = 0;
3669 TRACE("Is directory\n");
3670 }
3671 else {
3672 lpfp->bIsDirectory = FALSE;
3673 lpfp->nSize = atol(pszToken);
3674 TRACE("Size: %d\n", lpfp->nSize);
3675 }
3676
3677 pszToken = strtok(NULL, szSpace);
3678 if(!pszToken) continue;
3679 lpfp->lpszName = heap_strdupAtoW(pszToken);
3680 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3681 }
3682 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3683 else if(pszToken[0] == '+') {
3684 FIXME("EPLF Format not implemented\n");
3685 }
3686
3687 if(lpfp->lpszName) {
3688 if((lpszSearchFile == NULL) ||
3689 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3690 found = TRUE;
3691 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3692 }
3693 else {
3694 heap_free(lpfp->lpszName);
3695 lpfp->lpszName = NULL;
3696 }
3697 }
3698 } while(!found);
3699 return TRUE;
3700 }
3701
3702 /***********************************************************************
3703 * FTP_ParseDirectory (internal)
3704 *
3705 * Parse string of directory information
3706 *
3707 * RETURNS
3708 * TRUE on success
3709 * FALSE on failure
3710 */
3711 static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3712 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3713 {
3714 BOOL bSuccess = TRUE;
3715 INT sizeFilePropArray = 500;/*20; */
3716 INT indexFilePropArray = -1;
3717
3718 TRACE("\n");
3719
3720 /* Allocate initial file properties array */
3721 *lpafp = heap_alloc_zero(sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3722 if (!*lpafp)
3723 return FALSE;
3724
3725 do {
3726 if (indexFilePropArray+1 >= sizeFilePropArray)
3727 {
3728 LPFILEPROPERTIESW tmpafp;
3729
3730 sizeFilePropArray *= 2;
3731 tmpafp = heap_realloc_zero(*lpafp, sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3732 if (NULL == tmpafp)
3733 {
3734 bSuccess = FALSE;
3735 break;
3736 }
3737
3738 *lpafp = tmpafp;
3739 }
3740 indexFilePropArray++;
3741 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3742
3743 if (bSuccess && indexFilePropArray)
3744 {
3745 if (indexFilePropArray < sizeFilePropArray - 1)
3746 {
3747 LPFILEPROPERTIESW tmpafp;
3748
3749 tmpafp = heap_realloc(*lpafp, sizeof(FILEPROPERTIESW)*indexFilePropArray);
3750 if (NULL != tmpafp)
3751 *lpafp = tmpafp;
3752 }
3753 *dwfp = indexFilePropArray;
3754 }
3755 else
3756 {
3757 heap_free(*lpafp);
3758 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3759 bSuccess = FALSE;
3760 }
3761
3762 return bSuccess;
3763 }
3764
3765
3766 /***********************************************************************
3767 * FTP_ParsePermission (internal)
3768 *
3769 * Parse permission string of directory information
3770 *
3771 * RETURNS
3772 * TRUE on success
3773 * FALSE on failure
3774 *
3775 */
3776 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3777 {
3778 BOOL bSuccess = TRUE;
3779 unsigned short nPermission = 0;
3780 INT nPos = 1;
3781 INT nLast = 9;
3782
3783 TRACE("\n");
3784 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3785 {
3786 bSuccess = FALSE;
3787 return bSuccess;
3788 }
3789
3790 lpfp->bIsDirectory = (*lpszPermission == 'd');
3791 do
3792 {
3793 switch (nPos)
3794 {
3795 case 1:
3796 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3797 break;
3798 case 2:
3799 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3800 break;
3801 case 3:
3802 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3803 break;
3804 case 4:
3805 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3806 break;
3807 case 5:
3808 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3809 break;
3810 case 6:
3811 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3812 break;
3813 case 7:
3814 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3815 break;
3816 case 8:
3817 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3818 break;
3819 case 9:
3820 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3821 break;
3822 }
3823 nPos++;
3824 }while (nPos <= nLast);
3825
3826 lpfp->permissions = nPermission;
3827 return bSuccess;
3828 }
3829
3830
3831 /***********************************************************************
3832 * FTP_SetResponseError (internal)
3833 *
3834 * Set the appropriate error code for a given response from the server
3835 *
3836 * RETURNS
3837 *
3838 */
3839 static DWORD FTP_SetResponseError(DWORD dwResponse)
3840 {
3841 DWORD dwCode = 0;
3842
3843 switch(dwResponse)
3844 {
3845 case 425: /* Cannot open data connection. */
3846 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3847 break;
3848
3849 case 426: /* Connection closed, transer aborted. */
3850 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3851 break;
3852
3853 case 530: /* Not logged in. Login incorrect. */
3854 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3855 break;
3856
3857 case 421: /* Service not available - Server may be shutting down. */
3858 case 450: /* File action not taken. File may be busy. */
3859 case 451: /* Action aborted. Server error. */
3860 case 452: /* Action not taken. Insufficient storage space on server. */
3861 case 500: /* Syntax error. Command unrecognized. */
3862 case 501: /* Syntax error. Error in parameters or arguments. */
3863 case 502: /* Command not implemented. */
3864 case 503: /* Bad sequence of commands. */
3865 case 504: /* Command not implemented for that parameter. */
3866 case 532: /* Need account for storing files */
3867 case 550: /* File action not taken. File not found or no access. */
3868 case 551: /* Requested action aborted. Page type unknown */
3869 case 552: /* Action aborted. Exceeded storage allocation */
3870 case 553: /* Action not taken. File name not allowed. */
3871
3872 default:
3873 dwCode = ERROR_INTERNET_EXTENDED_ERROR;
3874 break;
3875 }
3876
3877 INTERNET_SetLastError(dwCode);
3878 return dwCode;
3879 }