winecoreaudio: Enable DirectSound HEL mode for input.
[wine/multimedia.git] / dlls / wininet / ftp.c
blobd9291c25ec0fd4458afd24eb416ea7a292133a9a
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
8 * Ulrich Czekalla
9 * Noureddine Jemmali
11 * Copyright 2000 Andreas Mohr
12 * Copyright 2002 Jaco Greeff
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "config.h"
30 #include "wine/port.h"
32 #include <errno.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44 #include <time.h>
45 #include <assert.h>
47 #include "windef.h"
48 #include "winbase.h"
49 #include "wingdi.h"
50 #include "winuser.h"
51 #include "wininet.h"
52 #include "winnls.h"
53 #include "winerror.h"
54 #include "winreg.h"
55 #include "winternl.h"
56 #include "shlwapi.h"
58 #include "wine/debug.h"
59 #include "internet.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define DATA_PACKET_SIZE 0x2000
64 #define szCRLF "\r\n"
65 #define MAX_BACKLOG 5
67 /* Testing shows that Windows only accepts dwFlags where the last
68 * 3 (yes 3) bits define FTP_TRANSFER_TYPE_UNKNOWN, FTP_TRANSFER_TYPE_ASCII or FTP_TRANSFER_TYPE_BINARY.
70 #define FTP_CONDITION_MASK 0x0007
72 typedef enum {
73 /* FTP commands with arguments. */
74 FTP_CMD_ACCT,
75 FTP_CMD_CWD,
76 FTP_CMD_DELE,
77 FTP_CMD_MKD,
78 FTP_CMD_PASS,
79 FTP_CMD_PORT,
80 FTP_CMD_RETR,
81 FTP_CMD_RMD,
82 FTP_CMD_RNFR,
83 FTP_CMD_RNTO,
84 FTP_CMD_STOR,
85 FTP_CMD_TYPE,
86 FTP_CMD_USER,
87 FTP_CMD_SIZE,
89 /* FTP commands without arguments. */
90 FTP_CMD_ABOR,
91 FTP_CMD_LIST,
92 FTP_CMD_NLST,
93 FTP_CMD_PASV,
94 FTP_CMD_PWD,
95 FTP_CMD_QUIT,
96 } FTP_COMMAND;
98 static const CHAR *const szFtpCommands[] = {
99 "ACCT",
100 "CWD",
101 "DELE",
102 "MKD",
103 "PASS",
104 "PORT",
105 "RETR",
106 "RMD",
107 "RNFR",
108 "RNTO",
109 "STOR",
110 "TYPE",
111 "USER",
112 "SIZE",
113 "ABOR",
114 "LIST",
115 "NLST",
116 "PASV",
117 "PWD",
118 "QUIT",
121 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
122 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
124 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr);
125 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr);
126 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr);
127 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
128 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext);
129 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
130 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
131 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
132 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext);
133 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
134 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile);
135 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
136 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
137 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
138 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
139 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
140 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize);
141 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
142 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
143 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
144 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
145 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
146 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
147 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
148 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
149 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext);
150 static DWORD FTP_SetResponseError(DWORD dwResponse);
152 /***********************************************************************
153 * FtpPutFileA (WININET.@)
155 * Uploads a file to the FTP server
157 * RETURNS
158 * TRUE on success
159 * FALSE on failure
162 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
163 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
165 LPWSTR lpwzLocalFile;
166 LPWSTR lpwzNewRemoteFile;
167 BOOL ret;
169 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
170 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
171 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
172 dwFlags, dwContext);
173 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
174 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
175 return ret;
178 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
180 struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
181 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
183 TRACE("%p\n", lpwfs);
185 FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
186 req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
188 HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
189 HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
192 /***********************************************************************
193 * FtpPutFileW (WININET.@)
195 * Uploads a file to the FTP server
197 * RETURNS
198 * TRUE on success
199 * FALSE on failure
202 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
203 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
205 LPWININETFTPSESSIONW lpwfs;
206 LPWININETAPPINFOW hIC = NULL;
207 BOOL r = FALSE;
209 if (!lpszLocalFile || !lpszNewRemoteFile)
211 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
212 return FALSE;
215 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
216 if (!lpwfs)
218 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
219 return FALSE;
222 if (WH_HFTPSESSION != lpwfs->hdr.htype)
224 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
225 goto lend;
228 if ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
230 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
231 goto lend;
234 hIC = lpwfs->lpAppInfo;
235 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
237 WORKREQUEST workRequest;
238 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
240 workRequest.asyncproc = AsyncFtpPutFileProc;
241 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
242 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
243 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
244 req->dwFlags = dwFlags;
245 req->dwContext = dwContext;
247 r = INTERNET_AsyncCall(&workRequest);
249 else
251 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
252 lpszNewRemoteFile, dwFlags, dwContext);
255 lend:
256 WININET_Release( &lpwfs->hdr );
258 return r;
261 /***********************************************************************
262 * FTP_FtpPutFileW (Internal)
264 * Uploads a file to the FTP server
266 * RETURNS
267 * TRUE on success
268 * FALSE on failure
271 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
272 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
274 HANDLE hFile;
275 BOOL bSuccess = FALSE;
276 LPWININETAPPINFOW hIC = NULL;
277 INT nResCode;
279 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
281 /* Clear any error information */
282 INTERNET_SetLastError(0);
284 /* Open file to be uploaded */
285 if (INVALID_HANDLE_VALUE ==
286 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
287 /* Let CreateFile set the appropriate error */
288 return FALSE;
290 hIC = lpwfs->lpAppInfo;
292 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
294 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
296 INT nDataSocket;
298 /* Get data socket to server */
299 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
301 FTP_SendData(lpwfs, nDataSocket, hFile);
302 closesocket(nDataSocket);
303 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
304 if (nResCode)
306 if (nResCode == 226)
307 bSuccess = TRUE;
308 else
309 FTP_SetResponseError(nResCode);
314 if (lpwfs->lstnSocket != -1)
315 closesocket(lpwfs->lstnSocket);
317 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
319 INTERNET_ASYNC_RESULT iar;
321 iar.dwResult = (DWORD)bSuccess;
322 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
323 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
324 &iar, sizeof(INTERNET_ASYNC_RESULT));
327 CloseHandle(hFile);
329 return bSuccess;
333 /***********************************************************************
334 * FtpSetCurrentDirectoryA (WININET.@)
336 * Change the working directory on the FTP server
338 * RETURNS
339 * TRUE on success
340 * FALSE on failure
343 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
345 LPWSTR lpwzDirectory;
346 BOOL ret;
348 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
349 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
350 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
351 return ret;
355 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
357 struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
358 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
360 TRACE("%p\n", lpwfs);
362 FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
363 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
366 /***********************************************************************
367 * FtpSetCurrentDirectoryW (WININET.@)
369 * Change the working directory on the FTP server
371 * RETURNS
372 * TRUE on success
373 * FALSE on failure
376 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
378 LPWININETFTPSESSIONW lpwfs = NULL;
379 LPWININETAPPINFOW hIC = NULL;
380 BOOL r = FALSE;
382 if (!lpszDirectory)
384 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
385 goto lend;
388 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
389 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
391 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
392 goto lend;
395 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
397 hIC = lpwfs->lpAppInfo;
398 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
400 WORKREQUEST workRequest;
401 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
403 workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
404 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
405 req = &workRequest.u.FtpSetCurrentDirectoryW;
406 req->lpszDirectory = WININET_strdupW(lpszDirectory);
408 r = INTERNET_AsyncCall(&workRequest);
410 else
412 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
415 lend:
416 if( lpwfs )
417 WININET_Release( &lpwfs->hdr );
419 return r;
423 /***********************************************************************
424 * FTP_FtpSetCurrentDirectoryW (Internal)
426 * Change the working directory on the FTP server
428 * RETURNS
429 * TRUE on success
430 * FALSE on failure
433 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
435 INT nResCode;
436 LPWININETAPPINFOW hIC = NULL;
437 DWORD bSuccess = FALSE;
439 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
441 /* Clear any error information */
442 INTERNET_SetLastError(0);
444 hIC = lpwfs->lpAppInfo;
445 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
446 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
447 goto lend;
449 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
451 if (nResCode)
453 if (nResCode == 250)
454 bSuccess = TRUE;
455 else
456 FTP_SetResponseError(nResCode);
459 lend:
460 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
462 INTERNET_ASYNC_RESULT iar;
464 iar.dwResult = (DWORD)bSuccess;
465 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
466 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
467 &iar, sizeof(INTERNET_ASYNC_RESULT));
469 return bSuccess;
473 /***********************************************************************
474 * FtpCreateDirectoryA (WININET.@)
476 * Create new directory on the FTP server
478 * RETURNS
479 * TRUE on success
480 * FALSE on failure
483 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
485 LPWSTR lpwzDirectory;
486 BOOL ret;
488 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
489 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
490 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
491 return ret;
495 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
497 struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
498 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
500 TRACE(" %p\n", lpwfs);
502 FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
503 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
506 /***********************************************************************
507 * FtpCreateDirectoryW (WININET.@)
509 * Create new directory on the FTP server
511 * RETURNS
512 * TRUE on success
513 * FALSE on failure
516 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
518 LPWININETFTPSESSIONW lpwfs;
519 LPWININETAPPINFOW hIC = NULL;
520 BOOL r = FALSE;
522 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
523 if (!lpwfs)
525 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
526 return FALSE;
529 if (WH_HFTPSESSION != lpwfs->hdr.htype)
531 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
532 goto lend;
535 if (!lpszDirectory)
537 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
538 goto lend;
541 hIC = lpwfs->lpAppInfo;
542 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
544 WORKREQUEST workRequest;
545 struct WORKREQ_FTPCREATEDIRECTORYW *req;
547 workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
548 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
549 req = &workRequest.u.FtpCreateDirectoryW;
550 req->lpszDirectory = WININET_strdupW(lpszDirectory);
552 r = INTERNET_AsyncCall(&workRequest);
554 else
556 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
558 lend:
559 WININET_Release( &lpwfs->hdr );
561 return r;
565 /***********************************************************************
566 * FTP_FtpCreateDirectoryW (Internal)
568 * Create new directory on the FTP server
570 * RETURNS
571 * TRUE on success
572 * FALSE on failure
575 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
577 INT nResCode;
578 BOOL bSuccess = FALSE;
579 LPWININETAPPINFOW hIC = NULL;
581 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
583 /* Clear any error information */
584 INTERNET_SetLastError(0);
586 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
587 goto lend;
589 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
590 if (nResCode)
592 if (nResCode == 257)
593 bSuccess = TRUE;
594 else
595 FTP_SetResponseError(nResCode);
598 lend:
599 hIC = lpwfs->lpAppInfo;
600 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
602 INTERNET_ASYNC_RESULT iar;
604 iar.dwResult = (DWORD)bSuccess;
605 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
606 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
607 &iar, sizeof(INTERNET_ASYNC_RESULT));
610 return bSuccess;
613 /***********************************************************************
614 * FtpFindFirstFileA (WININET.@)
616 * Search the specified directory
618 * RETURNS
619 * HINTERNET on success
620 * NULL on failure
623 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
624 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
626 LPWSTR lpwzSearchFile;
627 WIN32_FIND_DATAW wfd;
628 LPWIN32_FIND_DATAW lpFindFileDataW;
629 HINTERNET ret;
631 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
632 lpFindFileDataW = lpFindFileData?&wfd:NULL;
633 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
634 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
636 if(lpFindFileData) {
637 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
639 return ret;
643 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
645 struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
646 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
648 TRACE("%p\n", lpwfs);
650 FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
651 req->lpFindFileData, req->dwFlags, req->dwContext);
652 HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
655 /***********************************************************************
656 * FtpFindFirstFileW (WININET.@)
658 * Search the specified directory
660 * RETURNS
661 * HINTERNET on success
662 * NULL on failure
665 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
666 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
668 LPWININETFTPSESSIONW lpwfs;
669 LPWININETAPPINFOW hIC = NULL;
670 HINTERNET r = NULL;
672 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
673 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
675 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
676 goto lend;
679 hIC = lpwfs->lpAppInfo;
680 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
682 WORKREQUEST workRequest;
683 struct WORKREQ_FTPFINDFIRSTFILEW *req;
685 workRequest.asyncproc = AsyncFtpFindFirstFileProc;
686 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
687 req = &workRequest.u.FtpFindFirstFileW;
688 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
689 req->lpFindFileData = lpFindFileData;
690 req->dwFlags = dwFlags;
691 req->dwContext= dwContext;
693 INTERNET_AsyncCall(&workRequest);
694 r = NULL;
696 else
698 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
699 dwFlags, dwContext);
701 lend:
702 if( lpwfs )
703 WININET_Release( &lpwfs->hdr );
705 return r;
709 /***********************************************************************
710 * FTP_FtpFindFirstFileW (Internal)
712 * Search the specified directory
714 * RETURNS
715 * HINTERNET on success
716 * NULL on failure
719 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
720 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
722 INT nResCode;
723 LPWININETAPPINFOW hIC = NULL;
724 HINTERNET hFindNext = NULL;
726 TRACE("\n");
728 /* Clear any error information */
729 INTERNET_SetLastError(0);
731 if (!FTP_InitListenSocket(lpwfs))
732 goto lend;
734 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
735 goto lend;
737 if (!FTP_SendPortOrPasv(lpwfs))
738 goto lend;
740 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
741 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
742 goto lend;
744 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
745 if (nResCode)
747 if (nResCode == 125 || nResCode == 150)
749 INT nDataSocket;
751 /* Get data socket to server */
752 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
754 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
755 closesocket(nDataSocket);
756 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
757 if (nResCode != 226 && nResCode != 250)
758 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
761 else
762 FTP_SetResponseError(nResCode);
765 lend:
766 if (lpwfs->lstnSocket != -1)
767 closesocket(lpwfs->lstnSocket);
769 hIC = lpwfs->lpAppInfo;
770 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
772 INTERNET_ASYNC_RESULT iar;
774 if (hFindNext)
776 iar.dwResult = (DWORD)hFindNext;
777 iar.dwError = ERROR_SUCCESS;
778 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
779 &iar, sizeof(INTERNET_ASYNC_RESULT));
782 iar.dwResult = (DWORD)hFindNext;
783 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
784 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
785 &iar, sizeof(INTERNET_ASYNC_RESULT));
788 return hFindNext;
792 /***********************************************************************
793 * FtpGetCurrentDirectoryA (WININET.@)
795 * Retrieves the current directory
797 * RETURNS
798 * TRUE on success
799 * FALSE on failure
802 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
803 LPDWORD lpdwCurrentDirectory)
805 WCHAR *dir = NULL;
806 DWORD len;
807 BOOL ret;
809 if(lpdwCurrentDirectory) {
810 len = *lpdwCurrentDirectory;
811 if(lpszCurrentDirectory)
813 dir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
814 if (NULL == dir)
816 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
817 return FALSE;
821 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
822 if(lpdwCurrentDirectory) {
823 *lpdwCurrentDirectory = len;
824 if(lpszCurrentDirectory) {
825 WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL);
826 HeapFree(GetProcessHeap(), 0, dir);
829 return ret;
833 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
835 struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
836 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
838 TRACE("%p\n", lpwfs);
840 FTP_FtpGetCurrentDirectoryW(lpwfs, req->lpszDirectory, req->lpdwDirectory);
843 /***********************************************************************
844 * FtpGetCurrentDirectoryW (WININET.@)
846 * Retrieves the current directory
848 * RETURNS
849 * TRUE on success
850 * FALSE on failure
853 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
854 LPDWORD lpdwCurrentDirectory)
856 LPWININETFTPSESSIONW lpwfs;
857 LPWININETAPPINFOW hIC = NULL;
858 BOOL r = FALSE;
860 TRACE("len(%d)\n", *lpdwCurrentDirectory);
862 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
863 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
865 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
866 goto lend;
869 hIC = lpwfs->lpAppInfo;
870 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
872 WORKREQUEST workRequest;
873 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
875 workRequest.asyncproc = AsyncFtpGetCurrentDirectoryProc;
876 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
877 req = &workRequest.u.FtpGetCurrentDirectoryW;
878 req->lpszDirectory = lpszCurrentDirectory;
879 req->lpdwDirectory = lpdwCurrentDirectory;
881 r = INTERNET_AsyncCall(&workRequest);
883 else
885 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
886 lpdwCurrentDirectory);
889 lend:
890 if( lpwfs )
891 WININET_Release( &lpwfs->hdr );
893 return r;
897 /***********************************************************************
898 * FTP_FtpGetCurrentDirectoryW (Internal)
900 * Retrieves the current directory
902 * RETURNS
903 * TRUE on success
904 * FALSE on failure
907 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
908 LPDWORD lpdwCurrentDirectory)
910 INT nResCode;
911 LPWININETAPPINFOW hIC = NULL;
912 DWORD bSuccess = FALSE;
914 TRACE("len(%d)\n", *lpdwCurrentDirectory);
916 /* Clear any error information */
917 INTERNET_SetLastError(0);
919 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
921 hIC = lpwfs->lpAppInfo;
922 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
923 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
924 goto lend;
926 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
927 if (nResCode)
929 if (nResCode == 257) /* Extract directory name */
931 DWORD firstpos, lastpos, len;
932 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
934 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
936 if ('"' == lpszResponseBuffer[lastpos])
938 if (!firstpos)
939 firstpos = lastpos;
940 else
941 break;
945 len = lastpos - firstpos - 1;
946 lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
947 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
948 *lpdwCurrentDirectory = len;
949 bSuccess = TRUE;
951 else
952 FTP_SetResponseError(nResCode);
955 lend:
956 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
958 INTERNET_ASYNC_RESULT iar;
960 iar.dwResult = (DWORD)bSuccess;
961 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
962 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
963 &iar, sizeof(INTERNET_ASYNC_RESULT));
966 return (DWORD) bSuccess;
969 /***********************************************************************
970 * FtpOpenFileA (WININET.@)
972 * Open a remote file for writing or reading
974 * RETURNS
975 * HINTERNET handle on success
976 * NULL on failure
979 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
980 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
981 DWORD dwContext)
983 LPWSTR lpwzFileName;
984 HINTERNET ret;
986 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
987 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
988 HeapFree(GetProcessHeap(), 0, lpwzFileName);
989 return ret;
993 static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
995 struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
996 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
998 TRACE("%p\n", lpwfs);
1000 FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
1001 req->dwAccess, req->dwFlags, req->dwContext);
1002 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1005 /***********************************************************************
1006 * FtpOpenFileW (WININET.@)
1008 * Open a remote file for writing or reading
1010 * RETURNS
1011 * HINTERNET handle on success
1012 * NULL on failure
1015 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
1016 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1017 DWORD dwContext)
1019 LPWININETFTPSESSIONW lpwfs;
1020 LPWININETAPPINFOW hIC = NULL;
1021 HINTERNET r = NULL;
1023 TRACE("(%p,%s,0x%08x,0x%08x,0x%08x)\n", hFtpSession,
1024 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
1026 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1027 if (!lpwfs)
1029 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1030 return FALSE;
1033 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1035 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1036 goto lend;
1039 if ((!lpszFileName) ||
1040 ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
1041 ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
1043 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1044 goto lend;
1047 if (lpwfs->download_in_progress != NULL) {
1048 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1049 goto lend;
1051 hIC = lpwfs->lpAppInfo;
1052 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1054 WORKREQUEST workRequest;
1055 struct WORKREQ_FTPOPENFILEW *req;
1057 workRequest.asyncproc = AsyncFtpOpenFileProc;
1058 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1059 req = &workRequest.u.FtpOpenFileW;
1060 req->lpszFilename = WININET_strdupW(lpszFileName);
1061 req->dwAccess = fdwAccess;
1062 req->dwFlags = dwFlags;
1063 req->dwContext = dwContext;
1065 INTERNET_AsyncCall(&workRequest);
1066 r = NULL;
1068 else
1070 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
1073 lend:
1074 WININET_Release( &lpwfs->hdr );
1076 return r;
1080 /***********************************************************************
1081 * FTP_FtpOpenFileW (Internal)
1083 * Open a remote file for writing or reading
1085 * RETURNS
1086 * HINTERNET handle on success
1087 * NULL on failure
1090 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
1091 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
1092 DWORD dwContext)
1094 INT nDataSocket;
1095 BOOL bSuccess = FALSE;
1096 LPWININETFTPFILE lpwh = NULL;
1097 LPWININETAPPINFOW hIC = NULL;
1098 HINTERNET handle = NULL;
1100 TRACE("\n");
1102 /* Clear any error information */
1103 INTERNET_SetLastError(0);
1105 if (GENERIC_READ == fdwAccess)
1107 /* Set up socket to retrieve data */
1108 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1110 else if (GENERIC_WRITE == fdwAccess)
1112 /* Set up socket to send data */
1113 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1116 /* Get data socket to server */
1117 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1119 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
1120 lpwh->hdr.htype = WH_HFILE;
1121 lpwh->hdr.dwFlags = dwFlags;
1122 lpwh->hdr.dwContext = dwContext;
1123 lpwh->hdr.dwRefCount = 1;
1124 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1125 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1126 lpwh->nDataSocket = nDataSocket;
1127 lpwh->session_deleted = FALSE;
1129 WININET_AddRef( &lpwfs->hdr );
1130 lpwh->lpFtpSession = lpwfs;
1132 handle = WININET_AllocHandle( &lpwh->hdr );
1133 if( !handle )
1134 goto lend;
1136 /* Indicate that a download is currently in progress */
1137 lpwfs->download_in_progress = lpwh;
1140 if (lpwfs->lstnSocket != -1)
1141 closesocket(lpwfs->lstnSocket);
1143 hIC = lpwfs->lpAppInfo;
1144 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1146 INTERNET_ASYNC_RESULT iar;
1148 if (lpwh)
1150 iar.dwResult = (DWORD)handle;
1151 iar.dwError = ERROR_SUCCESS;
1152 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1153 &iar, sizeof(INTERNET_ASYNC_RESULT));
1156 iar.dwResult = (DWORD)bSuccess;
1157 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1158 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1159 &iar, sizeof(INTERNET_ASYNC_RESULT));
1162 lend:
1163 if( lpwh )
1164 WININET_Release( &lpwh->hdr );
1166 return handle;
1170 /***********************************************************************
1171 * FtpGetFileA (WININET.@)
1173 * Retrieve file from the FTP server
1175 * RETURNS
1176 * TRUE on success
1177 * FALSE on failure
1180 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1181 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1182 DWORD dwContext)
1184 LPWSTR lpwzRemoteFile;
1185 LPWSTR lpwzNewFile;
1186 BOOL ret;
1188 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1189 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1190 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1191 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1192 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1193 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1194 return ret;
1198 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
1200 struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
1201 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1203 TRACE("%p\n", lpwfs);
1205 FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
1206 req->lpszNewFile, req->fFailIfExists,
1207 req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
1208 HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
1209 HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
1213 /***********************************************************************
1214 * FtpGetFileW (WININET.@)
1216 * Retrieve file from the FTP server
1218 * RETURNS
1219 * TRUE on success
1220 * FALSE on failure
1223 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1224 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1225 DWORD dwContext)
1227 LPWININETFTPSESSIONW lpwfs;
1228 LPWININETAPPINFOW hIC = NULL;
1229 BOOL r = FALSE;
1231 if (!lpszRemoteFile || !lpszNewFile)
1233 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1234 return FALSE;
1237 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1238 if (!lpwfs)
1240 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1241 return FALSE;
1244 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1246 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1247 goto lend;
1250 if ((dwInternetFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY)
1252 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1253 goto lend;
1256 if (lpwfs->download_in_progress != NULL) {
1257 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1258 goto lend;
1261 hIC = lpwfs->lpAppInfo;
1262 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1264 WORKREQUEST workRequest;
1265 struct WORKREQ_FTPGETFILEW *req;
1267 workRequest.asyncproc = AsyncFtpGetFileProc;
1268 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1269 req = &workRequest.u.FtpGetFileW;
1270 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1271 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1272 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1273 req->fFailIfExists = fFailIfExists;
1274 req->dwFlags = dwInternetFlags;
1275 req->dwContext = dwContext;
1277 r = INTERNET_AsyncCall(&workRequest);
1279 else
1281 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1282 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1285 lend:
1286 WININET_Release( &lpwfs->hdr );
1288 return r;
1292 /***********************************************************************
1293 * FTP_FtpGetFileW (Internal)
1295 * Retrieve file from the FTP server
1297 * RETURNS
1298 * TRUE on success
1299 * FALSE on failure
1302 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1303 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1304 DWORD dwContext)
1306 DWORD nBytes;
1307 BOOL bSuccess = FALSE;
1308 HANDLE hFile;
1309 LPWININETAPPINFOW hIC = NULL;
1311 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1313 /* Clear any error information */
1314 INTERNET_SetLastError(0);
1316 /* Ensure we can write to lpszNewfile by opening it */
1317 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1318 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1319 if (INVALID_HANDLE_VALUE == hFile)
1320 return FALSE;
1322 /* Set up socket to retrieve data */
1323 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1325 if (nBytes > 0)
1327 INT nDataSocket;
1329 /* Get data socket to server */
1330 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1332 INT nResCode;
1334 /* Receive data */
1335 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1336 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1337 if (nResCode)
1339 if (nResCode == 226)
1340 bSuccess = TRUE;
1341 else
1342 FTP_SetResponseError(nResCode);
1344 closesocket(nDataSocket);
1348 if (lpwfs->lstnSocket != -1)
1349 closesocket(lpwfs->lstnSocket);
1351 CloseHandle(hFile);
1353 hIC = lpwfs->lpAppInfo;
1354 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1356 INTERNET_ASYNC_RESULT iar;
1358 iar.dwResult = (DWORD)bSuccess;
1359 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1360 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1361 &iar, sizeof(INTERNET_ASYNC_RESULT));
1364 return bSuccess;
1367 /***********************************************************************
1368 * FtpGetFileSize (WININET.@)
1370 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1372 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1374 if (lpdwFileSizeHigh)
1375 *lpdwFileSizeHigh = 0;
1377 return 0;
1380 /***********************************************************************
1381 * FtpDeleteFileA (WININET.@)
1383 * Delete a file on the ftp server
1385 * RETURNS
1386 * TRUE on success
1387 * FALSE on failure
1390 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1392 LPWSTR lpwzFileName;
1393 BOOL ret;
1395 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1396 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1397 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1398 return ret;
1401 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
1403 struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
1404 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1406 TRACE("%p\n", lpwfs);
1408 FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
1409 HeapFree(GetProcessHeap(), 0, req->lpszFilename);
1412 /***********************************************************************
1413 * FtpDeleteFileW (WININET.@)
1415 * Delete a file on the ftp server
1417 * RETURNS
1418 * TRUE on success
1419 * FALSE on failure
1422 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1424 LPWININETFTPSESSIONW lpwfs;
1425 LPWININETAPPINFOW hIC = NULL;
1426 BOOL r = FALSE;
1428 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1429 if (!lpwfs)
1431 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1432 return FALSE;
1435 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1437 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1438 goto lend;
1441 if (!lpszFileName)
1443 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1444 goto lend;
1447 hIC = lpwfs->lpAppInfo;
1448 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1450 WORKREQUEST workRequest;
1451 struct WORKREQ_FTPDELETEFILEW *req;
1453 workRequest.asyncproc = AsyncFtpDeleteFileProc;
1454 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1455 req = &workRequest.u.FtpDeleteFileW;
1456 req->lpszFilename = WININET_strdupW(lpszFileName);
1458 r = INTERNET_AsyncCall(&workRequest);
1460 else
1462 r = FTP_FtpDeleteFileW(lpwfs, lpszFileName);
1465 lend:
1466 WININET_Release( &lpwfs->hdr );
1468 return r;
1471 /***********************************************************************
1472 * FTP_FtpDeleteFileW (Internal)
1474 * Delete a file on the ftp server
1476 * RETURNS
1477 * TRUE on success
1478 * FALSE on failure
1481 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1483 INT nResCode;
1484 BOOL bSuccess = FALSE;
1485 LPWININETAPPINFOW hIC = NULL;
1487 TRACE("%p\n", lpwfs);
1489 /* Clear any error information */
1490 INTERNET_SetLastError(0);
1492 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1493 goto lend;
1495 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1496 if (nResCode)
1498 if (nResCode == 250)
1499 bSuccess = TRUE;
1500 else
1501 FTP_SetResponseError(nResCode);
1503 lend:
1504 hIC = lpwfs->lpAppInfo;
1505 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1507 INTERNET_ASYNC_RESULT iar;
1509 iar.dwResult = (DWORD)bSuccess;
1510 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1511 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1512 &iar, sizeof(INTERNET_ASYNC_RESULT));
1515 return bSuccess;
1519 /***********************************************************************
1520 * FtpRemoveDirectoryA (WININET.@)
1522 * Remove a directory on the ftp server
1524 * RETURNS
1525 * TRUE on success
1526 * FALSE on failure
1529 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1531 LPWSTR lpwzDirectory;
1532 BOOL ret;
1534 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1535 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1536 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1537 return ret;
1540 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
1542 struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
1543 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1545 TRACE("%p\n", lpwfs);
1547 FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
1548 HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
1551 /***********************************************************************
1552 * FtpRemoveDirectoryW (WININET.@)
1554 * Remove a directory on the ftp server
1556 * RETURNS
1557 * TRUE on success
1558 * FALSE on failure
1561 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1563 LPWININETFTPSESSIONW lpwfs;
1564 LPWININETAPPINFOW hIC = NULL;
1565 BOOL r = FALSE;
1567 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1568 if (!lpwfs)
1570 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1571 return FALSE;
1574 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1576 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1577 goto lend;
1580 if (!lpszDirectory)
1582 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1583 goto lend;
1586 hIC = lpwfs->lpAppInfo;
1587 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1589 WORKREQUEST workRequest;
1590 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1592 workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
1593 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1594 req = &workRequest.u.FtpRemoveDirectoryW;
1595 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1597 r = INTERNET_AsyncCall(&workRequest);
1599 else
1601 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1604 lend:
1605 WININET_Release( &lpwfs->hdr );
1607 return r;
1610 /***********************************************************************
1611 * FTP_FtpRemoveDirectoryW (Internal)
1613 * Remove a directory on the ftp server
1615 * RETURNS
1616 * TRUE on success
1617 * FALSE on failure
1620 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1622 INT nResCode;
1623 BOOL bSuccess = FALSE;
1624 LPWININETAPPINFOW hIC = NULL;
1626 TRACE("\n");
1628 /* Clear any error information */
1629 INTERNET_SetLastError(0);
1631 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1632 goto lend;
1634 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1635 if (nResCode)
1637 if (nResCode == 250)
1638 bSuccess = TRUE;
1639 else
1640 FTP_SetResponseError(nResCode);
1643 lend:
1644 hIC = lpwfs->lpAppInfo;
1645 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1647 INTERNET_ASYNC_RESULT iar;
1649 iar.dwResult = (DWORD)bSuccess;
1650 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1651 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1652 &iar, sizeof(INTERNET_ASYNC_RESULT));
1655 return bSuccess;
1659 /***********************************************************************
1660 * FtpRenameFileA (WININET.@)
1662 * Rename a file on the ftp server
1664 * RETURNS
1665 * TRUE on success
1666 * FALSE on failure
1669 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1671 LPWSTR lpwzSrc;
1672 LPWSTR lpwzDest;
1673 BOOL ret;
1675 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1676 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1677 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1678 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1679 HeapFree(GetProcessHeap(), 0, lpwzDest);
1680 return ret;
1683 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
1685 struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
1686 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
1688 TRACE("%p\n", lpwfs);
1690 FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
1691 HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
1692 HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
1695 /***********************************************************************
1696 * FtpRenameFileW (WININET.@)
1698 * Rename a file on the ftp server
1700 * RETURNS
1701 * TRUE on success
1702 * FALSE on failure
1705 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1707 LPWININETFTPSESSIONW lpwfs;
1708 LPWININETAPPINFOW hIC = NULL;
1709 BOOL r = FALSE;
1711 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1712 if (!lpwfs)
1714 INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1715 return FALSE;
1718 if (WH_HFTPSESSION != lpwfs->hdr.htype)
1720 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1721 goto lend;
1724 if (!lpszSrc || !lpszDest)
1726 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1727 goto lend;
1730 hIC = lpwfs->lpAppInfo;
1731 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1733 WORKREQUEST workRequest;
1734 struct WORKREQ_FTPRENAMEFILEW *req;
1736 workRequest.asyncproc = AsyncFtpRenameFileProc;
1737 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1738 req = &workRequest.u.FtpRenameFileW;
1739 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1740 req->lpszDestFile = WININET_strdupW(lpszDest);
1742 r = INTERNET_AsyncCall(&workRequest);
1744 else
1746 r = FTP_FtpRenameFileW(lpwfs, lpszSrc, lpszDest);
1749 lend:
1750 WININET_Release( &lpwfs->hdr );
1752 return r;
1755 /***********************************************************************
1756 * FTP_FtpRenameFileW (Internal)
1758 * Rename a file on the ftp server
1760 * RETURNS
1761 * TRUE on success
1762 * FALSE on failure
1765 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1766 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1768 INT nResCode;
1769 BOOL bSuccess = FALSE;
1770 LPWININETAPPINFOW hIC = NULL;
1772 TRACE("\n");
1774 /* Clear any error information */
1775 INTERNET_SetLastError(0);
1777 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1778 goto lend;
1780 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1781 if (nResCode == 350)
1783 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1784 goto lend;
1786 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1789 if (nResCode == 250)
1790 bSuccess = TRUE;
1791 else
1792 FTP_SetResponseError(nResCode);
1794 lend:
1795 hIC = lpwfs->lpAppInfo;
1796 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1798 INTERNET_ASYNC_RESULT iar;
1800 iar.dwResult = (DWORD)bSuccess;
1801 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1802 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1803 &iar, sizeof(INTERNET_ASYNC_RESULT));
1806 return bSuccess;
1809 /***********************************************************************
1810 * FtpCommandA (WININET.@)
1812 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1813 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1815 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1816 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1818 return TRUE;
1821 /***********************************************************************
1822 * FtpCommandW (WININET.@)
1824 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1825 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1827 FIXME("%p %d 0x%08x %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1828 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1830 return TRUE;
1833 /***********************************************************************
1834 * FTP_Connect (internal)
1836 * Connect to a ftp server
1838 * RETURNS
1839 * HINTERNET a session handle on success
1840 * NULL on failure
1842 * NOTES:
1844 * Windows uses 'anonymous' as the username, when given a NULL username
1845 * and a NULL password. The password is first looked up in:
1847 * HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings\EmailName
1849 * If this entry is not present it uses the current username as the password.
1853 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1854 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1855 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1856 DWORD dwInternalFlags)
1858 static const WCHAR szKey[] = {'S','o','f','t','w','a','r','e','\\',
1859 'M','i','c','r','o','s','o','f','t','\\',
1860 'W','i','n','d','o','w','s','\\',
1861 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
1862 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0};
1863 static const WCHAR szValue[] = {'E','m','a','i','l','N','a','m','e',0};
1864 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1865 static const WCHAR szEmpty[] = {'\0'};
1866 struct sockaddr_in socketAddr;
1867 INT nsocket = -1;
1868 UINT sock_namelen;
1869 BOOL bSuccess = FALSE;
1870 LPWININETFTPSESSIONW lpwfs = NULL;
1871 HINTERNET handle = NULL;
1873 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1874 hIC, debugstr_w(lpszServerName),
1875 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1877 assert( hIC->hdr.htype == WH_HINIT );
1879 if (NULL == lpszUserName && NULL != lpszPassword)
1881 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1882 goto lerror;
1885 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1886 if (NULL == lpwfs)
1888 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1889 goto lerror;
1892 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1893 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1895 lpwfs->hdr.htype = WH_HFTPSESSION;
1896 lpwfs->hdr.dwFlags = dwFlags;
1897 lpwfs->hdr.dwContext = dwContext;
1898 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1899 lpwfs->hdr.dwRefCount = 1;
1900 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1901 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1902 lpwfs->download_in_progress = NULL;
1904 WININET_AddRef( &hIC->hdr );
1905 lpwfs->lpAppInfo = hIC;
1907 handle = WININET_AllocHandle( &lpwfs->hdr );
1908 if( !handle )
1910 ERR("Failed to alloc handle\n");
1911 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1912 goto lerror;
1915 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1916 if(strchrW(hIC->lpszProxy, ' '))
1917 FIXME("Several proxies not implemented.\n");
1918 if(hIC->lpszProxyBypass)
1919 FIXME("Proxy bypass is ignored.\n");
1921 if ( !lpszUserName) {
1922 HKEY key;
1923 WCHAR szPassword[MAX_PATH];
1924 DWORD len = sizeof(szPassword);
1926 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1928 RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
1929 if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
1930 /* Nothing in the registry, get the username and use that as the password */
1931 if (!GetUserNameW(szPassword, &len)) {
1932 /* Should never get here, but use an empty password as failsafe */
1933 strcpyW(szPassword, szEmpty);
1936 RegCloseKey(key);
1938 TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
1939 lpwfs->lpszPassword = WININET_strdupW(szPassword);
1941 else {
1942 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1944 if (lpszPassword)
1945 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1946 else
1947 lpwfs->lpszPassword = WININET_strdupW(szEmpty);
1950 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1951 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1953 INTERNET_ASYNC_RESULT iar;
1955 iar.dwResult = (DWORD)handle;
1956 iar.dwError = ERROR_SUCCESS;
1958 SendAsyncCallback(&hIC->hdr, dwContext,
1959 INTERNET_STATUS_HANDLE_CREATED, &iar,
1960 sizeof(INTERNET_ASYNC_RESULT));
1963 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1964 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1966 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1968 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1969 goto lerror;
1972 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1973 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1975 nsocket = socket(AF_INET,SOCK_STREAM,0);
1976 if (nsocket == -1)
1978 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1979 goto lerror;
1982 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1983 &socketAddr, sizeof(struct sockaddr_in));
1985 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1987 ERR("Unable to connect (%s)\n", strerror(errno));
1988 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1990 else
1992 TRACE("Connected to server\n");
1993 lpwfs->sndSocket = nsocket;
1994 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1995 &socketAddr, sizeof(struct sockaddr_in));
1997 sock_namelen = sizeof(lpwfs->socketAddress);
1998 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
2000 if (FTP_ConnectToHost(lpwfs))
2002 TRACE("Successfully logged into server\n");
2003 bSuccess = TRUE;
2007 lerror:
2008 if (!bSuccess && nsocket != -1)
2009 closesocket(nsocket);
2011 if (!bSuccess && lpwfs)
2013 HeapFree(GetProcessHeap(), 0, lpwfs);
2014 WININET_FreeHandle( handle );
2015 handle = NULL;
2016 lpwfs = NULL;
2019 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2021 INTERNET_ASYNC_RESULT iar;
2023 iar.dwResult = (DWORD)lpwfs;
2024 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
2025 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
2026 &iar, sizeof(INTERNET_ASYNC_RESULT));
2029 return handle;
2033 /***********************************************************************
2034 * FTP_ConnectToHost (internal)
2036 * Connect to a ftp server
2038 * RETURNS
2039 * TRUE on success
2040 * NULL on failure
2043 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
2045 INT nResCode;
2046 BOOL bSuccess = FALSE;
2048 TRACE("\n");
2049 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2051 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
2052 goto lend;
2054 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2055 if (nResCode)
2057 /* Login successful... */
2058 if (nResCode == 230)
2059 bSuccess = TRUE;
2060 /* User name okay, need password... */
2061 else if (nResCode == 331)
2062 bSuccess = FTP_SendPassword(lpwfs);
2063 /* Need account for login... */
2064 else if (nResCode == 332)
2065 bSuccess = FTP_SendAccount(lpwfs);
2066 else
2067 FTP_SetResponseError(nResCode);
2070 TRACE("Returning %d\n", bSuccess);
2071 lend:
2072 return bSuccess;
2076 /***********************************************************************
2077 * FTP_SendCommandA (internal)
2079 * Send command to server
2081 * RETURNS
2082 * TRUE on success
2083 * NULL on failure
2086 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
2087 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2089 DWORD len;
2090 CHAR *buf;
2091 DWORD nBytesSent = 0;
2092 int nRC = 0;
2093 DWORD dwParamLen;
2095 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
2097 if (lpfnStatusCB)
2099 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
2102 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
2103 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
2104 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
2106 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2107 return FALSE;
2109 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
2110 dwParamLen ? lpszParam : "", szCRLF);
2112 TRACE("Sending (%s) len(%d)\n", buf, len);
2113 while((nBytesSent < len) && (nRC != -1))
2115 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
2116 nBytesSent += nRC;
2119 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
2121 if (lpfnStatusCB)
2123 lpfnStatusCB(hdr->hInternet, dwContext, INTERNET_STATUS_REQUEST_SENT,
2124 &nBytesSent, sizeof(DWORD));
2127 TRACE("Sent %d bytes\n", nBytesSent);
2128 return (nRC != -1);
2131 /***********************************************************************
2132 * FTP_SendCommand (internal)
2134 * Send command to server
2136 * RETURNS
2137 * TRUE on success
2138 * NULL on failure
2141 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
2142 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
2144 BOOL ret;
2145 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
2146 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
2147 HeapFree(GetProcessHeap(), 0, lpszParamA);
2148 return ret;
2151 /***********************************************************************
2152 * FTP_ReceiveResponse (internal)
2154 * Receive response from server
2156 * RETURNS
2157 * Reply code on success
2158 * 0 on failure
2161 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
2163 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
2164 DWORD nRecv;
2165 INT rc = 0;
2166 char firstprefix[5];
2167 BOOL multiline = FALSE;
2168 LPWININETAPPINFOW hIC = NULL;
2170 TRACE("socket(%d)\n", lpwfs->sndSocket);
2172 hIC = lpwfs->lpAppInfo;
2173 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
2175 while(1)
2177 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
2178 goto lerror;
2180 if (nRecv >= 3)
2182 if(!multiline)
2184 if(lpszResponse[3] != '-')
2185 break;
2186 else
2187 { /* Start of multiline repsonse. Loop until we get "nnn " */
2188 multiline = TRUE;
2189 memcpy(firstprefix, lpszResponse, 3);
2190 firstprefix[3] = ' ';
2191 firstprefix[4] = '\0';
2194 else
2196 if(!memcmp(firstprefix, lpszResponse, 4))
2197 break;
2202 if (nRecv >= 3)
2204 rc = atoi(lpszResponse);
2206 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
2207 &nRecv, sizeof(DWORD));
2210 lerror:
2211 TRACE("return %d\n", rc);
2212 return rc;
2216 /***********************************************************************
2217 * FTP_SendPassword (internal)
2219 * Send password to ftp server
2221 * RETURNS
2222 * TRUE on success
2223 * NULL on failure
2226 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2228 INT nResCode;
2229 BOOL bSuccess = FALSE;
2231 TRACE("\n");
2232 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2233 goto lend;
2235 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2236 if (nResCode)
2238 TRACE("Received reply code %d\n", nResCode);
2239 /* Login successful... */
2240 if (nResCode == 230)
2241 bSuccess = TRUE;
2242 /* Command not implemented, superfluous at the server site... */
2243 /* Need account for login... */
2244 else if (nResCode == 332)
2245 bSuccess = FTP_SendAccount(lpwfs);
2246 else
2247 FTP_SetResponseError(nResCode);
2250 lend:
2251 TRACE("Returning %d\n", bSuccess);
2252 return bSuccess;
2256 /***********************************************************************
2257 * FTP_SendAccount (internal)
2261 * RETURNS
2262 * TRUE on success
2263 * FALSE on failure
2266 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2268 INT nResCode;
2269 BOOL bSuccess = FALSE;
2271 TRACE("\n");
2272 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2273 goto lend;
2275 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2276 if (nResCode)
2277 bSuccess = TRUE;
2278 else
2279 FTP_SetResponseError(nResCode);
2281 lend:
2282 return bSuccess;
2286 /***********************************************************************
2287 * FTP_SendStore (internal)
2289 * Send request to upload file to ftp server
2291 * RETURNS
2292 * TRUE on success
2293 * FALSE on failure
2296 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2298 INT nResCode;
2299 BOOL bSuccess = FALSE;
2301 TRACE("\n");
2302 if (!FTP_InitListenSocket(lpwfs))
2303 goto lend;
2305 if (!FTP_SendType(lpwfs, dwType))
2306 goto lend;
2308 if (!FTP_SendPortOrPasv(lpwfs))
2309 goto lend;
2311 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2312 goto lend;
2313 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2314 if (nResCode)
2316 if (nResCode == 150 || nResCode == 125)
2317 bSuccess = TRUE;
2318 else
2319 FTP_SetResponseError(nResCode);
2322 lend:
2323 if (!bSuccess && lpwfs->lstnSocket != -1)
2325 closesocket(lpwfs->lstnSocket);
2326 lpwfs->lstnSocket = -1;
2329 return bSuccess;
2333 /***********************************************************************
2334 * FTP_InitListenSocket (internal)
2336 * Create a socket to listen for server response
2338 * RETURNS
2339 * TRUE on success
2340 * FALSE on failure
2343 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2345 BOOL bSuccess = FALSE;
2346 socklen_t namelen = sizeof(struct sockaddr_in);
2348 TRACE("\n");
2350 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2351 if (lpwfs->lstnSocket == -1)
2353 TRACE("Unable to create listening socket\n");
2354 goto lend;
2357 /* We obtain our ip addr from the name of the command channel socket */
2358 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2360 /* and get the system to assign us a port */
2361 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2363 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2365 TRACE("Unable to bind socket\n");
2366 goto lend;
2369 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2371 TRACE("listen failed\n");
2372 goto lend;
2375 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2376 bSuccess = TRUE;
2378 lend:
2379 if (!bSuccess && lpwfs->lstnSocket != -1)
2381 closesocket(lpwfs->lstnSocket);
2382 lpwfs->lstnSocket = -1;
2385 return bSuccess;
2389 /***********************************************************************
2390 * FTP_SendType (internal)
2392 * Tell server type of data being transferred
2394 * RETURNS
2395 * TRUE on success
2396 * FALSE on failure
2398 * W98SE doesn't cache the type that's currently set
2399 * (i.e. it sends it always),
2400 * so we probably don't want to do that either.
2402 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2404 INT nResCode;
2405 WCHAR type[] = { 'I','\0' };
2406 BOOL bSuccess = FALSE;
2408 TRACE("\n");
2409 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2410 type[0] = 'A';
2412 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2413 goto lend;
2415 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2416 if (nResCode)
2418 if (nResCode == 2)
2419 bSuccess = TRUE;
2420 else
2421 FTP_SetResponseError(nResCode);
2424 lend:
2425 return bSuccess;
2428 /***********************************************************************
2429 * FTP_GetFileSize (internal)
2431 * Retrieves from the server the size of the given file
2433 * RETURNS
2434 * TRUE on success
2435 * FALSE on failure
2438 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2440 INT nResCode;
2441 BOOL bSuccess = FALSE;
2443 TRACE("\n");
2445 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2446 goto lend;
2448 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2449 if (nResCode)
2451 if (nResCode == 213) {
2452 /* Now parses the output to get the actual file size */
2453 int i;
2454 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2456 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2457 if (lpszResponseBuffer[i] == '\0') return FALSE;
2458 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2460 bSuccess = TRUE;
2461 } else {
2462 FTP_SetResponseError(nResCode);
2466 lend:
2467 return bSuccess;
2471 /***********************************************************************
2472 * FTP_SendPort (internal)
2474 * Tell server which port to use
2476 * RETURNS
2477 * TRUE on success
2478 * FALSE on failure
2481 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2483 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2484 INT nResCode;
2485 WCHAR szIPAddress[64];
2486 BOOL bSuccess = FALSE;
2487 TRACE("\n");
2489 sprintfW(szIPAddress, szIPFormat,
2490 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2491 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2492 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2493 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2494 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2495 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2497 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2498 goto lend;
2500 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2501 if (nResCode)
2503 if (nResCode == 200)
2504 bSuccess = TRUE;
2505 else
2506 FTP_SetResponseError(nResCode);
2509 lend:
2510 return bSuccess;
2514 /***********************************************************************
2515 * FTP_DoPassive (internal)
2517 * Tell server that we want to do passive transfers
2518 * and connect data socket
2520 * RETURNS
2521 * TRUE on success
2522 * FALSE on failure
2525 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2527 INT nResCode;
2528 BOOL bSuccess = FALSE;
2530 TRACE("\n");
2531 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2532 goto lend;
2534 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2535 if (nResCode)
2537 if (nResCode == 227)
2539 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2540 LPSTR p;
2541 int f[6];
2542 int i;
2543 char *pAddr, *pPort;
2544 INT nsocket = -1;
2545 struct sockaddr_in dataSocketAddress;
2547 p = lpszResponseBuffer+4; /* skip status code */
2548 while (*p != '\0' && (*p < '0' || *p > '9')) p++;
2550 if (*p == '\0')
2552 ERR("no address found in response, aborting\n");
2553 goto lend;
2556 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2557 &f[4], &f[5]) != 6)
2559 ERR("unknown response address format '%s', aborting\n", p);
2560 goto lend;
2562 for (i=0; i < 6; i++)
2563 f[i] = f[i] & 0xff;
2565 dataSocketAddress = lpwfs->socketAddress;
2566 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2567 pPort = (char *)&(dataSocketAddress.sin_port);
2568 pAddr[0] = f[0];
2569 pAddr[1] = f[1];
2570 pAddr[2] = f[2];
2571 pAddr[3] = f[3];
2572 pPort[0] = f[4];
2573 pPort[1] = f[5];
2575 nsocket = socket(AF_INET,SOCK_STREAM,0);
2576 if (nsocket == -1)
2577 goto lend;
2579 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2581 ERR("can't connect passive FTP data port.\n");
2582 closesocket(nsocket);
2583 goto lend;
2585 lpwfs->pasvSocket = nsocket;
2586 bSuccess = TRUE;
2588 else
2589 FTP_SetResponseError(nResCode);
2592 lend:
2593 return bSuccess;
2597 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2599 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2601 if (!FTP_DoPassive(lpwfs))
2602 return FALSE;
2604 else
2606 if (!FTP_SendPort(lpwfs))
2607 return FALSE;
2609 return TRUE;
2613 /***********************************************************************
2614 * FTP_GetDataSocket (internal)
2616 * Either accepts an incoming data socket connection from the server
2617 * or just returns the already opened socket after a PASV command
2618 * in case of passive FTP.
2621 * RETURNS
2622 * TRUE on success
2623 * FALSE on failure
2626 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2628 struct sockaddr_in saddr;
2629 socklen_t addrlen = sizeof(struct sockaddr);
2631 TRACE("\n");
2632 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2634 *nDataSocket = lpwfs->pasvSocket;
2636 else
2638 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2639 closesocket(lpwfs->lstnSocket);
2640 lpwfs->lstnSocket = -1;
2642 return *nDataSocket != -1;
2646 /***********************************************************************
2647 * FTP_SendData (internal)
2649 * Send data to the server
2651 * RETURNS
2652 * TRUE on success
2653 * FALSE on failure
2656 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2658 BY_HANDLE_FILE_INFORMATION fi;
2659 DWORD nBytesRead = 0;
2660 DWORD nBytesSent = 0;
2661 DWORD nTotalSent = 0;
2662 DWORD nBytesToSend, nLen;
2663 int nRC = 1;
2664 time_t s_long_time, e_long_time;
2665 LONG nSeconds;
2666 CHAR *lpszBuffer;
2668 TRACE("\n");
2669 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2671 /* Get the size of the file. */
2672 GetFileInformationByHandle(hFile, &fi);
2673 time(&s_long_time);
2677 nBytesToSend = nBytesRead - nBytesSent;
2679 if (nBytesToSend <= 0)
2681 /* Read data from file. */
2682 nBytesSent = 0;
2683 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2684 ERR("Failed reading from file\n");
2686 if (nBytesRead > 0)
2687 nBytesToSend = nBytesRead;
2688 else
2689 break;
2692 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2693 DATA_PACKET_SIZE : nBytesToSend;
2694 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2696 if (nRC != -1)
2698 nBytesSent += nRC;
2699 nTotalSent += nRC;
2702 /* Do some computation to display the status. */
2703 time(&e_long_time);
2704 nSeconds = e_long_time - s_long_time;
2705 if( nSeconds / 60 > 0 )
2707 TRACE( "%d bytes of %d bytes (%d%%) in %d min %d sec estimated remaining time %d sec\n",
2708 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2709 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2711 else
2713 TRACE( "%d bytes of %d bytes (%d%%) in %d sec estimated remaining time %d sec\n",
2714 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2715 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2717 } while (nRC != -1);
2719 TRACE("file transfer complete!\n");
2721 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2723 return nTotalSent;
2727 /***********************************************************************
2728 * FTP_SendRetrieve (internal)
2730 * Send request to retrieve a file
2732 * RETURNS
2733 * Number of bytes to be received on success
2734 * 0 on failure
2737 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2739 INT nResCode;
2740 DWORD nResult = 0;
2742 TRACE("\n");
2743 if (!FTP_InitListenSocket(lpwfs))
2744 goto lend;
2746 if (!FTP_SendType(lpwfs, dwType))
2747 goto lend;
2749 if (!FTP_SendPortOrPasv(lpwfs))
2750 goto lend;
2752 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2753 goto lend;
2755 TRACE("Waiting to receive %d bytes\n", nResult);
2757 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2758 goto lend;
2760 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2761 if ((nResCode != 125) && (nResCode != 150)) {
2762 /* That means that we got an error getting the file. */
2763 nResult = 0;
2766 lend:
2767 if (0 == nResult && lpwfs->lstnSocket != -1)
2769 closesocket(lpwfs->lstnSocket);
2770 lpwfs->lstnSocket = -1;
2773 return nResult;
2777 /***********************************************************************
2778 * FTP_RetrieveData (internal)
2780 * Retrieve data from server
2782 * RETURNS
2783 * TRUE on success
2784 * FALSE on failure
2787 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2789 DWORD nBytesWritten;
2790 DWORD nBytesReceived = 0;
2791 INT nRC = 0;
2792 CHAR *lpszBuffer;
2794 TRACE("\n");
2796 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2797 if (NULL == lpszBuffer)
2799 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2800 return FALSE;
2803 while (nBytesReceived < nBytes && nRC != -1)
2805 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2806 if (nRC != -1)
2808 /* other side closed socket. */
2809 if (nRC == 0)
2810 goto recv_end;
2811 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2812 nBytesReceived += nRC;
2815 TRACE("%d bytes of %d (%d%%)\r", nBytesReceived, nBytes,
2816 nBytesReceived * 100 / nBytes);
2819 TRACE("Data transfer complete\n");
2821 recv_end:
2822 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2824 return (nRC != -1);
2828 /***********************************************************************
2829 * FTP_CloseSessionHandle (internal)
2831 * Deallocate session handle
2833 * RETURNS
2834 * TRUE on success
2835 * FALSE on failure
2838 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2840 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2842 TRACE("\n");
2844 WININET_Release(&lpwfs->lpAppInfo->hdr);
2846 if (lpwfs->download_in_progress != NULL)
2847 lpwfs->download_in_progress->session_deleted = TRUE;
2849 if (lpwfs->sndSocket != -1)
2850 closesocket(lpwfs->sndSocket);
2852 if (lpwfs->lstnSocket != -1)
2853 closesocket(lpwfs->lstnSocket);
2855 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2856 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2857 HeapFree(GetProcessHeap(), 0, lpwfs);
2861 /***********************************************************************
2862 * FTP_FindNextFileW (Internal)
2864 * Continues a file search from a previous call to FindFirstFile
2866 * RETURNS
2867 * TRUE on success
2868 * FALSE on failure
2871 BOOL WINAPI FTP_FindNextFileW(LPWININETFTPFINDNEXTW lpwh, LPVOID lpvFindData)
2873 BOOL bSuccess = TRUE;
2874 LPWIN32_FIND_DATAW lpFindFileData;
2876 TRACE("index(%d) size(%d)\n", lpwh->index, lpwh->size);
2878 assert (lpwh->hdr.htype == WH_HFTPFINDNEXT);
2880 /* Clear any error information */
2881 INTERNET_SetLastError(0);
2883 lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
2884 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2886 if (lpwh->index >= lpwh->size)
2888 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2889 bSuccess = FALSE;
2890 goto lend;
2893 FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
2894 lpwh->index++;
2896 TRACE("\nName: %s\nSize: %d\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
2898 lend:
2900 if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
2902 INTERNET_ASYNC_RESULT iar;
2904 iar.dwResult = (DWORD)bSuccess;
2905 iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
2906 INTERNET_GetLastError();
2908 INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
2909 INTERNET_STATUS_REQUEST_COMPLETE, &iar,
2910 sizeof(INTERNET_ASYNC_RESULT));
2913 return bSuccess;
2917 /***********************************************************************
2918 * FTP_CloseFindNextHandle (internal)
2920 * Deallocate session handle
2922 * RETURNS
2923 * TRUE on success
2924 * FALSE on failure
2927 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2929 LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
2930 DWORD i;
2932 TRACE("\n");
2934 WININET_Release(&lpwfn->lpFtpSession->hdr);
2936 for (i = 0; i < lpwfn->size; i++)
2938 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2941 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2942 HeapFree(GetProcessHeap(), 0, lpwfn);
2945 /***********************************************************************
2946 * FTP_CloseFileTransferHandle (internal)
2948 * Closes the file transfer handle. This also 'cleans' the data queue of
2949 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2952 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2954 LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
2955 LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
2956 INT nResCode;
2958 TRACE("\n");
2960 WININET_Release(&lpwh->lpFtpSession->hdr);
2962 if (!lpwh->session_deleted)
2963 lpwfs->download_in_progress = NULL;
2965 /* This just serves to flush the control socket of any spurrious lines written
2966 to it (like '226 Transfer complete.').
2968 Wonder what to do if the server sends us an error code though...
2970 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2972 if (lpwh->nDataSocket != -1)
2973 closesocket(lpwh->nDataSocket);
2975 HeapFree(GetProcessHeap(), 0, lpwh);
2978 /***********************************************************************
2979 * FTP_ReceiveFileList (internal)
2981 * Read file list from server
2983 * RETURNS
2984 * Handle to file list on success
2985 * NULL on failure
2988 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2989 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2991 DWORD dwSize = 0;
2992 LPFILEPROPERTIESW lpafp = NULL;
2993 LPWININETFTPFINDNEXTW lpwfn = NULL;
2994 HINTERNET handle = 0;
2996 TRACE("(%p,%d,%s,%p,%d)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2998 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
3000 if(lpFindFileData)
3001 FTP_ConvertFileProp(lpafp, lpFindFileData);
3003 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFTPFINDNEXTW));
3004 if (lpwfn)
3006 lpwfn->hdr.htype = WH_HFTPFINDNEXT;
3007 lpwfn->hdr.dwContext = dwContext;
3008 lpwfn->hdr.dwRefCount = 1;
3009 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
3010 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
3011 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
3012 lpwfn->size = dwSize;
3013 lpwfn->lpafp = lpafp;
3015 WININET_AddRef( &lpwfs->hdr );
3016 lpwfn->lpFtpSession = lpwfs;
3018 handle = WININET_AllocHandle( &lpwfn->hdr );
3022 if( lpwfn )
3023 WININET_Release( &lpwfn->hdr );
3025 TRACE("Matched %d files\n", dwSize);
3026 return handle;
3030 /***********************************************************************
3031 * FTP_ConvertFileProp (internal)
3033 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
3035 * RETURNS
3036 * TRUE on success
3037 * FALSE on failure
3040 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
3042 BOOL bSuccess = FALSE;
3044 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
3046 if (lpafp)
3048 /* Convert 'Unix' time to Windows time */
3049 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
3050 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
3051 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
3052 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
3054 /* Not all fields are filled in */
3055 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
3056 lpFindFileData->nFileSizeLow = lpafp->nSize;
3058 if (lpafp->bIsDirectory)
3059 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
3061 if (lpafp->lpszName)
3062 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
3064 bSuccess = TRUE;
3067 return bSuccess;
3070 /***********************************************************************
3071 * FTP_ParseNextFile (internal)
3073 * Parse the next line in file listing
3075 * RETURNS
3076 * TRUE on success
3077 * FALSE on failure
3079 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
3081 static const char szSpace[] = " \t";
3082 DWORD nBufLen;
3083 char *pszLine;
3084 char *pszToken;
3085 char *pszTmp;
3086 BOOL found = FALSE;
3087 int i;
3089 lpfp->lpszName = NULL;
3090 do {
3091 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
3092 return FALSE;
3094 pszToken = strtok(pszLine, szSpace);
3095 /* ls format
3096 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
3098 * For instance:
3099 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
3101 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
3102 if(!FTP_ParsePermission(pszToken, lpfp))
3103 lpfp->bIsDirectory = FALSE;
3104 for(i=0; i<=3; i++) {
3105 if(!(pszToken = strtok(NULL, szSpace)))
3106 break;
3108 if(!pszToken) continue;
3109 if(lpfp->bIsDirectory) {
3110 TRACE("Is directory\n");
3111 lpfp->nSize = 0;
3113 else {
3114 TRACE("Size: %s\n", pszToken);
3115 lpfp->nSize = atol(pszToken);
3118 lpfp->tmLastModified.tm_sec = 0;
3119 lpfp->tmLastModified.tm_min = 0;
3120 lpfp->tmLastModified.tm_hour = 0;
3121 lpfp->tmLastModified.tm_mday = 0;
3122 lpfp->tmLastModified.tm_mon = 0;
3123 lpfp->tmLastModified.tm_year = 0;
3125 /* Determine month */
3126 pszToken = strtok(NULL, szSpace);
3127 if(!pszToken) continue;
3128 if(strlen(pszToken) >= 3) {
3129 pszToken[3] = 0;
3130 if((pszTmp = StrStrIA(szMonths, pszToken)))
3131 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
3133 /* Determine day */
3134 pszToken = strtok(NULL, szSpace);
3135 if(!pszToken) continue;
3136 lpfp->tmLastModified.tm_mday = atoi(pszToken);
3137 /* Determine time or year */
3138 pszToken = strtok(NULL, szSpace);
3139 if(!pszToken) continue;
3140 if((pszTmp = strchr(pszToken, ':'))) {
3141 struct tm* apTM;
3142 time_t aTime;
3143 *pszTmp = 0;
3144 pszTmp++;
3145 lpfp->tmLastModified.tm_min = atoi(pszTmp);
3146 lpfp->tmLastModified.tm_hour = atoi(pszToken);
3147 time(&aTime);
3148 apTM = localtime(&aTime);
3149 lpfp->tmLastModified.tm_year = apTM->tm_year;
3151 else {
3152 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
3153 lpfp->tmLastModified.tm_hour = 12;
3155 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3156 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3157 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3158 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3160 pszToken = strtok(NULL, szSpace);
3161 if(!pszToken) continue;
3162 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3163 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
3165 /* NT way of parsing ... :
3167 07-13-03 08:55PM <DIR> sakpatch
3168 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
3170 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
3171 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
3173 sscanf(pszToken, "%d-%d-%d",
3174 &lpfp->tmLastModified.tm_mon,
3175 &lpfp->tmLastModified.tm_mday,
3176 &lpfp->tmLastModified.tm_year);
3178 /* Hacky and bad Y2K protection :-) */
3179 if (lpfp->tmLastModified.tm_year < 70)
3180 lpfp->tmLastModified.tm_year += 100;
3182 pszToken = strtok(NULL, szSpace);
3183 if(!pszToken) continue;
3184 sscanf(pszToken, "%d:%d",
3185 &lpfp->tmLastModified.tm_hour,
3186 &lpfp->tmLastModified.tm_min);
3187 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
3188 lpfp->tmLastModified.tm_hour += 12;
3190 lpfp->tmLastModified.tm_sec = 0;
3192 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
3193 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
3194 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
3195 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
3197 pszToken = strtok(NULL, szSpace);
3198 if(!pszToken) continue;
3199 if(!strcasecmp(pszToken, "<DIR>")) {
3200 lpfp->bIsDirectory = TRUE;
3201 lpfp->nSize = 0;
3202 TRACE("Is directory\n");
3204 else {
3205 lpfp->bIsDirectory = FALSE;
3206 lpfp->nSize = atol(pszToken);
3207 TRACE("Size: %d\n", lpfp->nSize);
3210 pszToken = strtok(NULL, szSpace);
3211 if(!pszToken) continue;
3212 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
3213 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
3215 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
3216 else if(pszToken[0] == '+') {
3217 FIXME("EPLF Format not implemented\n");
3220 if(lpfp->lpszName) {
3221 if((lpszSearchFile == NULL) ||
3222 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
3223 found = TRUE;
3224 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
3226 else {
3227 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
3228 lpfp->lpszName = NULL;
3231 } while(!found);
3232 return TRUE;
3235 /***********************************************************************
3236 * FTP_ParseDirectory (internal)
3238 * Parse string of directory information
3240 * RETURNS
3241 * TRUE on success
3242 * FALSE on failure
3244 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
3245 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
3247 BOOL bSuccess = TRUE;
3248 INT sizeFilePropArray = 500;/*20; */
3249 INT indexFilePropArray = -1;
3251 TRACE("\n");
3253 /* Allocate intial file properties array */
3254 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
3255 if (!*lpafp)
3256 return FALSE;
3258 do {
3259 if (indexFilePropArray+1 >= sizeFilePropArray)
3261 LPFILEPROPERTIESW tmpafp;
3263 sizeFilePropArray *= 2;
3264 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
3265 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
3266 if (NULL == tmpafp)
3268 bSuccess = FALSE;
3269 break;
3272 *lpafp = tmpafp;
3274 indexFilePropArray++;
3275 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3277 if (bSuccess && indexFilePropArray)
3279 if (indexFilePropArray < sizeFilePropArray - 1)
3281 LPFILEPROPERTIESW tmpafp;
3283 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3284 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3285 if (NULL == tmpafp)
3286 *lpafp = tmpafp;
3288 *dwfp = indexFilePropArray;
3290 else
3292 HeapFree(GetProcessHeap(), 0, *lpafp);
3293 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3294 bSuccess = FALSE;
3297 return bSuccess;
3301 /***********************************************************************
3302 * FTP_ParsePermission (internal)
3304 * Parse permission string of directory information
3306 * RETURNS
3307 * TRUE on success
3308 * FALSE on failure
3311 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3313 BOOL bSuccess = TRUE;
3314 unsigned short nPermission = 0;
3315 INT nPos = 1;
3316 INT nLast = 9;
3318 TRACE("\n");
3319 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3321 bSuccess = FALSE;
3322 return bSuccess;
3325 lpfp->bIsDirectory = (*lpszPermission == 'd');
3328 switch (nPos)
3330 case 1:
3331 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3332 break;
3333 case 2:
3334 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3335 break;
3336 case 3:
3337 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3338 break;
3339 case 4:
3340 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3341 break;
3342 case 5:
3343 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3344 break;
3345 case 6:
3346 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3347 break;
3348 case 7:
3349 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3350 break;
3351 case 8:
3352 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3353 break;
3354 case 9:
3355 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3356 break;
3358 nPos++;
3359 }while (nPos <= nLast);
3361 lpfp->permissions = nPermission;
3362 return bSuccess;
3366 /***********************************************************************
3367 * FTP_SetResponseError (internal)
3369 * Set the appropriate error code for a given response from the server
3371 * RETURNS
3374 static DWORD FTP_SetResponseError(DWORD dwResponse)
3376 DWORD dwCode = 0;
3378 switch(dwResponse)
3380 case 421: /* Service not available - Server may be shutting down. */
3381 dwCode = ERROR_INTERNET_TIMEOUT;
3382 break;
3384 case 425: /* Cannot open data connection. */
3385 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3386 break;
3388 case 426: /* Connection closed, transer aborted. */
3389 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3390 break;
3392 case 500: /* Syntax error. Command unrecognized. */
3393 case 501: /* Syntax error. Error in parameters or arguments. */
3394 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3395 break;
3397 case 530: /* Not logged in. Login incorrect. */
3398 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3399 break;
3401 case 550: /* File action not taken. File not found or no access. */
3402 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3403 break;
3405 case 450: /* File action not taken. File may be busy. */
3406 case 451: /* Action aborted. Server error. */
3407 case 452: /* Action not taken. Insufficient storage space on server. */
3408 case 502: /* Command not implemented. */
3409 case 503: /* Bad sequence of commands. */
3410 case 504: /* Command not implemented for that parameter. */
3411 case 532: /* Need account for storing files */
3412 case 551: /* Requested action aborted. Page type unknown */
3413 case 552: /* Action aborted. Exceeded storage allocation */
3414 case 553: /* Action not taken. File name not allowed. */
3416 default:
3417 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3418 break;
3421 INTERNET_SetLastError(dwCode);
3422 return dwCode;