1 /*
2 * WININET - Ftp implementation
3 *
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
7 * Copyright 2007 Hans Leidekker
8 *
9 * Ulrich Czekalla
10 * Noureddine Jemmali
11 *
12 * Copyright 2000 Andreas Mohr
13 * Copyright 2002 Jaco Greeff
14 *
15 * This library is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with this library; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 */
29
30 #include "config.h"
31 #include "wine/port.h"
32
33 #if defined(__MINGW32__) || defined (_MSC_VER)
34 #include <ws2tcpip.h>
35 #endif
36
37 #include <errno.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/types.h>
43 #ifdef HAVE_SYS_SOCKET_H
44 # include <sys/socket.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #ifdef HAVE_SYS_IOCTL_H
50 # include <sys/ioctl.h>
51 #endif
52 #include <time.h>
53 #include <assert.h>
54
55 #include "windef.h"
56 #include "winbase.h"
57 #include "wingdi.h"
58 #include "winuser.h"
59 #include "wininet.h"
60 #include "winnls.h"
61 #include "winerror.h"
62 #include "winreg.h"
63 #include "winternl.h"
64 #include "shlwapi.h"
65
66 #include "wine/debug.h"
67 #include "internet.h"
68
69 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
70
71 typedef struct _WININETFTPSESSIONW WININETFTPSESSIONW;
72
73 typedef struct
74 {
75 WININETHANDLEHEADER hdr;
76 WININETFTPSESSIONW *lpFtpSession;
77 BOOL session_deleted;
78 int nDataSocket;
79 } WININETFTPFILE, *LPWININETFTPFILE;
80
81 typedef struct _WININETFTPSESSIONW
82 {
83 WININETHANDLEHEADER hdr;
84 WININETAPPINFOW *lpAppInfo;
85 int sndSocket;
86 int lstnSocket;
87 int pasvSocket; /* data socket connected by us in case of passive FTP */
88 LPWININETFTPFILE download_in_progress;
89 struct sockaddr_in socketAddress;
90 struct sockaddr_in lstnSocketAddress;
91 LPWSTR lpszPassword;
92 LPWSTR lpszUserName;
93 } *LPWININETFTPSESSIONW;
94
95 typedef struct
96 {
97 BOOL bIsDirectory;
98 LPWSTR lpszName;
99 DWORD nSize;
100 SYSTEMTIME tmLastModified;
101 unsigned short permissions;
102 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
103
104 typedef struct
105 {
106 WININETHANDLEHEADER hdr;
107 WININETFTPSESSIONW *lpFtpSession;
108 DWORD index;
109 DWORD size;
110 LPFILEPROPERTIESW lpafp;
111 } WININETFTPFINDNEXTW, *LPWININETFTPFINDNEXTW;
112
113 #define DATA_PACKET_SIZE 0x2000
114 #define szCRLF "\r\n"
115 #define MAX_BACKLOG 5
116
117 /* Testing shows that Windows only accepts dwFlags where the last
118 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
119 */
120 #define FTP_CONDITION_MASK 0x0007
121
122 typedef enum {
123 /* FTP commands with arguments. */
124 FTP_CMD_ACCT,
125 FTP_CMD_CWD,
126 FTP_CMD_DELE,
127 FTP_CMD_MKD,
128 FTP_CMD_PASS,
129 FTP_CMD_PORT,
130 FTP_CMD_RETR,
131 FTP_CMD_RMD,
132 FTP_CMD_RNFR,
133 FTP_CMD_RNTO,
134 FTP_CMD_STOR,
135 FTP_CMD_TYPE,
136 FTP_CMD_USER,
137 FTP_CMD_SIZE,
138
139 /* FTP commands without arguments. */
140 FTP_CMD_ABOR,
141 FTP_CMD_LIST,
142 FTP_CMD_NLST,
143 FTP_CMD_PASV,
144 FTP_CMD_PWD,
145 FTP_CMD_QUIT,
146 } FTP_COMMAND;
147
148 static const CHAR *const szFtpCommands[] = {
149 "ACCT",
150 "CWD",
151 "DELE",
152 "MKD",
153 "PASS",
154 "PORT",
155 "RETR",
156 "RMD",
157 "RNFR",
158 "RNTO",
159 "STOR",
160 "TYPE",
161 "USER",
162 "SIZE",
163 "ABOR",
164 "LIST",
165 "NLST",
166 "PASV",
167 "PWD",
168 "QUIT",
169 };
170
171 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
172 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
173
174 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
175 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
176 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
177 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
178 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
179 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
180 static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
181 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
182 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
183 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
184 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
185 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
186 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
187 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
188 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
189 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
190 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
191 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
192 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
193 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
194 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
195 LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
196 static DWORD FTP_SetResponseError(DWORD dwResponse);
197 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
198 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
199 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
200 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
201 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
202 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
203 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
204 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
205 LPDWORD lpdwCurrentDirectory);
206 static BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
207 static BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
208 static BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
209 static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
210 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
211 DWORD_PTR dwContext);
212
213
214 /***********************************************************************
215 * FtpPutFileA (WININET.@)
216 *
217 * Uploads a file to the FTP server
218 *
219 * RETURNS
220 * TRUE on success
221 * FALSE on failure
222 *
223 */
224 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
225 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
226 {
227 LPWSTR lpwzLocalFile;
228 LPWSTR lpwzNewRemoteFile;
229 BOOL ret;
230
231 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
232 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
233 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
234 dwFlags, dwContext);
235 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
236 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
237 return ret;
238 }
239
240 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
241 {
242 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
243 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
244
245 TRACE("%p\n", lpwfs);
246
247 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
248 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
249
250 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
251 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
252 }
253
254 /***********************************************************************
255 * FtpPutFileW (WININET.@)
256 *
257 * Uploads a file to the FTP server
258 *
259 * RETURNS
260 * TRUE on success
261 * FALSE on failure
262 *
263 */
264 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
265 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
266 {
267 LPWININETFTPSESSIONW lpwfs;
268 LPWININETAPPINFOW hIC = NULL;
269 BOOL r = FALSE;
270
271 if (!lpszLocalFile || !lpszNewRemoteFile)
272 {
273 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
274 return FALSE;
275 }
276
277 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
278 if (!lpwfs)
279 {
280 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
281 return FALSE;
282 }
283
284 if (WH_HFTPSESSION != lpwfs->hdr.htype)
285 {
286 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
287 goto lend;
288 }
289
290 if (lpwfs->download_in_progress != NULL)
291 {
292 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
293 goto lend;
294 }
295
296 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
297 {
298 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
299 goto lend;
300 }
301
302 hIC = lpwfs->lpAppInfo;
303 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
304 {
305 WORKREQUEST workRequest;
306 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
307
308 workRequest.asyncproc = AsyncFtpPutFileProc;
309 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
310 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
311 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
312 req->dwFlags = dwFlags;
313 req->dwContext = dwContext;
314
315 r = INTERNET_AsyncCall(&workRequest);
316 }
317 else
318 {
319 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
320 lpszNewRemoteFile, dwFlags, dwContext);
321 }
322
323 lend:
324 WININET_Release( &lpwfs->hdr );
325
326 return r;
327 }
328
329 /***********************************************************************
330 * FTP_FtpPutFileW (Internal)
331 *
332 * Uploads a file to the FTP server
333 *
334 * RETURNS
335 * TRUE on success
336 * FALSE on failure
337 *
338 */
339 static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
340 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
341 {
342 HANDLE hFile;
343 BOOL bSuccess = FALSE;
344 LPWININETAPPINFOW hIC = NULL;
345 INT nResCode;
346
347 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
348
349 /* Clear any error information */
350 INTERNET_SetLastError(0);
351
352 /* Open file to be uploaded */
353 if (INVALID_HANDLE_VALUE ==
354 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
355 /* Let CreateFile set the appropriate error */
356 return FALSE;
357
358 hIC = lpwfs->lpAppInfo;
359
360 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
361
362 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
363 {
364 INT nDataSocket;
365
366 /* Get data socket to server */
367 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
368 {
369 FTP_SendData(lpwfs, nDataSocket, hFile);
370 closesocket(nDataSocket);
371 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
372 if (nResCode)
373 {
374 if (nResCode == 226)
375 bSuccess = TRUE;
376 else
377 FTP_SetResponseError(nResCode);
378 }
379 }
380 }
381
382 if (lpwfs->lstnSocket != -1)
383 closesocket(lpwfs->lstnSocket);
384
385 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
386 {
387 INTERNET_ASYNC_RESULT iar;
388
389 iar.dwResult = (DWORD)bSuccess;
390 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
391 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
392 &iar, sizeof(INTERNET_ASYNC_RESULT));
393 }
394
395 CloseHandle(hFile);
396
397 return bSuccess;
398 }
399
400
401 /***********************************************************************
402 * FtpSetCurrentDirectoryA (WININET.@)
403 *
404 * Change the working directory on the FTP server
405 *
406 * RETURNS
407 * TRUE on success
408 * FALSE on failure
409 *
410 */
411 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
412 {
413 LPWSTR lpwzDirectory;
414 BOOL ret;
415
416 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
417 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
418 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
419 return ret;
420 }
421
422
423 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
424 {
425 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
426 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
427
428 TRACE("%p\n", lpwfs);
429
430 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
431 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
432 }
433
434 /***********************************************************************
435 * FtpSetCurrentDirectoryW (WININET.@)
436 *
437 * Change the working directory on the FTP server
438 *
439 * RETURNS
440 * TRUE on success
441 * FALSE on failure
442 *
443 */
444 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
445 {
446 LPWININETFTPSESSIONW lpwfs = NULL;
447 LPWININETAPPINFOW hIC = NULL;
448 BOOL r = FALSE;
449
450 if (!lpszDirectory)
451 {
452 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
453 goto lend;
454 }
455
456 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
457 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
458 {
459 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
460 goto lend;
461 }
462
463 if (lpwfs->download_in_progress != NULL)
464 {
465 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
466 goto lend;
467 }
468
469 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
470
471 hIC = lpwfs->lpAppInfo;
472 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
473 {
474 WORKREQUEST workRequest;
475 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
476
477 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
478 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
479 req = &workRequest.u.FtpSetCurrentDirectoryW;
480 req->lpszDirectory = WININET_strdupW(lpszDirectory);
481
482 r = INTERNET_AsyncCall(&workRequest);
483 }
484 else
485 {
486 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
487 }
488
489 lend:
490 if( lpwfs )
491 WININET_Release( &lpwfs->hdr );
492
493 return r;
494 }
495
496
497 /***********************************************************************
498 * FTP_FtpSetCurrentDirectoryW (Internal)
499 *
500 * Change the working directory on the FTP server
501 *
502 * RETURNS
503 * TRUE on success
504 * FALSE on failure
505 *
506 */
507 static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
508 {
509 INT nResCode;
510 LPWININETAPPINFOW hIC = NULL;
511 DWORD bSuccess = FALSE;
512
513 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
514
515 /* Clear any error information */
516 INTERNET_SetLastError(0);
517
518 hIC = lpwfs->lpAppInfo;
519 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
520 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
521 goto lend;
522
523 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
524
525 if (nResCode)
526 {
527 if (nResCode == 250)
528 bSuccess = TRUE;
529 else
530 FTP_SetResponseError(nResCode);
531 }
532
533 lend:
534 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
535 {
536 INTERNET_ASYNC_RESULT iar;
537
538 iar.dwResult = bSuccess;
539 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
540 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
541 &iar, sizeof(INTERNET_ASYNC_RESULT));
542 }
543 return bSuccess;
544 }
545
546
547 /***********************************************************************
548 * FtpCreateDirectoryA (WININET.@)
549 *
550 * Create new directory on the FTP server
551 *
552 * RETURNS
553 * TRUE on success
554 * FALSE on failure
555 *
556 */
557 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
558 {
559 LPWSTR lpwzDirectory;
560 BOOL ret;
561
562 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
563 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
564 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
565 return ret;
566 }
567
568
569 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
570 {
571 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
572 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
573
574 TRACE(" %p\n", lpwfs);
575
576 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
577 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
578 }
579
580 /***********************************************************************
581 * FtpCreateDirectoryW (WININET.@)
582 *
583 * Create new directory on the FTP server
584 *
585 * RETURNS
586 * TRUE on success
587 * FALSE on failure
588 *
589 */
590 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
591 {
592 LPWININETFTPSESSIONW lpwfs;
593 LPWININETAPPINFOW hIC = NULL;
594 BOOL r = FALSE;
595
596 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
597 if (!lpwfs)
598 {
599 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
600 return FALSE;
601 }
602
603 if (WH_HFTPSESSION != lpwfs->hdr.htype)
604 {
605 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
606 goto lend;
607 }
608
609 if (lpwfs->download_in_progress != NULL)
610 {
611 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
612 goto lend;
613 }
614
615 if (!lpszDirectory)
616 {
617 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
618 goto lend;
619 }
620
621 hIC = lpwfs->lpAppInfo;
622 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
623 {
624 WORKREQUEST workRequest;
625 struct WORKREQ_FTPCREATEDIRECTORYW *req;
626
627 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
628 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
629 req = &workRequest.u.FtpCreateDirectoryW;
630 req->lpszDirectory = WININET_strdupW(lpszDirectory);
631
632 r = INTERNET_AsyncCall(&workRequest);
633 }
634 else
635 {
636 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
637 }
638 lend:
639 WININET_Release( &lpwfs->hdr );
640
641 return r;
642 }
643
644
645 /***********************************************************************
646 * FTP_FtpCreateDirectoryW (Internal)
647 *
648 * Create new directory on the FTP server
649 *
650 * RETURNS
651 * TRUE on success
652 * FALSE on failure
653 *
654 */
655 static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
656 {
657 INT nResCode;
658 BOOL bSuccess = FALSE;
659 LPWININETAPPINFOW hIC = NULL;
660
661 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
662
663 /* Clear any error information */
664 INTERNET_SetLastError(0);
665
666 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
667 goto lend;
668
669 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
670 if (nResCode)
671 {
672 if (nResCode == 257)
673 bSuccess = TRUE;
674 else
675 FTP_SetResponseError(nResCode);
676 }
677
678 lend:
679 hIC = lpwfs->lpAppInfo;
680 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
681 {
682 INTERNET_ASYNC_RESULT iar;
683
684 iar.dwResult = (DWORD)bSuccess;
685 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
686 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
687 &iar, sizeof(INTERNET_ASYNC_RESULT));
688 }
689
690 return bSuccess;
691 }
692
693 /***********************************************************************
694 * FtpFindFirstFileA (WININET.@)
695 *
696 * Search the specified directory
697 *
698 * RETURNS
699 * HINTERNET on success
700 * NULL on failure
701 *
702 */
703 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
704 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
705 {
706 LPWSTR lpwzSearchFile;
707 WIN32_FIND_DATAW wfd;
708 LPWIN32_FIND_DATAW lpFindFileDataW;
709 HINTERNET ret;
710
711 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
712 lpFindFileDataW = lpFindFileData?&wfd:NULL;
713 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
714 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
715
716 if(lpFindFileData) {
717 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
718 }
719 return ret;
720 }
721
722
723 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
724 {
725 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
726 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
727
728 TRACE("%p\n", lpwfs);
729
730 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
731 req->lpFindFileData, req->dwFlags, req->dwContext);
732 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
733 }
734
735 /***********************************************************************
736 * FtpFindFirstFileW (WININET.@)
737 *
738 * Search the specified directory
739 *
740 * RETURNS
741 * HINTERNET on success
742 * NULL on failure
743 *
744 */
745 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
746 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
747 {
748 LPWININETFTPSESSIONW lpwfs;
749 LPWININETAPPINFOW hIC = NULL;
750 HINTERNET r = NULL;
751
752 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
753 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
754 {
755 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
756 goto lend;
757 }
758
759 if (lpwfs->download_in_progress != NULL)
760 {
761 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
762 goto lend;
763 }
764
765 hIC = lpwfs->lpAppInfo;
766 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
767 {
768 WORKREQUEST workRequest;
769 struct WORKREQ_FTPFINDFIRSTFILEW *req;
770
771 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
772 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
773 req = &workRequest.u.FtpFindFirstFileW;
774 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
775 req->lpFindFileData = lpFindFileData;
776 req->dwFlags = dwFlags;
777 req->dwContext= dwContext;
778
779 INTERNET_AsyncCall(&workRequest);
780 r = NULL;
781 }
782 else
783 {
784 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
785 dwFlags, dwContext);
786 }
787 lend:
788 if( lpwfs )
789 WININET_Release( &lpwfs->hdr );
790
791 return r;
792 }
793
794
795 /***********************************************************************
796 * FTP_FtpFindFirstFileW (Internal)
797 *
798 * Search the specified directory
799 *
800 * RETURNS
801 * HINTERNET on success
802 * NULL on failure
803 *
804 */
805 static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
806 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
807 {
808 INT nResCode;
809 LPWININETAPPINFOW hIC = NULL;
810 HINTERNET hFindNext = NULL;
811
812 TRACE("\n");
813
814 /* Clear any error information */
815 INTERNET_SetLastError(0);
816
817 if (!FTP_InitListenSocket(lpwfs))
818 goto lend;
819
820 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
821 goto lend;
822
823 if (!FTP_SendPortOrPasv(lpwfs))
824 goto lend;
825
826 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
827 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
828 goto lend;
829
830 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
831 if (nResCode)
832 {
833 if (nResCode == 125 || nResCode == 150)
834 {
835 INT nDataSocket;
836
837 /* Get data socket to server */
838 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
839 {
840 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
841 closesocket(nDataSocket);
842 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
843 if (nResCode != 226 && nResCode != 250)
844 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
845 }
846 }
847 else
848 FTP_SetResponseError(nResCode);
849 }
850
851 lend:
852 if (lpwfs->lstnSocket != -1)
853 closesocket(lpwfs->lstnSocket);
854
855 hIC = lpwfs->lpAppInfo;
856 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
857 {
858 INTERNET_ASYNC_RESULT iar;
859
860 if (hFindNext)
861 {
862 iar.dwResult = (DWORD_PTR)hFindNext;
863 iar.dwError = ERROR_SUCCESS;
864 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
865 &iar, sizeof(INTERNET_ASYNC_RESULT));
866 }
867
868 iar.dwResult = (DWORD_PTR)hFindNext;
869 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
870 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
871 &iar, sizeof(INTERNET_ASYNC_RESULT));
872 }
873
874 return hFindNext;
875 }
876
877
878 /***********************************************************************
879 * FtpGetCurrentDirectoryA (WININET.@)
880 *
881 * Retrieves the current directory
882 *
883 * RETURNS
884 * TRUE on success
885 * FALSE on failure
886 *
887 */
888 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
889 LPDWORD lpdwCurrentDirectory)
890 {
891 WCHAR *dir = NULL;
892 DWORD len;
893 BOOL ret;
894
895 if(lpdwCurrentDirectory) {
896 len = *lpdwCurrentDirectory;
897 if(lpszCurrentDirectory)
898 {
899 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
900 if (NULL == dir)
901 {
902 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
903 return FALSE;
904 }
905 }
906 }
907 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
908
909 if (ret && lpszCurrentDirectory)
910 WideCharToMultiByte(CP_ACP, 0, dir, -1, lpszCurrentDirectory, len, NULL, NULL);
911
912 if (lpdwCurrentDirectory) *lpdwCurrentDirectory = len;
913 HeapFree(GetProcessHeap(), 0, dir);
914 return ret;
915 }
916
917
918 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
919 {
920 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
921 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
922
923 TRACE("%p\n", lpwfs);
924
925 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
926 }
927
928 /***********************************************************************
929 * FtpGetCurrentDirectoryW (WININET.@)
930 *
931 * Retrieves the current directory
932 *
933 * RETURNS
934 * TRUE on success
935 * FALSE on failure
936 *
937 */
938 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
939 LPDWORD lpdwCurrentDirectory)
940 {
941 LPWININETFTPSESSIONW lpwfs;
942 LPWININETAPPINFOW hIC = NULL;
943 BOOL r = FALSE;
944
945 TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
946
947 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
948 if (NULL == lpwfs)
949 {
950 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
951 goto lend;
952 }
953
954 if (WH_HFTPSESSION != lpwfs->hdr.htype)
955 {
956 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
957 goto lend;
958 }
959
960 if (!lpdwCurrentDirectory)
961 {
962 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
963 goto lend;
964 }
965
966 if (lpszCurrentDirectory == NULL)
967 {
968 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
969 goto lend;
970 }
971
972 if (lpwfs->download_in_progress != NULL)
973 {
974 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
975 goto lend;
976 }
977
978 hIC = lpwfs->lpAppInfo;
979 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
980 {
981 WORKREQUEST workRequest;
982 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
983
984 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
985 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
986 req = &workRequest.u.FtpGetCurrentDirectoryW;
987 req->lpszDirectory = lpszCurrentDirectory;
988 req->lpdwDirectory = lpdwCurrentDirectory;
989
990 r = INTERNET_AsyncCall(&workRequest);
991 }
992 else
993 {
994 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
995 lpdwCurrentDirectory);
996 }
997
998 lend:
999 if( lpwfs )
1000 WININET_Release( &lpwfs->hdr );
1001
1002 return r;
1003 }
1004
1005
1006 /***********************************************************************
1007 * FTP_FtpGetCurrentDirectoryW (Internal)
1008 *
1009 * Retrieves the current directory
1010 *
1011 * RETURNS
1012 * TRUE on success
1013 * FALSE on failure
1014 *
1015 */
1016 static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
1017 LPDWORD lpdwCurrentDirectory)
1018 {
1019 INT nResCode;
1020 LPWININETAPPINFOW hIC = NULL;
1021 DWORD bSuccess = FALSE;
1022
1023 /* Clear any error information */
1024 INTERNET_SetLastError(0);
1025
1026 hIC = lpwfs->lpAppInfo;
1027 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
1028 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
1029 goto lend;
1030
1031 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1032 if (nResCode)
1033 {
1034 if (nResCode == 257) /* Extract directory name */
1035 {
1036 DWORD firstpos, lastpos, len;
1037 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
1038
1039 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
1040 {
1041 if ('"' == lpszResponseBuffer[lastpos])
1042 {
1043 if (!firstpos)
1044 firstpos = lastpos;
1045 else
1046 break;
1047 }
1048 }
1049 len = lastpos - firstpos;
1050 if (*lpdwCurrentDirectory >= len)
1051 {
1052 memcpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos + 1], len * sizeof(WCHAR));
1053 lpszCurrentDirectory[len - 1] = 0;
1054 *lpdwCurrentDirectory = len;
1055 bSuccess = TRUE;
1056 }
1057 else INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1058
1059 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
1060 }
1061 else
1062 FTP_SetResponseError(nResCode);
1063 }
1064
1065 lend:
1066 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1067 {
1068 INTERNET_ASYNC_RESULT iar;
1069
1070 iar.dwResult = bSuccess;
1071 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
1072 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1073 &iar, sizeof(INTERNET_ASYNC_RESULT));
1074 }
1075
1076 return bSuccess;
1077 }
1078
1079
1080 /***********************************************************************
1081 * FTPFILE_Destroy(internal)
1082 *
1083 * Closes the file transfer handle. This also 'cleans' the data queue of
1084 * the 'transfer complete' message (this is a bit of a hack though :-/ )
1085 *
1086 */
1087 static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
1088 {
1089 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
1090 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
1091 INT nResCode;
1092
1093 TRACE("\n");
1094
1095 if (!lpwh->session_deleted)
1096 lpwfs->download_in_progress = NULL;
1097
1098 if (lpwh->nDataSocket != -1)
1099 closesocket(lpwh->nDataSocket);
1100
1101 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1102 if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
1103
1104 WININET_Release(&lpwh->lpFtpSession->hdr);
1105
1106 HeapFree(GetProcessHeap(), 0, lpwh);
1107 }
1108
1109 static DWORD FTPFILE_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
1110 {
1111 switch(option) {
1112 case INTERNET_OPTION_HANDLE_TYPE:
1113 TRACE("INTERNET_OPTION_HANDLE_TYPE\n");