Convert MRULists to Unicode.
[wine/testsucceed.git] / dlls / wininet / ftp.c
blob6f51ba2f7ba122386d8b50187bebc3dd279938d1
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 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #ifdef HAVE_SYS_SOCKET_H
20 # include <sys/socket.h>
21 #endif
22 #include <unistd.h>
23 #include <time.h>
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "wininet.h"
29 #include "winerror.h"
31 #include "debugtools.h"
32 #include "internet.h"
34 DEFAULT_DEBUG_CHANNEL(wininet);
36 #define NOACCOUNT "noaccount"
37 #define DATA_PACKET_SIZE 0x2000
38 #define szCRLF "\r\n"
39 #define MAX_BACKLOG 5
41 typedef enum {
42 /* FTP commands with arguments. */
43 FTP_CMD_ACCT,
44 FTP_CMD_CWD,
45 FTP_CMD_DELE,
46 FTP_CMD_MKD,
47 FTP_CMD_PASS,
48 FTP_CMD_PORT,
49 FTP_CMD_RETR,
50 FTP_CMD_RMD,
51 FTP_CMD_RNFR,
52 FTP_CMD_RNTO,
53 FTP_CMD_STOR,
54 FTP_CMD_TYPE,
55 FTP_CMD_USER,
57 /* FTP commands without arguments. */
58 FTP_CMD_ABOR,
59 FTP_CMD_LIST,
60 FTP_CMD_NLST,
61 FTP_CMD_PASV,
62 FTP_CMD_PWD,
63 FTP_CMD_QUIT,
64 } FTP_COMMAND;
66 static const CHAR *szFtpCommands[] = {
67 "ACCT",
68 "CWD",
69 "DELE",
70 "MKD",
71 "PASS",
72 "PORT",
73 "RETR",
74 "RMD",
75 "RNFR",
76 "RNTO",
77 "STOR",
78 "TYPE",
79 "USER",
80 "ABOR",
81 "LIST",
82 "NLST",
83 "PASV",
84 "PWD",
85 "QUIT",
88 static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
90 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
91 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext);
92 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType);
93 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket);
94 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile);
95 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
96 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext);
97 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType);
98 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile);
99 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs);
100 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs);
101 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs);
102 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs);
103 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType);
104 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs);
105 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs);
106 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs);
107 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp);
108 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp);
109 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket,
110 LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext);
111 DWORD FTP_SetResponseError(DWORD dwResponse);
113 inline static LPSTR FTP_strdup( LPCSTR str )
115 LPSTR ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 );
116 if (ret) strcpy( ret, str );
117 return ret;
120 /***********************************************************************
121 * FtpPutFileA (WININET.@)
123 * Uploads a file to the FTP server
125 * RETURNS
126 * TRUE on success
127 * FALSE on failure
130 BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
131 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
133 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
134 LPWININETAPPINFOA hIC = NULL;
136 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
138 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
139 return FALSE;
142 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
143 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
145 WORKREQUEST workRequest;
147 workRequest.asyncall = FTPPUTFILEA;
148 workRequest.HFTPSESSION = (DWORD)hConnect;
149 workRequest.LPSZLOCALFILE = (DWORD)FTP_strdup(lpszLocalFile);
150 workRequest.LPSZNEWREMOTEFILE = (DWORD)FTP_strdup(lpszNewRemoteFile);
151 workRequest.DWFLAGS = dwFlags;
152 workRequest.DWCONTEXT = dwContext;
154 return INTERNET_AsyncCall(&workRequest);
156 else
158 return FTP_FtpPutFileA(hConnect, lpszLocalFile,
159 lpszNewRemoteFile, dwFlags, dwContext);
163 /***********************************************************************
164 * FTP_FtpPutFileA (Internal)
166 * Uploads a file to the FTP server
168 * RETURNS
169 * TRUE on success
170 * FALSE on failure
173 BOOL WINAPI FTP_FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
174 LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext)
176 HANDLE hFile = (HANDLE)NULL;
177 BOOL bSuccess = FALSE;
178 LPWININETAPPINFOA hIC = NULL;
179 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
180 INT nResCode;
182 TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", lpszLocalFile, lpszNewRemoteFile);
183 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
185 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
186 return FALSE;
189 /* Clear any error information */
190 INTERNET_SetLastError(0);
192 /* Open file to be uploaded */
193 if (INVALID_HANDLE_VALUE ==
194 (hFile = CreateFileA(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0)))
196 INTERNET_SetLastError(ERROR_FILE_NOT_FOUND);
197 goto lend;
200 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
201 if (hIC->lpfnStatusCB)
202 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
204 if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags))
206 INT nDataSocket;
208 /* Get data socket to server */
209 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
211 FTP_SendData(lpwfs, nDataSocket, hFile);
212 close(nDataSocket);
213 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
214 MAX_REPLY_LEN, 0, 0, 0);
215 if (nResCode)
217 if (nResCode == 226)
218 bSuccess = TRUE;
219 else
220 FTP_SetResponseError(nResCode);
225 lend:
226 if (lpwfs->lstnSocket != -1)
227 close(lpwfs->lstnSocket);
229 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
231 INTERNET_ASYNC_RESULT iar;
233 iar.dwResult = (DWORD)bSuccess;
234 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
235 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
236 &iar, sizeof(INTERNET_ASYNC_RESULT));
239 if (hFile)
240 CloseHandle(hFile);
242 return bSuccess;
246 /***********************************************************************
247 * FtpSetCurrentDirectoryA (WININET.@)
249 * Change the working directory on the FTP server
251 * RETURNS
252 * TRUE on success
253 * FALSE on failure
256 BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
258 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
259 LPWININETAPPINFOA hIC = NULL;
261 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
263 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
264 return FALSE;
267 TRACE("lpszDirectory(%s)\n", lpszDirectory);
269 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
270 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
272 WORKREQUEST workRequest;
274 workRequest.asyncall = FTPSETCURRENTDIRECTORYA;
275 workRequest.HFTPSESSION = (DWORD)hConnect;
276 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
278 return INTERNET_AsyncCall(&workRequest);
280 else
282 return FTP_FtpSetCurrentDirectoryA(hConnect, lpszDirectory);
287 /***********************************************************************
288 * FTP_FtpSetCurrentDirectoryA (Internal)
290 * Change the working directory on the FTP server
292 * RETURNS
293 * TRUE on success
294 * FALSE on failure
297 BOOL WINAPI FTP_FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
299 INT nResCode;
300 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
301 LPWININETAPPINFOA hIC = NULL;
302 DWORD bSuccess = FALSE;
304 TRACE("lpszDirectory(%s)\n", lpszDirectory);
306 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
308 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
309 return FALSE;
312 /* Clear any error information */
313 INTERNET_SetLastError(0);
315 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
316 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory,
317 hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext))
318 goto lend;
320 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
321 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
323 if (nResCode)
325 if (nResCode == 250)
326 bSuccess = TRUE;
327 else
328 FTP_SetResponseError(nResCode);
331 lend:
332 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
334 INTERNET_ASYNC_RESULT iar;
336 iar.dwResult = (DWORD)bSuccess;
337 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
338 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
339 &iar, sizeof(INTERNET_ASYNC_RESULT));
341 return bSuccess;
345 /***********************************************************************
346 * FtpCreateDirectoryA (WININET.@)
348 * Create new directory on the FTP server
350 * RETURNS
351 * TRUE on success
352 * FALSE on failure
355 BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
357 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
358 LPWININETAPPINFOA hIC = NULL;
360 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
362 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
363 return FALSE;
366 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
367 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
369 WORKREQUEST workRequest;
371 workRequest.asyncall = FTPCREATEDIRECTORYA;
372 workRequest.HFTPSESSION = (DWORD)hConnect;
373 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
375 return INTERNET_AsyncCall(&workRequest);
377 else
379 return FTP_FtpCreateDirectoryA(hConnect, lpszDirectory);
384 /***********************************************************************
385 * FTP_FtpCreateDirectoryA (Internal)
387 * Create new directory on the FTP server
389 * RETURNS
390 * TRUE on success
391 * FALSE on failure
394 BOOL WINAPI FTP_FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
396 INT nResCode;
397 BOOL bSuccess = FALSE;
398 LPWININETAPPINFOA hIC = NULL;
399 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
401 TRACE("\n");
402 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
404 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
405 return FALSE;
408 /* Clear any error information */
409 INTERNET_SetLastError(0);
411 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0))
412 goto lend;
414 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
415 MAX_REPLY_LEN, 0, 0, 0);
416 if (nResCode)
418 if (nResCode == 257)
419 bSuccess = TRUE;
420 else
421 FTP_SetResponseError(nResCode);
424 lend:
425 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
426 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
428 INTERNET_ASYNC_RESULT iar;
430 iar.dwResult = (DWORD)bSuccess;
431 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
432 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
433 &iar, sizeof(INTERNET_ASYNC_RESULT));
436 return bSuccess;
440 /***********************************************************************
441 * FtpFindFirstFileA (WININET.@)
443 * Search the specified directory
445 * RETURNS
446 * HINTERNET on success
447 * NULL on failure
450 HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
451 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
453 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
454 LPWININETAPPINFOA hIC = NULL;
456 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
458 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
459 return FALSE;
462 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
463 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
465 WORKREQUEST workRequest;
467 workRequest.asyncall = FTPFINDFIRSTFILEA;
468 workRequest.HFTPSESSION = (DWORD)hConnect;
469 workRequest.LPSZSEARCHFILE = (DWORD)FTP_strdup(lpszSearchFile);
470 workRequest.LPFINDFILEDATA = (DWORD)lpFindFileData;
471 workRequest.DWFLAGS = dwFlags;
472 workRequest.DWCONTEXT= dwContext;
474 INTERNET_AsyncCall(&workRequest);
475 return NULL;
477 else
479 return FTP_FtpFindFirstFileA(hConnect, lpszSearchFile, lpFindFileData,
480 dwFlags, dwContext);
485 /***********************************************************************
486 * FTP_FtpFindFirstFileA (Internal)
488 * Search the specified directory
490 * RETURNS
491 * HINTERNET on success
492 * NULL on failure
495 HINTERNET WINAPI FTP_FtpFindFirstFileA(HINTERNET hConnect,
496 LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext)
498 INT nResCode;
499 LPWININETAPPINFOA hIC = NULL;
500 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hConnect;
501 LPWININETFINDNEXTA hFindNext = NULL;
503 TRACE("\n");
505 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
507 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
508 return FALSE;
511 /* Clear any error information */
512 INTERNET_SetLastError(0);
514 if (!FTP_InitListenSocket(lpwfs))
515 goto lend;
517 if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII))
518 goto lend;
520 if (!FTP_SendPortOrPasv(lpwfs))
521 goto lend;
523 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
524 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, lpszSearchFile,
525 hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext))
526 goto lend;
528 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
529 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
530 if (nResCode)
532 if (nResCode == 125 || nResCode == 150)
534 INT nDataSocket;
536 /* Get data socket to server */
537 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
539 hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpFindFileData, dwContext);
541 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
542 MAX_REPLY_LEN, hIC->lpfnStatusCB, hConnect, lpwfs->hdr.dwContext);
543 if (nResCode != 226 && nResCode != 250)
544 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
546 close(nDataSocket);
549 else
550 FTP_SetResponseError(nResCode);
553 lend:
554 if (lpwfs->lstnSocket != -1)
555 close(lpwfs->lstnSocket);
557 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
559 INTERNET_ASYNC_RESULT iar;
561 if (hFindNext)
563 iar.dwResult = (DWORD)hFindNext;
564 iar.dwError = ERROR_SUCCESS;
565 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
566 &iar, sizeof(INTERNET_ASYNC_RESULT));
569 iar.dwResult = (DWORD)hFindNext;
570 iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
571 hIC->lpfnStatusCB(hConnect, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
572 &iar, sizeof(INTERNET_ASYNC_RESULT));
575 return (HINTERNET)hFindNext;
579 /***********************************************************************
580 * FtpGetCurrentDirectoryA (WININET.@)
582 * Retrieves the current directory
584 * RETURNS
585 * TRUE on success
586 * FALSE on failure
589 BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
590 LPDWORD lpdwCurrentDirectory)
592 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
593 LPWININETAPPINFOA hIC = NULL;
595 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
597 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
599 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
600 return FALSE;
603 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
604 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
606 WORKREQUEST workRequest;
608 workRequest.asyncall = FTPGETCURRENTDIRECTORYA;
609 workRequest.HFTPSESSION = (DWORD)hFtpSession;
610 workRequest.LPSZDIRECTORY = (DWORD)lpszCurrentDirectory;
611 workRequest.LPDWDIRECTORY = (DWORD)lpdwCurrentDirectory;
613 return INTERNET_AsyncCall(&workRequest);
615 else
617 return FTP_FtpGetCurrentDirectoryA(hFtpSession, lpszCurrentDirectory,
618 lpdwCurrentDirectory);
623 /***********************************************************************
624 * FTP_FtpGetCurrentDirectoryA (Internal)
626 * Retrieves the current directory
628 * RETURNS
629 * TRUE on success
630 * FALSE on failure
633 BOOL WINAPI FTP_FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory,
634 LPDWORD lpdwCurrentDirectory)
636 INT nResCode;
637 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
638 LPWININETAPPINFOA hIC = NULL;
639 DWORD bSuccess = FALSE;
641 TRACE("len(%ld)\n", *lpdwCurrentDirectory);
643 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
645 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
646 return FALSE;
649 /* Clear any error information */
650 INTERNET_SetLastError(0);
652 ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory);
654 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
655 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL,
656 hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext))
657 goto lend;
659 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
660 MAX_REPLY_LEN, hIC->lpfnStatusCB, hFtpSession, lpwfs->hdr.dwContext);
661 if (nResCode)
663 if (nResCode == 257) /* Extract directory name */
665 INT firstpos, lastpos, len;
666 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
668 for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
670 if ('"' == lpszResponseBuffer[lastpos])
672 if (!firstpos)
673 firstpos = lastpos;
674 else
675 break;
679 len = lastpos - firstpos - 1;
680 strncpy(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1],
681 len < *lpdwCurrentDirectory ? len : *lpdwCurrentDirectory);
682 *lpdwCurrentDirectory = len;
683 bSuccess = TRUE;
685 else
686 FTP_SetResponseError(nResCode);
689 lend:
690 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
692 INTERNET_ASYNC_RESULT iar;
694 iar.dwResult = (DWORD)bSuccess;
695 iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR;
696 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
697 &iar, sizeof(INTERNET_ASYNC_RESULT));
700 return (DWORD) bSuccess;
703 /***********************************************************************
704 * FtpOpenFileA (WININET.@)
706 * Open a remote file for writing or reading
708 * RETURNS
709 * HINTERNET handle on success
710 * NULL on failure
713 HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
714 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
715 DWORD dwContext)
717 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
718 LPWININETAPPINFOA hIC = NULL;
720 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
722 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
723 return FALSE;
726 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
727 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
729 WORKREQUEST workRequest;
731 workRequest.asyncall = FTPOPENFILEA;
732 workRequest.HFTPSESSION = (DWORD)hFtpSession;
733 workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName);
734 workRequest.FDWACCESS = fdwAccess;
735 workRequest.DWFLAGS = dwFlags;
736 workRequest.DWCONTEXT = dwContext;
738 INTERNET_AsyncCall(&workRequest);
739 return NULL;
741 else
743 return FTP_FtpOpenFileA(hFtpSession, lpszFileName, fdwAccess, dwFlags, dwContext);
748 /***********************************************************************
749 * FTP_FtpOpenFileA (Internal)
751 * Open a remote file for writing or reading
753 * RETURNS
754 * HINTERNET handle on success
755 * NULL on failure
758 HINTERNET FTP_FtpOpenFileA(HINTERNET hFtpSession,
759 LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
760 DWORD dwContext)
762 INT nDataSocket;
763 BOOL bSuccess = FALSE;
764 LPWININETFILE hFile = NULL;
765 LPWININETAPPINFOA hIC = NULL;
766 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
768 TRACE("\n");
770 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
772 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
773 return FALSE;
776 /* Clear any error information */
777 INTERNET_SetLastError(0);
779 if (GENERIC_READ == fdwAccess)
781 /* Set up socket to retrieve data */
782 bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags);
784 else if (GENERIC_WRITE == fdwAccess)
786 /* Set up socket to send data */
787 bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags);
790 /* Get data socket to server */
791 if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
793 hFile = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE));
794 hFile->hdr.htype = WH_HFILE;
795 hFile->hdr.dwFlags = dwFlags;
796 hFile->hdr.dwContext = dwContext;
797 hFile->hdr.lpwhparent = hFtpSession;
798 hFile->nDataSocket = nDataSocket;
801 if (lpwfs->lstnSocket != -1)
802 close(lpwfs->lstnSocket);
804 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
805 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
807 INTERNET_ASYNC_RESULT iar;
809 if (hFile)
811 iar.dwResult = (DWORD)hFile;
812 iar.dwError = ERROR_SUCCESS;
813 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
814 &iar, sizeof(INTERNET_ASYNC_RESULT));
817 iar.dwResult = (DWORD)bSuccess;
818 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
819 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
820 &iar, sizeof(INTERNET_ASYNC_RESULT));
823 return (HINTERNET)hFile;
827 /***********************************************************************
828 * FtpGetFileA (WININET.@)
830 * Retrieve file from the FTP server
832 * RETURNS
833 * TRUE on success
834 * FALSE on failure
837 BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
838 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
839 DWORD dwContext)
841 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet;
842 LPWININETAPPINFOA hIC = NULL;
844 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
846 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
847 return FALSE;
850 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
851 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
853 WORKREQUEST workRequest;
855 workRequest.asyncall = FTPGETFILEA;
856 workRequest.HFTPSESSION = (DWORD)hInternet;
857 workRequest.LPSZREMOTEFILE = (DWORD)FTP_strdup(lpszRemoteFile);
858 workRequest.LPSZNEWFILE = (DWORD)FTP_strdup(lpszNewFile);
859 workRequest.DWLOCALFLAGSATTRIBUTE = dwLocalFlagsAttribute;
860 workRequest.FFAILIFEXISTS = (DWORD)fFailIfExists;
861 workRequest.DWFLAGS = dwInternetFlags;
862 workRequest.DWCONTEXT = dwContext;
864 return INTERNET_AsyncCall(&workRequest);
866 else
868 return FTP_FtpGetFileA(hInternet, lpszRemoteFile, lpszNewFile,
869 fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext);
874 /***********************************************************************
875 * FTP_FtpGetFileA (Internal)
877 * Retrieve file from the FTP server
879 * RETURNS
880 * TRUE on success
881 * FALSE on failure
884 BOOL WINAPI FTP_FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile,
885 BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
886 DWORD dwContext)
888 DWORD nBytes;
889 BOOL bSuccess = FALSE;
890 HANDLE hFile;
891 LPWININETAPPINFOA hIC = NULL;
892 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hInternet;
894 TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", lpszRemoteFile, lpszNewFile);
895 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
897 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
898 return FALSE;
901 /* Clear any error information */
902 INTERNET_SetLastError(0);
904 /* Ensure we can write to lpszNewfile by opening it */
905 hFile = CreateFileA(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ?
906 CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0);
907 if (INVALID_HANDLE_VALUE == hFile)
908 goto lend;
910 /* Set up socket to retrieve data */
911 nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags);
913 if (nBytes > 0)
915 INT nDataSocket;
917 /* Get data socket to server */
918 if (FTP_GetDataSocket(lpwfs, &nDataSocket))
920 INT nResCode;
922 /* Receive data */
923 FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile);
924 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
925 MAX_REPLY_LEN, 0, 0, 0);
926 if (nResCode)
928 if (nResCode == 226)
929 bSuccess = TRUE;
930 else
931 FTP_SetResponseError(nResCode);
933 close(nDataSocket);
937 lend:
938 if (lpwfs->lstnSocket != -1)
939 close(lpwfs->lstnSocket);
941 if (hFile)
942 CloseHandle(hFile);
944 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
945 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
947 INTERNET_ASYNC_RESULT iar;
949 iar.dwResult = (DWORD)bSuccess;
950 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
951 hIC->lpfnStatusCB(hInternet, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
952 &iar, sizeof(INTERNET_ASYNC_RESULT));
955 return bSuccess;
959 /***********************************************************************
960 * FtpDeleteFileA (WININET.@)
962 * Delete a file on the ftp server
964 * RETURNS
965 * TRUE on success
966 * FALSE on failure
969 BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
971 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
972 LPWININETAPPINFOA hIC = NULL;
974 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
976 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
977 return FALSE;
980 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
981 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
983 WORKREQUEST workRequest;
985 workRequest.asyncall = FTPRENAMEFILEA;
986 workRequest.HFTPSESSION = (DWORD)hFtpSession;
987 workRequest.LPSZFILENAME = (DWORD)FTP_strdup(lpszFileName);
989 return INTERNET_AsyncCall(&workRequest);
991 else
993 return FTP_FtpDeleteFileA(hFtpSession, lpszFileName);
998 /***********************************************************************
999 * FTP_FtpDeleteFileA (Internal)
1001 * Delete a file on the ftp server
1003 * RETURNS
1004 * TRUE on success
1005 * FALSE on failure
1008 BOOL FTP_FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
1010 INT nResCode;
1011 BOOL bSuccess = FALSE;
1012 LPWININETAPPINFOA hIC = NULL;
1013 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1015 TRACE("0x%08lx\n", (ULONG) hFtpSession);
1016 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1018 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1019 return FALSE;
1022 /* Clear any error information */
1023 INTERNET_SetLastError(0);
1025 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0))
1026 goto lend;
1028 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1029 MAX_REPLY_LEN, 0, 0, 0);
1030 if (nResCode)
1032 if (nResCode == 250)
1033 bSuccess = TRUE;
1034 else
1035 FTP_SetResponseError(nResCode);
1037 lend:
1038 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1039 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1041 INTERNET_ASYNC_RESULT iar;
1043 iar.dwResult = (DWORD)bSuccess;
1044 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1045 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1046 &iar, sizeof(INTERNET_ASYNC_RESULT));
1049 return bSuccess;
1053 /***********************************************************************
1054 * FtpRemoveDirectoryA (WININET.@)
1056 * Remove a directory on the ftp server
1058 * RETURNS
1059 * TRUE on success
1060 * FALSE on failure
1063 BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1065 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1066 LPWININETAPPINFOA hIC = NULL;
1068 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1070 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1071 return FALSE;
1074 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1075 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1077 WORKREQUEST workRequest;
1079 workRequest.asyncall = FTPREMOVEDIRECTORYA;
1080 workRequest.HFTPSESSION = (DWORD)hFtpSession;
1081 workRequest.LPSZDIRECTORY = (DWORD)FTP_strdup(lpszDirectory);
1083 return INTERNET_AsyncCall(&workRequest);
1085 else
1087 return FTP_FtpRemoveDirectoryA(hFtpSession, lpszDirectory);
1092 /***********************************************************************
1093 * FTP_FtpRemoveDirectoryA (Internal)
1095 * Remove a directory on the ftp server
1097 * RETURNS
1098 * TRUE on success
1099 * FALSE on failure
1102 BOOL FTP_FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
1104 INT nResCode;
1105 BOOL bSuccess = FALSE;
1106 LPWININETAPPINFOA hIC = NULL;
1107 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1109 TRACE("\n");
1110 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1112 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1113 return FALSE;
1116 /* Clear any error information */
1117 INTERNET_SetLastError(0);
1119 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0))
1120 goto lend;
1122 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1123 MAX_REPLY_LEN, 0, 0, 0);
1124 if (nResCode)
1126 if (nResCode == 250)
1127 bSuccess = TRUE;
1128 else
1129 FTP_SetResponseError(nResCode);
1132 lend:
1133 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1134 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1136 INTERNET_ASYNC_RESULT iar;
1138 iar.dwResult = (DWORD)bSuccess;
1139 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1140 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1141 &iar, sizeof(INTERNET_ASYNC_RESULT));
1144 return bSuccess;
1148 /***********************************************************************
1149 * FtpRenameFileA (WININET.@)
1151 * Rename a file on the ftp server
1153 * RETURNS
1154 * TRUE on success
1155 * FALSE on failure
1158 BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1160 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1161 LPWININETAPPINFOA hIC = NULL;
1163 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1165 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1166 return FALSE;
1169 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1170 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1172 WORKREQUEST workRequest;
1174 workRequest.asyncall = FTPRENAMEFILEA;
1175 workRequest.HFTPSESSION = (DWORD)hFtpSession;
1176 workRequest.LPSZSRCFILE = (DWORD)FTP_strdup(lpszSrc);
1177 workRequest.LPSZDESTFILE = (DWORD)FTP_strdup(lpszDest);
1179 return INTERNET_AsyncCall(&workRequest);
1181 else
1183 return FTP_FtpRenameFileA(hFtpSession, lpszSrc, lpszDest);
1187 /***********************************************************************
1188 * FTP_FtpRenameFileA (Internal)
1190 * Rename a file on the ftp server
1192 * RETURNS
1193 * TRUE on success
1194 * FALSE on failure
1197 BOOL FTP_FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest)
1199 INT nResCode;
1200 BOOL bSuccess = FALSE;
1201 LPWININETAPPINFOA hIC = NULL;
1202 LPWININETFTPSESSIONA lpwfs = (LPWININETFTPSESSIONA) hFtpSession;
1204 TRACE("\n");
1205 if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
1207 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1208 return FALSE;
1211 /* Clear any error information */
1212 INTERNET_SetLastError(0);
1214 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0))
1215 goto lend;
1217 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket,
1218 INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1219 if (nResCode == 350)
1221 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0))
1222 goto lend;
1224 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket,
1225 INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1228 if (nResCode == 250)
1229 bSuccess = TRUE;
1230 else
1231 FTP_SetResponseError(nResCode);
1233 lend:
1234 hIC = (LPWININETAPPINFOA) lpwfs->hdr.lpwhparent;
1235 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1237 INTERNET_ASYNC_RESULT iar;
1239 iar.dwResult = (DWORD)bSuccess;
1240 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1241 hIC->lpfnStatusCB(hFtpSession, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1242 &iar, sizeof(INTERNET_ASYNC_RESULT));
1245 return bSuccess;
1249 /***********************************************************************
1250 * FTP_Connect (internal)
1252 * Connect to a ftp server
1254 * RETURNS
1255 * HINTERNET a session handle on success
1256 * NULL on failure
1260 HINTERNET FTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName,
1261 INTERNET_PORT nServerPort, LPCSTR lpszUserName,
1262 LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext)
1264 struct sockaddr_in socketAddr;
1265 struct hostent *phe = NULL;
1266 INT nsocket = -1, sock_namelen;
1267 LPWININETAPPINFOA hIC = NULL;
1268 BOOL bSuccess = FALSE;
1269 LPWININETFTPSESSIONA lpwfs = NULL;
1271 TRACE("0x%08lx Server(%s) Port(%d) User(%s) Paswd(%s)\n",
1272 (ULONG) hInternet, lpszServerName,
1273 nServerPort, lpszUserName, lpszPassword);
1275 if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT)
1276 goto lerror;
1278 hIC = (LPWININETAPPINFOA) hInternet;
1280 if (NULL == lpszUserName && NULL != lpszPassword)
1282 INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_USER_NAME);
1283 goto lerror;
1286 if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
1287 nServerPort = INTERNET_DEFAULT_FTP_PORT;
1289 if (hIC->lpfnStatusCB)
1290 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME,
1291 (LPSTR) lpszServerName, strlen(lpszServerName));
1293 if (!GetAddress(lpszServerName, nServerPort, &phe, &socketAddr))
1295 INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
1296 goto lerror;
1299 if (hIC->lpfnStatusCB)
1300 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED,
1301 (LPSTR) lpszServerName, strlen(lpszServerName));
1303 nsocket = socket(AF_INET,SOCK_STREAM,0);
1304 if (nsocket == -1)
1306 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1307 goto lerror;
1310 if (hIC->lpfnStatusCB)
1311 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
1312 &socketAddr, sizeof(struct sockaddr_in));
1314 if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
1316 ERR("Unable to connect (%s)\n", strerror(errno));
1317 INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
1319 else
1321 TRACE("Connected to server\n");
1322 if (hIC->lpfnStatusCB)
1323 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
1324 &socketAddr, sizeof(struct sockaddr_in));
1326 lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONA));
1327 if (NULL == lpwfs)
1329 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1330 goto lerror;
1333 lpwfs->hdr.htype = WH_HFTPSESSION;
1334 lpwfs->hdr.dwFlags = dwFlags;
1335 lpwfs->hdr.dwContext = dwContext;
1336 lpwfs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
1337 lpwfs->sndSocket = nsocket;
1338 sock_namelen = sizeof(lpwfs->socketAddress);
1339 getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
1340 lpwfs->phostent = phe;
1342 if (NULL == lpszUserName)
1344 lpwfs->lpszUserName = FTP_strdup("anonymous");
1345 lpwfs->lpszPassword = FTP_strdup("user@server");
1347 else
1349 lpwfs->lpszUserName = FTP_strdup(lpszUserName);
1350 lpwfs->lpszPassword = FTP_strdup(lpszPassword);
1353 if (FTP_ConnectToHost(lpwfs))
1355 if (hIC->lpfnStatusCB)
1357 INTERNET_ASYNC_RESULT iar;
1359 iar.dwResult = (DWORD)lpwfs;
1360 iar.dwError = ERROR_SUCCESS;
1362 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
1363 &iar, sizeof(INTERNET_ASYNC_RESULT));
1365 TRACE("Successfully logged into server\n");
1366 bSuccess = TRUE;
1370 lerror:
1371 if (!bSuccess && nsocket == -1)
1372 close(nsocket);
1374 if (!bSuccess && lpwfs)
1376 HeapFree(GetProcessHeap(), 0, lpwfs);
1377 lpwfs = NULL;
1380 if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
1382 INTERNET_ASYNC_RESULT iar;
1384 iar.dwResult = (DWORD)lpwfs;
1385 iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
1386 hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
1387 &iar, sizeof(INTERNET_ASYNC_RESULT));
1390 return (HINTERNET) lpwfs;
1394 /***********************************************************************
1395 * FTP_ConnectToHost (internal)
1397 * Connect to a ftp server
1399 * RETURNS
1400 * TRUE on success
1401 * NULL on failure
1404 BOOL FTP_ConnectToHost(LPWININETFTPSESSIONA lpwfs)
1406 INT nResCode;
1407 BOOL bSuccess = FALSE;
1409 TRACE("\n");
1410 FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(), MAX_REPLY_LEN, 0, 0, 0);
1412 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0))
1413 goto lend;
1415 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1416 MAX_REPLY_LEN, 0, 0, 0);
1417 if (nResCode)
1419 /* Login successful... */
1420 if (nResCode == 230)
1421 bSuccess = TRUE;
1422 /* User name okay, need password... */
1423 else if (nResCode == 331)
1424 bSuccess = FTP_SendPassword(lpwfs);
1425 /* Need account for login... */
1426 else if (nResCode == 332)
1427 bSuccess = FTP_SendAccount(lpwfs);
1428 else
1429 FTP_SetResponseError(nResCode);
1432 TRACE("Returning %d\n", bSuccess);
1433 lend:
1434 return bSuccess;
1438 /***********************************************************************
1439 * FTP_SendCommand (internal)
1441 * Send command to server
1443 * RETURNS
1444 * TRUE on success
1445 * NULL on failure
1448 BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
1449 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1451 DWORD len;
1452 CHAR *buf;
1453 DWORD nBytesSent = 0;
1454 DWORD nRC = 0;
1455 BOOL bParamHasLen;
1457 TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket);
1459 if (lpfnStatusCB)
1460 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
1462 bParamHasLen = lpszParam && strlen(lpszParam) > 0;
1463 len = (bParamHasLen ? strlen(lpszParam) : -1) + strlen(szFtpCommands[ftpCmd]) +
1464 strlen(szCRLF)+ 1;
1465 if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1)))
1467 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
1468 return FALSE;
1470 sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], bParamHasLen ? " " : "",
1471 bParamHasLen ? lpszParam : "", szCRLF);
1473 TRACE("Sending (%s) len(%ld)\n", buf, len);
1474 while((nBytesSent < len) && (nRC != -1))
1476 nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0);
1477 nBytesSent += nRC;
1480 HeapFree(GetProcessHeap(), 0, (LPVOID)buf);
1482 if (lpfnStatusCB)
1483 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT,
1484 &nBytesSent, sizeof(DWORD));
1486 TRACE("Sent %ld bytes\n", nBytesSent);
1487 return (nRC != -1);
1491 /***********************************************************************
1492 * FTP_ReceiveResponse (internal)
1494 * Receive response from server
1496 * RETURNS
1497 * Reply code on success
1498 * 0 on failure
1502 INT FTP_ReceiveResponse(INT nSocket, LPSTR lpszResponse, DWORD dwResponse,
1503 INTERNET_STATUS_CALLBACK lpfnStatusCB, HINTERNET hHandle, DWORD dwContext)
1505 DWORD nRecv;
1506 INT rc = 0;
1507 char firstprefix[5];
1508 BOOL multiline = FALSE;
1511 TRACE("socket(%d) \n", nSocket);
1513 if (lpfnStatusCB)
1514 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
1516 while(1)
1518 nRecv = dwResponse;
1519 if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv))
1520 goto lerror;
1522 if (nRecv >= 3)
1524 if(!multiline)
1526 if(lpszResponse[3] != '-')
1527 break;
1528 else
1529 { /* Start of multiline repsonse. Loop until we get "nnn " */
1530 multiline = TRUE;
1531 memcpy(firstprefix, lpszResponse, 3);
1532 firstprefix[3] = ' ';
1533 firstprefix[4] = '\0';
1536 else
1538 if(!memcmp(firstprefix, lpszResponse, 4))
1539 break;
1544 if (nRecv >= 3)
1546 lpszResponse[nRecv] = '\0';
1547 rc = atoi(lpszResponse);
1549 if (lpfnStatusCB)
1550 lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
1551 &nRecv, sizeof(DWORD));
1554 lerror:
1555 TRACE("return %d\n", rc);
1556 return rc;
1560 /***********************************************************************
1561 * FTP_SendPassword (internal)
1563 * Send password to ftp server
1565 * RETURNS
1566 * TRUE on success
1567 * NULL on failure
1570 BOOL FTP_SendPassword(LPWININETFTPSESSIONA lpwfs)
1572 INT nResCode;
1573 BOOL bSuccess = FALSE;
1575 TRACE("\n");
1576 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0))
1577 goto lend;
1579 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1580 MAX_REPLY_LEN, 0, 0, 0);
1581 if (nResCode)
1583 TRACE("Received reply code %d\n", nResCode);
1584 /* Login successful... */
1585 if (nResCode == 230)
1586 bSuccess = TRUE;
1587 /* Command not implemented, superfluous at the server site... */
1588 /* Need account for login... */
1589 else if (nResCode == 332)
1590 bSuccess = FTP_SendAccount(lpwfs);
1591 else
1592 FTP_SetResponseError(nResCode);
1595 lend:
1596 TRACE("Returning %d\n", bSuccess);
1597 return bSuccess;
1601 /***********************************************************************
1602 * FTP_SendAccount (internal)
1606 * RETURNS
1607 * TRUE on success
1608 * FALSE on failure
1611 BOOL FTP_SendAccount(LPWININETFTPSESSIONA lpwfs)
1613 INT nResCode;
1614 BOOL bSuccess = FALSE;
1616 TRACE("\n");
1617 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, NOACCOUNT, 0, 0, 0))
1618 goto lend;
1620 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1621 MAX_REPLY_LEN, 0, 0, 0);
1622 if (nResCode)
1623 bSuccess = TRUE;
1624 else
1625 FTP_SetResponseError(nResCode);
1627 lend:
1628 return bSuccess;
1632 /***********************************************************************
1633 * FTP_SendStore (internal)
1635 * Send request to upload file to ftp server
1637 * RETURNS
1638 * TRUE on success
1639 * FALSE on failure
1642 BOOL FTP_SendStore(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
1644 INT nResCode;
1645 BOOL bSuccess = FALSE;
1647 TRACE("\n");
1648 if (!FTP_InitListenSocket(lpwfs))
1649 goto lend;
1651 if (!FTP_SendType(lpwfs, dwType))
1652 goto lend;
1654 if (!FTP_SendPortOrPasv(lpwfs))
1655 goto lend;
1657 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0))
1658 goto lend;
1659 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1660 MAX_REPLY_LEN, 0, 0, 0);
1661 if (nResCode)
1663 if (nResCode == 150)
1664 bSuccess = TRUE;
1665 else
1666 FTP_SetResponseError(nResCode);
1669 lend:
1670 if (!bSuccess && lpwfs->lstnSocket != -1)
1672 close(lpwfs->lstnSocket);
1673 lpwfs->lstnSocket = -1;
1676 return bSuccess;
1680 /***********************************************************************
1681 * FTP_InitListenSocket (internal)
1683 * Create a socket to listen for server response
1685 * RETURNS
1686 * TRUE on success
1687 * FALSE on failure
1690 BOOL FTP_InitListenSocket(LPWININETFTPSESSIONA lpwfs)
1692 BOOL bSuccess = FALSE;
1693 size_t namelen = sizeof(struct sockaddr_in);
1695 TRACE("\n");
1697 lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0);
1698 if (lpwfs->lstnSocket == -1)
1700 TRACE("Unable to create listening socket\n");
1701 goto lend;
1704 /* We obtain our ip addr from the name of the command channel socket */
1705 lpwfs->lstnSocketAddress = lpwfs->socketAddress;
1707 /* and get the system to assign us a port */
1708 lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
1710 if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
1712 TRACE("Unable to bind socket\n");
1713 goto lend;
1716 if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1)
1718 TRACE("listen failed\n");
1719 goto lend;
1722 if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1)
1723 bSuccess = TRUE;
1725 lend:
1726 if (!bSuccess && lpwfs->lstnSocket == -1)
1728 close(lpwfs->lstnSocket);
1729 lpwfs->lstnSocket = -1;
1732 return bSuccess;
1736 /***********************************************************************
1737 * FTP_SendType (internal)
1739 * Tell server type of data being transferred
1741 * RETURNS
1742 * TRUE on success
1743 * FALSE on failure
1745 * W98SE doesn't cache the type that's currently set
1746 * (i.e. it sends it always),
1747 * so we probably don't want to do that either.
1749 BOOL FTP_SendType(LPWININETFTPSESSIONA lpwfs, DWORD dwType)
1751 INT nResCode;
1752 CHAR type[2] = { "I\0" };
1753 BOOL bSuccess = FALSE;
1755 TRACE("\n");
1756 if (dwType & INTERNET_FLAG_TRANSFER_ASCII)
1757 *type = 'A';
1759 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0))
1760 goto lend;
1762 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1763 MAX_REPLY_LEN, 0, 0, 0)/100;
1764 if (nResCode)
1766 if (nResCode == 2)
1767 bSuccess = TRUE;
1768 else
1769 FTP_SetResponseError(nResCode);
1772 lend:
1773 return bSuccess;
1777 /***********************************************************************
1778 * FTP_SendPort (internal)
1780 * Tell server which port to use
1782 * RETURNS
1783 * TRUE on success
1784 * FALSE on failure
1787 BOOL FTP_SendPort(LPWININETFTPSESSIONA lpwfs)
1789 INT nResCode;
1790 CHAR szIPAddress[64];
1791 BOOL bSuccess = FALSE;
1792 TRACE("\n");
1794 sprintf(szIPAddress, "%d,%d,%d,%d,%d,%d",
1795 lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF,
1796 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8,
1797 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16,
1798 (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24,
1799 lpwfs->lstnSocketAddress.sin_port & 0xFF,
1800 (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8);
1802 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0))
1803 goto lend;
1805 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1806 MAX_REPLY_LEN,0, 0, 0);
1807 if (nResCode)
1809 if (nResCode == 200)
1810 bSuccess = TRUE;
1811 else
1812 FTP_SetResponseError(nResCode);
1815 lend:
1816 return bSuccess;
1820 /***********************************************************************
1821 * FTP_DoPassive (internal)
1823 * Tell server that we want to do passive transfers
1824 * and connect data socket
1826 * RETURNS
1827 * TRUE on success
1828 * FALSE on failure
1831 BOOL FTP_DoPassive(LPWININETFTPSESSIONA lpwfs)
1833 INT nResCode;
1834 BOOL bSuccess = FALSE;
1836 TRACE("\n");
1837 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0))
1838 goto lend;
1840 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
1841 MAX_REPLY_LEN,0, 0, 0);
1842 if (nResCode)
1844 if (nResCode == 227)
1846 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
1847 LPSTR p;
1848 int f[6];
1849 int i;
1850 char *pAddr, *pPort;
1851 INT nsocket = -1;
1852 struct sockaddr_in dataSocketAddress;
1854 p = lpszResponseBuffer+4; /* skip status code */
1856 /* do a very strict check; we can improve that later. */
1858 if (strncmp(p, "Entering Passive Mode", 21))
1860 ERR("unknown response '%.*s', aborting\n", 21, p);
1861 goto lend;
1863 p += 21; /* skip string */
1864 if ((*p++ != ' ') || (*p++ != '('))
1866 ERR("unknown response format, aborting\n");
1867 goto lend;
1870 if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3],
1871 &f[4], &f[5]) != 6)
1873 ERR("unknown response address format '%s', aborting\n", p);
1874 goto lend;
1876 for (i=0; i < 6; i++)
1877 f[i] = f[i] & 0xff;
1879 dataSocketAddress = lpwfs->socketAddress;
1880 pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr);
1881 pPort = (char *)&(dataSocketAddress.sin_port);
1882 pAddr[0] = f[0];
1883 pAddr[1] = f[1];
1884 pAddr[2] = f[2];
1885 pAddr[3] = f[3];
1886 pPort[0] = f[4];
1887 pPort[1] = f[5];
1889 nsocket = socket(AF_INET,SOCK_STREAM,0);
1890 if (nsocket == -1)
1891 goto lend;
1893 if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress)))
1895 ERR("can't connect passive FTP data port.\n");
1896 goto lend;
1898 lpwfs->pasvSocket = nsocket;
1899 bSuccess = TRUE;
1901 else
1902 FTP_SetResponseError(nResCode);
1905 lend:
1906 return bSuccess;
1910 BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONA lpwfs)
1912 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1914 if (!FTP_DoPassive(lpwfs))
1915 return FALSE;
1917 else
1919 if (!FTP_SendPort(lpwfs))
1920 return FALSE;
1922 return TRUE;
1926 /***********************************************************************
1927 * FTP_GetDataSocket (internal)
1929 * Either accepts an incoming data socket connection from the server
1930 * or just returns the already opened socket after a PASV command
1931 * in case of passive FTP.
1934 * RETURNS
1935 * TRUE on success
1936 * FALSE on failure
1939 BOOL FTP_GetDataSocket(LPWININETFTPSESSIONA lpwfs, LPINT nDataSocket)
1941 struct sockaddr_in saddr;
1942 size_t addrlen = sizeof(struct sockaddr);
1944 TRACE("\n");
1945 if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
1947 *nDataSocket = lpwfs->pasvSocket;
1949 else
1951 *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen);
1952 close(lpwfs->lstnSocket);
1953 lpwfs->lstnSocket = -1;
1955 return *nDataSocket != -1;
1959 /***********************************************************************
1960 * FTP_SendData (internal)
1962 * Send data to the server
1964 * RETURNS
1965 * TRUE on success
1966 * FALSE on failure
1969 BOOL FTP_SendData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, HANDLE hFile)
1971 BY_HANDLE_FILE_INFORMATION fi;
1972 DWORD nBytesRead = 0;
1973 DWORD nBytesSent = 0;
1974 DWORD nTotalSent = 0;
1975 DWORD nBytesToSend, nLen, nRC = 1;
1976 time_t s_long_time, e_long_time;
1977 LONG nSeconds;
1978 CHAR *lpszBuffer;
1980 TRACE("\n");
1981 lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1982 memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE);
1984 /* Get the size of the file. */
1985 GetFileInformationByHandle(hFile, &fi);
1986 time(&s_long_time);
1990 nBytesToSend = nBytesRead - nBytesSent;
1992 if (nBytesToSend <= 0)
1994 /* Read data from file. */
1995 nBytesSent = 0;
1996 if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0))
1997 ERR("Failed reading from file\n");
1999 if (nBytesRead > 0)
2000 nBytesToSend = nBytesRead;
2001 else
2002 break;
2005 nLen = DATA_PACKET_SIZE < nBytesToSend ?
2006 DATA_PACKET_SIZE : nBytesToSend;
2007 nRC = send(nDataSocket, lpszBuffer, nLen, 0);
2009 if (nRC != -1)
2011 nBytesSent += nRC;
2012 nTotalSent += nRC;
2015 /* Do some computation to display the status. */
2016 time(&e_long_time);
2017 nSeconds = e_long_time - s_long_time;
2018 if( nSeconds / 60 > 0 )
2020 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n",
2021 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60,
2022 nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent );
2024 else
2026 TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n",
2027 nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds,
2028 (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent);
2030 } while (nRC != -1);
2032 TRACE("file transfer complete!\n");
2034 if(lpszBuffer != NULL)
2035 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2037 return nTotalSent;
2041 /***********************************************************************
2042 * FTP_SendRetrieve (internal)
2044 * Send request to retrieve a file
2046 * RETURNS
2047 * Number of bytes to be received on success
2048 * 0 on failure
2051 DWORD FTP_SendRetrieve(LPWININETFTPSESSIONA lpwfs, LPCSTR lpszRemoteFile, DWORD dwType)
2053 INT nResCode;
2054 DWORD nResult = 0;
2056 TRACE("\n");
2057 if (!FTP_InitListenSocket(lpwfs))
2058 goto lend;
2060 if (!FTP_SendType(lpwfs, dwType))
2061 goto lend;
2063 if (!FTP_SendPortOrPasv(lpwfs))
2064 goto lend;
2066 if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0))
2067 goto lend;
2069 nResCode = FTP_ReceiveResponse(lpwfs->sndSocket, INTERNET_GetResponseBuffer(),
2070 MAX_REPLY_LEN, 0, 0, 0);
2071 if (nResCode)
2073 if (nResCode == 125 || nResCode == 150)
2075 /* Parse size of data to be retrieved */
2076 INT i, sizepos = -1;
2077 LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer();
2078 for (i = strlen(lpszResponseBuffer) - 1; i >= 0; i--)
2080 if ('(' == lpszResponseBuffer[i])
2082 sizepos = i;
2083 break;
2087 if (sizepos >= 0)
2089 nResult = atol(&lpszResponseBuffer[sizepos+1]);
2090 TRACE("Waiting to receive %ld bytes\n", nResult);
2095 lend:
2096 if (0 == nResult && lpwfs->lstnSocket != -1)
2098 close(lpwfs->lstnSocket);
2099 lpwfs->lstnSocket = -1;
2102 return nResult;
2106 /***********************************************************************
2107 * FTP_RetrieveData (internal)
2109 * Retrieve data from server
2111 * RETURNS
2112 * TRUE on success
2113 * FALSE on failure
2116 BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONA lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile)
2118 DWORD nBytesWritten;
2119 DWORD nBytesReceived = 0;
2120 INT nRC = 0;
2121 CHAR *lpszBuffer;
2123 TRACE("\n");
2125 if (INVALID_HANDLE_VALUE == hFile)
2126 return FALSE;
2128 lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE);
2129 if (NULL == lpszBuffer)
2131 INTERNET_SetLastError(ERROR_OUTOFMEMORY);
2132 return FALSE;
2135 while (nBytesReceived < nBytes && nRC != -1)
2137 nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0);
2138 if (nRC != -1)
2140 /* other side closed socket. */
2141 if (nRC == 0)
2142 goto recv_end;
2143 WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL);
2144 nBytesReceived += nRC;
2147 TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes,
2148 nBytesReceived * 100 / nBytes);
2151 TRACE("Data transfer complete\n");
2152 if (NULL != lpszBuffer)
2153 HeapFree(GetProcessHeap(), 0, lpszBuffer);
2155 recv_end:
2156 return (nRC != -1);
2160 /***********************************************************************
2161 * FTP_CloseSessionHandle (internal)
2163 * Deallocate session handle
2165 * RETURNS
2166 * TRUE on success
2167 * FALSE on failure
2170 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs)
2172 if (lpwfs->sndSocket != -1)
2173 close(lpwfs->sndSocket);
2175 if (lpwfs->lstnSocket != -1)
2176 close(lpwfs->lstnSocket);
2178 if (lpwfs->lpszPassword)
2179 HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
2181 if (lpwfs->lpszUserName)
2182 HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
2184 HeapFree(GetProcessHeap(), 0, lpwfs);
2186 return TRUE;
2190 /***********************************************************************
2191 * FTP_CloseSessionHandle (internal)
2193 * Deallocate session handle
2195 * RETURNS
2196 * TRUE on success
2197 * FALSE on failure
2200 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn)
2202 INT i;
2204 TRACE("\n");
2206 for (i = 0; i < lpwfn->size; i++)
2208 if (NULL != lpwfn->lpafp[i].lpszName)
2209 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName);
2212 HeapFree(GetProcessHeap(), 0, lpwfn->lpafp);
2213 HeapFree(GetProcessHeap(), 0, lpwfn);
2215 return TRUE;
2219 /***********************************************************************
2220 * FTP_ReceiveFileList (internal)
2222 * Read file list from server
2224 * RETURNS
2225 * Handle to file list on success
2226 * NULL on failure
2229 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket,
2230 LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext)
2232 DWORD dwSize = 0;
2233 LPFILEPROPERTIESA lpafp = NULL;
2234 LPWININETFINDNEXTA lpwfn = NULL;
2236 TRACE("\n");
2238 if (FTP_ParseDirectory(lpwfs, nSocket, &lpafp, &dwSize))
2240 FTP_ConvertFileProp(lpafp, lpFindFileData);
2242 lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTA));
2243 if (NULL != lpwfn)
2245 lpwfn->hdr.htype = WH_HFINDNEXT;
2246 lpwfn->hdr.lpwhparent = (LPWININETHANDLEHEADER)lpwfs;
2247 lpwfn->hdr.dwContext = dwContext;
2248 lpwfn->index = 1; /* Next index is 1 since we return index 0 */
2249 lpwfn->size = dwSize;
2250 lpwfn->lpafp = lpafp;
2254 TRACE("Matched %ld files\n", dwSize);
2255 return (HINTERNET)lpwfn;
2259 /***********************************************************************
2260 * FTP_ConvertFileProp (internal)
2262 * Converts FILEPROPERTIESA struct to WIN32_FIND_DATAA
2264 * RETURNS
2265 * TRUE on success
2266 * FALSE on failure
2269 BOOL FTP_ConvertFileProp(LPFILEPROPERTIESA lpafp, LPWIN32_FIND_DATAA lpFindFileData)
2271 BOOL bSuccess = FALSE;
2273 ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
2275 if (lpafp)
2277 DWORD access = mktime(&lpafp->tmLastModified);
2279 /* Not all fields are filled in */
2280 lpFindFileData->ftLastAccessTime.dwHighDateTime = HIWORD(access);
2281 lpFindFileData->ftLastAccessTime.dwLowDateTime = LOWORD(access);
2282 lpFindFileData->nFileSizeHigh = HIWORD(lpafp->nSize);
2283 lpFindFileData->nFileSizeLow = LOWORD(lpafp->nSize);
2285 if (lpafp->bIsDirectory)
2286 lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
2288 if (lpafp->lpszName)
2289 strncpy(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH);
2291 bSuccess = TRUE;
2294 return bSuccess;
2298 /***********************************************************************
2299 * FTP_ParseDirectory (internal)
2301 * Parse string of directory information
2303 * RETURNS
2304 * TRUE on success
2305 * FALSE on failure
2307 * FIXME: - This function needs serious clea-up
2308 * - We should consider both UNIX and NT list formats
2310 #define MAX_MONTH_LEN 10
2311 #define MIN_LEN_DIR_ENTRY 15
2313 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp)
2316 * <Permissions> <NoLinks> <owner> <group> <size> <date> <time or year> <filename>
2318 * For instance:
2319 * drwx--s--- 2 pcarrier ens 512 Sep 28 1995 pcarrier
2321 CHAR* pszMinutes;
2322 CHAR* pszHour;
2323 time_t aTime;
2324 struct tm* apTM;
2325 CHAR pszMonth[MAX_MONTH_LEN];
2326 CHAR* pszMatch;
2327 BOOL bSuccess = TRUE;
2328 DWORD nBufLen = MAX_REPLY_LEN;
2329 LPFILEPROPERTIESA curFileProp = NULL;
2330 CHAR* pszLine = NULL;
2331 CHAR* pszToken = NULL;
2332 INT nTokenToSkip = 3;
2333 INT nCount = 0;
2334 INT nSeconds = 0;
2335 INT nMinutes = 0;
2336 INT nHour = 0;
2337 INT nDay = 0;
2338 INT nMonth = 0;
2339 INT nYear = 0;
2340 INT sizeFilePropArray = 20;
2341 INT indexFilePropArray = 0;
2343 TRACE("\n");
2345 /* Allocate intial file properties array */
2346 *lpafp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(FILEPROPERTIESA)*(sizeFilePropArray));
2347 if (NULL == lpafp)
2349 bSuccess = FALSE;
2350 goto lend;
2353 while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
2355 if (sizeFilePropArray <= indexFilePropArray)
2357 LPFILEPROPERTIESA tmpafp;
2359 sizeFilePropArray *= 2;
2360 tmpafp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *lpafp,
2361 sizeof(FILEPROPERTIESA)*sizeFilePropArray);
2362 if (NULL == tmpafp)
2364 bSuccess = FALSE;
2365 goto lend;
2368 *lpafp = tmpafp;
2371 curFileProp = &((*lpafp)[indexFilePropArray]);
2373 /* First Parse the permissions. */
2374 pszToken = strtok(pszLine, " \t" );
2376 /* HACK! If this is not a file listing skip the line */
2377 if (!pszToken || 10 != strlen(pszToken) || nBufLen <= MIN_LEN_DIR_ENTRY)
2379 nBufLen = MAX_REPLY_LEN;
2380 continue;
2383 FTP_ParsePermission(pszToken, curFileProp);
2385 nTokenToSkip = 3;
2386 nCount = 0;
2389 pszToken = strtok( NULL, " \t" );
2390 nCount++;
2391 } while( nCount <= nTokenToSkip );
2393 /* Store the size of the file in the param list. */
2394 TRACE("nSize-> %s\n", pszToken);
2395 if (pszToken != NULL)
2396 curFileProp->nSize = atol(pszToken);
2398 /* Parse last modified time. */
2399 nSeconds = 0;
2400 nMinutes = 0;
2401 nHour = 0;
2402 nDay = 0;
2403 nMonth = 0;
2404 nYear = 0;
2406 pszToken = strtok( NULL, " \t" );
2407 strncpy(pszMonth, pszToken, MAX_MONTH_LEN);
2408 CharUpperA(pszMonth);
2409 pszMatch = strstr(szMonths, pszMonth);
2410 if( pszMatch != NULL )
2411 nMonth = (pszMatch - szMonths) / 3;
2413 pszToken = strtok(NULL, " \t");
2414 TRACE("nDay -> %s\n", pszToken);
2415 if (pszToken != NULL)
2416 nDay = atoi(pszToken);
2418 pszToken = strtok(NULL, " \t");
2419 pszMinutes = strchr(pszToken, ':');
2420 if( pszMinutes != NULL )
2422 pszMinutes++;
2423 nMinutes = atoi(pszMinutes);
2424 pszHour = pszMinutes - 3;
2425 if (pszHour != NULL)
2426 nHour = atoi(pszHour);
2427 time(&aTime);
2428 apTM = localtime( &aTime );
2429 nYear = apTM->tm_year;
2431 else
2433 nYear = atoi(pszToken);
2434 nYear -= 1900;
2435 nHour = 12;
2438 curFileProp->tmLastModified.tm_sec = nSeconds;
2439 curFileProp->tmLastModified.tm_min = nMinutes;
2440 curFileProp->tmLastModified.tm_hour = nHour;
2441 curFileProp->tmLastModified.tm_mday = nDay;
2442 curFileProp->tmLastModified.tm_mon = nMonth;
2443 curFileProp->tmLastModified.tm_year = nYear;
2445 pszToken = strtok(NULL, " \t");
2446 if(pszToken != NULL)
2448 curFileProp->lpszName = FTP_strdup(pszToken);
2449 TRACE(": %s\n", curFileProp->lpszName);
2452 nBufLen = MAX_REPLY_LEN;
2453 indexFilePropArray++;
2456 if (bSuccess && indexFilePropArray)
2458 if (indexFilePropArray < sizeFilePropArray - 1)
2460 LPFILEPROPERTIESA tmpafp;
2462 tmpafp = HeapReAlloc(GetProcessHeap(), 0, *lpafp,
2463 sizeof(FILEPROPERTIESA)*indexFilePropArray);
2464 if (NULL == tmpafp)
2465 *lpafp = tmpafp;
2467 *dwfp = indexFilePropArray;
2469 else
2471 HeapFree(GetProcessHeap(), 0, *lpafp);
2472 INTERNET_SetLastError(ERROR_NO_MORE_FILES);
2473 bSuccess = FALSE;
2476 lend:
2477 return bSuccess;
2481 /***********************************************************************
2482 * FTP_ParsePermission (internal)
2484 * Parse permission string of directory information
2486 * RETURNS
2487 * TRUE on success
2488 * FALSE on failure
2491 BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESA lpfp)
2493 BOOL bSuccess = TRUE;
2494 unsigned short nPermission = 0;
2495 INT nPos = 1;
2496 INT nLast = 9;
2498 TRACE("\n");
2499 if ((*lpszPermission != 'd') && (*lpszPermission != '-') && (*lpszPermission != 'l'))
2501 bSuccess = FALSE;
2502 return bSuccess;
2505 lpfp->bIsDirectory = (*lpszPermission == 'd');
2508 switch (nPos)
2510 case 1:
2511 nPermission |= (*(lpszPermission+1) == 'r' ? 1 : 0) << 8;
2512 break;
2513 case 2:
2514 nPermission |= (*(lpszPermission+2) == 'w' ? 1 : 0) << 7;
2515 break;
2516 case 3:
2517 nPermission |= (*(lpszPermission+3) == 'x' ? 1 : 0) << 6;
2518 break;
2519 case 4:
2520 nPermission |= (*(lpszPermission+4) == 'r' ? 1 : 0) << 5;
2521 break;
2522 case 5:
2523 nPermission |= (*(lpszPermission+5) == 'w' ? 1 : 0) << 4;
2524 break;
2525 case 6:
2526 nPermission |= (*(lpszPermission+6) == 'x' ? 1 : 0) << 3;
2527 break;
2528 case 7:
2529 nPermission |= (*(lpszPermission+7) == 'r' ? 1 : 0) << 2;
2530 break;
2531 case 8:
2532 nPermission |= (*(lpszPermission+8) == 'w' ? 1 : 0) << 1;
2533 break;
2534 case 9:
2535 nPermission |= (*(lpszPermission+9) == 'x' ? 1 : 0);
2536 break;
2538 nPos++;
2539 }while (nPos <= nLast);
2541 lpfp->permissions = nPermission;
2542 return bSuccess;
2546 /***********************************************************************
2547 * FTP_SetResponseError (internal)
2549 * Set the appropriate error code for a given response from the server
2551 * RETURNS
2554 DWORD FTP_SetResponseError(DWORD dwResponse)
2556 DWORD dwCode = 0;
2558 switch(dwResponse)
2560 case 421: /* Service not available - Server may be shutting down. */
2561 dwCode = ERROR_INTERNET_TIMEOUT;
2562 break;
2564 case 425: /* Cannot open data connection. */
2565 dwCode = ERROR_INTERNET_CANNOT_CONNECT;
2566 break;
2568 case 426: /* Connection closed, transer aborted. */
2569 dwCode = ERROR_INTERNET_CONNECTION_ABORTED;
2570 break;
2572 case 500: /* Syntax error. Command unrecognized. */
2573 case 501: /* Syntax error. Error in parameters or arguments. */
2574 dwCode = ERROR_INTERNET_INCORRECT_FORMAT;
2575 break;
2577 case 530: /* Not logged in. Login incorrect. */
2578 dwCode = ERROR_INTERNET_LOGIN_FAILURE;
2579 break;
2581 case 550: /* File action not taken. File not found or no access. */
2582 dwCode = ERROR_INTERNET_ITEM_NOT_FOUND;
2583 break;
2585 case 450: /* File action not taken. File may be busy. */
2586 case 451: /* Action aborted. Server error. */
2587 case 452: /* Action not taken. Insufficient storage space on server. */
2588 case 502: /* Command not implemented. */
2589 case 503: /* Bad sequence of command. */
2590 case 504: /* Command not implemented for that parameter. */
2591 case 532: /* Need account for storing files */
2592 case 551: /* Requested action aborted. Page type unknown */
2593 case 552: /* Action aborted. Exceeded storage allocation */
2594 case 553: /* Action not taken. File name not allowed. */
2596 default:
2597 dwCode = ERROR_INTERNET_INTERNAL_ERROR;
2598 break;
2601 INTERNET_SetLastError(dwCode);
2602 return dwCode;