1 /*
2  * Wininet - ftp tests
3  *
4  * Copyright 2007 Paul Vriens
5  * Copyright 2007 Hans Leidekker
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 /*
23  * FIXME:
24  *     Use InternetGetLastResponseInfo when the last error is set to ERROR_INTERNET_EXTENDED_ERROR.
25  * TODO:
26  *     Add W-function tests.
27  *     Add missing function tests:
28  *         FtpGetFileSize
29  *         FtpSetCurrentDirectory
30  */
31 
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wininet.h"
39 #include "winsock2.h"
40 
41 #include "wine/test.h"
42 
43 
44 static BOOL (WINAPI *pFtpCommandA)(HINTERNET,BOOL,DWORD,LPCSTR,DWORD_PTR,HINTERNET*);
45 static INTERNET_STATUS_CALLBACK (WINAPI *pInternetSetStatusCallbackA)(HINTERNET,INTERNET_STATUS_CALLBACK);
46 
47 
48 static void test_getfile_no_open(void)
49 {
50     BOOL      bRet;
51 
52     /* Invalid internet handle, the others are valid parameters */
53     SetLastError(0xdeadbeef);
54     bRet = FtpGetFileA(NULL, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
55     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
56     ok ( GetLastError() == ERROR_INTERNET_NOT_INITIALIZED ||
57          GetLastError() == ERROR_INVALID_HANDLE,
58         "Expected ERROR_INTERNET_NOT_INITIALIZED or ERROR_INVALID_HANDLE (win98), got %d\n", GetLastError());
59 }
60 
61 static void test_connect(HINTERNET hInternet)
62 {
63     HINTERNET hFtp;
64 
65     /* Try a few username/password combinations:
66      * anonymous : NULL
67      * NULL      : IEUser@
68      * NULL      : NULL
69      * ""        : IEUser@
70      * ""        : NULL
71      */
72 
73     SetLastError(0xdeadbeef);
74     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
75     if (hFtp)  /* some servers accept an empty password */
76     {
77         ok ( GetLastError() == ERROR_SUCCESS, "ERROR_SUCCESS, got %d\n", GetLastError());
78         InternetCloseHandle(hFtp);
79     }
80     else
81         ok ( GetLastError() == ERROR_INTERNET_LOGIN_FAILURE,
82              "Expected ERROR_INTERNET_LOGIN_FAILURE, got %d\n", GetLastError());
83 
84     SetLastError(0xdeadbeef);
85     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, NULL, "IEUser@", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
86     ok ( hFtp == NULL, "Expected InternetConnect to fail\n");
87     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
88         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
89 
90     SetLastError(0xdeadbeef);
91     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "", "IEUser@",
92             INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
93     ok(!hFtp, "Expected InternetConnect to fail\n");
94     ok(GetLastError() == ERROR_INVALID_PARAMETER,
95         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
96 
97     /* Using a NULL username and password will be interpreted as anonymous ftp. The username will be 'anonymous' the password
98      * is created via some simple heuristics (see dlls/wininet/ftp.c).
99      * On Wine this registry key is not set by default so (NULL, NULL) will result in anonymous ftp with an (most likely) not
100      * accepted password (the username).
101      * If the first call fails because we get an ERROR_INTERNET_LOGIN_FAILURE, we try again with a (more) correct password.
102      */
103 
104     SetLastError(0xdeadbeef);
105     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, NULL, NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
106     if (!hFtp && (GetLastError() == ERROR_INTERNET_LOGIN_FAILURE))
107     {
108         /* We are most likely running on a clean Wine install or a Windows install where the registry key is removed */
109         SetLastError(0xdeadbeef);
110         hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", "IEUser@", INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
111     }
112     ok ( hFtp != NULL, "InternetConnect failed : %d\n", GetLastError());
113     ok ( GetLastError() == ERROR_SUCCESS,
114         "ERROR_SUCCESS, got %d\n", GetLastError());
115     InternetCloseHandle(hFtp);
116 
117     SetLastError(0xdeadbeef);
118     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "", NULL,
119             INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
120     if (!hFtp)
121     {
122         ok(GetLastError() == ERROR_INTERNET_LOGIN_FAILURE,
123                 "Expected ERROR_INTERNET_LOGIN_FAILURE, got %d\n", GetLastError());
124     }
125     else
126     {
127         ok(GetLastError() == ERROR_SUCCESS,
128                 "Expected ERROR_SUCCESS, got %d\n", GetLastError());
129         InternetCloseHandle(hFtp);
130     }
131 }
132 
133 static void test_createdir(HINTERNET hFtp, HINTERNET hConnect)
134 {
135     BOOL      bRet;
136 
137     /* Invalid internet handle, the other is a valid parameter */
138     SetLastError(0xdeadbeef);
139     bRet = FtpCreateDirectoryA(NULL, "new_directory_deadbeef");
140     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
141     ok ( GetLastError() == ERROR_INVALID_HANDLE,
142         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
143 
144     /* No directory-name */
145     SetLastError(0xdeadbeef);
146     bRet = FtpCreateDirectoryA(hFtp, NULL);
147     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
148     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
149         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
150 
151     /* Parameters are OK, but we shouldn't be allowed to create the directory */
152     SetLastError(0xdeadbeef);
153     bRet = FtpCreateDirectoryA(hFtp, "new_directory_deadbeef");
154     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
155     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
156         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
157 
158     /* One small test to show that handle type is checked before parameters */
159     SetLastError(0xdeadbeef);
160     bRet = FtpCreateDirectoryA(hConnect, NULL);
161     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
162     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
163         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
164 
165     SetLastError(0xdeadbeef);
166     bRet = FtpCreateDirectoryA(hConnect, "new_directory_deadbeef");
167     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
168     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
169         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
170 }
171 
172 static void test_deletefile(HINTERNET hFtp, HINTERNET hConnect)
173 {
174     BOOL      bRet;
175 
176     /* Invalid internet handle, the other is a valid parameter */
177     SetLastError(0xdeadbeef);
178     bRet = FtpDeleteFileA(NULL, "non_existent_file_deadbeef");
179     ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n");
180     ok ( GetLastError() == ERROR_INVALID_HANDLE,
181         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
182 
183     /* No filename */
184     SetLastError(0xdeadbeef);
185     bRet = FtpDeleteFileA(hFtp, NULL);
186     ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n");
187     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
188         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
189 
190     /* Parameters are OK but remote file should not be there */
191     SetLastError(0xdeadbeef);
192     bRet = FtpDeleteFileA(hFtp, "non_existent_file_deadbeef");
193     ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n");
194     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
195         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
196 
197     /* One small test to show that handle type is checked before parameters */
198     SetLastError(0xdeadbeef);
199     bRet = FtpDeleteFileA(hConnect, NULL);
200     ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n");
201     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
202         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
203 
204     SetLastError(0xdeadbeef);
205     bRet = FtpDeleteFileA(hConnect, "non_existent_file_deadbeef");
206     ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
207     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
208         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
209 }
210 
211 static void test_getfile(HINTERNET hFtp, HINTERNET hConnect)
212 {
213     BOOL      bRet;
214     HANDLE    hFile;
215 
216     /* The order of checking is:
217      *
218      *   All parameters except 'session handle' and 'condition flags'
219      *   Session handle
220      *   Session handle type
221      *   Condition flags
222      */
223 
224     /* Test to show the parameter checking order depends on the Windows version */
225     SetLastError(0xdeadbeef);
226     bRet = FtpGetFileA(NULL, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
227     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
228     ok ( GetLastError() == ERROR_INVALID_HANDLE ||
229          GetLastError() == ERROR_INVALID_PARAMETER,
230         "Expected ERROR_INVALID_HANDLE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
231 
232     /* Test to show session handle is checked before 'condition flags' */
233     SetLastError(0xdeadbeef);
234     bRet = FtpGetFileA(NULL, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 5, 0);
235     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
236     ok ( GetLastError() == ERROR_INVALID_HANDLE,
237         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
238 
239     /* Make sure we start clean */
240 
241     DeleteFileA("should_be_non_existing_deadbeef");
242     DeleteFileA("should_also_be_non_existing_deadbeef");
243 
244     /* No remote file */
245     SetLastError(0xdeadbeef);
246     bRet = FtpGetFileA(hFtp, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
247     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
248     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
249         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
250     ok (GetFileAttributesA("should_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES,
251         "Local file should not have been created\n");
252     DeleteFileA("should_be_non_existing_deadbeef");
253 
254     /* No local file */
255     SetLastError(0xdeadbeef);
256     bRet = FtpGetFileA(hFtp, "welcome.msg", NULL, FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
257     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
258     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
259         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
260 
261     /* Zero attributes */
262     bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_existing_non_deadbeef", FALSE, 0, FTP_TRANSFER_TYPE_UNKNOWN, 0);
263     ok ( bRet == TRUE, "Expected FtpGetFileA to succeed\n");
264     ok (GetFileAttributesA("should_be_existing_non_deadbeef") != INVALID_FILE_ATTRIBUTES,
265         "Local file should have been created\n");
266     DeleteFileA("should_be_existing_non_deadbeef");
267 
268     /* Illegal condition flags */
269     SetLastError(0xdeadbeef);
270     bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 0xffffffff, 0);
271     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
272     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR || GetLastError() == ERROR_INVALID_PARAMETER,
273         "Expected ERROR_INTERNET_EXTENDED_ERROR or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError());
274     ok (GetFileAttributesA("should_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES,
275         "Local file should not have been created\n");
276     DeleteFileA("should_be_non_existing_deadbeef");
277 
278     /* Remote file doesn't exist (and local doesn't exist as well) */
279     SetLastError(0xdeadbeef);
280     bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_also_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
281     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
282     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
283         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
284     /* Currently Wine always creates the local file (even on failure) which is not correct, hence the test */
285     ok (GetFileAttributesA("should_also_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES,
286         "Local file should not have been created\n");
287 
288     DeleteFileA("should_also_be_non_existing_deadbeef");
289 
290     /* Same call as the previous but now the local file does exists. Windows just removes the file if the call fails
291      * even if the local existed before!
292      */
293 
294     /* Create a temporary local file */
295     SetLastError(0xdeadbeef);
296     hFile = CreateFileA("should_also_be_non_existing_deadbeef", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
297     ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError());
298     CloseHandle(hFile);
299     SetLastError(0xdeadbeef);
300     bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_also_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
301     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
302     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
303         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
304     /* Currently Wine always creates the local file (even on failure) which is not correct, hence the test */
305     ok (GetFileAttributesA("should_also_be_non_existing_deadbeef") == INVALID_FILE_ATTRIBUTES,
306         "Local file should not have been created\n");
307 
308     DeleteFileA("should_also_be_non_existing_deadbeef");
309 
310     /* This one should succeed */
311     SetLastError(0xdeadbeef);
312     bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_existing_non_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
313     ok ( bRet == TRUE, "Expected FtpGetFileA to fail\n");
314     ok ( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", GetLastError());
315 
316     if (GetFileAttributesA("should_be_existing_non_deadbeef") != INVALID_FILE_ATTRIBUTES)
317     {
318         /* Should succeed as fFailIfExists is set to FALSE (meaning don't fail if local file exists) */
319         SetLastError(0xdeadbeef);
320         bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
321         ok ( bRet == TRUE, "Expected FtpGetFileA to succeed\n");
322         ok ( GetLastError() == ERROR_SUCCESS,
323             "Expected ERROR_SUCCESS, got %d\n", GetLastError());
324 
325         /* Should fail as fFailIfExists is set to TRUE */
326         SetLastError(0xdeadbeef);
327         bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
328         ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
329         ok ( GetLastError() == ERROR_FILE_EXISTS,
330             "Expected ERROR_FILE_EXISTS, got %d\n", GetLastError());
331 
332         /* Prove that the existence of the local file is checked first (or at least reported last) */
333         SetLastError(0xdeadbeef);
334         bRet = FtpGetFileA(hFtp, "should_be_non_existing_deadbeef", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
335         ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
336         ok ( GetLastError() == ERROR_FILE_EXISTS,
337             "Expected ERROR_FILE_EXISTS, got %d\n", GetLastError());
338 
339         DeleteFileA("should_be_existing_non_deadbeef");
340     }
341 
342     /* Test to show the parameter checking order depends on the Windows version */
343     SetLastError(0xdeadbeef);
344     bRet = FtpGetFileA(hConnect, NULL, "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
345     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
346     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE ||
347          GetLastError() == ERROR_INVALID_PARAMETER,
348         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
349 
350     /* Test to show that 'session handle type' is checked before 'condition flags' */
351     SetLastError(0xdeadbeef);
352     bRet = FtpGetFileA(hConnect, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, 5, 0);
353     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
354     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
355         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
356 
357     SetLastError(0xdeadbeef);
358     bRet = FtpGetFileA(hConnect, "should_be_non_existing_deadbeef", "should_be_non_existing_deadbeef", TRUE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
359     ok ( bRet == FALSE, "Expected FtpGetFileA to fail\n");
360     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
361         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
362 }
363 
364 static void trace_extended_error(DWORD error)
365 {
366     DWORD code, buflen = 0;
367 
368     if (error != ERROR_INTERNET_EXTENDED_ERROR) return;
369     if (!InternetGetLastResponseInfoA(&code, NULL, &buflen) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
370     {
371         char *text = HeapAlloc(GetProcessHeap(), 0, ++buflen);
372         InternetGetLastResponseInfoA(&code, text, &buflen);
373         trace("%u %s\n", code, text);
374         HeapFree(GetProcessHeap(), 0, text);
375     }
376 }
377 
378 static void test_openfile(HINTERNET hFtp, HINTERNET hConnect)
379 {
380     HINTERNET hOpenFile;
381 
382     /* Invalid internet handle, the rest are valid parameters */
383     SetLastError(0xdeadbeef);
384     hOpenFile = FtpOpenFileA(NULL, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
385     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
386     ok ( GetLastError() == ERROR_INVALID_HANDLE,
387         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
388     InternetCloseHandle(hOpenFile); /* Just in case */
389 
390     /* No filename */
391     SetLastError(0xdeadbeef);
392     hOpenFile = FtpOpenFileA(hFtp, NULL, GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
393     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
394     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
395         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
396     InternetCloseHandle(hOpenFile); /* Just in case */
397 
398     /* Illegal access flags */
399     SetLastError(0xdeadbeef);
400     hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", 0, FTP_TRANSFER_TYPE_ASCII, 0);
401     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
402     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
403         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
404     InternetCloseHandle(hOpenFile); /* Just in case */
405 
406     /* Illegal combination of access flags */
407     SetLastError(0xdeadbeef);
408     hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ|GENERIC_WRITE, FTP_TRANSFER_TYPE_ASCII, 0);
409     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
410     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
411         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
412     InternetCloseHandle(hOpenFile); /* Just in case */
413 
414     /* Illegal condition flags */
415     SetLastError(0xdeadbeef);
416     hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, 0xffffffff, 0);
417     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
418     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR || GetLastError() == ERROR_INVALID_PARAMETER,
419         "Expected ERROR_INTERNET_EXTENDED_ERROR or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError());
420     InternetCloseHandle(hOpenFile); /* Just in case */
421 
422     SetLastError(0xdeadbeef);
423     hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
424     ok ( hOpenFile != NULL, "Expected FtpOpenFileA to succeed\n");
425     ok ( GetLastError() == ERROR_SUCCESS ||
426         broken(GetLastError() == ERROR_FILE_NOT_FOUND), /* Win98 */
427         "Expected ERROR_SUCCESS, got %u\n", GetLastError());
428 
429     if (hOpenFile)
430     {
431         BOOL bRet;
432         DWORD error;
433         HINTERNET hOpenFile2;
434         HANDLE    hFile;
435 
436         /* We have a handle so all ftp calls should fail (TODO: Put all ftp-calls in here) */
437         SetLastError(0xdeadbeef);
438         bRet = FtpCreateDirectoryA(hFtp, "new_directory_deadbeef");
439         error = GetLastError();
440         ok ( bRet == FALSE, "Expected FtpCreateDirectoryA to fail\n");
441         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
442             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
443         trace_extended_error(error);
444 
445         SetLastError(0xdeadbeef);
446         bRet = FtpDeleteFileA(hFtp, "non_existent_file_deadbeef");
447         error = GetLastError();
448         ok ( bRet == FALSE, "Expected FtpDeleteFileA to fail\n");
449         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
450             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
451         trace_extended_error(error);
452 
453         SetLastError(0xdeadbeef);
454         bRet = FtpGetFileA(hFtp, "welcome.msg", "should_be_non_existing_deadbeef", FALSE, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
455         error = GetLastError();
456         ok ( bRet == FALSE || broken(bRet == TRUE), "Expected FtpGetFileA to fail\n");
457         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_SUCCESS),
458             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
459         DeleteFileA("should_be_non_existing_deadbeef"); /* Just in case */
460 
461         SetLastError(0xdeadbeef);
462         hOpenFile2 = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
463         error = GetLastError();
464         ok ( bRet == FALSE || broken(bRet == TRUE), "Expected FtpOpenFileA to fail\n");
465         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_SUCCESS),
466             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
467         InternetCloseHandle(hOpenFile2); /* Just in case */
468 
469         /* Create a temporary local file */
470         SetLastError(0xdeadbeef);
471         hFile = CreateFileA("now_existing_local", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
472         ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError());
473         CloseHandle(hFile);
474         SetLastError(0xdeadbeef);
475         bRet = FtpPutFileA(hFtp, "now_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
476         error = GetLastError();
477         ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
478         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
479             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
480         DeleteFileA("now_existing_local");
481 
482         SetLastError(0xdeadbeef);
483         bRet = FtpRemoveDirectoryA(hFtp, "should_be_non_existing_deadbeef_dir");
484         error = GetLastError();
485         ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
486         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
487             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
488 
489         SetLastError(0xdeadbeef);
490         bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", "new");
491         error = GetLastError();
492         ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
493         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
494             "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error);
495     }
496 
497     InternetCloseHandle(hOpenFile);
498 
499     /* One small test to show that handle type is checked before parameters */
500     SetLastError(0xdeadbeef);
501     hOpenFile = FtpOpenFileA(hConnect, "welcome.msg", GENERIC_READ, 5, 0);
502     ok ( !hOpenFile, "Expected FtpOpenFileA to fail\n");
503     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
504         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
505     InternetCloseHandle(hOpenFile); /* Just in case */
506 
507     SetLastError(0xdeadbeef);
508     hOpenFile = FtpOpenFileA(hConnect, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
509     ok ( hOpenFile == NULL, "Expected FtpOpenFileA to fail\n");
510     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
511         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
512 
513     InternetCloseHandle(hOpenFile); /* Just in case */
514 }
515 
516 static void test_putfile(HINTERNET hFtp, HINTERNET hConnect)
517 {
518     BOOL      bRet;
519     HANDLE    hFile;
520 
521     /* The order of checking is:
522      *
523      *   All parameters except 'session handle' and 'condition flags'
524      *   Session handle
525      *   Session handle type
526      *   Condition flags
527      */
528 
529     /* Test to show the parameter checking order depends on the Windows version */
530     SetLastError(0xdeadbeef);
531     bRet = FtpPutFileA(NULL, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
532     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
533     ok ( GetLastError() == ERROR_INVALID_HANDLE ||
534          GetLastError() == ERROR_INVALID_PARAMETER,
535         "Expected ERROR_INVALID_HANDLE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
536 
537     /* Test to show session handle is checked before 'condition flags' */
538     SetLastError(0xdeadbeef);
539     bRet = FtpPutFileA(NULL, "non_existing_local", "non_existing_remote", 5, 0);
540     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
541     ok ( GetLastError() == ERROR_INVALID_HANDLE,
542         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
543 
544     /* Start clean */
545     DeleteFileA("non_existing_local");
546 
547     /* No local file given */
548     SetLastError(0xdeadbeef);
549     bRet = FtpPutFileA(hFtp, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
550     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
551     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
552         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
553 
554     /* No remote file given */
555     SetLastError(0xdeadbeef);
556     bRet = FtpPutFileA(hFtp, "non_existing_local", NULL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
557     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
558     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
559         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
560 
561     /* Illegal condition flags */
562     SetLastError(0xdeadbeef);
563     bRet = FtpPutFileA(hFtp, "non_existing_local", "non_existing_remote", 5, 0);
564     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
565     ok ( GetLastError() == ERROR_FILE_NOT_FOUND || GetLastError() == ERROR_INVALID_PARAMETER,
566         "Expected ERROR_FILE_NOT_FOUND or ERROR_INVALID_PARAMETER (win98), got %d\n", GetLastError());
567 
568     /* Parameters are OK but local file doesn't exist */
569     SetLastError(0xdeadbeef);
570     bRet = FtpPutFileA(hFtp, "non_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
571     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
572     ok ( GetLastError() == ERROR_FILE_NOT_FOUND,
573         "Expected ERROR_FILE_NOT_FOUND, got %d\n", GetLastError());
574 
575     /* Create a temporary local file */
576     SetLastError(0xdeadbeef);
577     hFile = CreateFileA("now_existing_local", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
578     ok ( hFile != NULL, "Error creating a local file : %d\n", GetLastError());
579     CloseHandle(hFile);
580 
581     /* Local file exists but we shouldn't be allowed to 'put' the file */
582     SetLastError(0xdeadbeef);
583     bRet = FtpPutFileA(hFtp, "now_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
584     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
585     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
586         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
587 
588     DeleteFileA("now_existing_local");
589 
590     /* Test to show the parameter checking order depends on the Windows version */
591     SetLastError(0xdeadbeef);
592     bRet = FtpPutFileA(hConnect, NULL, "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
593     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
594     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE ||
595          GetLastError() == ERROR_INVALID_PARAMETER,
596         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE (win98) or ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
597 
598     /* Test to show that 'session handle type' is checked before 'condition flags' */
599     SetLastError(0xdeadbeef);
600     bRet = FtpPutFileA(hConnect, "non_existing_local", "non_existing_remote", 5, 0);
601     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
602     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
603         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
604 
605     SetLastError(0xdeadbeef);
606     bRet = FtpPutFileA(hConnect, "non_existing_local", "non_existing_remote", FTP_TRANSFER_TYPE_UNKNOWN, 0);
607     ok ( bRet == FALSE, "Expected FtpPutFileA to fail\n");
608     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
609         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
610 }
611 
612 static void test_removedir(HINTERNET hFtp, HINTERNET hConnect)
613 {
614     BOOL      bRet;
615 
616     /* Invalid internet handle, the other is a valid parameter */
617     SetLastError(0xdeadbeef);
618     bRet = FtpRemoveDirectoryA(NULL, "should_be_non_existing_deadbeef_dir");
619     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
620     ok ( GetLastError() == ERROR_INVALID_HANDLE,
621         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
622 
623     /* No remote directory given */
624     SetLastError(0xdeadbeef);
625     bRet = FtpRemoveDirectoryA(hFtp, NULL);
626     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
627     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
628         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
629 
630     /* Remote directory doesn't exist */
631     SetLastError(0xdeadbeef);
632     bRet = FtpRemoveDirectoryA(hFtp, "should_be_non_existing_deadbeef_dir");
633     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
634     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
635         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
636 
637     /* We shouldn't be allowed to remove that directory */
638     SetLastError(0xdeadbeef);
639     bRet = FtpRemoveDirectoryA(hFtp, "pub");
640     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
641     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
642         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
643 
644     /* One small test to show that handle type is checked before parameters */
645     SetLastError(0xdeadbeef);
646     bRet = FtpRemoveDirectoryA(hConnect, NULL);
647     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
648     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
649         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
650 
651     SetLastError(0xdeadbeef);
652     bRet = FtpRemoveDirectoryA(hConnect, "should_be_non_existing_deadbeef_dir");
653     ok ( bRet == FALSE, "Expected FtpRemoveDirectoryA to fail\n");
654     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
655         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
656 }
657 
658 static void test_renamefile(HINTERNET hFtp, HINTERNET hConnect)
659 {
660     BOOL      bRet;
661 
662     /* Invalid internet handle, the rest are valid parameters */
663     SetLastError(0xdeadbeef);
664     bRet = FtpRenameFileA(NULL , "should_be_non_existing_deadbeef", "new");
665     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
666     ok ( GetLastError() == ERROR_INVALID_HANDLE,
667         "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
668 
669     /* No 'existing' file */
670     SetLastError(0xdeadbeef);
671     bRet = FtpRenameFileA(hFtp , NULL, "new");
672     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
673     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
674         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
675 
676     /* No new file */
677     SetLastError(0xdeadbeef);
678     bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", NULL);
679     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
680     ok ( GetLastError() == ERROR_INVALID_PARAMETER,
681         "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
682 
683     /* Existing file shouldn't be there */
684     SetLastError(0xdeadbeef);
685     bRet = FtpRenameFileA(hFtp , "should_be_non_existing_deadbeef", "new");
686     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
687     ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
688         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError());
689 
690     /* One small test to show that handle type is checked before parameters */
691     SetLastError(0xdeadbeef);
692     bRet = FtpRenameFileA(hConnect , "should_be_non_existing_deadbeef", NULL);
693     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
694     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
695         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
696 
697     SetLastError(0xdeadbeef);
698     bRet = FtpRenameFileA(hConnect , "should_be_non_existing_deadbeef", "new");
699     ok ( bRet == FALSE, "Expected FtpRenameFileA to fail\n");
700     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
701         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError());
702 }
703 
704 static void test_command(HINTERNET hFtp, HINTERNET hConnect)
705 {
706     BOOL ret;
707     DWORD error;
708     unsigned int i;
709     static const struct
710     {
711         BOOL  ret;
712         DWORD error;
713         const char *cmd;
714     }
715     command_test[] =
716     {
717         { FALSE, ERROR_INVALID_PARAMETER,       NULL },
718         { FALSE, ERROR_INVALID_PARAMETER,       "" },
719         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "HELO" },
720         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE " },
721         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, " SIZE" },
722         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE " },
723         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE /welcome.msg /welcome.msg" },
724         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE  /welcome.msg" },
725         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "SIZE /welcome.msg " },
726         { TRUE,  ERROR_SUCCESS,                 "SIZE\t/welcome.msg" },
727         { TRUE,  ERROR_SUCCESS,                 "SIZE /welcome.msg" },
728         { FALSE, ERROR_INTERNET_EXTENDED_ERROR, "PWD /welcome.msg" },
729         { TRUE,  ERROR_SUCCESS,                 "PWD" }
730     };
731 
732     if (!pFtpCommandA)
733     {
734         win_skip("FtpCommandA() is not available. Skipping the Ftp command tests\n");
735         return;
736     }
737 
738     for (i = 0; i < sizeof(command_test) / sizeof(command_test[0]); i++)
739     {
740         SetLastError(0xdeadbeef);
741         ret = pFtpCommandA(hFtp, FALSE, FTP_TRANSFER_TYPE_ASCII, command_test[i].cmd, 0, NULL);
742         error = GetLastError();
743 
744         ok(ret == command_test[i].ret, "%d: expected FtpCommandA to %s\n", i, command_test[i].ret ? "succeed" : "fail");
745         ok(error == command_test[i].error, "%d: expected error %u, got %u\n", i, command_test[i].error, error);
746     }
747 }
748 
749 static void test_find_first_file(HINTERNET hFtp, HINTERNET hConnect)
750 {
751     WIN32_FIND_DATAA findData;
752     HINTERNET hSearch;
753     HINTERNET hSearch2;
754     HINTERNET hOpenFile;
755     DWORD error;
756     BOOL success;
757 
758     /* NULL as the search file ought to return the first file in the directory */
759     SetLastError(0xdeadbeef);
760     hSearch = FtpFindFirstFileA(hFtp, NULL, &findData, 0, 0);
761     ok ( hSearch != NULL, "Expected FtpFindFirstFileA to pass\n" );
762 
763     /* This should fail as the previous handle wasn't closed */
764     SetLastError(0xdeadbeef);
765     hSearch2 = FtpFindFirstFileA(hFtp, "welcome.msg", &findData, 0, 0);
766     todo_wine ok ( hSearch2 == NULL, "Expected FtpFindFirstFileA to fail\n" );
767     todo_wine ok ( GetLastError() == ERROR_FTP_TRANSFER_IN_PROGRESS,
768         "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", GetLastError() );
769     InternetCloseHandle(hSearch2); /* Just in case */
770 
771     InternetCloseHandle(hSearch);
772 
773     /* Try a valid filename in a subdirectory search */
774     SetLastError(0xdeadbeef);
775     hSearch = FtpFindFirstFileA(hFtp, "pub/wine", &findData, 0, 0);
776     ok( hSearch != NULL, "Expected FtpFindFirstFileA to pass\n" );
777     InternetCloseHandle(hSearch);
778 
779     /* Try a valid filename in a subdirectory wildcard search */
780     SetLastError(0xdeadbeef);
781     hSearch = FtpFindFirstFileA(hFtp, "pub/w*", &findData, 0, 0);
782     ok( hSearch != NULL, "Expected FtpFindFirstFileA to pass\n" );
783     InternetCloseHandle(hSearch);
784 
785     /* Try an invalid wildcard search */
786     SetLastError(0xdeadbeef);
787     hSearch = FtpFindFirstFileA(hFtp, "*/w*", &findData, 0, 0);
788     ok ( hSearch == NULL, "Expected FtpFindFirstFileA to fail\n" );
789     InternetCloseHandle(hSearch); /* Just in case */
790 
791     /* change current directory, and repeat those tests - this shows
792      * that the search string is interpreted as relative directory. */
793     success = FtpSetCurrentDirectoryA(hFtp, "pub");
794     ok( success, "Expected FtpSetCurrentDirectory to succeed\n" );
795 
796     SetLastError(0xdeadbeef);
797     hSearch = FtpFindFirstFileA(hFtp, "wine", &findData, 0, 0);
798     ok( hSearch != NULL, "Expected FtpFindFirstFileA to pass\n" );
799     InternetCloseHandle(hSearch);
800 
801     SetLastError(0xdeadbeef);
802     hSearch = FtpFindFirstFileA(hFtp, "w*", &findData, 0, 0);
803     ok( hSearch != NULL, "Expected FtpFindFirstFileA to pass\n" );
804     InternetCloseHandle(hSearch);
805 
806     success = FtpSetCurrentDirectoryA(hFtp, "..");
807     ok( success, "Expected FtpSetCurrentDirectory to succeed\n" );
808 
809     /* Try FindFirstFile between FtpOpenFile and InternetCloseHandle */
810     SetLastError(0xdeadbeef);
811     hOpenFile = FtpOpenFileA(hFtp, "welcome.msg", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, 0);
812     ok ( hOpenFile != NULL, "Expected FtpOpenFileA to succeed\n" );
813     ok ( GetLastError() == ERROR_SUCCESS ||
814         broken(GetLastError() == ERROR_FILE_NOT_FOUND), /* Win98 */
815         "Expected ERROR_SUCCESS, got %u\n", GetLastError() );
816 
817     /* This should fail as the OpenFile handle wasn't closed */
818     SetLastError(0xdeadbeef);
819     hSearch = FtpFindFirstFileA(hFtp, "welcome.msg", &findData, 0, 0);
820     error = GetLastError();
821     ok ( hSearch == NULL || broken(hSearch != NULL), /* win2k */
822          "Expected FtpFindFirstFileA to fail\n" );
823     if (!hSearch)
824         ok ( error == ERROR_FTP_TRANSFER_IN_PROGRESS || broken(error == ERROR_INTERNET_EXTENDED_ERROR),
825              "Expected ERROR_FTP_TRANSFER_IN_PROGRESS, got %d\n", error );
826     else
827     {
828         ok( error == ERROR_SUCCESS, "wrong error %u on success\n", GetLastError() );
829         InternetCloseHandle(hSearch);
830     }
831 
832     InternetCloseHandle(hOpenFile);
833 
834     /* Test using a nonexistent filename */
835     SetLastError(0xdeadbeef);
836     hSearch = FtpFindFirstFileA(hFtp, "this_file_should_not_exist", &findData, 0, 0);
837     ok ( hSearch == NULL, "Expected FtpFindFirstFileA to fail\n" );
838     todo_wine ok ( GetLastError() == ERROR_INTERNET_EXTENDED_ERROR,
839         "Expected ERROR_INTERNET_EXTENDED_ERROR, got %d\n", GetLastError() );
840     InternetCloseHandle(hSearch); /* Just in case */
841 
842     /* Test using a nonexistent filename and a wildcard */
843     SetLastError(0xdeadbeef);
844     hSearch = FtpFindFirstFileA(hFtp, "this_file_should_not_exist*", &findData, 0, 0);
845     ok ( hSearch == NULL, "Expected FtpFindFirstFileA to fail\n" );
846     todo_wine ok ( GetLastError() == ERROR_NO_MORE_FILES,
847         "Expected ERROR_NO_MORE_FILES, got %d\n", GetLastError() );
848     InternetCloseHandle(hSearch); /* Just in case */
849 
850     /* Test using an invalid handle type */
851     SetLastError(0xdeadbeef);
852     hSearch = FtpFindFirstFileA(hConnect, "welcome.msg", &findData, 0, 0);
853     ok ( hSearch == NULL, "Expected FtpFindFirstFileA to fail\n" );
854     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
855         "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got %d\n", GetLastError() );
856     InternetCloseHandle(hSearch); /* Just in case */
857 }
858 
859 static void test_get_current_dir(HINTERNET hFtp, HINTERNET hConnect)
860 {
861     BOOL    bRet;
862     DWORD   dwCurrentDirectoryLen = MAX_PATH;
863     CHAR    lpszCurrentDirectory[MAX_PATH];
864 
865     if (!pFtpCommandA)
866     {
867         win_skip("FtpCommandA() is not available. Skipping the Ftp get_current_dir tests\n");
868         return;
869     }
870 
871     /* change directories to get a more interesting pwd */
872     bRet = pFtpCommandA(hFtp, FALSE, FTP_TRANSFER_TYPE_ASCII, "CWD pub/", 0, NULL);
873     if(bRet == FALSE)
874     {
875         skip("Failed to change directories in test_get_current_dir(HINTERNET hFtp).\n");
876         return;
877     }
878 
879     /* test with all NULL arguments */
880     SetLastError(0xdeadbeef);
881     bRet = FtpGetCurrentDirectoryA( NULL, NULL, 0 );
882     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
883     ok ( GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got: %d\n", GetLastError());
884 
885     /* test with NULL parameters instead of expected LPSTR/LPDWORD */
886     SetLastError(0xdeadbeef);
887     bRet = FtpGetCurrentDirectoryA( hFtp, NULL, 0 );
888     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
889     ok ( GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got: %d\n", GetLastError());
890 
891     /* test with no valid handle and valid parameters */
892     SetLastError(0xdeadbeef);
893     bRet = FtpGetCurrentDirectoryA( NULL, lpszCurrentDirectory, &dwCurrentDirectoryLen );
894     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
895     ok ( GetLastError() == ERROR_INVALID_HANDLE, "Expected ERROR_INVALID_HANDLE, got: %d\n", GetLastError());
896 
897     /* test with invalid dwCurrentDirectory and all other parameters correct */
898     SetLastError(0xdeadbeef);
899     bRet = FtpGetCurrentDirectoryA( hFtp, lpszCurrentDirectory, 0 );
900     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
901     ok ( GetLastError() == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got: %d\n", GetLastError());
902 
903     /* test with invalid lpszCurrentDirectory and all other parameters correct */
904     SetLastError(0xdeadbeef);
905     bRet = FtpGetCurrentDirectoryA( hFtp, NULL, &dwCurrentDirectoryLen );
906     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
907     ok ( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got: %d\n", GetLastError());
908 
909     /* test to show it checks the handle type */
910     SetLastError(0xdeadbeef);
911     bRet = FtpGetCurrentDirectoryA( hConnect, lpszCurrentDirectory, &dwCurrentDirectoryLen );
912     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n" );
913     ok ( GetLastError() == ERROR_INTERNET_INCORRECT_HANDLE_TYPE,
914     "Expected ERROR_INTERNET_INCORRECT_HANDLE_TYPE, got: %d\n", GetLastError());
915 
916     /* test for the current directory with legitimate values */
917     SetLastError(0xdeadbeef);
918     bRet = FtpGetCurrentDirectoryA( hFtp, lpszCurrentDirectory, &dwCurrentDirectoryLen );
919     ok ( bRet == TRUE, "Expected FtpGetCurrentDirectoryA to pass\n" );
920     ok ( !strcmp(lpszCurrentDirectory, "/pub"), "Expected returned value \"%s\" to match \"/pub\"\n", lpszCurrentDirectory);
921     ok ( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got: %d\n", GetLastError());
922 
923     /* test for the current directory with a size only large enough to
924      * fit the string and not the null terminating character */
925     SetLastError(0xdeadbeef);
926     dwCurrentDirectoryLen = 4;
927     lpszCurrentDirectory[4] = 'a'; /* set position 4 of the array to something else to make sure a leftover \0 isn't fooling the test */
928     bRet = FtpGetCurrentDirectoryA( hFtp, lpszCurrentDirectory, &dwCurrentDirectoryLen );
929     ok ( bRet == FALSE, "Expected FtpGetCurrentDirectoryA to fail\n");
930     ok ( strcmp(lpszCurrentDirectory, "/pub"), "Expected returned value \"%s\" to not match \"/pub\"\n", lpszCurrentDirectory);
931     ok ( GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Expected ERROR_INSUFFICIENT_BUFFER, got: %d\n", GetLastError());
932 
933     /* test for the current directory with a size large enough to store
934      * the expected string as well as the null terminating character */
935     SetLastError(0xdeadbeef);
936     dwCurrentDirectoryLen = 5;
937     bRet = FtpGetCurrentDirectoryA( hFtp, lpszCurrentDirectory, &dwCurrentDirectoryLen );
938     ok ( bRet == TRUE, "Expected FtpGetCurrentDirectoryA to pass\n");
939     ok ( !strcmp(lpszCurrentDirectory, "/pub"), "Expected returned value \"%s\" to match \"/pub\"\n", lpszCurrentDirectory);
940     ok ( GetLastError() == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got: %d\n", GetLastError());
941 }
942 
943 static void WINAPI status_callback(HINTERNET handle, DWORD_PTR ctx, DWORD status, LPVOID info, DWORD info_len)
944 {
945     switch (status)
946     {
947     case INTERNET_STATUS_RESOLVING_NAME:
948     case INTERNET_STATUS_NAME_RESOLVED:
949     case INTERNET_STATUS_CONNECTING_TO_SERVER:
950     case INTERNET_STATUS_CONNECTED_TO_SERVER:
951         trace("%p %lx %u %s %u\n", handle, ctx, status, (char *)info, info_len);
952         break;
953     default:
954         break;
955     }
956 }
957 
958 static void test_status_callbacks(HINTERNET hInternet)
959 {
960     INTERNET_STATUS_CALLBACK cb;
961     HINTERNET hFtp;
962     BOOL ret;
963 
964     cb = pInternetSetStatusCallbackA(hInternet, status_callback);
965     ok(cb == NULL, "expected NULL got %p\n", cb);
966 
967     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", NULL,
968                            INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 1);
969     if (!hFtp)
970     {
971         skip("No ftp connection could be made to ftp.winehq.org %u\n", GetLastError());
972         return;
973     }
974 
975     ret = InternetCloseHandle(hFtp);
976     ok(ret, "InternetCloseHandle failed %u\n", GetLastError());
977 
978     cb = pInternetSetStatusCallbackA(hInternet, NULL);
979     ok(cb == status_callback, "expected check_status got %p\n", cb);
980 }
981 
982 START_TEST(ftp)
983 {
984     HMODULE hWininet;
985     HANDLE hInternet, hFtp, hHttp;
986 
987     hWininet = GetModuleHandleA("wininet.dll");
988 
989     if(!GetProcAddress(hWininet, "InternetGetCookieExW")) {
990         win_skip("Too old IE (older than 6.0)\n");
991         return;
992     }
993 
994     pFtpCommandA = (void*)GetProcAddress(hWininet, "FtpCommandA");
995     pInternetSetStatusCallbackA = (void*)GetProcAddress(hWininet, "InternetSetStatusCallbackA");
996 
997     SetLastError(0xdeadbeef);
998     hInternet = InternetOpenA("winetest", 0, NULL, NULL, 0);
999     ok(hInternet != NULL, "InternetOpen failed: %u\n", GetLastError());
1000 
1001     hFtp = InternetConnectA(hInternet, "ftp.winehq.org", INTERNET_DEFAULT_FTP_PORT, "anonymous", NULL, INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
1002     if (!hFtp)
1003     {
1004         InternetCloseHandle(hInternet);
1005         skip("No ftp connection could be made to ftp.winehq.org\n");
1006         return;
1007     }
1008     hHttp = InternetConnectA(hInternet, "www.winehq.org", INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
1009     if (!hHttp)
1010     {
1011         InternetCloseHandle(hFtp);
1012         InternetCloseHandle(hInternet);
1013         skip("No http connection could be made to www.winehq.org\n");
1014         return;
1015     }
1016 
1017     /* The first call should always be a proper InternetOpen, if not
1018      * several calls will return ERROR_INTERNET_NOT_INITIALIZED when
1019      * all parameters are correct but no session handle is given. Whereas
1020      * the same call will return ERROR_INVALID_HANDLE if an InternetOpen
1021      * is done before.
1022      * The following test will show that behaviour, where the tests inside
1023      * the other sub-tests will show the other situation.
1024      */
1025     test_getfile_no_open();
1026     test_connect(hInternet);
1027     test_createdir(hFtp, hHttp);
1028     test_deletefile(hFtp, hHttp);
1029     test_getfile(hFtp, hHttp);
1030     test_openfile(hFtp, hHttp);
1031     test_putfile(hFtp, hHttp);
1032     test_removedir(hFtp, hHttp);
1033     test_renamefile(hFtp, hHttp);
1034     test_command(hFtp, hHttp);
1035     test_find_first_file(hFtp, hHttp);
1036     test_get_current_dir(hFtp, hHttp);
1037     test_status_callbacks(hInternet);
1038 
1039     InternetCloseHandle(hHttp);
1040     InternetCloseHandle(hFtp);
1041     InternetCloseHandle(hInternet);
1042 }
1043