2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
5 * Copyright 2004 Mike McCormack for CodeWeavers
6 * Copyright 2004 Kevin Koltzau
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include "wine/port.h"
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
58 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define DATA_PACKET_SIZE 0x2000
68 /* FTP commands with arguments. */
84 /* FTP commands without arguments. */
93 static const CHAR *szFtpCommands[] = {
116 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
117 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
119 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr);
120 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr);
121 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr);
122 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
123 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext);
124 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
125 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
126 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
127 static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext);
128 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
129 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile);
130 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
131 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
132 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
133 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
134 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
135 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize);
136 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
137 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
138 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
139 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
140 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
141 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
142 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
143 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
144 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext);
145 static DWORD FTP_SetResponseError(DWORD dwResponse);
147 /***********************************************************************
148 * FtpPutFileA (WININET.@)
150 * Uploads a file to the FTP server
157 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
158 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
160 LPWSTR lpwzLocalFile;
161 LPWSTR lpwzNewRemoteFile;
164 lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
165 lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
166 ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
168 HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
169 HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile);
173 /***********************************************************************
174 * FtpPutFileW (WININET.@)
176 * Uploads a file to the FTP server
183 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
184 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
186 LPWININETFTPSESSIONW lpwfs;
187 LPWININETAPPINFOW hIC = NULL;
190 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
191 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
193 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
197 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
198 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
200 WORKREQUEST workRequest;
201 struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
203 workRequest.asyncall = FTPPUTFILEW;
204 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
205 req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
206 req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
207 req->dwFlags = dwFlags;
208 req->dwContext = dwContext;
210 r = INTERNET_AsyncCall(&workRequest);
214 r = FTP_FtpPutFileW(lpwfs, lpszLocalFile,
215 lpszNewRemoteFile, dwFlags, dwContext);
220 WININET_Release( &lpwfs->hdr );
225 /***********************************************************************
226 * FTP_FtpPutFileW (Internal)
228 * Uploads a file to the FTP server
235 BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
236 LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
239 BOOL bSuccess = FALSE;
240 LPWININETAPPINFOW hIC = NULL;
243 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
245 if (!lpszLocalFile || !lpszNewRemoteFile)
247 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
251 assert( WH_HFTPSESSION == lpwfs->hdr.htype);
253 /* Clear any error information */
254 INTERNET_SetLastError(0);
256 /* Open file to be uploaded */
257 if (INVALID_HANDLE_VALUE ==
258 (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
260 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
264 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
265 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
267 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
271 /* Get data socket to server */
272 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
274 FTP_SendData(lpwfs, nDataSocket, hFile);
275 closesocket(nDataSocket);
276 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
282 FTP_SetResponseError(nResCode);
288 if (lpwfs->lstnSocket != -1)
289 closesocket(lpwfs->lstnSocket);
291 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
293 INTERNET_ASYNC_RESULT iar;
295 iar.dwResult = (DWORD)bSuccess;
296 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
297 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
298 &iar, sizeof(INTERNET_ASYNC_RESULT));
308 /***********************************************************************
309 * FtpSetCurrentDirectoryA (WININET.@)
311 * Change the working directory on the FTP server
318 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
320 LPWSTR lpwzDirectory;
323 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
324 ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
325 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
330 /***********************************************************************
331 * FtpSetCurrentDirectoryW (WININET.@)
333 * Change the working directory on the FTP server
340 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
342 LPWININETFTPSESSIONW lpwfs;
343 LPWININETAPPINFOW hIC = NULL;
346 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
347 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
349 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
353 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
355 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
356 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
358 WORKREQUEST workRequest;
359 struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
361 workRequest.asyncall = FTPSETCURRENTDIRECTORYW;
362 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
363 req = &workRequest.u.FtpSetCurrentDirectoryW;
364 req->lpszDirectory = WININET_strdupW(lpszDirectory);
366 r = INTERNET_AsyncCall(&workRequest);
370 r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory);
375 WININET_Release( &lpwfs->hdr );
381 /***********************************************************************
382 * FTP_FtpSetCurrentDirectoryW (Internal)
384 * Change the working directory on the FTP server
391 BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
394 LPWININETAPPINFOW hIC = NULL;
395 DWORD bSuccess = FALSE;
397 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
399 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
401 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
405 /* Clear any error information */
406 INTERNET_SetLastError(0);
408 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
409 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
410 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
413 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
420 FTP_SetResponseError(nResCode);
424 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
426 INTERNET_ASYNC_RESULT iar;
428 iar.dwResult = (DWORD)bSuccess;
429 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
430 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
431 &iar, sizeof(INTERNET_ASYNC_RESULT));
437 /***********************************************************************
438 * FtpCreateDirectoryA (WININET.@)
440 * Create new directory on the FTP server
447 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
449 LPWSTR lpwzDirectory;
452 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
453 ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
454 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
459 /***********************************************************************
460 * FtpCreateDirectoryW (WININET.@)
462 * Create new directory on the FTP server
469 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
471 LPWININETFTPSESSIONW lpwfs;
472 LPWININETAPPINFOW hIC = NULL;
475 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
476 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
478 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
482 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
483 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
485 WORKREQUEST workRequest;
486 struct WORKREQ_FTPCREATEDIRECTORYW *req;
488 workRequest.asyncall = FTPCREATEDIRECTORYW;
489 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
490 req = &workRequest.u.FtpCreateDirectoryW;
491 req->lpszDirectory = WININET_strdupW(lpszDirectory);
493 r = INTERNET_AsyncCall(&workRequest);
497 r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory);
501 WININET_Release( &lpwfs->hdr );
507 /***********************************************************************
508 * FTP_FtpCreateDirectoryW (Internal)
510 * Create new directory on the FTP server
517 BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
520 BOOL bSuccess = FALSE;
521 LPWININETAPPINFOW hIC = NULL;
523 TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
525 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
527 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
531 /* Clear any error information */
532 INTERNET_SetLastError(0);
534 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
537 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
543 FTP_SetResponseError(nResCode);
547 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
548 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
550 INTERNET_ASYNC_RESULT iar;
552 iar.dwResult = (DWORD)bSuccess;
553 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
554 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
555 &iar, sizeof(INTERNET_ASYNC_RESULT));
561 /***********************************************************************
562 * FtpFindFirstFileA (WININET.@)
564 * Search the specified directory
567 * HINTERNET on success
571 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
572 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
574 LPWSTR lpwzSearchFile;
575 WIN32_FIND_DATAW wfd;
576 LPWIN32_FIND_DATAW lpFindFileDataW;
579 lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
580 lpFindFileDataW = lpFindFileData?&wfd:NULL;
581 ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
582 HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
585 WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
591 /***********************************************************************
592 * FtpFindFirstFileW (WININET.@)
594 * Search the specified directory
597 * HINTERNET on success
601 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
602 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
604 LPWININETFTPSESSIONW lpwfs;
605 LPWININETAPPINFOW hIC = NULL;
608 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
609 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
611 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
615 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
616 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
618 WORKREQUEST workRequest;
619 struct WORKREQ_FTPFINDFIRSTFILEW *req;
621 workRequest.asyncall = FTPFINDFIRSTFILEW;
622 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
623 req = &workRequest.u.FtpFindFirstFileW;
624 req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
625 req->lpFindFileData = lpFindFileData;
626 req->dwFlags = dwFlags;
627 req->dwContext= dwContext;
629 INTERNET_AsyncCall(&workRequest);
634 r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData,
639 WININET_Release( &lpwfs->hdr );
645 /***********************************************************************
646 * FTP_FtpFindFirstFileW (Internal)
648 * Search the specified directory
651 * HINTERNET on success
655 HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
656 LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext)
659 LPWININETAPPINFOW hIC = NULL;
660 HINTERNET hFindNext = NULL;
664 assert(WH_HFTPSESSION == lpwfs->hdr.htype);
666 /* Clear any error information */
667 INTERNET_SetLastError(0);
669 if (!FTP_InitListenSocket(lpwfs))
672 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
675 if (!FTP_SendPortOrPasv(lpwfs))
678 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
679 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL,
680 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
683 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
686 if (nResCode == 125 || nResCode == 150)
690 /* Get data socket to server */
691 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
693 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext);
694 closesocket(nDataSocket);
695 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
696 if (nResCode != 226 && nResCode != 250)
697 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
701 FTP_SetResponseError(nResCode);
705 if (lpwfs->lstnSocket != -1)
706 closesocket(lpwfs->lstnSocket);
708 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
710 INTERNET_ASYNC_RESULT iar;
714 iar.dwResult = (DWORD)hFindNext;
715 iar.dwError = ERROR_SUCCESS;
716 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
717 &iar, sizeof(INTERNET_ASYNC_RESULT));
720 iar.dwResult = (DWORD)hFindNext;
721 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
722 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
723 &iar, sizeof(INTERNET_ASYNC_RESULT));
730 /***********************************************************************
731 * FtpGetCurrentDirectoryA (WININET.@)
733 * Retrieves the current directory
740 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
741 LPDWORD lpdwCurrentDirectory)
747 if(lpdwCurrentDirectory) len = *lpdwCurrentDirectory;
748 ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL);
749 if(lpdwCurrentDirectory) {
750 *lpdwCurrentDirectory = len;
751 if(lpszCurrentDirectory)
752 WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL);
758 /***********************************************************************
759 * FtpGetCurrentDirectoryW (WININET.@)
761 * Retrieves the current directory
768 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
769 LPDWORD lpdwCurrentDirectory)
771 LPWININETFTPSESSIONW lpwfs;
772 LPWININETAPPINFOW hIC = NULL;
775 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
777 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
778 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
780 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
784 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
785 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
787 WORKREQUEST workRequest;
788 struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
790 workRequest.asyncall = FTPGETCURRENTDIRECTORYW;
791 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
792 req = &workRequest.u.FtpGetCurrentDirectoryW;
793 req->lpszDirectory = lpszCurrentDirectory;
794 req->lpdwDirectory = lpdwCurrentDirectory;
796 r = INTERNET_AsyncCall(&workRequest);
800 r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory,
801 lpdwCurrentDirectory);
806 WININET_Release( &lpwfs->hdr );
812 /***********************************************************************
813 * FTP_FtpGetCurrentDirectoryA (Internal)
815 * Retrieves the current directory
822 BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
823 LPDWORD lpdwCurrentDirectory)
826 LPWININETAPPINFOW hIC = NULL;
827 DWORD bSuccess = FALSE;
829 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
831 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
833 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
837 /* Clear any error information */
838 INTERNET_SetLastError(0);
840 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
842 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
843 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
844 lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext))
847 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
850 if (nResCode == 257) /* Extract directory name */
852 DWORD firstpos, lastpos, len;
853 LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
855 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
857 if ('"' == lpszResponseBuffer[lastpos])
866 len = lastpos - firstpos - 1;
867 lstrcpynW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], *lpdwCurrentDirectory);
868 HeapFree(GetProcessHeap(), 0, lpszResponseBuffer);
869 *lpdwCurrentDirectory = len;
873 FTP_SetResponseError(nResCode);
877 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
879 INTERNET_ASYNC_RESULT iar;
881 iar.dwResult = (DWORD)bSuccess;
882 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
883 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
884 &iar, sizeof(INTERNET_ASYNC_RESULT));
887 return (DWORD) bSuccess;
890 /***********************************************************************
891 * FtpOpenFileA (WININET.@)
893 * Open a remote file for writing or reading
896 * HINTERNET handle on success
900 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
901 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
907 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
908 ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
909 HeapFree(GetProcessHeap(), 0, lpwzFileName);
914 /***********************************************************************
915 * FtpOpenFileW (WININET.@)
917 * Open a remote file for writing or reading
920 * HINTERNET handle on success
924 HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
925 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
928 LPWININETFTPSESSIONW lpwfs;
929 LPWININETAPPINFOW hIC = NULL;
932 TRACE("(%p,%s,0x%08lx,0x%08lx,0x%08lx)\n", hFtpSession,
933 debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
935 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
936 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
938 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
942 if (lpwfs->download_in_progress != NULL) {
943 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
946 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
947 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
949 WORKREQUEST workRequest;
950 struct WORKREQ_FTPOPENFILEW *req;
952 workRequest.asyncall = FTPOPENFILEW;
953 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
954 req = &workRequest.u.FtpOpenFileW;
955 req->lpszFilename = WININET_strdupW(lpszFileName);
956 req->dwAccess = fdwAccess;
957 req->dwFlags = dwFlags;
958 req->dwContext = dwContext;
960 INTERNET_AsyncCall(&workRequest);
965 r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
970 WININET_Release( &lpwfs->hdr );
976 /***********************************************************************
977 * FTP_FtpOpenFileW (Internal)
979 * Open a remote file for writing or reading
982 * HINTERNET handle on success
986 HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
987 LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
991 BOOL bSuccess = FALSE;
992 LPWININETFILE lpwh = NULL;
993 LPWININETAPPINFOW hIC = NULL;
994 HINTERNET handle = NULL;
998 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1000 /* Clear any error information */
1001 INTERNET_SetLastError(0);
1003 if (GENERIC_READ == fdwAccess)
1005 /* Set up socket to retrieve data */
1006 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
1008 else if (GENERIC_WRITE == fdwAccess)
1010 /* Set up socket to send data */
1011 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
1014 /* Get data socket to server */
1015 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
1017 lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE));
1018 lpwh->hdr.htype = WH_HFILE;
1019 lpwh->hdr.dwFlags = dwFlags;
1020 lpwh->hdr.dwContext = dwContext;
1021 lpwh->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
1022 lpwh->hdr.dwRefCount = 1;
1023 lpwh->hdr.destroy = FTP_CloseFileTransferHandle;
1024 lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
1025 lpwh->nDataSocket = nDataSocket;
1026 lpwh->session_deleted = FALSE;
1028 handle = WININET_AllocHandle( &lpwh->hdr );
1032 /* Indicate that a download is currently in progress */
1033 lpwfs->download_in_progress = lpwh;
1036 if (lpwfs->lstnSocket != -1)
1037 closesocket(lpwfs->lstnSocket);
1039 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1040 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1042 INTERNET_ASYNC_RESULT iar;
1046 iar.dwResult = (DWORD)handle;
1047 iar.dwError = ERROR_SUCCESS;
1048 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
1049 &iar, sizeof(INTERNET_ASYNC_RESULT));
1052 iar.dwResult = (DWORD)bSuccess;
1053 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1054 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1055 &iar, sizeof(INTERNET_ASYNC_RESULT));
1060 WININET_Release( &lpwh->hdr );
1066 /***********************************************************************
1067 * FtpGetFileA (WININET.@)
1069 * Retrieve file from the FTP server
1076 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
1077 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1080 LPWSTR lpwzRemoteFile;
1084 lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
1085 lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
1086 ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
1087 dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1088 HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
1089 HeapFree(GetProcessHeap(), 0, lpwzNewFile);
1094 /***********************************************************************
1095 * FtpGetFileW (WININET.@)
1097 * Retrieve file from the FTP server
1104 BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1105 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1108 LPWININETFTPSESSIONW lpwfs;
1109 LPWININETAPPINFOW hIC = NULL;
1112 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
1113 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1115 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1119 if (lpwfs->download_in_progress != NULL) {
1120 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1124 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1125 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1127 WORKREQUEST workRequest;
1128 struct WORKREQ_FTPGETFILEW *req;
1130 workRequest.asyncall = FTPGETFILEW;
1131 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1132 req = &workRequest.u.FtpGetFileW;
1133 req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
1134 req->lpszNewFile = WININET_strdupW(lpszNewFile);
1135 req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
1136 req->fFailIfExists = fFailIfExists;
1137 req->dwFlags = dwInternetFlags;
1138 req->dwContext = dwContext;
1140 r = INTERNET_AsyncCall(&workRequest);
1144 r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile,
1145 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
1150 WININET_Release( &lpwfs->hdr );
1156 /***********************************************************************
1157 * FTP_FtpGetFileW (Internal)
1159 * Retrieve file from the FTP server
1166 BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
1167 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
1171 BOOL bSuccess = FALSE;
1173 LPWININETAPPINFOW hIC = NULL;
1175 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
1177 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1179 /* Clear any error information */
1180 INTERNET_SetLastError(0);
1182 /* Ensure we can write to lpszNewfile by opening it */
1183 hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
1184 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
1185 if (INVALID_HANDLE_VALUE == hFile)
1188 /* Set up socket to retrieve data */
1189 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
1195 /* Get data socket to server */
1196 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
1201 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
1202 nResCode = FTP_ReceiveResponse(lpwfs, dwContext);
1205 if (nResCode == 226)
1208 FTP_SetResponseError(nResCode);
1210 closesocket(nDataSocket);
1215 if (lpwfs->lstnSocket != -1)
1216 closesocket(lpwfs->lstnSocket);
1221 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1222 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1224 INTERNET_ASYNC_RESULT iar;
1226 iar.dwResult = (DWORD)bSuccess;
1227 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1228 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1229 &iar, sizeof(INTERNET_ASYNC_RESULT));
1235 /***********************************************************************
1236 * FtpGetFileSize (WININET.@)
1238 DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh )
1240 FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh);
1242 if (lpdwFileSizeHigh)
1243 *lpdwFileSizeHigh = 0;
1248 /***********************************************************************
1249 * FtpDeleteFileA (WININET.@)
1251 * Delete a file on the ftp server
1258 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1260 LPWSTR lpwzFileName;
1263 lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
1264 ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
1265 HeapFree(GetProcessHeap(), 0, lpwzFileName);
1269 /***********************************************************************
1270 * FtpDeleteFileW (WININET.@)
1272 * Delete a file on the ftp server
1279 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
1281 LPWININETFTPSESSIONW lpwfs;
1282 LPWININETAPPINFOW hIC = NULL;
1285 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1286 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1288 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1292 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1293 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1295 WORKREQUEST workRequest;
1296 struct WORKREQ_FTPDELETEFILEW *req;
1298 workRequest.asyncall = FTPDELETEFILEW;
1299 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1300 req = &workRequest.u.FtpDeleteFileW;
1301 req->lpszFilename = WININET_strdupW(lpszFileName);
1303 r = INTERNET_AsyncCall(&workRequest);
1307 r = FTP_FtpDeleteFileW(hFtpSession, lpszFileName);
1312 WININET_Release( &lpwfs->hdr );
1317 /***********************************************************************
1318 * FTP_FtpDeleteFileW (Internal)
1320 * Delete a file on the ftp server
1327 BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
1330 BOOL bSuccess = FALSE;
1331 LPWININETAPPINFOW hIC = NULL;
1333 TRACE("%p\n", lpwfs);
1335 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1337 /* Clear any error information */
1338 INTERNET_SetLastError(0);
1340 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1343 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1346 if (nResCode == 250)
1349 FTP_SetResponseError(nResCode);
1352 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1353 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1355 INTERNET_ASYNC_RESULT iar;
1357 iar.dwResult = (DWORD)bSuccess;
1358 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1359 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1360 &iar, sizeof(INTERNET_ASYNC_RESULT));
1367 /***********************************************************************
1368 * FtpRemoveDirectoryA (WININET.@)
1370 * Remove a directory on the ftp server
1377 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1379 LPWSTR lpwzDirectory;
1382 lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
1383 ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
1384 HeapFree(GetProcessHeap(), 0, lpwzDirectory);
1388 /***********************************************************************
1389 * FtpRemoveDirectoryW (WININET.@)
1391 * Remove a directory on the ftp server
1398 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
1400 LPWININETFTPSESSIONW lpwfs;
1401 LPWININETAPPINFOW hIC = NULL;
1404 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1405 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1407 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1411 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1412 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1414 WORKREQUEST workRequest;
1415 struct WORKREQ_FTPREMOVEDIRECTORYW *req;
1417 workRequest.asyncall = FTPREMOVEDIRECTORYW;
1418 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1419 req = &workRequest.u.FtpRemoveDirectoryW;
1420 req->lpszDirectory = WININET_strdupW(lpszDirectory);
1422 r = INTERNET_AsyncCall(&workRequest);
1426 r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory);
1431 WININET_Release( &lpwfs->hdr );
1436 /***********************************************************************
1437 * FTP_FtpRemoveDirectoryW (Internal)
1439 * Remove a directory on the ftp server
1446 BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
1449 BOOL bSuccess = FALSE;
1450 LPWININETAPPINFOW hIC = NULL;
1454 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1456 /* Clear any error information */
1457 INTERNET_SetLastError(0);
1459 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1462 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1465 if (nResCode == 250)
1468 FTP_SetResponseError(nResCode);
1472 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1473 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1475 INTERNET_ASYNC_RESULT iar;
1477 iar.dwResult = (DWORD)bSuccess;
1478 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1479 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1480 &iar, sizeof(INTERNET_ASYNC_RESULT));
1487 /***********************************************************************
1488 * FtpRenameFileA (WININET.@)
1490 * Rename a file on the ftp server
1497 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1503 lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
1504 lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
1505 ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
1506 HeapFree(GetProcessHeap(), 0, lpwzSrc);
1507 HeapFree(GetProcessHeap(), 0, lpwzDest);
1511 /***********************************************************************
1512 * FtpRenameFileW (WININET.@)
1514 * Rename a file on the ftp server
1521 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
1523 LPWININETFTPSESSIONW lpwfs;
1524 LPWININETAPPINFOW hIC = NULL;
1527 lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
1528 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1530 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1534 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1535 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1537 WORKREQUEST workRequest;
1538 struct WORKREQ_FTPRENAMEFILEW *req;
1540 workRequest.asyncall = FTPRENAMEFILEW;
1541 workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
1542 req = &workRequest.u.FtpRenameFileW;
1543 req->lpszSrcFile = WININET_strdupW(lpszSrc);
1544 req->lpszDestFile = WININET_strdupW(lpszDest);
1546 r = INTERNET_AsyncCall(&workRequest);
1550 r = FTP_FtpRenameFileW(hFtpSession, lpszSrc, lpszDest);
1555 WININET_Release( &lpwfs->hdr );
1560 /***********************************************************************
1561 * FTP_FtpRenameFileA (Internal)
1563 * Rename a file on the ftp server
1570 BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
1571 LPCWSTR lpszSrc, LPCWSTR lpszDest)
1574 BOOL bSuccess = FALSE;
1575 LPWININETAPPINFOW hIC = NULL;
1579 assert (WH_HFTPSESSION == lpwfs->hdr.htype);
1581 /* Clear any error information */
1582 INTERNET_SetLastError(0);
1584 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1587 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1588 if (nResCode == 350)
1590 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1593 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1596 if (nResCode == 250)
1599 FTP_SetResponseError(nResCode);
1602 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1603 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1605 INTERNET_ASYNC_RESULT iar;
1607 iar.dwResult = (DWORD)bSuccess;
1608 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1609 SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1610 &iar, sizeof(INTERNET_ASYNC_RESULT));
1616 /***********************************************************************
1617 * FtpCommandA (WININET.@)
1619 BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1620 LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1622 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1623 debugstr_a(lpszCommand), dwContext, phFtpCommand);
1628 /***********************************************************************
1629 * FtpCommandW (WININET.@)
1631 BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags,
1632 LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
1634 FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags,
1635 debugstr_w(lpszCommand), dwContext, phFtpCommand);
1640 /***********************************************************************
1641 * FTP_Connect (internal)
1643 * Connect to a ftp server
1646 * HINTERNET a session handle on success
1651 HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
1652 INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
1653 LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext,
1654 DWORD dwInternalFlags)
1656 static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'};
1657 static const WCHAR szDefaultPassword[] = {'u','s','e','r','@','s','e','r','v','e','r','\0'};
1658 struct sockaddr_in socketAddr;
1661 BOOL bSuccess = FALSE;
1662 LPWININETFTPSESSIONW lpwfs = NULL;
1663 HINTERNET handle = NULL;
1665 TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1666 hIC, debugstr_w(lpszServerName),
1667 nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword));
1669 assert( hIC->hdr.htype == WH_HINIT );
1671 if (NULL == lpszUserName && NULL != lpszPassword)
1673 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1677 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
1680 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1684 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1685 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1687 lpwfs->hdr.htype = WH_HFTPSESSION;
1688 lpwfs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr );
1689 lpwfs->hdr.dwFlags = dwFlags;
1690 lpwfs->hdr.dwContext = dwContext;
1691 lpwfs->hdr.dwInternalFlags = dwInternalFlags;
1692 lpwfs->hdr.dwRefCount = 1;
1693 lpwfs->hdr.destroy = FTP_CloseSessionHandle;
1694 lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB;
1695 lpwfs->download_in_progress = NULL;
1697 handle = WININET_AllocHandle( &lpwfs->hdr );
1700 ERR("Failed to alloc handle\n");
1701 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1705 if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1706 if(strchrW(hIC->lpszProxy, ' '))
1707 FIXME("Several proxies not implemented.\n");
1708 if(hIC->lpszProxyBypass)
1709 FIXME("Proxy bypass is ignored.\n");
1711 if ( !lpszUserName) {
1712 lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
1713 lpwfs->lpszPassword = WININET_strdupW(szDefaultPassword);
1716 lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
1717 lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
1720 /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
1721 if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
1723 INTERNET_ASYNC_RESULT iar;
1725 iar.dwResult = (DWORD)handle;
1726 iar.dwError = ERROR_SUCCESS;
1728 SendAsyncCallback(&hIC->hdr, dwContext,
1729 INTERNET_STATUS_HANDLE_CREATED, &iar,
1730 sizeof(INTERNET_ASYNC_RESULT));
1733 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1734 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1736 if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
1738 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1742 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1743 (LPWSTR) lpszServerName, strlenW(lpszServerName));
1745 nsocket = socket(AF_INET,SOCK_STREAM,0);
1748 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1752 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1753 &socketAddr, sizeof(struct sockaddr_in));
1755 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1757 ERR("Unable to connect (%s)\n", strerror(errno));
1758 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1762 TRACE("Connected to server\n");
1763 lpwfs->sndSocket = nsocket;
1764 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1765 &socketAddr, sizeof(struct sockaddr_in));
1767 sock_namelen = sizeof(lpwfs->socketAddress);
1768 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1770 if (FTP_ConnectToHost(lpwfs))
1772 TRACE("Successfully logged into server\n");
1778 if (!bSuccess && nsocket == -1)
1779 closesocket(nsocket);
1781 if (!bSuccess && lpwfs)
1783 HeapFree(GetProcessHeap(), 0, lpwfs);
1784 WININET_FreeHandle( handle );
1788 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1790 INTERNET_ASYNC_RESULT iar;
1792 iar.dwResult = (DWORD)lpwfs;
1793 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1794 SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1795 &iar, sizeof(INTERNET_ASYNC_RESULT));
1802 /***********************************************************************
1803 * FTP_ConnectToHost (internal)
1805 * Connect to a ftp server
1812 static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
1815 BOOL bSuccess = FALSE;
1818 FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1820 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1823 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
1826 /* Login successful... */
1827 if (nResCode == 230)
1829 /* User name okay, need password... */
1830 else if (nResCode == 331)
1831 bSuccess = FTP_SendPassword(lpwfs);
1832 /* Need account for login... */
1833 else if (nResCode == 332)
1834 bSuccess = FTP_SendAccount(lpwfs);
1836 FTP_SetResponseError(nResCode);
1839 TRACE("Returning %d\n", bSuccess);
1845 /***********************************************************************
1846 * FTP_SendCommandA (internal)
1848 * Send command to server
1855 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1856 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1860 DWORD nBytesSent = 0;
1864 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1868 HINTERNET hHandle = WININET_FindHandle( hdr );
1871 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1872 WININET_Release( hdr );
1876 dwParamLen = lpszParam?strlen(lpszParam)+1:0;
1877 len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF);
1878 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1880 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1883 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "",
1884 dwParamLen ? lpszParam : "", szCRLF);
1886 TRACE("Sending (%s) len(%ld)\n", buf, len);
1887 while((nBytesSent < len) && (nRC != -1))
1889 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1893 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1897 HINTERNET hHandle = WININET_FindHandle( hdr );
1900 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1901 &nBytesSent, sizeof(DWORD));
1902 WININET_Release( hdr );
1906 TRACE("Sent %ld bytes\n", nBytesSent);
1910 /***********************************************************************
1911 * FTP_SendCommand (internal)
1913 * Send command to server
1920 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
1921 INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext)
1924 LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
1925 ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
1926 HeapFree(GetProcessHeap(), 0, lpszParamA);
1930 /***********************************************************************
1931 * FTP_ReceiveResponse (internal)
1933 * Receive response from server
1936 * Reply code on success
1940 INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext)
1942 LPSTR lpszResponse = INTERNET_GetResponseBuffer();
1945 char firstprefix[5];
1946 BOOL multiline = FALSE;
1947 LPWININETAPPINFOW hIC = NULL;
1949 TRACE("socket(%d)\n", lpwfs->sndSocket);
1951 hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent;
1952 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1956 if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv))
1963 if(lpszResponse[3] != '-')
1966 { /* Start of multiline repsonse. Loop until we get "nnn " */
1968 memcpy(firstprefix, lpszResponse, 3);
1969 firstprefix[3] = ' ';
1970 firstprefix[4] = '\0';
1975 if(!memcmp(firstprefix, lpszResponse, 4))
1983 rc = atoi(lpszResponse);
1985 SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
1986 &nRecv, sizeof(DWORD));
1990 TRACE("return %d\n", rc);
1995 /***********************************************************************
1996 * FTP_SendPassword (internal)
1998 * Send password to ftp server
2005 static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
2008 BOOL bSuccess = FALSE;
2011 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
2014 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2017 TRACE("Received reply code %d\n", nResCode);
2018 /* Login successful... */
2019 if (nResCode == 230)
2021 /* Command not implemented, superfluous at the server site... */
2022 /* Need account for login... */
2023 else if (nResCode == 332)
2024 bSuccess = FTP_SendAccount(lpwfs);
2026 FTP_SetResponseError(nResCode);
2030 TRACE("Returning %d\n", bSuccess);
2035 /***********************************************************************
2036 * FTP_SendAccount (internal)
2045 static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
2048 BOOL bSuccess = FALSE;
2051 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0))
2054 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2058 FTP_SetResponseError(nResCode);
2065 /***********************************************************************
2066 * FTP_SendStore (internal)
2068 * Send request to upload file to ftp server
2075 static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2078 BOOL bSuccess = FALSE;
2081 if (!FTP_InitListenSocket(lpwfs))
2084 if (!FTP_SendType(lpwfs, dwType))
2087 if (!FTP_SendPortOrPasv(lpwfs))
2090 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
2092 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2095 if (nResCode == 150)
2098 FTP_SetResponseError(nResCode);
2102 if (!bSuccess && lpwfs->lstnSocket != -1)
2104 closesocket(lpwfs->lstnSocket);
2105 lpwfs->lstnSocket = -1;
2112 /***********************************************************************
2113 * FTP_InitListenSocket (internal)
2115 * Create a socket to listen for server response
2122 static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
2124 BOOL bSuccess = FALSE;
2125 size_t namelen = sizeof(struct sockaddr_in);
2129 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
2130 if (lpwfs->lstnSocket == -1)
2132 TRACE("Unable to create listening socket\n");
2136 /* We obtain our ip addr from the name of the command channel socket */
2137 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
2139 /* and get the system to assign us a port */
2140 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
2142 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
2144 TRACE("Unable to bind socket\n");
2148 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
2150 TRACE("listen failed\n");
2154 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
2158 if (!bSuccess && lpwfs->lstnSocket == -1)
2160 closesocket(lpwfs->lstnSocket);
2161 lpwfs->lstnSocket = -1;
2168 /***********************************************************************
2169 * FTP_SendType (internal)
2171 * Tell server type of data being transferred
2177 * W98SE doesn't cache the type that's currently set
2178 * (i.e. it sends it always),
2179 * so we probably don't want to do that either.
2181 static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
2184 WCHAR type[] = { 'I','\0' };
2185 BOOL bSuccess = FALSE;
2188 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
2191 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
2194 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100;
2200 FTP_SetResponseError(nResCode);
2207 /***********************************************************************
2208 * FTP_GetFileSize (internal)
2210 * Retrieves from the server the size of the given file
2217 static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
2220 BOOL bSuccess = FALSE;
2224 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0))
2227 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2230 if (nResCode == 213) {
2231 /* Now parses the output to get the actual file size */
2233 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2235 for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ;
2236 if (lpszResponseBuffer[i] == '\0') return FALSE;
2237 *dwSize = atol(&(lpszResponseBuffer[i + 1]));
2241 FTP_SetResponseError(nResCode);
2250 /***********************************************************************
2251 * FTP_SendPort (internal)
2253 * Tell server which port to use
2260 static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
2262 static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
2264 WCHAR szIPAddress[64];
2265 BOOL bSuccess = FALSE;
2268 sprintfW(szIPAddress, szIPFormat,
2269 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
2270 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
2271 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
2272 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
2273 lpwfs->lstnSocketAddress.sin_port & 0xFF,
2274 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
2276 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
2279 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2282 if (nResCode == 200)
2285 FTP_SetResponseError(nResCode);
2293 /***********************************************************************
2294 * FTP_DoPassive (internal)
2296 * Tell server that we want to do passive transfers
2297 * and connect data socket
2304 static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
2307 BOOL bSuccess = FALSE;
2310 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
2313 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2316 if (nResCode == 227)
2318 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2322 char *pAddr, *pPort;
2324 struct sockaddr_in dataSocketAddress;
2326 p = lpszResponseBuffer+4; /* skip status code */
2328 /* do a very strict check; we can improve that later. */
2330 if (strncmp(p, "Entering Passive Mode", 21))
2332 ERR("unknown response '%.*s', aborting\n", 21, p);
2335 p += 21; /* skip string */
2336 if ((*p++ != ' ') || (*p++ != '('))
2338 ERR("unknown response format, aborting\n");
2342 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
2345 ERR("unknown response address format '%s', aborting\n", p);
2348 for (i=0; i < 6; i++)
2351 dataSocketAddress = lpwfs->socketAddress;
2352 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
2353 pPort = (char *)&(dataSocketAddress.sin_port);
2361 nsocket = socket(AF_INET,SOCK_STREAM,0);
2365 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
2367 ERR("can't connect passive FTP data port.\n");
2370 lpwfs->pasvSocket = nsocket;
2374 FTP_SetResponseError(nResCode);
2382 static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
2384 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2386 if (!FTP_DoPassive(lpwfs))
2391 if (!FTP_SendPort(lpwfs))
2398 /***********************************************************************
2399 * FTP_GetDataSocket (internal)
2401 * Either accepts an incoming data socket connection from the server
2402 * or just returns the already opened socket after a PASV command
2403 * in case of passive FTP.
2411 static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
2413 struct sockaddr_in saddr;
2414 size_t addrlen = sizeof(struct sockaddr);
2417 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
2419 *nDataSocket = lpwfs->pasvSocket;
2423 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
2424 closesocket(lpwfs->lstnSocket);
2425 lpwfs->lstnSocket = -1;
2427 return *nDataSocket != -1;
2431 /***********************************************************************
2432 * FTP_SendData (internal)
2434 * Send data to the server
2441 static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
2443 BY_HANDLE_FILE_INFORMATION fi;
2444 DWORD nBytesRead = 0;
2445 DWORD nBytesSent = 0;
2446 DWORD nTotalSent = 0;
2447 DWORD nBytesToSend, nLen;
2449 time_t s_long_time, e_long_time;
2454 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2455 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
2457 /* Get the size of the file. */
2458 GetFileInformationByHandle(hFile, &fi);
2463 nBytesToSend = nBytesRead - nBytesSent;
2465 if (nBytesToSend <= 0)
2467 /* Read data from file. */
2469 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2470 ERR("Failed reading from file\n");
2473 nBytesToSend = nBytesRead;
2478 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2479 DATA_PACKET_SIZE : nBytesToSend;
2480 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2488 /* Do some computation to display the status. */
2490 nSeconds = e_long_time - s_long_time;
2491 if( nSeconds / 60 > 0 )
2493 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remaining time %ld sec\n",
2494 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2495 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2499 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remaining time %ld sec\n",
2500 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2501 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2503 } while (nRC != -1);
2505 TRACE("file transfer complete!\n");
2507 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2513 /***********************************************************************
2514 * FTP_SendRetrieve (internal)
2516 * Send request to retrieve a file
2519 * Number of bytes to be received on success
2523 static DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
2529 if (!FTP_InitListenSocket(lpwfs))
2532 if (!FTP_SendType(lpwfs, dwType))
2535 if (!FTP_SendPortOrPasv(lpwfs))
2538 if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult))
2541 TRACE("Waiting to receive %ld bytes\n", nResult);
2543 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2546 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2547 if ((nResCode != 125) && (nResCode != 150)) {
2548 /* That means that we got an error getting the file. */
2553 if (0 == nResult && lpwfs->lstnSocket != -1)
2555 closesocket(lpwfs->lstnSocket);
2556 lpwfs->lstnSocket = -1;
2563 /***********************************************************************
2564 * FTP_RetrieveData (internal)
2566 * Retrieve data from server
2573 static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2575 DWORD nBytesWritten;
2576 DWORD nBytesReceived = 0;
2582 if (INVALID_HANDLE_VALUE == hFile)
2585 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2586 if (NULL == lpszBuffer)
2588 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2592 while (nBytesReceived < nBytes && nRC != -1)
2594 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2597 /* other side closed socket. */
2600 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2601 nBytesReceived += nRC;
2604 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2605 nBytesReceived * 100 / nBytes);
2608 TRACE("Data transfer complete\n");
2609 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2616 /***********************************************************************
2617 * FTP_CloseSessionHandle (internal)
2619 * Deallocate session handle
2626 static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr)
2628 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
2632 if (lpwfs->download_in_progress != NULL)
2633 lpwfs->download_in_progress->session_deleted = TRUE;
2635 if (lpwfs->sndSocket != -1)
2636 closesocket(lpwfs->sndSocket);
2638 if (lpwfs->lstnSocket != -1)
2639 closesocket(lpwfs->lstnSocket);
2641 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2642 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2643 HeapFree(GetProcessHeap(), 0, lpwfs);
2647 /***********************************************************************
2648 * FTP_CloseFindNextHandle (internal)
2650 * Deallocate session handle
2657 static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr)
2659 LPWININETFINDNEXTW lpwfn = (LPWININETFINDNEXTW) hdr;
2664 for (i = 0; i < lpwfn->size; i++)
2666 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2669 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2670 HeapFree(GetProcessHeap(), 0, lpwfn);
2673 /***********************************************************************
2674 * FTP_CloseFileTransferHandle (internal)
2676 * Closes the file transfer handle. This also 'cleans' the data queue of
2677 * the 'transfer conplete' message (this is a bit of a hack though :-/ )
2680 static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr)
2682 LPWININETFILE lpwh = (LPWININETFILE) hdr;
2683 LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) lpwh->hdr.lpwhparent;
2688 if (!lpwh->session_deleted)
2689 lpwfs->download_in_progress = NULL;
2691 /* This just serves to flush the control socket of any spurrious lines written
2692 to it (like '226 Transfer complete.').
2694 Wonder what to do if the server sends us an error code though...
2696 nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
2698 if (lpwh->nDataSocket != -1)
2699 closesocket(lpwh->nDataSocket);
2701 HeapFree(GetProcessHeap(), 0, lpwh);
2704 /***********************************************************************
2705 * FTP_ReceiveFileList (internal)
2707 * Read file list from server
2710 * Handle to file list on success
2714 static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2715 LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext)
2718 LPFILEPROPERTIESW lpafp = NULL;
2719 LPWININETFINDNEXTW lpwfn = NULL;
2720 HINTERNET handle = 0;
2722 TRACE("(%p,%d,%s,%p,%ld)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext);
2724 if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize))
2727 FTP_ConvertFileProp(lpafp, lpFindFileData);
2729 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTW));
2732 lpwfn->hdr.htype = WH_HFINDNEXT;
2733 lpwfn->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr );
2734 lpwfn->hdr.dwContext = dwContext;
2735 lpwfn->hdr.dwRefCount = 1;
2736 lpwfn->hdr.destroy = FTP_CloseFindNextHandle;
2737 lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
2738 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2739 lpwfn->size = dwSize;
2740 lpwfn->lpafp = lpafp;
2742 handle = WININET_AllocHandle( &lpwfn->hdr );
2747 WININET_Release( &lpwfn->hdr );
2749 TRACE("Matched %ld files\n", dwSize);
2754 /***********************************************************************
2755 * FTP_ConvertFileProp (internal)
2757 * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA
2764 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData)
2766 BOOL bSuccess = FALSE;
2768 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW));
2772 /* Convert 'Unix' time to Windows time */
2773 RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
2774 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
2775 lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
2776 lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
2778 /* Not all fields are filled in */
2779 lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */
2780 lpFindFileData->nFileSizeLow = lpafp->nSize;
2782 if (lpafp->bIsDirectory)
2783 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2785 if (lpafp->lpszName)
2786 lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2794 /***********************************************************************
2795 * FTP_ParseNextFile (internal)
2797 * Parse the next line in file listing
2803 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp)
2805 static const char szSpace[] = " \t";
2813 lpfp->lpszName = NULL;
2815 if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen)))
2818 pszToken = strtok(pszLine, szSpace);
2820 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2823 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2825 if(!isdigit(pszToken[0]) && 10 == strlen(pszToken)) {
2826 if(!FTP_ParsePermission(pszToken, lpfp))
2827 lpfp->bIsDirectory = FALSE;
2828 for(i=0; i<=3; i++) {
2829 if(!(pszToken = strtok(NULL, szSpace)))
2832 if(!pszToken) continue;
2833 if(lpfp->bIsDirectory) {
2834 TRACE("Is directory\n");
2838 TRACE("Size: %s\n", pszToken);
2839 lpfp->nSize = atol(pszToken);
2842 lpfp->tmLastModified.tm_sec = 0;
2843 lpfp->tmLastModified.tm_min = 0;
2844 lpfp->tmLastModified.tm_hour = 0;
2845 lpfp->tmLastModified.tm_mday = 0;
2846 lpfp->tmLastModified.tm_mon = 0;
2847 lpfp->tmLastModified.tm_year = 0;
2849 /* Determine month */
2850 pszToken = strtok(NULL, szSpace);
2851 if(!pszToken) continue;
2852 if(strlen(pszToken) >= 3) {
2854 if((pszTmp = StrStrIA(szMonths, pszToken)))
2855 lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
2858 pszToken = strtok(NULL, szSpace);
2859 if(!pszToken) continue;
2860 lpfp->tmLastModified.tm_mday = atoi(pszToken);
2861 /* Determine time or year */
2862 pszToken = strtok(NULL, szSpace);
2863 if(!pszToken) continue;
2864 if((pszTmp = strchr(pszToken, ':'))) {
2869 lpfp->tmLastModified.tm_min = atoi(pszTmp);
2870 lpfp->tmLastModified.tm_hour = atoi(pszToken);
2872 apTM = localtime(&aTime);
2873 lpfp->tmLastModified.tm_year = apTM->tm_year;
2876 lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
2877 lpfp->tmLastModified.tm_hour = 12;
2879 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2880 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2881 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2882 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2884 pszToken = strtok(NULL, szSpace);
2885 if(!pszToken) continue;
2886 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2887 TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
2889 /* NT way of parsing ... :
2891 07-13-03 08:55PM <DIR> sakpatch
2892 05-09-03 06:02PM 12656686 2003-04-21bgm_cmd_e.rgz
2894 else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
2895 lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
2897 sscanf(pszToken, "%d-%d-%d",
2898 &lpfp->tmLastModified.tm_mon,
2899 &lpfp->tmLastModified.tm_mday,
2900 &lpfp->tmLastModified.tm_year);
2902 /* Hacky and bad Y2K protection :-) */
2903 if (lpfp->tmLastModified.tm_year < 70)
2904 lpfp->tmLastModified.tm_year += 100;
2906 pszToken = strtok(NULL, szSpace);
2907 if(!pszToken) continue;
2908 sscanf(pszToken, "%d:%d",
2909 &lpfp->tmLastModified.tm_hour,
2910 &lpfp->tmLastModified.tm_min);
2911 if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
2912 lpfp->tmLastModified.tm_hour += 12;
2914 lpfp->tmLastModified.tm_sec = 0;
2916 TRACE("Mod time: %02d:%02d:%02d %02d/%02d/%02d\n",
2917 lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
2918 (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
2919 lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
2921 pszToken = strtok(NULL, szSpace);
2922 if(!pszToken) continue;
2923 if(!strcasecmp(pszToken, "<DIR>")) {
2924 lpfp->bIsDirectory = TRUE;
2926 TRACE("Is directory\n");
2929 lpfp->bIsDirectory = FALSE;
2930 lpfp->nSize = atol(pszToken);
2931 TRACE("Size: %ld\n", lpfp->nSize);
2934 pszToken = strtok(NULL, szSpace);
2935 if(!pszToken) continue;
2936 lpfp->lpszName = WININET_strdup_AtoW(pszToken);
2937 TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
2939 /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
2940 else if(pszToken[0] == '+') {
2941 FIXME("EPLF Format not implemented\n");
2944 if(lpfp->lpszName) {
2945 if((lpszSearchFile == NULL) ||
2946 (PathMatchSpecW(lpfp->lpszName, lpszSearchFile))) {
2948 TRACE("Matched: %s\n", debugstr_w(lpfp->lpszName));
2951 HeapFree(GetProcessHeap(), 0, lpfp->lpszName);
2952 lpfp->lpszName = NULL;
2959 /***********************************************************************
2960 * FTP_ParseDirectory (internal)
2962 * Parse string of directory information
2968 static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
2969 LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
2971 BOOL bSuccess = TRUE;
2972 INT sizeFilePropArray = 500;/*20; */
2973 INT indexFilePropArray = -1;
2977 /* Allocate intial file properties array */
2978 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESW)*(sizeFilePropArray));
2983 if (indexFilePropArray+1 >= sizeFilePropArray)
2985 LPFILEPROPERTIESW tmpafp;
2987 sizeFilePropArray *= 2;
2988 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
2989 sizeof(FILEPROPERTIESW)*sizeFilePropArray);
2998 indexFilePropArray++;
2999 } while (FTP_ParseNextFile(nSocket, lpszSearchFile, &(*lpafp)[indexFilePropArray]));
3001 if (bSuccess && indexFilePropArray)
3003 if (indexFilePropArray < sizeFilePropArray - 1)
3005 LPFILEPROPERTIESW tmpafp;
3007 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
3008 sizeof(FILEPROPERTIESW)*indexFilePropArray);
3012 *dwfp = indexFilePropArray;
3016 HeapFree(GetProcessHeap(), 0, *lpafp);
3017 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
3025 /***********************************************************************
3026 * FTP_ParsePermission (internal)
3028 * Parse permission string of directory information
3035 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp)
3037 BOOL bSuccess = TRUE;
3038 unsigned short nPermission = 0;
3043 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
3049 lpfp->bIsDirectory = (*lpszPermission == 'd');
3055 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
3058 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
3061 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
3064 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
3067 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
3070 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
3073 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
3076 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
3079 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
3083 }while (nPos <= nLast);
3085 lpfp->permissions = nPermission;
3090 /***********************************************************************
3091 * FTP_SetResponseError (internal)
3093 * Set the appropriate error code for a given response from the server
3098 static DWORD FTP_SetResponseError(DWORD dwResponse)
3104 case 421: /* Service not available - Server may be shutting down. */
3105 dwCode = ERROR_INTERNET_TIMEOUT;
3108 case 425: /* Cannot open data connection. */
3109 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
3112 case 426: /* Connection closed, transer aborted. */
3113 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
3116 case 500: /* Syntax error. Command unrecognized. */
3117 case 501: /* Syntax error. Error in parameters or arguments. */
3118 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
3121 case 530: /* Not logged in. Login incorrect. */
3122 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
3125 case 550: /* File action not taken. File not found or no access. */
3126 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
3129 case 450: /* File action not taken. File may be busy. */
3130 case 451: /* Action aborted. Server error. */
3131 case 452: /* Action not taken. Insufficient storage space on server. */
3132 case 502: /* Command not implemented. */
3133 case 503: /* Bad sequence of command. */
3134 case 504: /* Command not implemented for that parameter. */
3135 case 532: /* Need account for storing files */
3136 case 551: /* Requested action aborted. Page type unknown */
3137 case 552: /* Action aborted. Exceeded storage allocation */
3138 case 553: /* Action not taken. File name not allowed. */
3141 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
3145 INTERNET_SetLastError(dwCode);