diff --git a/reactos/lib/wininet/.cvsignore b/reactos/lib/wininet/.cvsignore new file mode 100644 index 00000000000..cfaac75c3f5 --- /dev/null +++ b/reactos/lib/wininet/.cvsignore @@ -0,0 +1,5 @@ +Makefile +rsrc.res +version.res +wininet.dll.dbg.c +wininet.spec.def diff --git a/reactos/lib/wininet/Makefile.in b/reactos/lib/wininet/Makefile.in new file mode 100644 index 00000000000..cea2a82a299 --- /dev/null +++ b/reactos/lib/wininet/Makefile.in @@ -0,0 +1,30 @@ +EXTRADEFS = -D_WINX32_ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = wininet.dll +IMPORTS = mpr shlwapi shell32 user32 advapi32 kernel32 ntdll +EXTRALIBS = $(LIBUNICODE) @SOCKETLIBS@ + +C_SRCS = \ + cookie.c \ + dialogs.c \ + ftp.c \ + gopher.c \ + http.c \ + internet.c \ + netconnection.c \ + urlcache.c \ + utility.c \ + wininet_main.c + +RC_SRCS = \ + rsrc.rc \ + version.rc + +SUBDIRS = tests + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/lib/wininet/cookie.c b/reactos/lib/wininet/cookie.c new file mode 100644 index 00000000000..80bd8ea58b0 --- /dev/null +++ b/reactos/lib/wininet/cookie.c @@ -0,0 +1,501 @@ +/* + * Wininet - cookie handling stuff + * + * Copyright 2002 TransGaming Technologies Inc. + * + * David Hammerton + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif + +#include "windef.h" +#include "winbase.h" +#include "wininet.h" +#include "winerror.h" + +#include "wine/debug.h" +#include "internet.h" + +#include "wine/list.h" + +#define RESPONSE_TIMEOUT 30 /* FROM internet.c */ + + +WINE_DEFAULT_DEBUG_CHANNEL(wininet); + +/* FIXME + * Cookies are currently memory only. + * Cookies are NOT THREAD SAFE + * Cookies could use A LOT OF MEMORY. We need some kind of memory management here! + * Cookies should care about the expiry time + */ + +typedef struct _cookie_domain cookie_domain; +typedef struct _cookie cookie; + +struct _cookie +{ + struct list entry; + + struct _cookie_domain *parent; + + LPWSTR lpCookieName; + LPWSTR lpCookieData; + time_t expiry; /* FIXME: not used */ +}; + +struct _cookie_domain +{ + struct list entry; + + LPWSTR lpCookieDomain; + LPWSTR lpCookiePath; + struct list cookie_list; +}; + +static struct list domain_list = LIST_INIT(domain_list); + +static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data); +static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName); +static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain); +static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path); +static void COOKIE_deleteDomain(cookie_domain *deadDomain); + + +/* adds a cookie to the domain */ +static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data) +{ + cookie *newCookie = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie)); + + list_init(&newCookie->entry); + newCookie->lpCookieName = NULL; + newCookie->lpCookieData = NULL; + + if (name) + { + newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1)*sizeof(WCHAR)); + lstrcpyW(newCookie->lpCookieName, name); + } + if (data) + { + newCookie->lpCookieData = HeapAlloc(GetProcessHeap(), 0, (strlenW(data) + 1)*sizeof(WCHAR)); + lstrcpyW(newCookie->lpCookieData, data); + } + + TRACE("added cookie %p (data is %s)\n", newCookie, debugstr_w(data) ); + + list_add_tail(&domain->cookie_list, &newCookie->entry); + newCookie->parent = domain; + return newCookie; +} + + +/* finds a cookie in the domain matching the cookie name */ +static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName) +{ + struct list * cursor; + TRACE("(%p, %s)\n", domain, debugstr_w(lpszCookieName)); + + LIST_FOR_EACH(cursor, &domain->cookie_list) + { + cookie *searchCookie = LIST_ENTRY(cursor, cookie, entry); + BOOL candidate = TRUE; + if (candidate && lpszCookieName) + { + if (candidate && !searchCookie->lpCookieName) + candidate = FALSE; + if (candidate && strcmpW(lpszCookieName, searchCookie->lpCookieName) != 0) + candidate = FALSE; + } + if (candidate) + return searchCookie; + } + return NULL; +} + +/* removes a cookie from the list, if its the last cookie we also remove the domain */ +static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain) +{ + HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieName); + HeapFree(GetProcessHeap(), 0, deadCookie->lpCookieData); + list_remove(&deadCookie->entry); + + /* special case: last cookie, lets remove the domain to save memory */ + if (list_empty(&deadCookie->parent->cookie_list) && deleteDomain) + COOKIE_deleteDomain(deadCookie->parent); + HeapFree(GetProcessHeap(), 0, deadCookie); +} + +/* allocates a domain and adds it to the end */ +static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path) +{ + cookie_domain *newDomain = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie_domain)); + + list_init(&newDomain->entry); + list_init(&newDomain->cookie_list); + newDomain->lpCookieDomain = NULL; + newDomain->lpCookiePath = NULL; + + if (domain) + { + newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, (strlenW(domain) + 1)*sizeof(WCHAR)); + strcpyW(newDomain->lpCookieDomain, domain); + } + if (path) + { + newDomain->lpCookiePath = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1)*sizeof(WCHAR)); + lstrcpyW(newDomain->lpCookiePath, path); + } + + list_add_tail(&domain_list, &newDomain->entry); + + TRACE("Adding domain: %p\n", newDomain); + return newDomain; +} + +static void COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostNameLen, LPWSTR path, int pathLen) +{ + URL_COMPONENTSW UrlComponents; + + UrlComponents.lpszExtraInfo = NULL; + UrlComponents.lpszPassword = NULL; + UrlComponents.lpszScheme = NULL; + UrlComponents.lpszUrlPath = path; + UrlComponents.lpszUserName = NULL; + UrlComponents.lpszHostName = hostName; + UrlComponents.dwHostNameLength = hostNameLen; + UrlComponents.dwUrlPathLength = pathLen; + + InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents); +} + +/* match a domain. domain must match if the domain is not NULL. path must match if the path is not NULL */ +static BOOL COOKIE_matchDomain(LPCWSTR lpszCookieDomain, LPCWSTR lpszCookiePath, + cookie_domain *searchDomain, BOOL allow_partial) +{ + TRACE("searching on domain %p\n", searchDomain); + if (lpszCookieDomain) + { + if (!searchDomain->lpCookieDomain) + return FALSE; + + TRACE("comparing domain %s with %s\n", + debugstr_w(lpszCookieDomain), + debugstr_w(searchDomain->lpCookieDomain)); + + if (allow_partial && !strstrW(lpszCookieDomain, searchDomain->lpCookieDomain)) + return FALSE; + else if (!allow_partial && lstrcmpW(lpszCookieDomain, searchDomain->lpCookieDomain) != 0) + return FALSE; + } + if (lpszCookiePath) + { + TRACE("comparing paths: %s with %s\n", debugstr_w(lpszCookiePath), debugstr_w(searchDomain->lpCookiePath)); + if (!searchDomain->lpCookiePath) + return FALSE; + if (strcmpW(lpszCookiePath, searchDomain->lpCookiePath)) + return FALSE; + } + return TRUE; +} + +/* remove a domain from the list and delete it */ +static void COOKIE_deleteDomain(cookie_domain *deadDomain) +{ + struct list * cursor; + while ((cursor = list_tail(&deadDomain->cookie_list))) + { + COOKIE_deleteCookie(LIST_ENTRY(cursor, cookie, entry), FALSE); + list_remove(cursor); + } + + HeapFree(GetProcessHeap(), 0, deadDomain->lpCookieDomain); + HeapFree(GetProcessHeap(), 0, deadDomain->lpCookiePath); + + list_remove(&deadDomain->entry); + + HeapFree(GetProcessHeap(), 0, deadDomain); +} + +/*********************************************************************** + * InternetGetCookieW (WININET.@) + * + * Retrieve cookie from the specified url + * + * It should be noted that on windows the lpszCookieName parameter is "not implemented". + * So it won't be implemented here. + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, + LPWSTR lpCookieData, LPDWORD lpdwSize) +{ + struct list * cursor; + int cnt = 0, domain_count = 0; + int cookie_count = 0; + WCHAR hostName[2048], path[2048]; + + TRACE("(%s, %s, %p, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName), + lpCookieData, lpdwSize); + + COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0])); + + LIST_FOR_EACH(cursor, &domain_list) + { + cookie_domain *cookiesDomain = LIST_ENTRY(cursor, cookie_domain, entry); + if (COOKIE_matchDomain(hostName, NULL /* FIXME: path */, cookiesDomain, TRUE)) + { + struct list * cursor; + domain_count++; + TRACE("found domain %p\n", cookiesDomain); + + LIST_FOR_EACH(cursor, &cookiesDomain->cookie_list) + { + cookie *thisCookie = LIST_ENTRY(cursor, cookie, entry); + if (lpCookieData == NULL) /* return the size of the buffer required to lpdwSize */ + { + if (cookie_count != 0) + cnt += 2; /* '; ' */ + cnt += strlenW(thisCookie->lpCookieName); + cnt += 1; /* = */ + cnt += strlenW(thisCookie->lpCookieData); + } + else + { + static const WCHAR szsc[] = { ';',' ',0 }; + static const WCHAR szpseq[] = { '%','s','=','%','s',0 }; + if (cookie_count != 0) + cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szsc); + cnt += snprintfW(lpCookieData + cnt, *lpdwSize - cnt, szpseq, + thisCookie->lpCookieName, + thisCookie->lpCookieData); + TRACE("Cookie: %s=%s\n", debugstr_w(thisCookie->lpCookieName), debugstr_w(thisCookie->lpCookieData)); + } + cookie_count++; + } + } + } + if (lpCookieData == NULL) + { + cnt += 1; /* NULL */ + *lpdwSize = cnt*sizeof(WCHAR); + TRACE("returning\n"); + return TRUE; + } + + if (!domain_count) + return FALSE; + + *lpdwSize = (cnt + 1)*sizeof(WCHAR); + + TRACE("Returning %i (from %i domains): %s\n", cnt, domain_count, + debugstr_w(lpCookieData)); + + return (cnt ? TRUE : FALSE); +} + + +/*********************************************************************** + * InternetGetCookieA (WININET.@) + * + * Retrieve cookie from the specified url + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName, + LPSTR lpCookieData, LPDWORD lpdwSize) +{ + DWORD len; + LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL; + BOOL r; + + TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName), + lpCookieData); + + if( lpszUrl ) + { + len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 ); + szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len ); + } + + if( lpszCookieName ) + { + len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 ); + szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len ); + } + + r = InternetGetCookieW( szUrl, szCookieName, NULL, &len ); + if( r ) + { + szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + if( !szCookieData ) + return FALSE; + + r = InternetGetCookieW( szUrl, szCookieName, szCookieData, &len ); + + *lpdwSize = WideCharToMultiByte( CP_ACP, 0, szCookieData, len, + lpCookieData, *lpdwSize, NULL, NULL ); + } + + HeapFree( GetProcessHeap(), 0, szCookieData ); + HeapFree( GetProcessHeap(), 0, szCookieName ); + HeapFree( GetProcessHeap(), 0, szUrl ); + + return r; +} + + +/*********************************************************************** + * InternetSetCookieW (WININET.@) + * + * Sets cookie for the specified url + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName, + LPCWSTR lpCookieData) +{ + cookie_domain *thisCookieDomain = NULL; + cookie *thisCookie; + WCHAR hostName[2048], path[2048]; + struct list * cursor; + + TRACE("(%s,%s,%s)\n", debugstr_w(lpszUrl), + debugstr_w(lpszCookieName), debugstr_w(lpCookieData)); + + if (!lpCookieData || !strlenW(lpCookieData)) + { + TRACE("no cookie data, not adding\n"); + return FALSE; + } + if (!lpszCookieName) + { + /* some apps (or is it us??) try to add a cookie with no cookie name, but + * the cookie data in the form of name=data. */ + /* FIXME, probably a bug here, for now I don't care */ + WCHAR *ourCookieName, *ourCookieData; + int ourCookieNameSize; + BOOL ret; + + if (!(ourCookieData = strchrW(lpCookieData, '='))) + { + TRACE("something terribly wrong with cookie data %s\n", + debugstr_w(ourCookieData)); + return FALSE; + } + ourCookieNameSize = ourCookieData - lpCookieData; + ourCookieData += 1; + ourCookieName = HeapAlloc(GetProcessHeap(), 0, + (ourCookieNameSize + 1)*sizeof(WCHAR)); + memcpy(ourCookieName, ourCookieData, ourCookieNameSize * sizeof(WCHAR)); + ourCookieName[ourCookieNameSize] = '\0'; + TRACE("setting (hacked) cookie of %s, %s\n", + debugstr_w(ourCookieName), debugstr_w(ourCookieData)); + ret = InternetSetCookieW(lpszUrl, ourCookieName, ourCookieData); + HeapFree(GetProcessHeap(), 0, ourCookieName); + return ret; + } + + COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0])); + + LIST_FOR_EACH(cursor, &domain_list) + { + thisCookieDomain = LIST_ENTRY(cursor, cookie_domain, entry); + if (COOKIE_matchDomain(hostName, NULL /* FIXME: path */, thisCookieDomain, FALSE)) + break; + thisCookieDomain = NULL; + } + if (!thisCookieDomain) + thisCookieDomain = COOKIE_addDomain(hostName, path); + + if ((thisCookie = COOKIE_findCookie(thisCookieDomain, lpszCookieName))) + COOKIE_deleteCookie(thisCookie, FALSE); + + thisCookie = COOKIE_addCookie(thisCookieDomain, lpszCookieName, lpCookieData); + return TRUE; +} + + +/*********************************************************************** + * InternetSetCookieA (WININET.@) + * + * Sets cookie for the specified url + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName, + LPCSTR lpCookieData) +{ + DWORD len; + LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL; + BOOL r; + + TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl), + debugstr_a(lpszCookieName), debugstr_a(lpCookieData)); + + if( lpszUrl ) + { + len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 ); + szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len ); + } + + if( lpszCookieName ) + { + len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 ); + szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len ); + } + + if( lpCookieData ) + { + len = MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, NULL, 0 ); + szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, szCookieData, len ); + } + + r = InternetSetCookieW( szUrl, szCookieName, szCookieData ); + + HeapFree( GetProcessHeap(), 0, szCookieData ); + HeapFree( GetProcessHeap(), 0, szCookieName ); + HeapFree( GetProcessHeap(), 0, szUrl ); + + return r; +} diff --git a/reactos/lib/wininet/dialogs.c b/reactos/lib/wininet/dialogs.c new file mode 100644 index 00000000000..7fc6207e2b2 --- /dev/null +++ b/reactos/lib/wininet/dialogs.c @@ -0,0 +1,384 @@ +/* + * Wininet + * + * Copyright 2003 Mike McCormack for CodeWeavers Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "wininet.h" +#include "winnetwk.h" +#include "winnls.h" +#include "wine/debug.h" +#include "winerror.h" +#define NO_SHLWAPI_STREAM +#include "shlwapi.h" + +#include "internet.h" + +#include "wine/unicode.h" + +#include "resource.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wininet); + +struct WININET_ErrorDlgParams +{ + HWND hWnd; + HINTERNET hRequest; + DWORD dwError; + DWORD dwFlags; + LPVOID* lppvData; +}; + +/*********************************************************************** + * WININET_GetProxyServer + * + * Determine the name of the proxy server the request is using + */ +static BOOL WININET_GetProxyServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz ) +{ + LPWININETHTTPREQW lpwhr; + LPWININETHTTPSESSIONW lpwhs = NULL; + LPWININETAPPINFOW hIC = NULL; + LPWSTR p; + + lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest ); + if (NULL == lpwhr) + return FALSE; + + lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent; + if (NULL == lpwhs) + return FALSE; + + hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent; + if (NULL == hIC) + return FALSE; + + lstrcpynW(szBuf, hIC->lpszProxy, sz); + + /* FIXME: perhaps it would be better to use InternetCrackUrl here */ + p = strchrW(szBuf, ':'); + if(*p) + *p = 0; + + return TRUE; +} + +/*********************************************************************** + * WININET_GetAuthRealm + * + * Determine the name of the (basic) Authentication realm + */ +static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz ) +{ + LPWSTR p, q; + DWORD index; + static const WCHAR szRealm[] = { 'r','e','a','l','m','=',0 }; + + /* extract the Realm from the proxy response and show it */ + index = 0; + if( !HttpQueryInfoW( hRequest, HTTP_QUERY_PROXY_AUTHENTICATE, + szBuf, &sz, &index) ) + return FALSE; + + /* + * FIXME: maybe we should check that we're + * dealing with 'Basic' Authentication + */ + p = strchrW( szBuf, ' ' ); + if( p && !strncmpW( p+1, szRealm, strlenW(szRealm) ) ) + { + /* remove quotes */ + p += 7; + if( *p == '"' ) + { + p++; + q = strrchrW( p, '"' ); + if( q ) + *q = 0; + } + } + + strcpyW( szBuf, p ); + + return TRUE; +} + +/*********************************************************************** + * WININET_GetSetPassword + */ +static BOOL WININET_GetSetPassword( HWND hdlg, LPCWSTR szServer, + LPCWSTR szRealm, BOOL bSet ) +{ + WCHAR szResource[0x80], szUserPass[0x40]; + LPWSTR p; + HWND hUserItem, hPassItem; + DWORD r, dwMagic = 19; + UINT r_len, u_len; + WORD sz; + static const WCHAR szColon[] = { ':',0 }; + static const WCHAR szbs[] = { '/', 0 }; + + hUserItem = GetDlgItem( hdlg, IDC_USERNAME ); + hPassItem = GetDlgItem( hdlg, IDC_PASSWORD ); + + /* now try fetch the username and password */ + lstrcpyW( szResource, szServer); + lstrcatW( szResource, szbs); + lstrcatW( szResource, szRealm); + + /* + * WNetCachePassword is only concerned with the length + * of the data stored (which we tell it) and it does + * not use strlen() internally so we can add WCHAR data + * instead of ASCII data and get it back the same way. + */ + if( bSet ) + { + szUserPass[0] = 0; + GetWindowTextW( hUserItem, szUserPass, + (sizeof szUserPass-1)/sizeof(WCHAR) ); + lstrcatW(szUserPass, szColon); + u_len = strlenW( szUserPass ); + GetWindowTextW( hPassItem, szUserPass+u_len, + (sizeof szUserPass)/sizeof(WCHAR)-u_len ); + + r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR); + u_len = (strlenW( szUserPass ) + 1)*sizeof(WCHAR); + r = WNetCachePassword( (CHAR*)szResource, r_len, + (CHAR*)szUserPass, u_len, dwMagic, 0 ); + + return ( r == WN_SUCCESS ); + } + + sz = sizeof szUserPass; + r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR); + r = WNetGetCachedPassword( (CHAR*)szResource, r_len, + (CHAR*)szUserPass, &sz, dwMagic ); + if( r != WN_SUCCESS ) + return FALSE; + + p = strchrW( szUserPass, ':' ); + if( p ) + { + *p = 0; + SetWindowTextW( hUserItem, szUserPass ); + SetWindowTextW( hPassItem, p+1 ); + } + + return TRUE; +} + +/*********************************************************************** + * WININET_SetProxyAuthorization + */ +static BOOL WININET_SetProxyAuthorization( HINTERNET hRequest, + LPWSTR username, LPWSTR password ) +{ + LPWININETHTTPREQW lpwhr; + LPWININETHTTPSESSIONW lpwhs; + LPWININETAPPINFOW hIC; + LPWSTR p; + + lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest ); + if( !lpwhr ) + return FALSE; + + lpwhs = (LPWININETHTTPSESSIONW) lpwhr->hdr.lpwhparent; + if (NULL == lpwhs || lpwhs->hdr.htype != WH_HHTTPSESSION) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + hIC = (LPWININETAPPINFOW) lpwhs->hdr.lpwhparent; + + p = HeapAlloc( GetProcessHeap(), 0, (strlenW( username ) + 1)*sizeof(WCHAR) ); + if( !p ) + return FALSE; + + lstrcpyW( p, username ); + hIC->lpszProxyUsername = p; + + p = HeapAlloc( GetProcessHeap(), 0, (strlenW( password ) + 1)*sizeof(WCHAR) ); + if( !p ) + return FALSE; + + lstrcpyW( p, password ); + hIC->lpszProxyPassword = p; + + return TRUE; +} + +/*********************************************************************** + * WININET_ProxyPasswordDialog + */ +static INT_PTR WINAPI WININET_ProxyPasswordDialog( + HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + HWND hitem; + struct WININET_ErrorDlgParams *params; + WCHAR szRealm[0x80], szServer[0x80]; + + if( uMsg == WM_INITDIALOG ) + { + TRACE("WM_INITDIALOG (%08lx)\n", lParam); + + /* save the parameter list */ + params = (struct WININET_ErrorDlgParams*) lParam; + SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam ); + + /* extract the Realm from the proxy response and show it */ + if( WININET_GetAuthRealm( params->hRequest, + szRealm, sizeof szRealm/sizeof(WCHAR)) ) + { + hitem = GetDlgItem( hdlg, IDC_REALM ); + SetWindowTextW( hitem, szRealm ); + } + + /* extract the name of the proxy server */ + if( WININET_GetProxyServer( params->hRequest, + szServer, sizeof szServer/sizeof(WCHAR)) ) + { + hitem = GetDlgItem( hdlg, IDC_PROXY ); + SetWindowTextW( hitem, szServer ); + } + + WININET_GetSetPassword( hdlg, szServer, szRealm, FALSE ); + + return TRUE; + } + + params = (struct WININET_ErrorDlgParams*) + GetWindowLongPtrW( hdlg, GWLP_USERDATA ); + + switch( uMsg ) + { + case WM_COMMAND: + if( wParam == IDOK ) + { + WCHAR username[0x20], password[0x20]; + + username[0] = 0; + hitem = GetDlgItem( hdlg, IDC_USERNAME ); + if( hitem ) + GetWindowTextW( hitem, username, sizeof username/sizeof(WCHAR) ); + + password[0] = 0; + hitem = GetDlgItem( hdlg, IDC_PASSWORD ); + if( hitem ) + GetWindowTextW( hitem, password, sizeof password/sizeof(WCHAR) ); + + hitem = GetDlgItem( hdlg, IDC_SAVEPASSWORD ); + if( hitem && + SendMessageW( hitem, BM_GETSTATE, 0, 0 ) && + WININET_GetAuthRealm( params->hRequest, + szRealm, sizeof szRealm/sizeof(WCHAR)) && + WININET_GetProxyServer( params->hRequest, + szServer, sizeof szServer/sizeof(WCHAR)) ) + { + WININET_GetSetPassword( hdlg, szServer, szRealm, TRUE ); + } + WININET_SetProxyAuthorization( params->hRequest, username, password ); + + EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY ); + return TRUE; + } + if( wParam == IDCANCEL ) + { + EndDialog( hdlg, 0 ); + return TRUE; + } + break; + } + return FALSE; +} + +/*********************************************************************** + * WININET_GetConnectionStatus + */ +static INT WININET_GetConnectionStatus( HINTERNET hRequest ) +{ + WCHAR szStatus[0x20]; + DWORD sz, index, dwStatus; + + TRACE("%p\n", hRequest ); + + sz = sizeof szStatus; + index = 0; + if( !HttpQueryInfoW( hRequest, HTTP_QUERY_STATUS_CODE, + szStatus, &sz, &index)) + return -1; + dwStatus = atoiW( szStatus ); + + TRACE("request %p status = %ld\n", hRequest, dwStatus ); + + return dwStatus; +} + + +/*********************************************************************** + * InternetErrorDlg + */ +DWORD WINAPI InternetErrorDlg(HWND hWnd, HINTERNET hRequest, + DWORD dwError, DWORD dwFlags, LPVOID* lppvData) +{ + struct WININET_ErrorDlgParams params; + HMODULE hwininet = GetModuleHandleA( "wininet.dll" ); + INT dwStatus; + + TRACE("%p %p %ld %08lx %p\n", hWnd, hRequest, dwError, dwFlags, lppvData); + + params.hWnd = hWnd; + params.hRequest = hRequest; + params.dwError = dwError; + params.dwFlags = dwFlags; + params.lppvData = lppvData; + + switch( dwError ) + { + case ERROR_SUCCESS: + if( !(dwFlags & FLAGS_ERROR_UI_FILTER_FOR_ERRORS ) ) + return 0; + dwStatus = WININET_GetConnectionStatus( hRequest ); + if( HTTP_STATUS_PROXY_AUTH_REQ != dwStatus ) + return ERROR_SUCCESS; + return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ), + hWnd, WININET_ProxyPasswordDialog, (LPARAM) ¶ms ); + + case ERROR_INTERNET_INCORRECT_PASSWORD: + return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ), + hWnd, WININET_ProxyPasswordDialog, (LPARAM) ¶ms ); + + case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: + case ERROR_INTERNET_INVALID_CA: + case ERROR_INTERNET_POST_IS_NON_SECURE: + case ERROR_INTERNET_SEC_CERT_CN_INVALID: + case ERROR_INTERNET_SEC_CERT_DATE_INVALID: + FIXME("Need to display dialog for error %ld\n", dwError); + return ERROR_SUCCESS; + } + return ERROR_INVALID_PARAMETER; +} diff --git a/reactos/lib/wininet/ftp.c b/reactos/lib/wininet/ftp.c new file mode 100644 index 00000000000..77f16a0d075 --- /dev/null +++ b/reactos/lib/wininet/ftp.c @@ -0,0 +1,3141 @@ +/* + * WININET - Ftp implementation + * + * Copyright 1999 Corel Corporation + * Copyright 2004 Mike McCormack for CodeWeavers + * Copyright 2004 Kevin Koltzau + * + * Ulrich Czekalla + * Noureddine Jemmali + * + * Copyright 2000 Andreas Mohr + * Copyright 2002 Jaco Greeff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "wininet.h" +#include "winnls.h" +#include "winerror.h" +#include "winreg.h" +#include "winternl.h" +#include "shlwapi.h" + +#include "wine/debug.h" +#include "internet.h" + +WINE_DEFAULT_DEBUG_CHANNEL(wininet); + +#define DATA_PACKET_SIZE 0x2000 +#define szCRLF "\r\n" +#define MAX_BACKLOG 5 + +typedef enum { + /* FTP commands with arguments. */ + FTP_CMD_ACCT, + FTP_CMD_CWD, + FTP_CMD_DELE, + FTP_CMD_MKD, + FTP_CMD_PASS, + FTP_CMD_PORT, + FTP_CMD_RETR, + FTP_CMD_RMD, + FTP_CMD_RNFR, + FTP_CMD_RNTO, + FTP_CMD_STOR, + FTP_CMD_TYPE, + FTP_CMD_USER, + FTP_CMD_SIZE, + + /* FTP commands without arguments. */ + FTP_CMD_ABOR, + FTP_CMD_LIST, + FTP_CMD_NLST, + FTP_CMD_PASV, + FTP_CMD_PWD, + FTP_CMD_QUIT, +} FTP_COMMAND; + +static const CHAR *szFtpCommands[] = { + "ACCT", + "CWD", + "DELE", + "MKD", + "PASS", + "PORT", + "RETR", + "RMD", + "RNFR", + "RNTO", + "STOR", + "TYPE", + "USER", + "SIZE", + "ABOR", + "LIST", + "NLST", + "PASV", + "PWD", + "QUIT", +}; + +static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; +static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'}; + +static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr); +static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr); +static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr); +BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam, + INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext); +BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType); +BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket); +BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile); +INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext); +DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType); +BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile); +BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType); +BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize); +BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs); +BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp); +BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop); +BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile, + LPFILEPROPERTIESW *lpafp, LPDWORD dwfp); +HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile, + LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext); +DWORD FTP_SetResponseError(DWORD dwResponse); + +/*********************************************************************** + * FtpPutFileA (WININET.@) + * + * Uploads a file to the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile, + LPCSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) +{ + LPWSTR lpwzLocalFile; + LPWSTR lpwzNewRemoteFile; + BOOL ret; + + lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL; + lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL; + ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile, + dwFlags, dwContext); + HeapFree(GetProcessHeap(), 0, lpwzLocalFile); + HeapFree(GetProcessHeap(), 0, lpwzNewRemoteFile); + return ret; +} + +/*********************************************************************** + * FtpPutFileW (WININET.@) + * + * Uploads a file to the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile, + LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW; + + workRequest.asyncall = FTPPUTFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req->lpszLocalFile = WININET_strdupW(lpszLocalFile); + req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile); + req->dwFlags = dwFlags; + req->dwContext = dwContext; + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpPutFileW(lpwfs, lpszLocalFile, + lpszNewRemoteFile, dwFlags, dwContext); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + +/*********************************************************************** + * FTP_FtpPutFileW (Internal) + * + * Uploads a file to the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile, + LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD dwContext) +{ + HANDLE hFile = NULL; + BOOL bSuccess = FALSE; + LPWININETAPPINFOW hIC = NULL; + INT nResCode; + + TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile)); + + if (!lpszLocalFile || !lpszNewRemoteFile) + { + INTERNET_SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + assert( WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + /* Open file to be uploaded */ + if (INVALID_HANDLE_VALUE == + (hFile = CreateFileW(lpszLocalFile, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0))) + { + INTERNET_SetLastError(ERROR_FILE_NOT_FOUND); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); + + if (FTP_SendStore(lpwfs, lpszNewRemoteFile, dwFlags)) + { + INT nDataSocket; + + /* Get data socket to server */ + if (FTP_GetDataSocket(lpwfs, &nDataSocket)) + { + FTP_SendData(lpwfs, nDataSocket, hFile); + closesocket(nDataSocket); + nResCode = FTP_ReceiveResponse(lpwfs, dwContext); + if (nResCode) + { + if (nResCode == 226) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + } + } + +lend: + if (lpwfs->lstnSocket != -1) + closesocket(lpwfs->lstnSocket); + + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + if (hFile) + CloseHandle(hFile); + + return bSuccess; +} + + +/*********************************************************************** + * FtpSetCurrentDirectoryA (WININET.@) + * + * Change the working directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + LPWSTR lpwzDirectory; + BOOL ret; + + lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL; + ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory); + HeapFree(GetProcessHeap(), 0, lpwzDirectory); + return ret; +} + + +/*********************************************************************** + * FtpSetCurrentDirectoryW (WININET.@) + * + * Change the working directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory)); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPSETCURRENTDIRECTORYW *req; + + workRequest.asyncall = FTPSETCURRENTDIRECTORYW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpSetCurrentDirectoryW; + req->lpszDirectory = WININET_strdupW(lpszDirectory); + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpSetCurrentDirectoryW(lpwfs, lpszDirectory); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpSetCurrentDirectoryW (Internal) + * + * Change the working directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory) +{ + INT nResCode; + LPWININETAPPINFOW hIC = NULL; + DWORD bSuccess = FALSE; + + TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory)); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_CWD, lpszDirectory, + lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + return bSuccess; +} + + +/*********************************************************************** + * FtpCreateDirectoryA (WININET.@) + * + * Create new directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory) +{ + LPWSTR lpwzDirectory; + BOOL ret; + + lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL; + ret = FtpCreateDirectoryW(hConnect, lpwzDirectory); + HeapFree(GetProcessHeap(), 0, lpwzDirectory); + return ret; +} + + +/*********************************************************************** + * FtpCreateDirectoryW (WININET.@) + * + * Create new directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPCREATEDIRECTORYW *req; + + workRequest.asyncall = FTPCREATEDIRECTORYW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpCreateDirectoryW; + req->lpszDirectory = WININET_strdupW(lpszDirectory); + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpCreateDirectoryW(lpwfs, lpszDirectory); + } +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpCreateDirectoryW (Internal) + * + * Create new directory on the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOW hIC = NULL; + + TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory)); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_MKD, lpszDirectory, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 257) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + +/*********************************************************************** + * FtpFindFirstFileA (WININET.@) + * + * Search the specified directory + * + * RETURNS + * HINTERNET on success + * NULL on failure + * + */ +HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect, + LPCSTR lpszSearchFile, LPWIN32_FIND_DATAA lpFindFileData, DWORD dwFlags, DWORD dwContext) +{ + LPWSTR lpwzSearchFile; + WIN32_FIND_DATAW wfd; + LPWIN32_FIND_DATAW lpFindFileDataW; + HINTERNET ret; + + lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL; + lpFindFileDataW = lpFindFileData?&wfd:NULL; + ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext); + HeapFree(GetProcessHeap(), 0, lpwzSearchFile); + + if(lpFindFileData) { + WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData); + } + return ret; +} + + +/*********************************************************************** + * FtpFindFirstFileW (WININET.@) + * + * Search the specified directory + * + * RETURNS + * HINTERNET on success + * NULL on failure + * + */ +HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect, + LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + HINTERNET r = NULL; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPFINDFIRSTFILEW *req; + + workRequest.asyncall = FTPFINDFIRSTFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpFindFirstFileW; + req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile); + req->lpFindFileData = lpFindFileData; + req->dwFlags = dwFlags; + req->dwContext= dwContext; + + INTERNET_AsyncCall(&workRequest); + r = NULL; + } + else + { + r = FTP_FtpFindFirstFileW(lpwfs, lpszSearchFile, lpFindFileData, + dwFlags, dwContext); + } +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpFindFirstFileW (Internal) + * + * Search the specified directory + * + * RETURNS + * HINTERNET on success + * NULL on failure + * + */ +HINTERNET WINAPI FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs, + LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD dwContext) +{ + INT nResCode; + LPWININETAPPINFOW hIC = NULL; + HINTERNET hFindNext = NULL; + + TRACE("\n"); + + assert(WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, INTERNET_FLAG_TRANSFER_ASCII)) + goto lend; + + if (!FTP_SendPortOrPasv(lpwfs)) + goto lend; + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_LIST, NULL, + lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 125 || nResCode == 150) + { + INT nDataSocket; + + /* Get data socket to server */ + if (FTP_GetDataSocket(lpwfs, &nDataSocket)) + { + hFindNext = FTP_ReceiveFileList(lpwfs, nDataSocket, lpszSearchFile, lpFindFileData, dwContext); + closesocket(nDataSocket); + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode != 226 && nResCode != 250) + INTERNET_SetLastError(ERROR_NO_MORE_FILES); + } + } + else + FTP_SetResponseError(nResCode); + } + +lend: + if (lpwfs->lstnSocket != -1) + closesocket(lpwfs->lstnSocket); + + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + if (hFindNext) + { + iar.dwResult = (DWORD)hFindNext; + iar.dwError = ERROR_SUCCESS; + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + iar.dwResult = (DWORD)hFindNext; + iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return hFindNext; +} + + +/*********************************************************************** + * FtpGetCurrentDirectoryA (WININET.@) + * + * Retrieves the current directory + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDirectory, + LPDWORD lpdwCurrentDirectory) +{ + WCHAR dir[MAX_PATH]; + DWORD len; + BOOL ret; + + if(lpdwCurrentDirectory) len = *lpdwCurrentDirectory; + ret = FtpGetCurrentDirectoryW(hFtpSession, lpszCurrentDirectory?dir:NULL, lpdwCurrentDirectory?&len:NULL); + if(lpdwCurrentDirectory) { + *lpdwCurrentDirectory = len; + if(lpszCurrentDirectory) + WideCharToMultiByte(CP_ACP, 0, dir, len, lpszCurrentDirectory, *lpdwCurrentDirectory, NULL, NULL); + } + return ret; +} + + +/*********************************************************************** + * FtpGetCurrentDirectoryW (WININET.@) + * + * Retrieves the current directory + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory, + LPDWORD lpdwCurrentDirectory) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + TRACE("len(%ld)\n", *lpdwCurrentDirectory); + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPGETCURRENTDIRECTORYW *req; + + workRequest.asyncall = FTPGETCURRENTDIRECTORYW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpGetCurrentDirectoryW; + req->lpszDirectory = lpszCurrentDirectory; + req->lpdwDirectory = lpdwCurrentDirectory; + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpGetCurrentDirectoryW(lpwfs, lpszCurrentDirectory, + lpdwCurrentDirectory); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpGetCurrentDirectoryA (Internal) + * + * Retrieves the current directory + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory, + LPDWORD lpdwCurrentDirectory) +{ + INT nResCode; + LPWININETAPPINFOW hIC = NULL; + DWORD bSuccess = FALSE; + + TRACE("len(%ld)\n", *lpdwCurrentDirectory); + + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + return FALSE; + } + + /* Clear any error information */ + INTERNET_SetLastError(0); + + ZeroMemory(lpszCurrentDirectory, *lpdwCurrentDirectory); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PWD, NULL, + lpwfs->hdr.lpfnStatusCB, &lpwfs->hdr, lpwfs->hdr.dwContext)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 257) /* Extract directory name */ + { + DWORD firstpos, lastpos, len; + LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer()); + + for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++) + { + if ('"' == lpszResponseBuffer[lastpos]) + { + if (!firstpos) + firstpos = lastpos; + else + break; + } + } + + len = lastpos - firstpos - 1; + strncpyW(lpszCurrentDirectory, &lpszResponseBuffer[firstpos+1], + len < *lpdwCurrentDirectory ? len : *lpdwCurrentDirectory); + HeapFree(GetProcessHeap(), 0, lpszResponseBuffer); + *lpdwCurrentDirectory = len; + bSuccess = TRUE; + } + else + FTP_SetResponseError(nResCode); + } + +lend: + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : ERROR_INTERNET_EXTENDED_ERROR; + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return (DWORD) bSuccess; +} + +/*********************************************************************** + * FtpOpenFileA (WININET.@) + * + * Open a remote file for writing or reading + * + * RETURNS + * HINTERNET handle on success + * NULL on failure + * + */ +HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession, + LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, + DWORD dwContext) +{ + LPWSTR lpwzFileName; + HINTERNET ret; + + lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL; + ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext); + HeapFree(GetProcessHeap(), 0, lpwzFileName); + return ret; +} + + +/*********************************************************************** + * FtpOpenFileW (WININET.@) + * + * Open a remote file for writing or reading + * + * RETURNS + * HINTERNET handle on success + * NULL on failure + * + */ +HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession, + LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, + DWORD dwContext) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + HINTERNET r = NULL; + + TRACE("(%p,%s,0x%08lx,0x%08lx,0x%08lx)\n", hFtpSession, + debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext); + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + if (lpwfs->download_in_progress != NULL) { + INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS); + goto lend; + } + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPOPENFILEW *req; + + workRequest.asyncall = FTPOPENFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpOpenFileW; + req->lpszFilename = WININET_strdupW(lpszFileName); + req->dwAccess = fdwAccess; + req->dwFlags = dwFlags; + req->dwContext = dwContext; + + INTERNET_AsyncCall(&workRequest); + r = NULL; + } + else + { + r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpOpenFileW (Internal) + * + * Open a remote file for writing or reading + * + * RETURNS + * HINTERNET handle on success + * NULL on failure + * + */ +HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs, + LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags, + DWORD dwContext) +{ + INT nDataSocket; + BOOL bSuccess = FALSE; + LPWININETFILE lpwh = NULL; + LPWININETAPPINFOW hIC = NULL; + HINTERNET handle = NULL; + + TRACE("\n"); + + assert (WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (GENERIC_READ == fdwAccess) + { + /* Set up socket to retrieve data */ + bSuccess = FTP_SendRetrieve(lpwfs, lpszFileName, dwFlags); + } + else if (GENERIC_WRITE == fdwAccess) + { + /* Set up socket to send data */ + bSuccess = FTP_SendStore(lpwfs, lpszFileName, dwFlags); + } + + /* Get data socket to server */ + if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket)) + { + lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFILE)); + lpwh->hdr.htype = WH_HFILE; + lpwh->hdr.dwFlags = dwFlags; + lpwh->hdr.dwContext = dwContext; + lpwh->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr ); + lpwh->hdr.dwRefCount = 1; + lpwh->hdr.destroy = FTP_CloseFileTransferHandle; + lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB; + lpwh->nDataSocket = nDataSocket; + lpwh->session_deleted = FALSE; + + handle = WININET_AllocHandle( &lpwh->hdr ); + if( !handle ) + goto lend; + + /* Indicate that a download is currently in progress */ + lpwfs->download_in_progress = lpwh; + } + + if (lpwfs->lstnSocket != -1) + closesocket(lpwfs->lstnSocket); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + if (lpwh) + { + iar.dwResult = (DWORD)handle; + iar.dwError = ERROR_SUCCESS; + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + +lend: + if( lpwh ) + WININET_Release( &lpwh->hdr ); + + return handle; +} + + +/*********************************************************************** + * FtpGetFileA (WININET.@) + * + * Retrieve file from the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszNewFile, + BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, + DWORD dwContext) +{ + LPWSTR lpwzRemoteFile; + LPWSTR lpwzNewFile; + BOOL ret; + + lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL; + lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL; + ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists, + dwLocalFlagsAttribute, dwInternetFlags, dwContext); + HeapFree(GetProcessHeap(), 0, lpwzRemoteFile); + HeapFree(GetProcessHeap(), 0, lpwzNewFile); + return ret; +} + + +/*********************************************************************** + * FtpGetFileW (WININET.@) + * + * Retrieve file from the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile, + BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, + DWORD dwContext) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + if (lpwfs->download_in_progress != NULL) { + INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPGETFILEW *req; + + workRequest.asyncall = FTPGETFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpGetFileW; + req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile); + req->lpszNewFile = WININET_strdupW(lpszNewFile); + req->dwLocalFlagsAttribute = dwLocalFlagsAttribute; + req->fFailIfExists = fFailIfExists; + req->dwFlags = dwInternetFlags; + req->dwContext = dwContext; + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpGetFileW(lpwfs, lpszRemoteFile, lpszNewFile, + fFailIfExists, dwLocalFlagsAttribute, dwInternetFlags, dwContext); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + + +/*********************************************************************** + * FTP_FtpGetFileW (Internal) + * + * Retrieve file from the FTP server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile, + BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags, + DWORD dwContext) +{ + DWORD nBytes; + BOOL bSuccess = FALSE; + HANDLE hFile; + LPWININETAPPINFOW hIC = NULL; + + TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile)); + + assert (WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + /* Ensure we can write to lpszNewfile by opening it */ + hFile = CreateFileW(lpszNewFile, GENERIC_WRITE, 0, 0, fFailIfExists ? + CREATE_NEW : CREATE_ALWAYS, dwLocalFlagsAttribute, 0); + if (INVALID_HANDLE_VALUE == hFile) + goto lend; + + /* Set up socket to retrieve data */ + nBytes = FTP_SendRetrieve(lpwfs, lpszRemoteFile, dwInternetFlags); + + if (nBytes > 0) + { + INT nDataSocket; + + /* Get data socket to server */ + if (FTP_GetDataSocket(lpwfs, &nDataSocket)) + { + INT nResCode; + + /* Receive data */ + FTP_RetrieveFileData(lpwfs, nDataSocket, nBytes, hFile); + nResCode = FTP_ReceiveResponse(lpwfs, dwContext); + if (nResCode) + { + if (nResCode == 226) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + closesocket(nDataSocket); + } + } + +lend: + if (lpwfs->lstnSocket != -1) + closesocket(lpwfs->lstnSocket); + + if (hFile) + CloseHandle(hFile); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + +DWORD WINAPI FtpGetFileSize( HINTERNET hFile, LPDWORD lpdwFileSizeHigh ) +{ + FIXME("(%p, %p)\n", hFile, lpdwFileSizeHigh); + + if (lpdwFileSizeHigh) + *lpdwFileSizeHigh = 0; + + return 0; +} + +/*********************************************************************** + * FtpDeleteFileA (WININET.@) + * + * Delete a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName) +{ + LPWSTR lpwzFileName; + BOOL ret; + + lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL; + ret = FtpDeleteFileW(hFtpSession, lpwzFileName); + HeapFree(GetProcessHeap(), 0, lpwzFileName); + return ret; +} + +/*********************************************************************** + * FtpDeleteFileW (WININET.@) + * + * Delete a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPDELETEFILEW *req; + + workRequest.asyncall = FTPDELETEFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpDeleteFileW; + req->lpszFilename = WININET_strdupW(lpszFileName); + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpDeleteFileW(hFtpSession, lpszFileName); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + +/*********************************************************************** + * FTP_FtpDeleteFileW (Internal) + * + * Delete a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOW hIC = NULL; + + TRACE("%p\n", lpwfs); + + assert (WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_DELE, lpszFileName, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } +lend: + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpRemoveDirectoryA (WININET.@) + * + * Remove a directory on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory) +{ + LPWSTR lpwzDirectory; + BOOL ret; + + lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL; + ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory); + HeapFree(GetProcessHeap(), 0, lpwzDirectory); + return ret; +} + +/*********************************************************************** + * FtpRemoveDirectoryW (WININET.@) + * + * Remove a directory on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPREMOVEDIRECTORYW *req; + + workRequest.asyncall = FTPREMOVEDIRECTORYW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpRemoveDirectoryW; + req->lpszDirectory = WININET_strdupW(lpszDirectory); + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpRemoveDirectoryW(lpwfs, lpszDirectory); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + +/*********************************************************************** + * FTP_FtpRemoveDirectoryW (Internal) + * + * Remove a directory on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOW hIC = NULL; + + TRACE("\n"); + + assert (WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RMD, lpszDirectory, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + + +/*********************************************************************** + * FtpRenameFileA (WININET.@) + * + * Rename a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDest) +{ + LPWSTR lpwzSrc; + LPWSTR lpwzDest; + BOOL ret; + + lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL; + lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL; + ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest); + HeapFree(GetProcessHeap(), 0, lpwzSrc); + HeapFree(GetProcessHeap(), 0, lpwzDest); + return ret; +} + +/*********************************************************************** + * FtpRenameFileW (WININET.@) + * + * Rename a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest) +{ + LPWININETFTPSESSIONW lpwfs; + LPWININETAPPINFOW hIC = NULL; + BOOL r = FALSE; + + lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession ); + if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype) + { + INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE); + goto lend; + } + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + WORKREQUEST workRequest; + struct WORKREQ_FTPRENAMEFILEW *req; + + workRequest.asyncall = FTPRENAMEFILEW; + workRequest.hdr = WININET_AddRef( &lpwfs->hdr ); + req = &workRequest.u.FtpRenameFileW; + req->lpszSrcFile = WININET_strdupW(lpszSrc); + req->lpszDestFile = WININET_strdupW(lpszDest); + + r = INTERNET_AsyncCall(&workRequest); + } + else + { + r = FTP_FtpRenameFileW(hFtpSession, lpszSrc, lpszDest); + } + +lend: + if( lpwfs ) + WININET_Release( &lpwfs->hdr ); + + return r; +} + +/*********************************************************************** + * FTP_FtpRenameFileA (Internal) + * + * Rename a file on the ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs, + LPCWSTR lpszSrc, LPCWSTR lpszDest) +{ + INT nResCode; + BOOL bSuccess = FALSE; + LPWININETAPPINFOW hIC = NULL; + + TRACE("\n"); + + assert (WH_HFTPSESSION == lpwfs->hdr.htype); + + /* Clear any error information */ + INTERNET_SetLastError(0); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNFR, lpszSrc, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode == 350) + { + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RNTO, lpszDest, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + } + + if (nResCode == 250) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + +lend: + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)bSuccess; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return bSuccess; +} + +BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags, + LPCSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand ) +{ + FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags, + debugstr_a(lpszCommand), dwContext, phFtpCommand); + + return TRUE; +} + +BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags, + LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand ) +{ + FIXME("%p %d 0x%08lx %s 0x%08lx %p\n", hConnect, fExpectResponse, dwFlags, + debugstr_w(lpszCommand), dwContext, phFtpCommand); + + return TRUE; +} + +/*********************************************************************** + * FTP_Connect (internal) + * + * Connect to a ftp server + * + * RETURNS + * HINTERNET a session handle on success + * NULL on failure + * + */ + +HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName, + INTERNET_PORT nServerPort, LPCWSTR lpszUserName, + LPCWSTR lpszPassword, DWORD dwFlags, DWORD dwContext, + DWORD dwInternalFlags) +{ + static const WCHAR szDefaultUsername[] = {'a','n','o','n','y','m','o','u','s','\0'}; + static const WCHAR szDefaultPassword[] = {'u','s','e','r','@','s','e','r','v','e','r','\0'}; + struct sockaddr_in socketAddr; + struct hostent *phe = NULL; + INT nsocket = -1; + UINT sock_namelen; + BOOL bSuccess = FALSE; + LPWININETFTPSESSIONW lpwfs = NULL; + HINTERNET handle = NULL; + + TRACE("%p Server(%s) Port(%d) User(%s) Paswd(%s)\n", + hIC, debugstr_w(lpszServerName), + nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword)); + + assert( hIC->hdr.htype == WH_HINIT ); + + if (NULL == lpszUserName && NULL != lpszPassword) + { + INTERNET_SetLastError(ERROR_INVALID_PARAMETER); + goto lerror; + } + + lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW)); + if (NULL == lpwfs) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + goto lerror; + } + + if (nServerPort == INTERNET_INVALID_PORT_NUMBER) + nServerPort = INTERNET_DEFAULT_FTP_PORT; + + lpwfs->hdr.htype = WH_HFTPSESSION; + lpwfs->hdr.lpwhparent = WININET_AddRef( &hIC->hdr ); + lpwfs->hdr.dwFlags = dwFlags; + lpwfs->hdr.dwContext = dwContext; + lpwfs->hdr.dwInternalFlags = dwInternalFlags; + lpwfs->hdr.dwRefCount = 1; + lpwfs->hdr.destroy = FTP_CloseSessionHandle; + lpwfs->hdr.lpfnStatusCB = hIC->hdr.lpfnStatusCB; + lpwfs->download_in_progress = NULL; + + handle = WININET_AllocHandle( &lpwfs->hdr ); + if( !handle ) + { + ERR("Failed to alloc handle\n"); + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + goto lerror; + } + + if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) { + if(strchrW(hIC->lpszProxy, ' ')) + FIXME("Several proxies not implemented.\n"); + if(hIC->lpszProxyBypass) + FIXME("Proxy bypass is ignored.\n"); + } + if ( !lpszUserName) { + lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername); + lpwfs->lpszPassword = WININET_strdupW(szDefaultPassword); + } + else { + lpwfs->lpszUserName = WININET_strdupW(lpszUserName); + lpwfs->lpszPassword = WININET_strdupW(lpszPassword); + } + + /* Don't send a handle created callback if this handle was created with InternetOpenUrl */ + if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL)) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)handle; + iar.dwError = ERROR_SUCCESS; + + SendAsyncCallback(&hIC->hdr, dwContext, + INTERNET_STATUS_HANDLE_CREATED, &iar, + sizeof(INTERNET_ASYNC_RESULT)); + } + + SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME, + (LPWSTR) lpszServerName, strlenW(lpszServerName)); + + if (!GetAddress(lpszServerName, nServerPort, &phe, &socketAddr)) + { + INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED); + goto lerror; + } + + SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED, + (LPWSTR) lpszServerName, strlenW(lpszServerName)); + + nsocket = socket(AF_INET,SOCK_STREAM,0); + if (nsocket == -1) + { + INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); + goto lerror; + } + + SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER, + &socketAddr, sizeof(struct sockaddr_in)); + + if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0) + { + ERR("Unable to connect (%s)\n", strerror(errno)); + INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT); + } + else + { + TRACE("Connected to server\n"); + lpwfs->sndSocket = nsocket; + SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER, + &socketAddr, sizeof(struct sockaddr_in)); + + sock_namelen = sizeof(lpwfs->socketAddress); + getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen); + lpwfs->phostent = phe; + + if (FTP_ConnectToHost(lpwfs)) + { + TRACE("Successfully logged into server\n"); + bSuccess = TRUE; + } + } + +lerror: + if (!bSuccess && nsocket == -1) + closesocket(nsocket); + + if (!bSuccess && lpwfs) + { + HeapFree(GetProcessHeap(), 0, lpwfs); + WININET_FreeHandle( handle ); + lpwfs = NULL; + } + + if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) + { + INTERNET_ASYNC_RESULT iar; + + iar.dwResult = (DWORD)lpwfs; + iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError(); + SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE, + &iar, sizeof(INTERNET_ASYNC_RESULT)); + } + + return handle; +} + + +/*********************************************************************** + * FTP_ConnectToHost (internal) + * + * Connect to a ftp server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_USER, lpwfs->lpszUserName, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + /* Login successful... */ + if (nResCode == 230) + bSuccess = TRUE; + /* User name okay, need password... */ + else if (nResCode == 331) + bSuccess = FTP_SendPassword(lpwfs); + /* Need account for login... */ + else if (nResCode == 332) + bSuccess = FTP_SendAccount(lpwfs); + else + FTP_SetResponseError(nResCode); + } + + TRACE("Returning %d\n", bSuccess); +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendCommandA (internal) + * + * Send command to server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam, + INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext) +{ + DWORD len; + CHAR *buf; + DWORD nBytesSent = 0; + int nRC = 0; + DWORD dwParamLen; + + TRACE("%d: (%s) %d\n", ftpCmd, lpszParam, nSocket); + + if (lpfnStatusCB) + { + HINTERNET hHandle = WININET_FindHandle( hdr ); + if( hHandle ) + { + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0); + WININET_Release( hdr ); + } + } + + dwParamLen = lpszParam?strlen(lpszParam)+1:0; + len = dwParamLen + strlen(szFtpCommands[ftpCmd]) + strlen(szCRLF); + if (NULL == (buf = HeapAlloc(GetProcessHeap(), 0, len+1))) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + sprintf(buf, "%s%s%s%s", szFtpCommands[ftpCmd], dwParamLen ? " " : "", + dwParamLen ? lpszParam : "", szCRLF); + + TRACE("Sending (%s) len(%ld)\n", buf, len); + while((nBytesSent < len) && (nRC != -1)) + { + nRC = send(nSocket, buf+nBytesSent, len - nBytesSent, 0); + nBytesSent += nRC; + } + + HeapFree(GetProcessHeap(), 0, (LPVOID)buf); + + if (lpfnStatusCB) + { + HINTERNET hHandle = WININET_FindHandle( hdr ); + if( hHandle ) + { + lpfnStatusCB(hHandle, dwContext, INTERNET_STATUS_REQUEST_SENT, + &nBytesSent, sizeof(DWORD)); + WININET_Release( hdr ); + } + } + + TRACE("Sent %ld bytes\n", nBytesSent); + return (nRC != -1); +} + +/*********************************************************************** + * FTP_SendCommand (internal) + * + * Send command to server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam, + INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD dwContext) +{ + BOOL ret; + LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL; + ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext); + HeapFree(GetProcessHeap(), 0, lpszParamA); + return ret; +} + +/*********************************************************************** + * FTP_ReceiveResponse (internal) + * + * Receive response from server + * + * RETURNS + * Reply code on success + * 0 on failure + * + */ +INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD dwContext) +{ + LPSTR lpszResponse = INTERNET_GetResponseBuffer(); + DWORD nRecv; + INT rc = 0; + char firstprefix[5]; + BOOL multiline = FALSE; + LPWININETAPPINFOW hIC = NULL; + + TRACE("socket(%d) \n", lpwfs->sndSocket); + + hIC = (LPWININETAPPINFOW) lpwfs->hdr.lpwhparent; + SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0); + + while(1) + { + if (!INTERNET_GetNextLine(lpwfs->sndSocket, &nRecv)) + goto lerror; + + if (nRecv >= 3) + { + if(!multiline) + { + if(lpszResponse[3] != '-') + break; + else + { /* Start of multiline repsonse. Loop until we get "nnn " */ + multiline = TRUE; + memcpy(firstprefix, lpszResponse, 3); + firstprefix[3] = ' '; + firstprefix[4] = '\0'; + } + } + else + { + if(!memcmp(firstprefix, lpszResponse, 4)) + break; + } + } + } + + if (nRecv >= 3) + { + rc = atoi(lpszResponse); + + SendAsyncCallback(&lpwfs->hdr, dwContext, INTERNET_STATUS_RESPONSE_RECEIVED, + &nRecv, sizeof(DWORD)); + } + +lerror: + TRACE("return %d\n", rc); + return rc; +} + + +/*********************************************************************** + * FTP_SendPassword (internal) + * + * Send password to ftp server + * + * RETURNS + * TRUE on success + * NULL on failure + * + */ +BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASS, lpwfs->lpszPassword, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + TRACE("Received reply code %d\n", nResCode); + /* Login successful... */ + if (nResCode == 230) + bSuccess = TRUE; + /* Command not implemented, superfluous at the server site... */ + /* Need account for login... */ + else if (nResCode == 332) + bSuccess = FTP_SendAccount(lpwfs); + else + FTP_SetResponseError(nResCode); + } + +lend: + TRACE("Returning %d\n", bSuccess); + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendAccount (internal) + * + * + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_ACCT, szNoAccount, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendStore (internal) + * + * Send request to upload file to ftp server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, dwType)) + goto lend; + + if (!FTP_SendPortOrPasv(lpwfs)) + goto lend; + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_STOR, lpszRemoteFile, 0, 0, 0)) + goto lend; + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 150) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + if (!bSuccess && lpwfs->lstnSocket != -1) + { + closesocket(lpwfs->lstnSocket); + lpwfs->lstnSocket = -1; + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_InitListenSocket (internal) + * + * Create a socket to listen for server response + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs) +{ + BOOL bSuccess = FALSE; + size_t namelen = sizeof(struct sockaddr_in); + + TRACE("\n"); + + lpwfs->lstnSocket = socket(PF_INET, SOCK_STREAM, 0); + if (lpwfs->lstnSocket == -1) + { + TRACE("Unable to create listening socket\n"); + goto lend; + } + + /* We obtain our ip addr from the name of the command channel socket */ + lpwfs->lstnSocketAddress = lpwfs->socketAddress; + + /* and get the system to assign us a port */ + lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0); + + if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1) + { + TRACE("Unable to bind socket\n"); + goto lend; + } + + if (listen(lpwfs->lstnSocket, MAX_BACKLOG) == -1) + { + TRACE("listen failed\n"); + goto lend; + } + + if (getsockname(lpwfs->lstnSocket, (struct sockaddr *) &lpwfs->lstnSocketAddress, &namelen) != -1) + bSuccess = TRUE; + +lend: + if (!bSuccess && lpwfs->lstnSocket == -1) + { + closesocket(lpwfs->lstnSocket); + lpwfs->lstnSocket = -1; + } + + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendType (internal) + * + * Tell server type of data being transferred + * + * RETURNS + * TRUE on success + * FALSE on failure + * + * W98SE doesn't cache the type that's currently set + * (i.e. it sends it always), + * so we probably don't want to do that either. + */ +BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType) +{ + INT nResCode; + WCHAR type[] = { 'I','\0' }; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (dwType & INTERNET_FLAG_TRANSFER_ASCII) + type[0] = 'A'; + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_TYPE, type, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext)/100; + if (nResCode) + { + if (nResCode == 2) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + return bSuccess; +} + +/*********************************************************************** + * FTP_GetFileSize (internal) + * + * Retrieves from the server the size of the given file + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_SIZE, lpszRemoteFile, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 213) { + /* Now parses the output to get the actual file size */ + int i; + LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); + + for (i = 0; (lpszResponseBuffer[i] != ' ') && (lpszResponseBuffer[i] != '\0'); i++) ; + if (lpszResponseBuffer[i] == '\0') return FALSE; + *dwSize = atol(&(lpszResponseBuffer[i + 1])); + + bSuccess = TRUE; + } else { + FTP_SetResponseError(nResCode); + } + } + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_SendPort (internal) + * + * Tell server which port to use + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs) +{ + static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'}; + INT nResCode; + WCHAR szIPAddress[64]; + BOOL bSuccess = FALSE; + TRACE("\n"); + + sprintfW(szIPAddress, szIPFormat, + lpwfs->lstnSocketAddress.sin_addr.s_addr&0x000000FF, + (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x0000FF00)>>8, + (lpwfs->lstnSocketAddress.sin_addr.s_addr&0x00FF0000)>>16, + (lpwfs->lstnSocketAddress.sin_addr.s_addr&0xFF000000)>>24, + lpwfs->lstnSocketAddress.sin_port & 0xFF, + (lpwfs->lstnSocketAddress.sin_port & 0xFF00)>>8); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PORT, szIPAddress, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 200) + bSuccess = TRUE; + else + FTP_SetResponseError(nResCode); + } + +lend: + return bSuccess; +} + + +/*********************************************************************** + * FTP_DoPassive (internal) + * + * Tell server that we want to do passive transfers + * and connect data socket + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs) +{ + INT nResCode; + BOOL bSuccess = FALSE; + + TRACE("\n"); + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_PASV, NULL, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if (nResCode) + { + if (nResCode == 227) + { + LPSTR lpszResponseBuffer = INTERNET_GetResponseBuffer(); + LPSTR p; + int f[6]; + int i; + char *pAddr, *pPort; + INT nsocket = -1; + struct sockaddr_in dataSocketAddress; + + p = lpszResponseBuffer+4; /* skip status code */ + + /* do a very strict check; we can improve that later. */ + + if (strncmp(p, "Entering Passive Mode", 21)) + { + ERR("unknown response '%.*s', aborting\n", 21, p); + goto lend; + } + p += 21; /* skip string */ + if ((*p++ != ' ') || (*p++ != '(')) + { + ERR("unknown response format, aborting\n"); + goto lend; + } + + if (sscanf(p, "%d,%d,%d,%d,%d,%d", &f[0], &f[1], &f[2], &f[3], + &f[4], &f[5]) != 6) + { + ERR("unknown response address format '%s', aborting\n", p); + goto lend; + } + for (i=0; i < 6; i++) + f[i] = f[i] & 0xff; + + dataSocketAddress = lpwfs->socketAddress; + pAddr = (char *)&(dataSocketAddress.sin_addr.s_addr); + pPort = (char *)&(dataSocketAddress.sin_port); + pAddr[0] = f[0]; + pAddr[1] = f[1]; + pAddr[2] = f[2]; + pAddr[3] = f[3]; + pPort[0] = f[4]; + pPort[1] = f[5]; + + nsocket = socket(AF_INET,SOCK_STREAM,0); + if (nsocket == -1) + goto lend; + + if (connect(nsocket, (struct sockaddr *)&dataSocketAddress, sizeof(dataSocketAddress))) + { + ERR("can't connect passive FTP data port.\n"); + goto lend; + } + lpwfs->pasvSocket = nsocket; + bSuccess = TRUE; + } + else + FTP_SetResponseError(nResCode); + } + +lend: + return bSuccess; +} + + +BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs) +{ + if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE) + { + if (!FTP_DoPassive(lpwfs)) + return FALSE; + } + else + { + if (!FTP_SendPort(lpwfs)) + return FALSE; + } + return TRUE; +} + + +/*********************************************************************** + * FTP_GetDataSocket (internal) + * + * Either accepts an incoming data socket connection from the server + * or just returns the already opened socket after a PASV command + * in case of passive FTP. + * + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket) +{ + struct sockaddr_in saddr; + size_t addrlen = sizeof(struct sockaddr); + + TRACE("\n"); + if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE) + { + *nDataSocket = lpwfs->pasvSocket; + } + else + { + *nDataSocket = accept(lpwfs->lstnSocket, (struct sockaddr *) &saddr, &addrlen); + closesocket(lpwfs->lstnSocket); + lpwfs->lstnSocket = -1; + } + return *nDataSocket != -1; +} + + +/*********************************************************************** + * FTP_SendData (internal) + * + * Send data to the server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile) +{ + BY_HANDLE_FILE_INFORMATION fi; + DWORD nBytesRead = 0; + DWORD nBytesSent = 0; + DWORD nTotalSent = 0; + DWORD nBytesToSend, nLen; + int nRC = 1; + time_t s_long_time, e_long_time; + LONG nSeconds; + CHAR *lpszBuffer; + + TRACE("\n"); + lpszBuffer = HeapAlloc(GetProcessHeap(), 0, sizeof(CHAR)*DATA_PACKET_SIZE); + memset(lpszBuffer, 0, sizeof(CHAR)*DATA_PACKET_SIZE); + + /* Get the size of the file. */ + GetFileInformationByHandle(hFile, &fi); + time(&s_long_time); + + do + { + nBytesToSend = nBytesRead - nBytesSent; + + if (nBytesToSend <= 0) + { + /* Read data from file. */ + nBytesSent = 0; + if (!ReadFile(hFile, lpszBuffer, DATA_PACKET_SIZE, &nBytesRead, 0)) + ERR("Failed reading from file\n"); + + if (nBytesRead > 0) + nBytesToSend = nBytesRead; + else + break; + } + + nLen = DATA_PACKET_SIZE < nBytesToSend ? + DATA_PACKET_SIZE : nBytesToSend; + nRC = send(nDataSocket, lpszBuffer, nLen, 0); + + if (nRC != -1) + { + nBytesSent += nRC; + nTotalSent += nRC; + } + + /* Do some computation to display the status. */ + time(&e_long_time); + nSeconds = e_long_time - s_long_time; + if( nSeconds / 60 > 0 ) + { + TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld min %ld sec estimated remainig time %ld sec\n", + nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds / 60, + nSeconds % 60, (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent ); + } + else + { + TRACE( "%ld bytes of %ld bytes (%ld%%) in %ld sec estimated remainig time %ld sec\n", + nTotalSent, fi.nFileSizeLow, nTotalSent*100/fi.nFileSizeLow, nSeconds, + (fi.nFileSizeLow - nTotalSent) * nSeconds / nTotalSent); + } + } while (nRC != -1); + + TRACE("file transfer complete!\n"); + + HeapFree(GetProcessHeap(), 0, lpszBuffer); + + return nTotalSent; +} + + +/*********************************************************************** + * FTP_SendRetrieve (internal) + * + * Send request to retrieve a file + * + * RETURNS + * Number of bytes to be received on success + * 0 on failure + * + */ +DWORD FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType) +{ + INT nResCode; + DWORD nResult = 0; + + TRACE("\n"); + if (!FTP_InitListenSocket(lpwfs)) + goto lend; + + if (!FTP_SendType(lpwfs, dwType)) + goto lend; + + if (!FTP_SendPortOrPasv(lpwfs)) + goto lend; + + if (!FTP_GetFileSize(lpwfs, lpszRemoteFile, &nResult)) + goto lend; + + TRACE("Waiting to receive %ld bytes\n", nResult); + + if (!FTP_SendCommand(lpwfs->sndSocket, FTP_CMD_RETR, lpszRemoteFile, 0, 0, 0)) + goto lend; + + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + if ((nResCode != 125) && (nResCode != 150)) { + /* That means that we got an error getting the file. */ + nResult = 0; + } + +lend: + if (0 == nResult && lpwfs->lstnSocket != -1) + { + closesocket(lpwfs->lstnSocket); + lpwfs->lstnSocket = -1; + } + + return nResult; +} + + +/*********************************************************************** + * FTP_RetrieveData (internal) + * + * Retrieve data from server + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, DWORD nBytes, HANDLE hFile) +{ + DWORD nBytesWritten; + DWORD nBytesReceived = 0; + INT nRC = 0; + CHAR *lpszBuffer; + + TRACE("\n"); + + if (INVALID_HANDLE_VALUE == hFile) + return FALSE; + + lpszBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CHAR)*DATA_PACKET_SIZE); + if (NULL == lpszBuffer) + { + INTERNET_SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + while (nBytesReceived < nBytes && nRC != -1) + { + nRC = recv(nDataSocket, lpszBuffer, DATA_PACKET_SIZE, 0); + if (nRC != -1) + { + /* other side closed socket. */ + if (nRC == 0) + goto recv_end; + WriteFile(hFile, lpszBuffer, nRC, &nBytesWritten, NULL); + nBytesReceived += nRC; + } + + TRACE("%ld bytes of %ld (%ld%%)\r", nBytesReceived, nBytes, + nBytesReceived * 100 / nBytes); + } + + TRACE("Data transfer complete\n"); + HeapFree(GetProcessHeap(), 0, lpszBuffer); + +recv_end: + return (nRC != -1); +} + + +/*********************************************************************** + * FTP_CloseSessionHandle (internal) + * + * Deallocate session handle + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +static void FTP_CloseSessionHandle(LPWININETHANDLEHEADER hdr) +{ + LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr; + + TRACE("\n"); + + if (lpwfs->download_in_progress != NULL) + lpwfs->download_in_progress->session_deleted = TRUE; + + if (lpwfs->sndSocket != -1) + closesocket(lpwfs->sndSocket); + + if (lpwfs->lstnSocket != -1) + closesocket(lpwfs->lstnSocket); + + HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword); + HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName); + HeapFree(GetProcessHeap(), 0, lpwfs); +} + + +/*********************************************************************** + * FTP_CloseFindNextHandle (internal) + * + * Deallocate session handle + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +static void FTP_CloseFindNextHandle(LPWININETHANDLEHEADER hdr) +{ + LPWININETFINDNEXTW lpwfn = (LPWININETFINDNEXTW) hdr; + DWORD i; + + TRACE("\n"); + + for (i = 0; i < lpwfn->size; i++) + { + HeapFree(GetProcessHeap(), 0, lpwfn->lpafp[i].lpszName); + } + + HeapFree(GetProcessHeap(), 0, lpwfn->lpafp); + HeapFree(GetProcessHeap(), 0, lpwfn); +} + +/*********************************************************************** + * FTP_CloseFileTransferHandle (internal) + * + * Closes the file transfer handle. This also 'cleans' the data queue of + * the 'transfer conplete' message (this is a bit of a hack though :-/ ) + * + */ +static void FTP_CloseFileTransferHandle(LPWININETHANDLEHEADER hdr) +{ + LPWININETFILE lpwh = (LPWININETFILE) hdr; + LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) lpwh->hdr.lpwhparent; + INT nResCode; + + TRACE("\n"); + + if (!lpwh->session_deleted) + lpwfs->download_in_progress = NULL; + + /* This just serves to flush the control socket of any spurrious lines written + to it (like '226 Transfer complete.'). + + Wonder what to do if the server sends us an error code though... + */ + nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext); + + if (lpwh->nDataSocket != -1) + closesocket(lpwh->nDataSocket); + + HeapFree(GetProcessHeap(), 0, lpwh); +} + +/*********************************************************************** + * FTP_ReceiveFileList (internal) + * + * Read file list from server + * + * RETURNS + * Handle to file list on success + * NULL on failure + * + */ +HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile, + LPWIN32_FIND_DATAW lpFindFileData, DWORD dwContext) +{ + DWORD dwSize = 0; + LPFILEPROPERTIESW lpafp = NULL; + LPWININETFINDNEXTW lpwfn = NULL; + HINTERNET handle = 0; + + TRACE("(%p,%d,%s,%p,%ld)\n", lpwfs, nSocket, debugstr_w(lpszSearchFile), lpFindFileData, dwContext); + + if (FTP_ParseDirectory(lpwfs, nSocket, lpszSearchFile, &lpafp, &dwSize)) + { + if(lpFindFileData) + FTP_ConvertFileProp(lpafp, lpFindFileData); + + lpwfn = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETFINDNEXTW)); + if (lpwfn) + { + lpwfn->hdr.htype = WH_HFINDNEXT; + lpwfn->hdr.lpwhparent = WININET_AddRef( &lpwfs->hdr ); + lpwfn->hdr.dwContext = dwContext; + lpwfn->hdr.dwRefCount = 1; + lpwfn->hdr.destroy = FTP_CloseFindNextHandle; + lpwfn->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB; + lpwfn->index = 1; /* Next index is 1 since we return index 0 */ + lpwfn->size = dwSize; + lpwfn->lpafp = lpafp; + + handle = WININET_AllocHandle( &lpwfn->hdr ); + } + } + + if( lpwfn ) + WININET_Release( &lpwfn->hdr ); + + TRACE("Matched %ld files\n", dwSize); + return handle; +} + + +/*********************************************************************** + * FTP_ConvertFileProp (internal) + * + * Converts FILEPROPERTIESW struct to WIN32_FIND_DATAA + * + * RETURNS + * TRUE on success + * FALSE on failure + * + */ +BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData) +{ + BOOL bSuccess = FALSE; + + ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAW)); + + if (lpafp) + { + /* Convert 'Unix' time to Windows time */ + RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified), + (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime)); + lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime; + lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime; + + /* Not all fields are filled in */ + lpFindFileData->nFileSizeHigh = 0; /* We do not handle files bigger than 0xFFFFFFFF bytes yet :-) */ + lpFindFileData->nFileSizeLow = lpafp->nSize; + + if (lpafp->bIsDirectory) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (lpafp->lpszName) + lstrcpynW(lpFindFileData->cFileName, lpafp->lpszName, MAX_PATH); + + bSuccess = TRUE; + } + + return bSuccess; +} + +/*********************************************************************** + * FTP_ParseNextFile (internal) + * + * Parse the next line in file listing + * + * RETURNS + * TRUE on success + * FALSE on failure + */ +BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW lpfp) +{ + static const char szSpace[] = " \t"; + DWORD nBufLen; + char *pszLine; + char *pszToken; + char *pszTmp; + BOOL found = FALSE; + int i; + + lpfp->lpszName = NULL; + do { + if(!(pszLine = INTERNET_GetNextLine(nSocket, &nBufLen))) + return FALSE; + + pszToken = strtok(pszLine, szSpace); + /* ls format + *