Added LGPL standard comment, and copyright notices where necessary.
[wine/multimedia.git] / dlls / wininet / ftp.c
blob099f99a9352dd950a605848de3be1b35260608cd
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
6 * Ulrich Czekalla
7 * Noureddine Jemmali
9 * Copyright 2000 Andreas Mohr
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include "config.h"
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/types.h>
33 #ifdef HAVE_SYS_SOCKET_H
34 # include <sys/socket.h>
35 #endif
36 #include <unistd.h>
37 #include <time.h>
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "winuser.h"
42 #include "wininet.h"
43 #include "winerror.h"
45 #include "wine/debug.h"
46 #include "internet.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
50 #define NOACCOUNT "noaccount"
51 #define DATA_PACKET_SIZE 0x2000
52 #define szCRLF "\r\n"
53 #define MAX_BACKLOG 5
55 typedef enum {
56 /* FTP commands with arguments. */
57 FTP_CMD_ACCT,
58 FTP_CMD_CWD,
59 FTP_CMD_DELE,
60 FTP_CMD_MKD,
61 FTP_CMD_PASS,
62 FTP_CMD_PORT,
63 FTP_CMD_RETR,
64 FTP_CMD_RMD,
65 FTP_CMD_RNFR,
66 FTP_CMD_RNTO,
67 FTP_CMD_STOR,
68 FTP_CMD_TYPE,
69 FTP_CMD_USER,
71 /* FTP commands without arguments. */
72 FTP_CMD_ABOR,
73 FTP_CMD_LIST,
74 FTP_CMD_NLST,
75 FTP_CMD_PASV,
76 FTP_CMD_PWD,
77 FTP_CMD_QUIT,
78 } FTP_COMMAND;
80 static const CHAR *szFtpCommands[] = {
81 "ACCT",
82 "CWD",
83 "DELE",
84 "MKD",
85 "PASS",
86 "PORT",
87 "RETR",
88 "RMD",
89 "RNFR",
90 "RNTO",
91 "STOR",
92 "TYPE",
93 "USER",
94 "ABOR",
95 "LIST",
96 "NLST",
97 "PASV",
98 "PWD",
99 "QUIT",
102 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
104 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
105 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext);
106 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType);
107 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket);
108 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile);
109 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
110 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext);
111 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType);
112 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile);
113 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs);
114 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs);
115 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs);
116 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs);
117 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType);
118 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs);
119 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs);
120 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs);
121 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp);
122 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp);
123 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket,
124 LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext);
125 DWORD FTP_SetResponseError(DWORD dwResponse);
127 inline static LPSTR FTP_strdup( LPCSTR str )
129 LPSTR ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 );
130 if (ret) strcpy( ret, str );
131 return ret;
134 /***********************************************************************
135 * FtpPutFileA (WININET.@)
137 * Uploads a file to the FTP server
139 * RETURNS
140 * TRUE on success
141 * FALSE on failure
144 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
145 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
147 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
148 LPWININETAPPINFOA hIC = NULL;
150 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
152 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
153 return FALSE;
156 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
157 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
159 WORKREQUEST workRequest;
161 workRequest.asyncall = FTPPUTFILEA;
162 workRequest.HFTPSESSION = (DWORD)hConnect;
163 workRequest.LPSZLOCALFILE = (DWORD)FTP_strdup(lpszLocalFile);
164 workRequest.LPSZNEWREMOTEFILE = (DWORD)FTP_strdup(lpszNewRemoteFile);
165 workRequest.DWFLAGS = dwFlags;
166 workRequest.DWCONTEXT = dwContext;
168 return INTERNET_AsyncCall(&workRequest);
170 else
172 return FTP_FtpPutFileA(hConnect, lpszLocalFile,
173 lpszNewRemoteFile, dwFlags, dwContext);
177 /***********************************************************************
178 * FTP_FtpPutFileA (Internal)
180 * Uploads a file to the FTP server
182 * RETURNS
183 * TRUE on success
184 * FALSE on failure
187 BOOL WINAPI FTP_FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
188 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
190 HANDLE hFile = (HANDLE)NULL;
191 BOOL bSuccess = FALSE;
192 LPWININETAPPINFOA hIC = NULL;
193 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
194 INT nResCode;
196 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", lpszLocalFile, lpszNewRemoteFile);
197 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
199 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
200 return FALSE;
203 /* Clear any error information */
204 INTERNET_SetLastError(0);
206 /* Open file to be uploaded */
207 if (INVALID_HANDLE_VALUE ==
208 (hFile = CreateFileA(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
210 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
211 goto lend;
214 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
215 if (hIC->lpfnStatusCB)
216 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
218 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
220 INT nDataSocket;
222 /* Get data socket to server */
223 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
225 FTP_SendData(lpwfs, nDataSocket, hFile);
226 close(nDataSocket);
227 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
228 MAX_REPLY_LEN, 0, 0, 0);
229 if (nResCode)
231 if (nResCode == 226)
232 bSuccess = TRUE;
233 else
234 FTP_SetResponseError(nResCode);
239 lend:
240 if (lpwfs->lstnSocket != -1)
241 close(lpwfs->lstnSocket);
243 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
245 INTERNET_ASYNC_RESULT iar;
247 iar.dwResult = (DWORD)bSuccess;
248 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
249 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
250 &iar, sizeof(INTERNET_ASYNC_RESULT));
253 if (hFile)
254 CloseHandle(hFile);
256 return bSuccess;
260 /***********************************************************************
261 * FtpSetCurrentDirectoryA (WININET.@)
263 * Change the working directory on the FTP server
265 * RETURNS
266 * TRUE on success
267 * FALSE on failure
270 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
272 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
273 LPWININETAPPINFOA hIC = NULL;
275 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
277 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
278 return FALSE;
281 TRACE("lpszDirectory(%s)\n", lpszDirectory);
283 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
284 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
286 WORKREQUEST workRequest;
288 workRequest.asyncall = FTPSETCURRENTDIRECTORYA;
289 workRequest.HFTPSESSION = (DWORD)hConnect;
290 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
292 return INTERNET_AsyncCall(&workRequest);
294 else
296 return FTP_FtpSetCurrentDirectoryA(hConnect, lpszDirectory);
301 /***********************************************************************
302 * FTP_FtpSetCurrentDirectoryA (Internal)
304 * Change the working directory on the FTP server
306 * RETURNS
307 * TRUE on success
308 * FALSE on failure
311 BOOL WINAPI FTP_FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
313 INT nResCode;
314 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
315 LPWININETAPPINFOA hIC = NULL;
316 DWORD bSuccess = FALSE;
318 TRACE("lpszDirectory(%s)\n", lpszDirectory);
320 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
322 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
323 return FALSE;
326 /* Clear any error information */
327 INTERNET_SetLastError(0);
329 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
330 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
331 hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext))
332 goto lend;
334 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
335 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
337 if (nResCode)
339 if (nResCode == 250)
340 bSuccess = TRUE;
341 else
342 FTP_SetResponseError(nResCode);
345 lend:
346 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
348 INTERNET_ASYNC_RESULT iar;
350 iar.dwResult = (DWORD)bSuccess;
351 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
352 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
353 &iar, sizeof(INTERNET_ASYNC_RESULT));
355 return bSuccess;
359 /***********************************************************************
360 * FtpCreateDirectoryA (WININET.@)
362 * Create new directory on the FTP server
364 * RETURNS
365 * TRUE on success
366 * FALSE on failure
369 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
371 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
372 LPWININETAPPINFOA hIC = NULL;
374 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
376 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
377 return FALSE;
380 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
381 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
383 WORKREQUEST workRequest;
385 workRequest.asyncall = FTPCREATEDIRECTORYA;
386 workRequest.HFTPSESSION = (DWORD)hConnect;
387 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
389 return INTERNET_AsyncCall(&workRequest);
391 else
393 return FTP_FtpCreateDirectoryA(hConnect, lpszDirectory);
398 /***********************************************************************
399 * FTP_FtpCreateDirectoryA (Internal)
401 * Create new directory on the FTP server
403 * RETURNS
404 * TRUE on success
405 * FALSE on failure
408 BOOL WINAPI FTP_FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
410 INT nResCode;
411 BOOL bSuccess = FALSE;
412 LPWININETAPPINFOA hIC = NULL;
413 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
415 TRACE("\n");
416 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
418 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
419 return FALSE;
422 /* Clear any error information */
423 INTERNET_SetLastError(0);
425 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
426 goto lend;
428 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
429 MAX_REPLY_LEN, 0, 0, 0);
430 if (nResCode)
432 if (nResCode == 257)
433 bSuccess = TRUE;
434 else
435 FTP_SetResponseError(nResCode);
438 lend:
439 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
440 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
442 INTERNET_ASYNC_RESULT iar;
444 iar.dwResult = (DWORD)bSuccess;
445 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
446 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
447 &iar, sizeof(INTERNET_ASYNC_RESULT));
450 return bSuccess;
454 /***********************************************************************
455 * FtpFindFirstFileA (WININET.@)
457 * Search the specified directory
459 * RETURNS
460 * HINTERNET on success
461 * NULL on failure
464 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
465 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
467 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
468 LPWININETAPPINFOA hIC = NULL;
470 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
472 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
473 return FALSE;
476 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
477 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
479 WORKREQUEST workRequest;
481 workRequest.asyncall = FTPFINDFIRSTFILEA;
482 workRequest.HFTPSESSION = (DWORD)hConnect;
483 workRequest.LPSZSEARCHFILE = (DWORD)FTP_strdup(lpszSearchFile);
484 workRequest.LPFINDFILEDATA = (DWORD)lpFindFileData;
485 workRequest.DWFLAGS = dwFlags;
486 workRequest.DWCONTEXT= dwContext;
488 INTERNET_AsyncCall(&workRequest);
489 return NULL;
491 else
493 return FTP_FtpFindFirstFileA(hConnect, lpszSearchFile, lpFindFileData,
494 dwFlags, dwContext);
499 /***********************************************************************
500 * FTP_FtpFindFirstFileA (Internal)
502 * Search the specified directory
504 * RETURNS
505 * HINTERNET on success
506 * NULL on failure
509 HINTERNET WINAPI FTP_FtpFindFirstFileA(HINTERNET hConnect,
510 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
512 INT nResCode;
513 LPWININETAPPINFOA hIC = NULL;
514 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
515 LPWININETFINDNEXTA hFindNext = NULL;
517 TRACE("\n");
519 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
521 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
522 return FALSE;
525 /* Clear any error information */
526 INTERNET_SetLastError(0);
528 if (!FTP_InitListenSocket(lpwfs))
529 goto lend;
531 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
532 goto lend;
534 if (!FTP_SendPortOrPasv(lpwfs))
535 goto lend;
537 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
538 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchFile,
539 hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext))
540 goto lend;
542 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
543 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
544 if (nResCode)
546 if (nResCode == 125 || nResCode == 150)
548 INT nDataSocket;
550 /* Get data socket to server */
551 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
553 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpFindFileData, dwContext);
555 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
556 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
557 if (nResCode != 226 && nResCode != 250)
558 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
560 close(nDataSocket);
563 else
564 FTP_SetResponseError(nResCode);
567 lend:
568 if (lpwfs->lstnSocket != -1)
569 close(lpwfs->lstnSocket);
571 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
573 INTERNET_ASYNC_RESULT iar;
575 if (hFindNext)
577 iar.dwResult = (DWORD)hFindNext;
578 iar.dwError = ERROR_SUCCESS;
579 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
580 &iar, sizeof(INTERNET_ASYNC_RESULT));
583 iar.dwResult = (DWORD)hFindNext;
584 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
585 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
586 &iar, sizeof(INTERNET_ASYNC_RESULT));
589 return (HINTERNET)hFindNext;
593 /***********************************************************************
594 * FtpGetCurrentDirectoryA (WININET.@)
596 * Retrieves the current directory
598 * RETURNS
599 * TRUE on success
600 * FALSE on failure
603 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
604 LPDWORD lpdwCurrentDirectory)
606 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
607 LPWININETAPPINFOA hIC = NULL;
609 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
611 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
613 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
614 return FALSE;
617 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
618 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
620 WORKREQUEST workRequest;
622 workRequest.asyncall = FTPGETCURRENTDIRECTORYA;
623 workRequest.HFTPSESSION = (DWORD)hFtpSession;
624 workRequest.LPSZDIRECTORY = (DWORD)lpszCurrentDirectory;
625 workRequest.LPDWDIRECTORY = (DWORD)lpdwCurrentDirectory;
627 return INTERNET_AsyncCall(&workRequest);
629 else
631 return FTP_FtpGetCurrentDirectoryA(hFtpSession, lpszCurrentDirectory,
632 lpdwCurrentDirectory);
637 /***********************************************************************
638 * FTP_FtpGetCurrentDirectoryA (Internal)
640 * Retrieves the current directory
642 * RETURNS
643 * TRUE on success
644 * FALSE on failure
647 BOOL WINAPI FTP_FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
648 LPDWORD lpdwCurrentDirectory)
650 INT nResCode;
651 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
652 LPWININETAPPINFOA hIC = NULL;
653 DWORD bSuccess = FALSE;
655 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
657 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
659 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
660 return FALSE;
663 /* Clear any error information */
664 INTERNET_SetLastError(0);
666 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
668 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
669 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
670 hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext))
671 goto lend;
673 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
674 MAX_REPLY_LEN, hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext);
675 if (nResCode)
677 if (nResCode == 257) /* Extract directory name */
679 INT firstpos, lastpos, len;
680 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
682 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
684 if ('"' == lpszResponseBuffer[lastpos])
686 if (!firstpos)
687 firstpos = lastpos;
688 else
689 break;
693 len = lastpos - firstpos - 1;
694 strncpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1],
695 len < *lpdwCurrentDirectory ? len : *lpdwCurrentDirectory);
696 *lpdwCurrentDirectory = len;
697 bSuccess = TRUE;
699 else
700 FTP_SetResponseError(nResCode);
703 lend:
704 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
706 INTERNET_ASYNC_RESULT iar;
708 iar.dwResult = (DWORD)bSuccess;
709 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
710 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
711 &iar, sizeof(INTERNET_ASYNC_RESULT));
714 return (DWORD) bSuccess;
717 /***********************************************************************
718 * FtpOpenFileA (WININET.@)
720 * Open a remote file for writing or reading
722 * RETURNS
723 * HINTERNET handle on success
724 * NULL on failure
727 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
728 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
729 DWORD dwContext)
731 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
732 LPWININETAPPINFOA hIC = NULL;
734 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
736 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
737 return FALSE;
740 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
741 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
743 WORKREQUEST workRequest;
745 workRequest.asyncall = FTPOPENFILEA;
746 workRequest.HFTPSESSION = (DWORD)hFtpSession;
747 workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName);
748 workRequest.FDWACCESS = fdwAccess;
749 workRequest.DWFLAGS = dwFlags;
750 workRequest.DWCONTEXT = dwContext;
752 INTERNET_AsyncCall(&workRequest);
753 return NULL;
755 else
757 return FTP_FtpOpenFileA(hFtpSession, lpszFileName, fdwAccess, dwFlags, dwContext);
762 /***********************************************************************
763 * FTP_FtpOpenFileA (Internal)
765 * Open a remote file for writing or reading
767 * RETURNS
768 * HINTERNET handle on success
769 * NULL on failure
772 HINTERNET FTP_FtpOpenFileA(HINTERNET hFtpSession,
773 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
774 DWORD dwContext)
776 INT nDataSocket;
777 BOOL bSuccess = FALSE;
778 LPWININETFILE hFile = NULL;
779 LPWININETAPPINFOA hIC = NULL;
780 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
782 TRACE("\n");
784 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
786 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
787 return FALSE;
790 /* Clear any error information */
791 INTERNET_SetLastError(0);
793 if (GENERIC_READ == fdwAccess)
795 /* Set up socket to retrieve data */
796 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
798 else if (GENERIC_WRITE == fdwAccess)
800 /* Set up socket to send data */
801 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
804 /* Get data socket to server */
805 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
807 hFile = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE));
808 hFile->hdr.htype = WH_HFILE;
809 hFile->hdr.dwFlags = dwFlags;
810 hFile->hdr.dwContext = dwContext;
811 hFile->hdr.lpwhparent = hFtpSession;
812 hFile->nDataSocket = nDataSocket;
815 if (lpwfs->lstnSocket != -1)
816 close(lpwfs->lstnSocket);
818 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
819 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
821 INTERNET_ASYNC_RESULT iar;
823 if (hFile)
825 iar.dwResult = (DWORD)hFile;
826 iar.dwError = ERROR_SUCCESS;
827 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
828 &iar, sizeof(INTERNET_ASYNC_RESULT));
831 iar.dwResult = (DWORD)bSuccess;
832 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
833 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
834 &iar, sizeof(INTERNET_ASYNC_RESULT));
837 return (HINTERNET)hFile;
841 /***********************************************************************
842 * FtpGetFileA (WININET.@)
844 * Retrieve file from the FTP server
846 * RETURNS
847 * TRUE on success
848 * FALSE on failure
851 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
852 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
853 DWORD dwContext)
855 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet;
856 LPWININETAPPINFOA hIC = NULL;
858 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
860 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
861 return FALSE;
864 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
865 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
867 WORKREQUEST workRequest;
869 workRequest.asyncall = FTPGETFILEA;
870 workRequest.HFTPSESSION = (DWORD)hInternet;
871 workRequest.LPSZREMOTEFILE = (DWORD)FTP_strdup(lpszRemoteFile);
872 workRequest.LPSZNEWFILE = (DWORD)FTP_strdup(lpszNewFile);
873 workRequest.DWLOCALFLAGSATTRIBUTE = dwLocalFlagsAttribute;
874 workRequest.FFAILIFEXISTS = (DWORD)fFailIfExists;
875 workRequest.DWFLAGS = dwInternetFlags;
876 workRequest.DWCONTEXT = dwContext;
878 return INTERNET_AsyncCall(&workRequest);
880 else
882 return FTP_FtpGetFileA(hInternet, lpszRemoteFile, lpszNewFile,
883 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
888 /***********************************************************************
889 * FTP_FtpGetFileA (Internal)
891 * Retrieve file from the FTP server
893 * RETURNS
894 * TRUE on success
895 * FALSE on failure
898 BOOL WINAPI FTP_FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
899 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
900 DWORD dwContext)
902 DWORD nBytes;
903 BOOL bSuccess = FALSE;
904 HANDLE hFile;
905 LPWININETAPPINFOA hIC = NULL;
906 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet;
908 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", lpszRemoteFile, lpszNewFile);
909 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
911 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
912 return FALSE;
915 /* Clear any error information */
916 INTERNET_SetLastError(0);
918 /* Ensure we can write to lpszNewfile by opening it */
919 hFile = CreateFileA(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
920 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
921 if (INVALID_HANDLE_VALUE == hFile)
922 goto lend;
924 /* Set up socket to retrieve data */
925 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
927 if (nBytes > 0)
929 INT nDataSocket;
931 /* Get data socket to server */
932 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
934 INT nResCode;
936 /* Receive data */
937 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
938 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
939 MAX_REPLY_LEN, 0, 0, 0);
940 if (nResCode)
942 if (nResCode == 226)
943 bSuccess = TRUE;
944 else
945 FTP_SetResponseError(nResCode);
947 close(nDataSocket);
951 lend:
952 if (lpwfs->lstnSocket != -1)
953 close(lpwfs->lstnSocket);
955 if (hFile)
956 CloseHandle(hFile);
958 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
959 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
961 INTERNET_ASYNC_RESULT iar;
963 iar.dwResult = (DWORD)bSuccess;
964 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
965 hIC->lpfnStatusCB(hInternet, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
966 &iar, sizeof(INTERNET_ASYNC_RESULT));
969 return bSuccess;
973 /***********************************************************************
974 * FtpDeleteFileA (WININET.@)
976 * Delete a file on the ftp server
978 * RETURNS
979 * TRUE on success
980 * FALSE on failure
983 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
985 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
986 LPWININETAPPINFOA hIC = NULL;
988 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
990 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
991 return FALSE;
994 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
995 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
997 WORKREQUEST workRequest;
999 workRequest.asyncall = FTPRENAMEFILEA;
1000 workRequest.HFTPSESSION = (DWORD)hFtpSession;
1001 workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName);
1003 return INTERNET_AsyncCall(&workRequest);
1005 else
1007 return FTP_FtpDeleteFileA(hFtpSession, lpszFileName);
1012 /***********************************************************************
1013 * FTP_FtpDeleteFileA (Internal)
1015 * Delete a file on the ftp server
1017 * RETURNS
1018 * TRUE on success
1019 * FALSE on failure
1022 BOOL FTP_FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1024 INT nResCode;
1025 BOOL bSuccess = FALSE;
1026 LPWININETAPPINFOA hIC = NULL;
1027 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1029 TRACE("0x%08lx\n", (ULONG) hFtpSession);
1030 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1032 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1033 return FALSE;
1036 /* Clear any error information */
1037 INTERNET_SetLastError(0);
1039 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1040 goto lend;
1042 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1043 MAX_REPLY_LEN, 0, 0, 0);
1044 if (nResCode)
1046 if (nResCode == 250)
1047 bSuccess = TRUE;
1048 else
1049 FTP_SetResponseError(nResCode);
1051 lend:
1052 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1053 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1055 INTERNET_ASYNC_RESULT iar;
1057 iar.dwResult = (DWORD)bSuccess;
1058 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1059 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1060 &iar, sizeof(INTERNET_ASYNC_RESULT));
1063 return bSuccess;
1067 /***********************************************************************
1068 * FtpRemoveDirectoryA (WININET.@)
1070 * Remove a directory on the ftp server
1072 * RETURNS
1073 * TRUE on success
1074 * FALSE on failure
1077 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1079 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1080 LPWININETAPPINFOA hIC = NULL;
1082 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1084 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1085 return FALSE;
1088 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1089 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1091 WORKREQUEST workRequest;
1093 workRequest.asyncall = FTPREMOVEDIRECTORYA;
1094 workRequest.HFTPSESSION = (DWORD)hFtpSession;
1095 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
1097 return INTERNET_AsyncCall(&workRequest);
1099 else
1101 return FTP_FtpRemoveDirectoryA(hFtpSession, lpszDirectory);
1106 /***********************************************************************
1107 * FTP_FtpRemoveDirectoryA (Internal)
1109 * Remove a directory on the ftp server
1111 * RETURNS
1112 * TRUE on success
1113 * FALSE on failure
1116 BOOL FTP_FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1118 INT nResCode;
1119 BOOL bSuccess = FALSE;
1120 LPWININETAPPINFOA hIC = NULL;
1121 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1123 TRACE("\n");
1124 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1126 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1127 return FALSE;
1130 /* Clear any error information */
1131 INTERNET_SetLastError(0);
1133 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1134 goto lend;
1136 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1137 MAX_REPLY_LEN, 0, 0, 0);
1138 if (nResCode)
1140 if (nResCode == 250)
1141 bSuccess = TRUE;
1142 else
1143 FTP_SetResponseError(nResCode);
1146 lend:
1147 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1148 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1150 INTERNET_ASYNC_RESULT iar;
1152 iar.dwResult = (DWORD)bSuccess;
1153 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1154 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1155 &iar, sizeof(INTERNET_ASYNC_RESULT));
1158 return bSuccess;
1162 /***********************************************************************
1163 * FtpRenameFileA (WININET.@)
1165 * Rename a file on the ftp server
1167 * RETURNS
1168 * TRUE on success
1169 * FALSE on failure
1172 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1174 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1175 LPWININETAPPINFOA hIC = NULL;
1177 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1179 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1180 return FALSE;
1183 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1184 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1186 WORKREQUEST workRequest;
1188 workRequest.asyncall = FTPRENAMEFILEA;
1189 workRequest.HFTPSESSION = (DWORD)hFtpSession;
1190 workRequest.LPSZSRCFILE = (DWORD)FTP_strdup(lpszSrc);
1191 workRequest.LPSZDESTFILE = (DWORD)FTP_strdup(lpszDest);
1193 return INTERNET_AsyncCall(&workRequest);
1195 else
1197 return FTP_FtpRenameFileA(hFtpSession, lpszSrc, lpszDest);
1201 /***********************************************************************
1202 * FTP_FtpRenameFileA (Internal)
1204 * Rename a file on the ftp server
1206 * RETURNS
1207 * TRUE on success
1208 * FALSE on failure
1211 BOOL FTP_FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1213 INT nResCode;
1214 BOOL bSuccess = FALSE;
1215 LPWININETAPPINFOA hIC = NULL;
1216 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1218 TRACE("\n");
1219 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1221 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1222 return FALSE;
1225 /* Clear any error information */
1226 INTERNET_SetLastError(0);
1228 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1229 goto lend;
1231 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket,
1232 INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1233 if (nResCode == 350)
1235 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1236 goto lend;
1238 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket,
1239 INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1242 if (nResCode == 250)
1243 bSuccess = TRUE;
1244 else
1245 FTP_SetResponseError(nResCode);
1247 lend:
1248 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1249 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1251 INTERNET_ASYNC_RESULT iar;
1253 iar.dwResult = (DWORD)bSuccess;
1254 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1255 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1256 &iar, sizeof(INTERNET_ASYNC_RESULT));
1259 return bSuccess;
1263 /***********************************************************************
1264 * FTP_Connect (internal)
1266 * Connect to a ftp server
1268 * RETURNS
1269 * HINTERNET a session handle on success
1270 * NULL on failure
1274 HINTERNET FTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName,
1275 INTERNET_PORT nServerPort, LPCSTR lpszUserName,
1276 LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext)
1278 struct sockaddr_in socketAddr;
1279 struct hostent *phe = NULL;
1280 INT nsocket = -1, sock_namelen;
1281 LPWININETAPPINFOA hIC = NULL;
1282 BOOL bSuccess = FALSE;
1283 LPWININETFTPSESSIONA lpwfs = NULL;
1285 TRACE("0x%08lx Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1286 (ULONG) hInternet, lpszServerName,
1287 nServerPort, lpszUserName, lpszPassword);
1289 if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT)
1290 goto lerror;
1292 hIC = (LPWININETAPPINFOA) hInternet;
1294 if (NULL == lpszUserName && NULL != lpszPassword)
1296 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_USER_NAME);
1297 goto lerror;
1300 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1301 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1303 if (hIC->lpfnStatusCB)
1304 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1305 (LPSTR) lpszServerName, strlen(lpszServerName));
1307 if (!GetAddress(lpszServerName, nServerPort, &phe, &socketAddr))
1309 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1310 goto lerror;
1313 if (hIC->lpfnStatusCB)
1314 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1315 (LPSTR) lpszServerName, strlen(lpszServerName));
1317 nsocket = socket(AF_INET,SOCK_STREAM,0);
1318 if (nsocket == -1)
1320 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1321 goto lerror;
1324 if (hIC->lpfnStatusCB)
1325 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1326 &socketAddr, sizeof(struct sockaddr_in));
1328 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1330 ERR("Unable to connect (%s)\n", strerror(errno));
1331 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1333 else
1335 TRACE("Connected to server\n");
1336 if (hIC->lpfnStatusCB)
1337 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1338 &socketAddr, sizeof(struct sockaddr_in));
1340 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA));
1341 if (NULL == lpwfs)
1343 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1344 goto lerror;
1347 lpwfs->hdr.htype = WH_HFTPSESSION;
1348 lpwfs->hdr.dwFlags = dwFlags;
1349 lpwfs->hdr.dwContext = dwContext;
1350 lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
1351 lpwfs->sndSocket = nsocket;
1352 sock_namelen = sizeof(lpwfs->socketAddress);
1353 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1354 lpwfs->phostent = phe;
1356 if (NULL == lpszUserName)
1358 lpwfs->lpszUserName = FTP_strdup("anonymous");
1359 lpwfs->lpszPassword = FTP_strdup("user@server");
1361 else
1363 lpwfs->lpszUserName = FTP_strdup(lpszUserName);
1364 lpwfs->lpszPassword = FTP_strdup(lpszPassword);
1367 if (FTP_ConnectToHost(lpwfs))
1369 if (hIC->lpfnStatusCB)
1371 INTERNET_ASYNC_RESULT iar;
1373 iar.dwResult = (DWORD)lpwfs;
1374 iar.dwError = ERROR_SUCCESS;
1376 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
1377 &iar, sizeof(INTERNET_ASYNC_RESULT));
1379 TRACE("Successfully logged into server\n");
1380 bSuccess = TRUE;
1384 lerror:
1385 if (!bSuccess && nsocket == -1)
1386 close(nsocket);
1388 if (!bSuccess && lpwfs)
1390 HeapFree(GetProcessHeap(), 0, lpwfs);
1391 lpwfs = NULL;
1394 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1396 INTERNET_ASYNC_RESULT iar;
1398 iar.dwResult = (DWORD)lpwfs;
1399 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1400 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1401 &iar, sizeof(INTERNET_ASYNC_RESULT));
1404 return (HINTERNET) lpwfs;
1408 /***********************************************************************
1409 * FTP_ConnectToHost (internal)
1411 * Connect to a ftp server
1413 * RETURNS
1414 * TRUE on success
1415 * NULL on failure
1418 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs)
1420 INT nResCode;
1421 BOOL bSuccess = FALSE;
1423 TRACE("\n");
1424 FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1426 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1427 goto lend;
1429 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1430 MAX_REPLY_LEN, 0, 0, 0);
1431 if (nResCode)
1433 /* Login successful... */
1434 if (nResCode == 230)
1435 bSuccess = TRUE;
1436 /* User name okay, need password... */
1437 else if (nResCode == 331)
1438 bSuccess = FTP_SendPassword(lpwfs);
1439 /* Need account for login... */
1440 else if (nResCode == 332)
1441 bSuccess = FTP_SendAccount(lpwfs);
1442 else
1443 FTP_SetResponseError(nResCode);
1446 TRACE("Returning %d\n", bSuccess);
1447 lend:
1448 return bSuccess;
1452 /***********************************************************************
1453 * FTP_SendCommand (internal)
1455 * Send command to server
1457 * RETURNS
1458 * TRUE on success
1459 * NULL on failure
1462 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1463 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1465 DWORD len;
1466 CHAR *buf;
1467 DWORD nBytesSent = 0;
1468 DWORD nRC = 0;
1469 BOOL bParamHasLen;
1471 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1473 if (lpfnStatusCB)
1474 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1476 bParamHasLen = lpszParam && strlen(lpszParam) > 0;
1477 len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) +
1478 strlen(szCRLF)+ 1;
1479 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1481 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1482 return FALSE;
1484 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "",
1485 bParamHasLen ? lpszParam : "", szCRLF);
1487 TRACE("Sending (%s) len(%ld)\n", buf, len);
1488 while((nBytesSent < len) && (nRC != -1))
1490 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1491 nBytesSent += nRC;
1494 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1496 if (lpfnStatusCB)
1497 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1498 &nBytesSent, sizeof(DWORD));
1500 TRACE("Sent %ld bytes\n", nBytesSent);
1501 return (nRC != -1);
1505 /***********************************************************************
1506 * FTP_ReceiveResponse (internal)
1508 * Receive response from server
1510 * RETURNS
1511 * Reply code on success
1512 * 0 on failure
1516 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
1517 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1519 DWORD nRecv;
1520 INT rc = 0;
1521 char firstprefix[5];
1522 BOOL multiline = FALSE;
1525 TRACE("socket(%d) \n", nSocket);
1527 if (lpfnStatusCB)
1528 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1530 while(1)
1532 nRecv = dwResponse;
1533 if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv))
1534 goto lerror;
1536 if (nRecv >= 3)
1538 if(!multiline)
1540 if(lpszResponse[3] != '-')
1541 break;
1542 else
1543 { /* Start of multiline repsonse. Loop until we get "nnn " */
1544 multiline = TRUE;
1545 memcpy(firstprefix, lpszResponse, 3);
1546 firstprefix[3] = ' ';
1547 firstprefix[4] = '\0';
1550 else
1552 if(!memcmp(firstprefix, lpszResponse, 4))
1553 break;
1558 if (nRecv >= 3)
1560 lpszResponse[nRecv] = '\0';
1561 rc = atoi(lpszResponse);
1563 if (lpfnStatusCB)
1564 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
1565 &nRecv, sizeof(DWORD));
1568 lerror:
1569 TRACE("return %d\n", rc);
1570 return rc;
1574 /***********************************************************************
1575 * FTP_SendPassword (internal)
1577 * Send password to ftp server
1579 * RETURNS
1580 * TRUE on success
1581 * NULL on failure
1584 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs)
1586 INT nResCode;
1587 BOOL bSuccess = FALSE;
1589 TRACE("\n");
1590 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
1591 goto lend;
1593 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1594 MAX_REPLY_LEN, 0, 0, 0);
1595 if (nResCode)
1597 TRACE("Received reply code %d\n", nResCode);
1598 /* Login successful... */
1599 if (nResCode == 230)
1600 bSuccess = TRUE;
1601 /* Command not implemented, superfluous at the server site... */
1602 /* Need account for login... */
1603 else if (nResCode == 332)
1604 bSuccess = FTP_SendAccount(lpwfs);
1605 else
1606 FTP_SetResponseError(nResCode);
1609 lend:
1610 TRACE("Returning %d\n", bSuccess);
1611 return bSuccess;
1615 /***********************************************************************
1616 * FTP_SendAccount (internal)
1620 * RETURNS
1621 * TRUE on success
1622 * FALSE on failure
1625 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs)
1627 INT nResCode;
1628 BOOL bSuccess = FALSE;
1630 TRACE("\n");
1631 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0))
1632 goto lend;
1634 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1635 MAX_REPLY_LEN, 0, 0, 0);
1636 if (nResCode)
1637 bSuccess = TRUE;
1638 else
1639 FTP_SetResponseError(nResCode);
1641 lend:
1642 return bSuccess;
1646 /***********************************************************************
1647 * FTP_SendStore (internal)
1649 * Send request to upload file to ftp server
1651 * RETURNS
1652 * TRUE on success
1653 * FALSE on failure
1656 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
1658 INT nResCode;
1659 BOOL bSuccess = FALSE;
1661 TRACE("\n");
1662 if (!FTP_InitListenSocket(lpwfs))
1663 goto lend;
1665 if (!FTP_SendType(lpwfs, dwType))
1666 goto lend;
1668 if (!FTP_SendPortOrPasv(lpwfs))
1669 goto lend;
1671 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
1672 goto lend;
1673 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1674 MAX_REPLY_LEN, 0, 0, 0);
1675 if (nResCode)
1677 if (nResCode == 150)
1678 bSuccess = TRUE;
1679 else
1680 FTP_SetResponseError(nResCode);
1683 lend:
1684 if (!bSuccess && lpwfs->lstnSocket != -1)
1686 close(lpwfs->lstnSocket);
1687 lpwfs->lstnSocket = -1;
1690 return bSuccess;
1694 /***********************************************************************
1695 * FTP_InitListenSocket (internal)
1697 * Create a socket to listen for server response
1699 * RETURNS
1700 * TRUE on success
1701 * FALSE on failure
1704 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs)
1706 BOOL bSuccess = FALSE;
1707 size_t namelen = sizeof(struct sockaddr_in);
1709 TRACE("\n");
1711 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
1712 if (lpwfs->lstnSocket == -1)
1714 TRACE("Unable to create listening socket\n");
1715 goto lend;
1718 /* We obtain our ip addr from the name of the command channel socket */
1719 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
1721 /* and get the system to assign us a port */
1722 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
1724 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
1726 TRACE("Unable to bind socket\n");
1727 goto lend;
1730 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
1732 TRACE("listen failed\n");
1733 goto lend;
1736 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
1737 bSuccess = TRUE;
1739 lend:
1740 if (!bSuccess && lpwfs->lstnSocket == -1)
1742 close(lpwfs->lstnSocket);
1743 lpwfs->lstnSocket = -1;
1746 return bSuccess;
1750 /***********************************************************************
1751 * FTP_SendType (internal)
1753 * Tell server type of data being transferred
1755 * RETURNS
1756 * TRUE on success
1757 * FALSE on failure
1759 * W98SE doesn't cache the type that's currently set
1760 * (i.e. it sends it always),
1761 * so we probably don't want to do that either.
1763 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType)
1765 INT nResCode;
1766 CHAR type[2] = { "I\0" };
1767 BOOL bSuccess = FALSE;
1769 TRACE("\n");
1770 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
1771 *type = 'A';
1773 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
1774 goto lend;
1776 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1777 MAX_REPLY_LEN, 0, 0, 0)/100;
1778 if (nResCode)
1780 if (nResCode == 2)
1781 bSuccess = TRUE;
1782 else
1783 FTP_SetResponseError(nResCode);
1786 lend:
1787 return bSuccess;
1791 /***********************************************************************
1792 * FTP_SendPort (internal)
1794 * Tell server which port to use
1796 * RETURNS
1797 * TRUE on success
1798 * FALSE on failure
1801 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs)
1803 INT nResCode;
1804 CHAR szIPAddress[64];
1805 BOOL bSuccess = FALSE;
1806 TRACE("\n");
1808 sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d",
1809 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
1810 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
1811 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
1812 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
1813 lpwfs->lstnSocketAddress.sin_port & 0xFF,
1814 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
1816 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
1817 goto lend;
1819 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1820 MAX_REPLY_LEN,0, 0, 0);
1821 if (nResCode)
1823 if (nResCode == 200)
1824 bSuccess = TRUE;
1825 else
1826 FTP_SetResponseError(nResCode);
1829 lend:
1830 return bSuccess;
1834 /***********************************************************************
1835 * FTP_DoPassive (internal)
1837 * Tell server that we want to do passive transfers
1838 * and connect data socket
1840 * RETURNS
1841 * TRUE on success
1842 * FALSE on failure
1845 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs)
1847 INT nResCode;
1848 BOOL bSuccess = FALSE;
1850 TRACE("\n");
1851 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
1852 goto lend;
1854 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1855 MAX_REPLY_LEN,0, 0, 0);
1856 if (nResCode)
1858 if (nResCode == 227)
1860 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
1861 LPSTR p;
1862 int f[6];
1863 int i;
1864 char *pAddr, *pPort;
1865 INT nsocket = -1;
1866 struct sockaddr_in dataSocketAddress;
1868 p = lpszResponseBuffer+4; /* skip status code */
1870 /* do a very strict check; we can improve that later. */
1872 if (strncmp(p, "Entering Passive Mode", 21))
1874 ERR("unknown response '%.*s', aborting\n", 21, p);
1875 goto lend;
1877 p += 21; /* skip string */
1878 if ((*p++ != ' ') || (*p++ != '('))
1880 ERR("unknown response format, aborting\n");
1881 goto lend;
1884 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
1885 &f[4], &f[5]) != 6)
1887 ERR("unknown response address format '%s', aborting\n", p);
1888 goto lend;
1890 for (i=0; i < 6; i++)
1891 f[i] = f[i] & 0xff;
1893 dataSocketAddress = lpwfs->socketAddress;
1894 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
1895 pPort = (char *)&(dataSocketAddress.sin_port);
1896 pAddr[0] = f[0];
1897 pAddr[1] = f[1];
1898 pAddr[2] = f[2];
1899 pAddr[3] = f[3];
1900 pPort[0] = f[4];
1901 pPort[1] = f[5];
1903 nsocket = socket(AF_INET,SOCK_STREAM,0);
1904 if (nsocket == -1)
1905 goto lend;
1907 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
1909 ERR("can't connect passive FTP data port.\n");
1910 goto lend;
1912 lpwfs->pasvSocket = nsocket;
1913 bSuccess = TRUE;
1915 else
1916 FTP_SetResponseError(nResCode);
1919 lend:
1920 return bSuccess;
1924 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs)
1926 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1928 if (!FTP_DoPassive(lpwfs))
1929 return FALSE;
1931 else
1933 if (!FTP_SendPort(lpwfs))
1934 return FALSE;
1936 return TRUE;
1940 /***********************************************************************
1941 * FTP_GetDataSocket (internal)
1943 * Either accepts an incoming data socket connection from the server
1944 * or just returns the already opened socket after a PASV command
1945 * in case of passive FTP.
1948 * RETURNS
1949 * TRUE on success
1950 * FALSE on failure
1953 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket)
1955 struct sockaddr_in saddr;
1956 size_t addrlen = sizeof(struct sockaddr);
1958 TRACE("\n");
1959 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1961 *nDataSocket = lpwfs->pasvSocket;
1963 else
1965 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
1966 close(lpwfs->lstnSocket);
1967 lpwfs->lstnSocket = -1;
1969 return *nDataSocket != -1;
1973 /***********************************************************************
1974 * FTP_SendData (internal)
1976 * Send data to the server
1978 * RETURNS
1979 * TRUE on success
1980 * FALSE on failure
1983 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile)
1985 BY_HANDLE_FILE_INFORMATION fi;
1986 DWORD nBytesRead = 0;
1987 DWORD nBytesSent = 0;
1988 DWORD nTotalSent = 0;
1989 DWORD nBytesToSend, nLen, nRC = 1;
1990 time_t s_long_time, e_long_time;
1991 LONG nSeconds;
1992 CHAR *lpszBuffer;
1994 TRACE("\n");
1995 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1996 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1998 /* Get the size of the file. */
1999 GetFileInformationByHandle(hFile, &fi);
2000 time(&s_long_time);
2004 nBytesToSend = nBytesRead - nBytesSent;
2006 if (nBytesToSend <= 0)
2008 /* Read data from file. */
2009 nBytesSent = 0;
2010 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2011 ERR("Failed reading from file\n");
2013 if (nBytesRead > 0)
2014 nBytesToSend = nBytesRead;
2015 else
2016 break;
2019 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2020 DATA_PACKET_SIZE : nBytesToSend;
2021 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2023 if (nRC != -1)
2025 nBytesSent += nRC;
2026 nTotalSent += nRC;
2029 /* Do some computation to display the status. */
2030 time(&e_long_time);
2031 nSeconds = e_long_time - s_long_time;
2032 if( nSeconds / 60 > 0 )
2034 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n",
2035 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2036 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2038 else
2040 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n",
2041 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2042 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2044 } while (nRC != -1);
2046 TRACE("file transfer complete!\n");
2048 if(lpszBuffer != NULL)
2049 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2051 return nTotalSent;
2055 /***********************************************************************
2056 * FTP_SendRetrieve (internal)
2058 * Send request to retrieve a file
2060 * RETURNS
2061 * Number of bytes to be received on success
2062 * 0 on failure
2065 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
2067 INT nResCode;
2068 DWORD nResult = 0;
2070 TRACE("\n");
2071 if (!FTP_InitListenSocket(lpwfs))
2072 goto lend;
2074 if (!FTP_SendType(lpwfs, dwType))
2075 goto lend;
2077 if (!FTP_SendPortOrPasv(lpwfs))
2078 goto lend;
2080 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2081 goto lend;
2083 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
2084 MAX_REPLY_LEN, 0, 0, 0);
2085 if (nResCode)
2087 if (nResCode == 125 || nResCode == 150)
2089 /* Parse size of data to be retrieved */
2090 INT i, sizepos = -1;
2091 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2092 for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--)
2094 if ('(' == lpszResponseBuffer[i])
2096 sizepos = i;
2097 break;
2101 if (sizepos >= 0)
2103 nResult = atol(&lpszResponseBuffer[sizepos+1]);
2104 TRACE("Waiting to receive %ld bytes\n", nResult);
2109 lend:
2110 if (0 == nResult && lpwfs->lstnSocket != -1)
2112 close(lpwfs->lstnSocket);
2113 lpwfs->lstnSocket = -1;
2116 return nResult;
2120 /***********************************************************************
2121 * FTP_RetrieveData (internal)
2123 * Retrieve data from server
2125 * RETURNS
2126 * TRUE on success
2127 * FALSE on failure
2130 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2132 DWORD nBytesWritten;
2133 DWORD nBytesReceived = 0;
2134 INT nRC = 0;
2135 CHAR *lpszBuffer;
2137 TRACE("\n");
2139 if (INVALID_HANDLE_VALUE == hFile)
2140 return FALSE;
2142 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2143 if (NULL == lpszBuffer)
2145 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2146 return FALSE;
2149 while (nBytesReceived < nBytes && nRC != -1)
2151 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2152 if (nRC != -1)
2154 /* other side closed socket. */
2155 if (nRC == 0)
2156 goto recv_end;
2157 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2158 nBytesReceived += nRC;
2161 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2162 nBytesReceived * 100 / nBytes);
2165 TRACE("Data transfer complete\n");
2166 if (NULL != lpszBuffer)
2167 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2169 recv_end:
2170 return (nRC != -1);
2174 /***********************************************************************
2175 * FTP_CloseSessionHandle (internal)
2177 * Deallocate session handle
2179 * RETURNS
2180 * TRUE on success
2181 * FALSE on failure
2184 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs)
2186 if (lpwfs->sndSocket != -1)
2187 close(lpwfs->sndSocket);
2189 if (lpwfs->lstnSocket != -1)
2190 close(lpwfs->lstnSocket);
2192 if (lpwfs->lpszPassword)
2193 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2195 if (lpwfs->lpszUserName)
2196 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2198 HeapFree(GetProcessHeap(), 0, lpwfs);
2200 return TRUE;
2204 /***********************************************************************
2205 * FTP_CloseSessionHandle (internal)
2207 * Deallocate session handle
2209 * RETURNS
2210 * TRUE on success
2211 * FALSE on failure
2214 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn)
2216 INT i;
2218 TRACE("\n");
2220 for (i = 0; i < lpwfn->size; i++)
2222 if (NULL != lpwfn->lpafp[i].lpszName)
2223 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2226 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2227 HeapFree(GetProcessHeap(), 0, lpwfn);
2229 return TRUE;
2233 /***********************************************************************
2234 * FTP_ReceiveFileList (internal)
2236 * Read file list from server
2238 * RETURNS
2239 * Handle to file list on success
2240 * NULL on failure
2243 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket,
2244 LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext)
2246 DWORD dwSize = 0;
2247 LPFILEPROPERTIESA lpafp = NULL;
2248 LPWININETFINDNEXTA lpwfn = NULL;
2250 TRACE("\n");
2252 if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize))
2254 FTP_ConvertFileProp(lpafp, lpFindFileData);
2256 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA));
2257 if (NULL != lpwfn)
2259 lpwfn->hdr.htype = WH_HFINDNEXT;
2260 lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs;
2261 lpwfn->hdr.dwContext = dwContext;
2262 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2263 lpwfn->size = dwSize;
2264 lpwfn->lpafp = lpafp;
2268 TRACE("Matched %ld files\n", dwSize);
2269 return (HINTERNET)lpwfn;
2273 /***********************************************************************
2274 * FTP_ConvertFileProp (internal)
2276 * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA
2278 * RETURNS
2279 * TRUE on success
2280 * FALSE on failure
2283 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData)
2285 BOOL bSuccess = FALSE;
2287 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2289 if (lpafp)
2291 DWORD access = mktime(&lpafp->tmLastModified);
2293 /* Not all fields are filled in */
2294 lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access);
2295 lpFindFileData->ftLastAccessTime.dwLowDateTime = LOWORD(access);
2296 lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize);
2297 lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize);
2299 if (lpafp->bIsDirectory)
2300 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2302 if (lpafp->lpszName)
2303 strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2305 bSuccess = TRUE;
2308 return bSuccess;
2312 /***********************************************************************
2313 * FTP_ParseDirectory (internal)
2315 * Parse string of directory information
2317 * RETURNS
2318 * TRUE on success
2319 * FALSE on failure
2321 * FIXME: - This function needs serious clea-up
2322 * - We should consider both UNIX and NT list formats
2324 #define MAX_MONTH_LEN 10
2325 #define MIN_LEN_DIR_ENTRY 15
2327 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp)
2330 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2332 * For instance:
2333 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2335 CHAR* pszMinutes;
2336 CHAR* pszHour;
2337 time_t aTime;
2338 struct tm* apTM;
2339 CHAR pszMonth[MAX_MONTH_LEN];
2340 CHAR* pszMatch;
2341 BOOL bSuccess = TRUE;
2342 DWORD nBufLen = MAX_REPLY_LEN;
2343 LPFILEPROPERTIESA curFileProp = NULL;
2344 CHAR* pszLine = NULL;
2345 CHAR* pszToken = NULL;
2346 INT nTokenToSkip = 3;
2347 INT nCount = 0;
2348 INT nSeconds = 0;
2349 INT nMinutes = 0;
2350 INT nHour = 0;
2351 INT nDay = 0;
2352 INT nMonth = 0;
2353 INT nYear = 0;
2354 INT sizeFilePropArray = 20;
2355 INT indexFilePropArray = 0;
2357 TRACE("\n");
2359 /* Allocate intial file properties array */
2360 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESA)*(sizeFilePropArray));
2361 if (NULL == lpafp)
2363 bSuccess = FALSE;
2364 goto lend;
2367 while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
2369 if (sizeFilePropArray <= indexFilePropArray)
2371 LPFILEPROPERTIESA tmpafp;
2373 sizeFilePropArray *= 2;
2374 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
2375 sizeof(FILEPROPERTIESA)*sizeFilePropArray);
2376 if (NULL == tmpafp)
2378 bSuccess = FALSE;
2379 goto lend;
2382 *lpafp = tmpafp;
2385 curFileProp = &((*lpafp)[indexFilePropArray]);
2387 /* First Parse the permissions. */
2388 pszToken = strtok(pszLine, " \t" );
2390 /* HACK! If this is not a file listing skip the line */
2391 if (!pszToken || 10 != strlen(pszToken) || nBufLen <= MIN_LEN_DIR_ENTRY)
2393 nBufLen = MAX_REPLY_LEN;
2394 continue;
2397 FTP_ParsePermission(pszToken, curFileProp);
2399 nTokenToSkip = 3;
2400 nCount = 0;
2403 pszToken = strtok( NULL, " \t" );
2404 nCount++;
2405 } while( nCount <= nTokenToSkip );
2407 /* Store the size of the file in the param list. */
2408 TRACE("nSize-> %s\n", pszToken);
2409 if (pszToken != NULL)
2410 curFileProp->nSize = atol(pszToken);
2412 /* Parse last modified time. */
2413 nSeconds = 0;
2414 nMinutes = 0;
2415 nHour = 0;
2416 nDay = 0;
2417 nMonth = 0;
2418 nYear = 0;
2420 pszToken = strtok( NULL, " \t" );
2421 strncpy(pszMonth, pszToken, MAX_MONTH_LEN);
2422 CharUpperA(pszMonth);
2423 pszMatch = strstr(szMonths, pszMonth);
2424 if( pszMatch != NULL )
2425 nMonth = (pszMatch - szMonths) / 3;
2427 pszToken = strtok(NULL, " \t");
2428 TRACE("nDay -> %s\n", pszToken);
2429 if (pszToken != NULL)
2430 nDay = atoi(pszToken);
2432 pszToken = strtok(NULL, " \t");
2433 pszMinutes = strchr(pszToken, ':');
2434 if( pszMinutes != NULL )
2436 pszMinutes++;
2437 nMinutes = atoi(pszMinutes);
2438 pszHour = pszMinutes - 3;
2439 if (pszHour != NULL)
2440 nHour = atoi(pszHour);
2441 time(&aTime);
2442 apTM = localtime( &aTime );
2443 nYear = apTM->tm_year;
2445 else
2447 nYear = atoi(pszToken);
2448 nYear -= 1900;
2449 nHour = 12;
2452 curFileProp->tmLastModified.tm_sec = nSeconds;
2453 curFileProp->tmLastModified.tm_min = nMinutes;
2454 curFileProp->tmLastModified.tm_hour = nHour;
2455 curFileProp->tmLastModified.tm_mday = nDay;
2456 curFileProp->tmLastModified.tm_mon = nMonth;
2457 curFileProp->tmLastModified.tm_year = nYear;
2459 pszToken = strtok(NULL, " \t");
2460 if(pszToken != NULL)
2462 curFileProp->lpszName = FTP_strdup(pszToken);
2463 TRACE(": %s\n", curFileProp->lpszName);
2466 nBufLen = MAX_REPLY_LEN;
2467 indexFilePropArray++;
2470 if (bSuccess && indexFilePropArray)
2472 if (indexFilePropArray < sizeFilePropArray - 1)
2474 LPFILEPROPERTIESA tmpafp;
2476 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
2477 sizeof(FILEPROPERTIESA)*indexFilePropArray);
2478 if (NULL == tmpafp)
2479 *lpafp = tmpafp;
2481 *dwfp = indexFilePropArray;
2483 else
2485 HeapFree(GetProcessHeap(), 0, *lpafp);
2486 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2487 bSuccess = FALSE;
2490 lend:
2491 return bSuccess;
2495 /***********************************************************************
2496 * FTP_ParsePermission (internal)
2498 * Parse permission string of directory information
2500 * RETURNS
2501 * TRUE on success
2502 * FALSE on failure
2505 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp)
2507 BOOL bSuccess = TRUE;
2508 unsigned short nPermission = 0;
2509 INT nPos = 1;
2510 INT nLast = 9;
2512 TRACE("\n");
2513 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
2515 bSuccess = FALSE;
2516 return bSuccess;
2519 lpfp->bIsDirectory = (*lpszPermission == 'd');
2522 switch (nPos)
2524 case 1:
2525 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
2526 break;
2527 case 2:
2528 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
2529 break;
2530 case 3:
2531 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
2532 break;
2533 case 4:
2534 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
2535 break;
2536 case 5:
2537 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
2538 break;
2539 case 6:
2540 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
2541 break;
2542 case 7:
2543 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
2544 break;
2545 case 8:
2546 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
2547 break;
2548 case 9:
2549 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
2550 break;
2552 nPos++;
2553 }while (nPos <= nLast);
2555 lpfp->permissions = nPermission;
2556 return bSuccess;
2560 /***********************************************************************
2561 * FTP_SetResponseError (internal)
2563 * Set the appropriate error code for a given response from the server
2565 * RETURNS
2568 DWORD FTP_SetResponseError(DWORD dwResponse)
2570 DWORD dwCode = 0;
2572 switch(dwResponse)
2574 case 421: /* Service not available - Server may be shutting down. */
2575 dwCode = ERROR_INTERNET_TIMEOUT;
2576 break;
2578 case 425: /* Cannot open data connection. */
2579 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
2580 break;
2582 case 426: /* Connection closed, transer aborted. */
2583 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
2584 break;
2586 case 500: /* Syntax error. Command unrecognized. */
2587 case 501: /* Syntax error. Error in parameters or arguments. */
2588 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
2589 break;
2591 case 530: /* Not logged in. Login incorrect. */
2592 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
2593 break;
2595 case 550: /* File action not taken. File not found or no access. */
2596 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
2597 break;
2599 case 450: /* File action not taken. File may be busy. */
2600 case 451: /* Action aborted. Server error. */
2601 case 452: /* Action not taken. Insufficient storage space on server. */
2602 case 502: /* Command not implemented. */
2603 case 503: /* Bad sequence of command. */
2604 case 504: /* Command not implemented for that parameter. */
2605 case 532: /* Need account for storing files */
2606 case 551: /* Requested action aborted. Page type unknown */
2607 case 552: /* Action aborted. Exceeded storage allocation */
2608 case 553: /* Action not taken. File name not allowed. */
2610 default:
2611 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
2612 break;
2615 INTERNET_SetLastError(dwCode);
2616 return dwCode;