- implemented passive FTP transfers (PASV, needed for firewalls)
[wine/multimedia.git] / dlls / wininet / ftp.c
blobfd602163401c7967e75097d8e6b828000f2f48d9
1 /*
2 * WININET - Ftp implementation
4 * Copyright 1999 Corel Corporation
6 * Ulrich Czekalla
7 * Noureddine Jemmali
9 * Copyright 2000 Andreas Mohr
12 #include "config.h"
14 #include <errno.h>
15 #ifdef HAVE_NETDB_H
16 # include <netdb.h>
17 #endif
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #ifdef HAVE_SYS_SOCKET_H
23 # include <sys/socket.h>
24 #endif
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #ifdef HAVE_NETINET_IN_SYSTM_H
28 # include <netinet/in_systm.h>
29 #endif
30 #ifdef HAVE_NETINET_IN_H
31 # include <netinet/in.h>
32 #endif
33 #ifdef HAVE_NETINET_IP_H
34 # include <netinet/ip.h>
35 #endif
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "wininet.h"
41 #include "winerror.h"
42 #include "winsock.h"
43 #include "heap.h"
45 #include "debugtools.h"
46 #include "internet.h"
48 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.43)
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 != INVALID_SOCKET)
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.49)
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.31)
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.35)
457 * Search the specified directory
459 * RETURNS
460 * HINTERNET on success
461 * NULL on failure
464 INTERNETAPI 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 INTERNETAPI 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 != INVALID_SOCKET)
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.37)
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.41)
720 * Open a remote file for writing or reading
722 * RETURNS
723 * HINTERNET handle on success
724 * NULL on failure
727 INTERNETAPI 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 != INVALID_SOCKET)
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.39)
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 != INVALID_SOCKET)
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.33)
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.45)
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.47)
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 = INVALID_SOCKET, 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 if (INVALID_SOCKET == (nsocket = socket(AF_INET,SOCK_STREAM,0)))
1319 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1320 goto lerror;
1323 if (hIC->lpfnStatusCB)
1324 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1325 &socketAddr, sizeof(struct sockaddr_in));
1327 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1329 ERR("Unable to connect (%s)\n", strerror(errno));
1330 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1332 else
1334 TRACE("Connected to server\n");
1335 if (hIC->lpfnStatusCB)
1336 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1337 &socketAddr, sizeof(struct sockaddr_in));
1339 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA));
1340 if (NULL == lpwfs)
1342 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1343 goto lerror;
1346 lpwfs->hdr.htype = WH_HFTPSESSION;
1347 lpwfs->hdr.dwFlags = dwFlags;
1348 lpwfs->hdr.dwContext = dwContext;
1349 lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
1350 lpwfs->sndSocket = nsocket;
1351 sock_namelen = sizeof(lpwfs->socketAddress);
1352 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1353 lpwfs->phostent = phe;
1355 if (NULL == lpszUserName)
1357 lpwfs->lpszUserName = FTP_strdup("anonymous");
1358 lpwfs->lpszPassword = FTP_strdup("user@server");
1360 else
1362 lpwfs->lpszUserName = FTP_strdup(lpszUserName);
1363 lpwfs->lpszPassword = FTP_strdup(lpszPassword);
1366 if (FTP_ConnectToHost(lpwfs))
1368 if (hIC->lpfnStatusCB)
1370 INTERNET_ASYNC_RESULT iar;
1372 iar.dwResult = (DWORD)lpwfs;
1373 iar.dwError = ERROR_SUCCESS;
1375 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
1376 &iar, sizeof(INTERNET_ASYNC_RESULT));
1378 TRACE("Successfully logged into server\n");
1379 bSuccess = TRUE;
1383 lerror:
1384 if (!bSuccess && INVALID_SOCKET != nsocket)
1385 close(nsocket);
1387 if (!bSuccess && lpwfs)
1389 HeapFree(GetProcessHeap(), 0, lpwfs);
1390 lpwfs = NULL;
1393 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1395 INTERNET_ASYNC_RESULT iar;
1397 iar.dwResult = (DWORD)lpwfs;
1398 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1399 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1400 &iar, sizeof(INTERNET_ASYNC_RESULT));
1403 return (HINTERNET) lpwfs;
1407 /***********************************************************************
1408 * FTP_ConnectToHost (internal)
1410 * Connect to a ftp server
1412 * RETURNS
1413 * TRUE on success
1414 * NULL on failure
1417 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs)
1419 INT nResCode;
1420 BOOL bSuccess = FALSE;
1422 TRACE("\n");
1423 FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1425 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1426 goto lend;
1428 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1429 MAX_REPLY_LEN, 0, 0, 0);
1430 if (nResCode)
1432 /* Login successful... */
1433 if (nResCode == 230)
1434 bSuccess = TRUE;
1435 /* User name okay, need password... */
1436 else if (nResCode == 331)
1437 bSuccess = FTP_SendPassword(lpwfs);
1438 /* Need account for login... */
1439 else if (nResCode == 332)
1440 bSuccess = FTP_SendAccount(lpwfs);
1441 else
1442 FTP_SetResponseError(nResCode);
1445 TRACE("Returning %d\n", bSuccess);
1446 lend:
1447 return bSuccess;
1451 /***********************************************************************
1452 * FTP_SendCommand (internal)
1454 * Send command to server
1456 * RETURNS
1457 * TRUE on success
1458 * NULL on failure
1461 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1462 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1464 DWORD len;
1465 CHAR *buf;
1466 DWORD nBytesSent = 0;
1467 DWORD nRC = 0;
1468 BOOL bParamHasLen;
1470 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1472 if (lpfnStatusCB)
1473 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1475 bParamHasLen = lpszParam && strlen(lpszParam) > 0;
1476 len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) +
1477 strlen(szCRLF)+ 1;
1478 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1480 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1481 return FALSE;
1483 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "",
1484 bParamHasLen ? lpszParam : "", szCRLF);
1486 TRACE("Sending (%s) len(%ld)\n", buf, len);
1487 while((nBytesSent < len) && (nRC != SOCKET_ERROR))
1489 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1490 nBytesSent += nRC;
1493 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1495 if (lpfnStatusCB)
1496 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1497 &nBytesSent, sizeof(DWORD));
1499 TRACE("Sent %ld bytes\n", nBytesSent);
1500 return (nRC != SOCKET_ERROR);
1504 /***********************************************************************
1505 * FTP_ReceiveResponse (internal)
1507 * Receive response from server
1509 * RETURNS
1510 * Reply code on success
1511 * 0 on failure
1515 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
1516 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1518 DWORD nRecv;
1519 INT rc = 0;
1520 char firstprefix[5];
1521 BOOL multiline = FALSE;
1524 TRACE("socket(%d) \n", nSocket);
1526 if (lpfnStatusCB)
1527 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1529 while(1)
1531 nRecv = dwResponse;
1532 if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv))
1533 goto lerror;
1535 if (nRecv >= 3)
1537 if(!multiline)
1539 if(lpszResponse[3] != '-')
1540 break;
1541 else
1542 { /* Start of multiline repsonse. Loop until we get "nnn " */
1543 multiline = TRUE;
1544 memcpy(firstprefix, lpszResponse, 3);
1545 firstprefix[3] = ' ';
1546 firstprefix[4] = '\0';
1549 else
1551 if(!memcmp(firstprefix, lpszResponse, 4))
1552 break;
1557 if (nRecv >= 3)
1559 lpszResponse[nRecv] = '\0';
1560 rc = atoi(lpszResponse);
1562 if (lpfnStatusCB)
1563 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
1564 &nRecv, sizeof(DWORD));
1567 lerror:
1568 TRACE("return %d\n", rc);
1569 return rc;
1573 /***********************************************************************
1574 * FTP_SendPassword (internal)
1576 * Send password to ftp server
1578 * RETURNS
1579 * TRUE on success
1580 * NULL on failure
1583 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs)
1585 INT nResCode;
1586 BOOL bSuccess = FALSE;
1588 TRACE("\n");
1589 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
1590 goto lend;
1592 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1593 MAX_REPLY_LEN, 0, 0, 0);
1594 if (nResCode)
1596 TRACE("Received reply code %d\n", nResCode);
1597 /* Login successful... */
1598 if (nResCode == 230)
1599 bSuccess = TRUE;
1600 /* Command not implemented, superfluous at the server site... */
1601 /* Need account for login... */
1602 else if (nResCode == 332)
1603 bSuccess = FTP_SendAccount(lpwfs);
1604 else
1605 FTP_SetResponseError(nResCode);
1608 lend:
1609 TRACE("Returning %d\n", bSuccess);
1610 return bSuccess;
1614 /***********************************************************************
1615 * FTP_SendAccount (internal)
1619 * RETURNS
1620 * TRUE on success
1621 * FALSE on failure
1624 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs)
1626 INT nResCode;
1627 BOOL bSuccess = FALSE;
1629 TRACE("\n");
1630 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0))
1631 goto lend;
1633 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1634 MAX_REPLY_LEN, 0, 0, 0);
1635 if (nResCode)
1636 bSuccess = TRUE;
1637 else
1638 FTP_SetResponseError(nResCode);
1640 lend:
1641 return bSuccess;
1645 /***********************************************************************
1646 * FTP_SendStore (internal)
1648 * Send request to upload file to ftp server
1650 * RETURNS
1651 * TRUE on success
1652 * FALSE on failure
1655 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
1657 INT nResCode;
1658 BOOL bSuccess = FALSE;
1660 TRACE("\n");
1661 if (!FTP_InitListenSocket(lpwfs))
1662 goto lend;
1664 if (!FTP_SendType(lpwfs, dwType))
1665 goto lend;
1667 if (!FTP_SendPortOrPasv(lpwfs))
1668 goto lend;
1670 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
1671 goto lend;
1672 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1673 MAX_REPLY_LEN, 0, 0, 0);
1674 if (nResCode)
1676 if (nResCode == 150)
1677 bSuccess = TRUE;
1678 else
1679 FTP_SetResponseError(nResCode);
1682 lend:
1683 if (!bSuccess && INVALID_SOCKET != lpwfs->lstnSocket)
1685 close(lpwfs->lstnSocket);
1686 lpwfs->lstnSocket = INVALID_SOCKET;
1689 return bSuccess;
1693 /***********************************************************************
1694 * FTP_InitListenSocket (internal)
1696 * Create a socket to listen for server response
1698 * RETURNS
1699 * TRUE on success
1700 * FALSE on failure
1703 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs)
1705 BOOL bSuccess = FALSE;
1706 size_t namelen = sizeof(struct sockaddr_in);
1708 TRACE("\n");
1710 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
1711 if (INVALID_SOCKET == lpwfs->lstnSocket)
1713 TRACE("Unable to create listening socket\n");
1714 goto lend;
1717 /* We obtain our ip addr from the name of the command channel socket */
1718 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
1720 /* and get the system to assign us a port */
1721 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
1723 if (SOCKET_ERROR == bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)))
1725 TRACE("Unable to bind socket\n");
1726 goto lend;
1729 if (SOCKET_ERROR == listen(lpwfs->lstnSocket, MAX_BACKLOG))
1731 TRACE("listen failed\n");
1732 goto lend;
1735 if (SOCKET_ERROR != getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen))
1736 bSuccess = TRUE;
1738 lend:
1739 if (!bSuccess && INVALID_SOCKET == lpwfs->lstnSocket)
1741 close(lpwfs->lstnSocket);
1742 lpwfs->lstnSocket = INVALID_SOCKET;
1745 return bSuccess;
1749 /***********************************************************************
1750 * FTP_SendType (internal)
1752 * Tell server type of data being transfered
1754 * RETURNS
1755 * TRUE on success
1756 * FALSE on failure
1758 * W98SE doesn't cache the type that's currently set
1759 * (i.e. it sends it always),
1760 * so we probably don't want to do that either.
1762 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType)
1764 INT nResCode;
1765 CHAR type[2] = { "I\0" };
1766 BOOL bSuccess = FALSE;
1768 TRACE("\n");
1769 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
1770 *type = 'A';
1772 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
1773 goto lend;
1775 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1776 MAX_REPLY_LEN, 0, 0, 0)/100;
1777 if (nResCode)
1779 if (nResCode == 2)
1780 bSuccess = TRUE;
1781 else
1782 FTP_SetResponseError(nResCode);
1785 lend:
1786 return bSuccess;
1790 /***********************************************************************
1791 * FTP_SendPort (internal)
1793 * Tell server which port to use
1795 * RETURNS
1796 * TRUE on success
1797 * FALSE on failure
1800 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs)
1802 INT nResCode;
1803 CHAR szIPAddress[64];
1804 BOOL bSuccess = FALSE;
1805 TRACE("\n");
1807 sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d",
1808 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
1809 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
1810 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
1811 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
1812 lpwfs->lstnSocketAddress.sin_port & 0xFF,
1813 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
1815 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
1816 goto lend;
1818 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1819 MAX_REPLY_LEN,0, 0, 0);
1820 if (nResCode)
1822 if (nResCode == 200)
1823 bSuccess = TRUE;
1824 else
1825 FTP_SetResponseError(nResCode);
1828 lend:
1829 return bSuccess;
1833 /***********************************************************************
1834 * FTP_DoPassive (internal)
1836 * Tell server that we want to do passive transfers
1837 * and connect data socket
1839 * RETURNS
1840 * TRUE on success
1841 * FALSE on failure
1844 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs)
1846 INT nResCode;
1847 BOOL bSuccess = FALSE;
1849 TRACE("\n");
1850 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
1851 goto lend;
1853 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1854 MAX_REPLY_LEN,0, 0, 0);
1855 if (nResCode)
1857 if (nResCode == 227)
1859 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
1860 LPSTR p;
1861 int f[6];
1862 int i;
1863 char *pAddr, *pPort;
1864 INT nsocket = INVALID_SOCKET;
1865 struct sockaddr_in dataSocketAddress;
1867 p = lpszResponseBuffer+4; /* skip status code */
1869 /* do a very strict check; we can improve that later. */
1871 if (strncmp(p, "Entering Passive Mode", 21))
1873 ERR("unknown response '%.*s', aborting\n", 21, p);
1874 goto lend;
1876 p += 21; /* skip string */
1877 if ((*p++ != ' ') || (*p++ != '('))
1879 ERR("unknown response format, aborting\n");
1880 goto lend;
1883 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
1884 &f[4], &f[5]) != 6)
1886 ERR("unknown response address format '%s', aborting\n", p);
1887 goto lend;
1889 for (i=0; i < 6; i++)
1890 f[i] = f[i] & 0xff;
1892 dataSocketAddress = lpwfs->socketAddress;
1893 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
1894 pPort = (char *)&(dataSocketAddress.sin_port);
1895 pAddr[0] = f[0];
1896 pAddr[1] = f[1];
1897 pAddr[2] = f[2];
1898 pAddr[3] = f[3];
1899 pPort[0] = f[4];
1900 pPort[1] = f[5];
1902 if (INVALID_SOCKET == (nsocket = socket(AF_INET,SOCK_STREAM,0)))
1903 goto lend;
1905 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
1907 ERR("can't connect passive FTP data port.\n");
1908 goto lend;
1910 lpwfs->pasvSocket = nsocket;
1911 bSuccess = TRUE;
1913 else
1914 FTP_SetResponseError(nResCode);
1917 lend:
1918 return bSuccess;
1922 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs)
1924 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1926 if (!FTP_DoPassive(lpwfs))
1927 return FALSE;
1929 else
1931 if (!FTP_SendPort(lpwfs))
1932 return FALSE;
1934 return TRUE;
1938 /***********************************************************************
1939 * FTP_GetDataSocket (internal)
1941 * Either accepts an incoming data socket connection from the server
1942 * or just returns the already opened socket after a PASV command
1943 * in case of passive FTP.
1946 * RETURNS
1947 * TRUE on success
1948 * FALSE on failure
1951 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket)
1953 struct sockaddr_in saddr;
1954 size_t addrlen = sizeof(struct sockaddr);
1956 TRACE("\n");
1957 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1959 *nDataSocket = lpwfs->pasvSocket;
1961 else
1963 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
1964 close(lpwfs->lstnSocket);
1965 lpwfs->lstnSocket = INVALID_SOCKET;
1967 return *nDataSocket != INVALID_SOCKET;
1971 /***********************************************************************
1972 * FTP_SendData (internal)
1974 * Send data to the server
1976 * RETURNS
1977 * TRUE on success
1978 * FALSE on failure
1981 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile)
1983 BY_HANDLE_FILE_INFORMATION fi;
1984 DWORD nBytesRead = 0;
1985 DWORD nBytesSent = 0;
1986 DWORD nTotalSent = 0;
1987 DWORD nBytesToSend, nLen, nRC = 1;
1988 time_t s_long_time, e_long_time;
1989 LONG nSeconds;
1990 CHAR *lpszBuffer;
1992 TRACE("\n");
1993 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1994 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1996 /* Get the size of the file. */
1997 GetFileInformationByHandle(hFile, &fi);
1998 time(&s_long_time);
2002 nBytesToSend = nBytesRead - nBytesSent;
2004 if (nBytesToSend <= 0)
2006 /* Read data from file. */
2007 nBytesSent = 0;
2008 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
2009 ERR("Failed reading from file\n");
2011 if (nBytesRead > 0)
2012 nBytesToSend = nBytesRead;
2013 else
2014 break;
2017 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2018 DATA_PACKET_SIZE : nBytesToSend;
2019 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2021 if (nRC != SOCKET_ERROR)
2023 nBytesSent += nRC;
2024 nTotalSent += nRC;
2027 /* Do some computation to display the status. */
2028 time(&e_long_time);
2029 nSeconds = e_long_time - s_long_time;
2030 if( nSeconds / 60 > 0 )
2032 TRACE( "%ld bytes of %d bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n",
2033 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2034 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2036 else
2038 TRACE( "%ld bytes of %d bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n",
2039 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2040 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2042 } while (nRC != SOCKET_ERROR);
2044 TRACE("file transfer complete!\n");
2046 if(lpszBuffer != NULL)
2047 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2049 return nTotalSent;
2053 /***********************************************************************
2054 * FTP_SendRetrieve (internal)
2056 * Send request to retrieve a file
2058 * RETURNS
2059 * Number of bytes to be received on success
2060 * 0 on failure
2063 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
2065 INT nResCode;
2066 DWORD nResult = 0;
2068 TRACE("\n");
2069 if (!FTP_InitListenSocket(lpwfs))
2070 goto lend;
2072 if (!FTP_SendType(lpwfs, dwType))
2073 goto lend;
2075 if (!FTP_SendPortOrPasv(lpwfs))
2076 goto lend;
2078 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2079 goto lend;
2081 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
2082 MAX_REPLY_LEN, 0, 0, 0);
2083 if (nResCode)
2085 if (nResCode == 125 || nResCode == 150)
2087 /* Parse size of data to be retrieved */
2088 INT i, sizepos = -1;
2089 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2090 for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--)
2092 if ('(' == lpszResponseBuffer[i])
2094 sizepos = i;
2095 break;
2099 if (sizepos >= 0)
2101 nResult = atol(&lpszResponseBuffer[sizepos+1]);
2102 TRACE("Waiting to receive %ld bytes\n", nResult);
2107 lend:
2108 if (0 == nResult && INVALID_SOCKET != lpwfs->lstnSocket)
2110 close(lpwfs->lstnSocket);
2111 lpwfs->lstnSocket = INVALID_SOCKET;
2114 return nResult;
2118 /***********************************************************************
2119 * FTP_RetrieveData (internal)
2121 * Retrieve data from server
2123 * RETURNS
2124 * TRUE on success
2125 * FALSE on failure
2128 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2130 DWORD nBytesWritten;
2131 DWORD nBytesReceived = 0;
2132 INT nRC = 0;
2133 CHAR *lpszBuffer;
2135 TRACE("\n");
2137 if (INVALID_HANDLE_VALUE == hFile)
2138 return FALSE;
2140 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2141 if (NULL == lpszBuffer)
2143 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2144 return FALSE;
2147 while (nBytesReceived < nBytes && nRC != SOCKET_ERROR)
2149 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2150 if (nRC != SOCKET_ERROR)
2152 /* other side closed socket. */
2153 if (nRC == 0)
2154 goto recv_end;
2155 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2156 nBytesReceived += nRC;
2159 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2160 nBytesReceived * 100 / nBytes);
2163 TRACE("Data transfer complete\n");
2164 if (NULL != lpszBuffer)
2165 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2167 recv_end:
2168 return (nRC != SOCKET_ERROR);
2172 /***********************************************************************
2173 * FTP_CloseSessionHandle (internal)
2175 * Deallocate session handle
2177 * RETURNS
2178 * TRUE on success
2179 * FALSE on failure
2182 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs)
2184 if (INVALID_SOCKET != lpwfs->sndSocket)
2185 close(lpwfs->sndSocket);
2187 if (INVALID_SOCKET != lpwfs->lstnSocket)
2188 close(lpwfs->lstnSocket);
2190 if (lpwfs->lpszPassword)
2191 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2193 if (lpwfs->lpszUserName)
2194 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2196 HeapFree(GetProcessHeap(), 0, lpwfs);
2198 return TRUE;
2202 /***********************************************************************
2203 * FTP_CloseSessionHandle (internal)
2205 * Deallocate session handle
2207 * RETURNS
2208 * TRUE on success
2209 * FALSE on failure
2212 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn)
2214 INT i;
2216 TRACE("\n");
2218 for (i = 0; i < lpwfn->size; i++)
2220 if (NULL != lpwfn->lpafp[i].lpszName)
2221 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2224 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2225 HeapFree(GetProcessHeap(), 0, lpwfn);
2227 return TRUE;
2231 /***********************************************************************
2232 * FTP_ReceiveFileList (internal)
2234 * Read file list from server
2236 * RETURNS
2237 * Handle to file list on success
2238 * NULL on failure
2241 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket,
2242 LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext)
2244 DWORD dwSize = 0;
2245 LPFILEPROPERTIESA lpafp = NULL;
2246 LPWININETFINDNEXTA lpwfn = NULL;
2248 TRACE("\n");
2250 if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize))
2252 FTP_ConvertFileProp(lpafp, lpFindFileData);
2254 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA));
2255 if (NULL != lpwfn)
2257 lpwfn->hdr.htype = WH_HFINDNEXT;
2258 lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs;
2259 lpwfn->hdr.dwContext = dwContext;
2260 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2261 lpwfn->size = dwSize;
2262 lpwfn->lpafp = lpafp;
2266 TRACE("Matched %ld files\n", dwSize);
2267 return (HINTERNET)lpwfn;
2271 /***********************************************************************
2272 * FTP_ConvertFileProp (internal)
2274 * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA
2276 * RETURNS
2277 * TRUE on success
2278 * FALSE on failure
2281 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData)
2283 BOOL bSuccess = FALSE;
2285 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2287 if (lpafp)
2289 DWORD access = mktime(&lpafp->tmLastModified);
2291 /* Not all fields are filled in */
2292 lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access);
2293 lpFindFileData->ftLastAccessTime.dwLowDateTime = LOWORD(access);
2294 lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize);
2295 lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize);
2297 if (lpafp->bIsDirectory)
2298 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2300 if (lpafp->lpszName)
2301 strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2303 bSuccess = TRUE;
2306 return bSuccess;
2310 /***********************************************************************
2311 * FTP_ParseDirectory (internal)
2313 * Parse string of directory information
2315 * RETURNS
2316 * TRUE on success
2317 * FALSE on failure
2319 * FIXME: - This function needs serious clea-up
2320 * - We should consider both UNIX and NT list formats
2322 #define MAX_MONTH_LEN 10
2323 #define MIN_LEN_DIR_ENTRY 15
2325 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp)
2328 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2330 * For instance:
2331 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2333 CHAR* pszMinutes;
2334 CHAR* pszHour;
2335 time_t aTime;
2336 struct tm* apTM;
2337 CHAR pszMonth[MAX_MONTH_LEN];
2338 CHAR* pszMatch;
2339 BOOL bSuccess = TRUE;
2340 DWORD nBufLen = MAX_REPLY_LEN;
2341 LPFILEPROPERTIESA curFileProp = NULL;
2342 CHAR* pszLine = NULL;
2343 CHAR* pszToken = NULL;
2344 INT nTokenToSkip = 3;
2345 INT nCount = 0;
2346 INT nSeconds = 0;
2347 INT nMinutes = 0;
2348 INT nHour = 0;
2349 INT nDay = 0;
2350 INT nMonth = 0;
2351 INT nYear = 0;
2352 INT sizeFilePropArray = 20;
2353 INT indexFilePropArray = 0;
2355 TRACE("\n");
2357 /* Allocate intial file properties array */
2358 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESA)*(sizeFilePropArray));
2359 if (NULL == lpafp)
2361 bSuccess = FALSE;
2362 goto lend;
2365 while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
2367 if (sizeFilePropArray <= indexFilePropArray)
2369 LPFILEPROPERTIESA tmpafp;
2371 sizeFilePropArray *= 2;
2372 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
2373 sizeof(FILEPROPERTIESA)*sizeFilePropArray);
2374 if (NULL == tmpafp)
2376 bSuccess = FALSE;
2377 goto lend;
2380 *lpafp = tmpafp;
2383 curFileProp = &((*lpafp)[indexFilePropArray]);
2385 /* First Parse the permissions. */
2386 pszToken = strtok(pszLine, " \t" );
2388 /* HACK! If this is not a file listing skip the line */
2389 if (!pszToken || 10 != strlen(pszToken) || nBufLen <= MIN_LEN_DIR_ENTRY)
2391 nBufLen = MAX_REPLY_LEN;
2392 continue;
2395 FTP_ParsePermission(pszToken, curFileProp);
2397 nTokenToSkip = 3;
2398 nCount = 0;
2401 pszToken = strtok( NULL, " \t" );
2402 nCount++;
2403 } while( nCount <= nTokenToSkip );
2405 /* Store the size of the file in the param list. */
2406 TRACE("nSize-> %s\n", pszToken);
2407 if (pszToken != NULL)
2408 curFileProp->nSize = atol(pszToken);
2410 /* Parse last modified time. */
2411 nSeconds = 0;
2412 nMinutes = 0;
2413 nHour = 0;
2414 nDay = 0;
2415 nMonth = 0;
2416 nYear = 0;
2418 pszToken = strtok( NULL, " \t" );
2419 strncpy(pszMonth, pszToken, MAX_MONTH_LEN);
2420 CharUpperA(pszMonth);
2421 pszMatch = strstr(szMonths, pszMonth);
2422 if( pszMatch != NULL )
2423 nMonth = (pszMatch - szMonths) / 3;
2425 pszToken = strtok(NULL, " \t");
2426 TRACE("nDay -> %s\n", pszToken);
2427 if (pszToken != NULL)
2428 nDay = atoi(pszToken);
2430 pszToken = strtok(NULL, " \t");
2431 pszMinutes = strchr(pszToken, ':');
2432 if( pszMinutes != NULL )
2434 pszMinutes++;
2435 nMinutes = atoi(pszMinutes);
2436 pszHour = pszMinutes - 3;
2437 if (pszHour != NULL)
2438 nHour = atoi(pszHour);
2439 time(&aTime);
2440 apTM = localtime( &aTime );
2441 nYear = apTM->tm_year;
2443 else
2445 nYear = atoi(pszToken);
2446 nYear -= 1900;
2447 nHour = 12;
2450 curFileProp->tmLastModified.tm_sec = nSeconds;
2451 curFileProp->tmLastModified.tm_min = nMinutes;
2452 curFileProp->tmLastModified.tm_hour = nHour;
2453 curFileProp->tmLastModified.tm_mday = nDay;
2454 curFileProp->tmLastModified.tm_mon = nMonth;
2455 curFileProp->tmLastModified.tm_year = nYear;
2457 pszToken = strtok(NULL, " \t");
2458 if(pszToken != NULL)
2460 curFileProp->lpszName = FTP_strdup(pszToken);
2461 TRACE(": %s\n", curFileProp->lpszName);
2464 nBufLen = MAX_REPLY_LEN;
2465 indexFilePropArray++;
2468 if (bSuccess && indexFilePropArray)
2470 if (indexFilePropArray < sizeFilePropArray - 1)
2472 LPFILEPROPERTIESA tmpafp;
2474 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
2475 sizeof(FILEPROPERTIESA)*indexFilePropArray);
2476 if (NULL == tmpafp)
2477 *lpafp = tmpafp;
2479 *dwfp = indexFilePropArray;
2481 else
2483 HeapFree(GetProcessHeap(), 0, *lpafp);
2484 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2485 bSuccess = FALSE;
2488 lend:
2489 return bSuccess;
2493 /***********************************************************************
2494 * FTP_ParsePermission (internal)
2496 * Parse permission string of directory information
2498 * RETURNS
2499 * TRUE on success
2500 * FALSE on failure
2503 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp)
2505 BOOL bSuccess = TRUE;
2506 unsigned short nPermission = 0;
2507 INT nPos = 1;
2508 INT nLast = 9;
2510 TRACE("\n");
2511 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
2513 bSuccess = FALSE;
2514 return bSuccess;
2517 lpfp->bIsDirectory = (*lpszPermission == 'd');
2520 switch (nPos)
2522 case 1:
2523 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
2524 break;
2525 case 2:
2526 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
2527 break;
2528 case 3:
2529 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
2530 break;
2531 case 4:
2532 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
2533 break;
2534 case 5:
2535 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
2536 break;
2537 case 6:
2538 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
2539 break;
2540 case 7:
2541 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
2542 break;
2543 case 8:
2544 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
2545 break;
2546 case 9:
2547 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
2548 break;
2550 nPos++;
2551 }while (nPos <= nLast);
2553 lpfp->permissions = nPermission;
2554 return bSuccess;
2558 /***********************************************************************
2559 * FTP_SetResponseError (internal)
2561 * Set the appropriate error code for a given response from the server
2563 * RETURNS
2566 DWORD FTP_SetResponseError(DWORD dwResponse)
2568 DWORD dwCode = 0;
2570 switch(dwResponse)
2572 case 421: /* Service not available - Server may be shutting down. */
2573 dwCode = ERROR_INTERNET_TIMEOUT;
2574 break;
2576 case 425: /* Cannot open data connection. */
2577 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
2578 break;
2580 case 426: /* Connection closed, transer aborted. */
2581 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
2582 break;
2584 case 500: /* Syntax error. Command unrecognized. */
2585 case 501: /* Syntax error. Error in parameters or arguments. */
2586 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
2587 break;
2589 case 530: /* Not logged in. Login incorrect. */
2590 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
2591 break;
2593 case 550: /* File action not taken. File not found or no access. */
2594 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
2595 break;
2597 case 450: /* File action not taken. File may be busy. */
2598 case 451: /* Action aborted. Server error. */
2599 case 452: /* Action not taken. Insufficient storage space on server. */
2600 case 502: /* Command not implemented. */
2601 case 503: /* Bad sequence of command. */
2602 case 504: /* Command not implemented for that parameter. */
2603 case 532: /* Need account for storing files */
2604 case 551: /* Requested action aborted. Page type unknown */
2605 case 552: /* Action aborted. Exceeded storage allocation */
2606 case 553: /* Action not taken. File name not allowed. */
2608 default:
2609 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
2610 break;
2613 INTERNET_SetLastError(dwCode);
2614 return dwCode;