~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/wininet/ftp.c

Version: ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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");