1 /* $Id: test_ncbi_ftp_connector.c 597284 2019-11-19 18:55:42Z lavr $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author:  Anton Lavrentiev
27  *
28  * File Description:
29  *   Test case for FTP-based CONNECTOR
30  *
31  */
32 
33 #include <connect/ncbi_connutil.h>
34 #include <connect/ncbi_ftp_connector.h>
35 #include "../ncbi_ansi_ext.h"
36 #include "../ncbi_priv.h"               /* CORE logging facilities */
37 #include <stdlib.h>
38 #ifdef HAVE_GETTIMEOFDAY
39 #  include <sys/time.h>
40 #endif /*HAVE_GETTIMEOFDAY*/
41 #include <time.h>
42 
43 #include "test_assert.h"  /* This header must go last */
44 
45 #define CONN_NCBI_FTP_HOST  "ftp-ext.ncbi.nlm.nih.gov"
46 
47 #define TEST_HOST  CONN_NCBI_FTP_HOST
48 #define TEST_PORT  0
49 #define TEST_USER  "ftp"
50 #define TEST_PASS  "none"
51 #define TEST_PATH  ((char*) 0)
52 
53 
s_GetTime(void)54 static double s_GetTime(void)
55 {
56 #ifdef HAVE_GETTIMEOFDAY
57     struct timeval t;
58     return gettimeofday(&t, 0) == 0 ? t.tv_sec + t.tv_usec / 1000000.0 : 0.0;
59 #else
60     time_t t = time(0);
61     return (double)((unsigned long) t);
62 #endif /*HAVE_GETTIMEOFDAY*/
63 }
64 
65 
main(int argc,char * argv[])66 int main(int argc, char* argv[])
67 {
68     static const char kChdir[] = "CWD /toolbox/ncbi_tools++\n";
69     static const char kFile[] = "DATA/Misc/test_ncbi_conn_stream.FTP.data";
70     int/*bool*/   cancel = 0, first;
71     TFTP_Flags    flag = 0;
72     SConnNetInfo* net_info;
73     char          buf[1024];
74     CONNECTOR     connector;
75     FILE*         data_file;
76     size_t        size, n;
77     double        elapsed;
78     EIO_Status    status;
79     CONN          conn;
80 
81     g_NCBI_ConnectRandomSeed
82         = (unsigned int) time(0) ^ NCBI_CONNECT_SRAND_ADDEND;
83     srand(g_NCBI_ConnectRandomSeed);
84 
85     /* Log and data-log streams */
86     CORE_SetLOGFormatFlags(fLOG_None          | fLOG_Level   |
87                            fLOG_OmitNoteLevel | fLOG_DateTime);
88     CORE_SetLOGFILE(stderr, 0/*false*/);
89     data_file = fopen("test_ncbi_ftp_connector.dat", "wb");
90     assert(data_file);
91 
92     assert((net_info = ConnNetInfo_Create(0)) != 0);
93     if (net_info->debug_printout == eDebugPrintout_Some)
94         flag |= fFTP_LogControl;
95     else if (net_info->debug_printout == eDebugPrintout_Data) {
96         char val[32];
97         ConnNetInfo_GetValue(0, REG_CONN_DEBUG_PRINTOUT, val, sizeof(val),
98                              DEF_CONN_DEBUG_PRINTOUT);
99         flag |= strcasecmp(val, "all") == 0 ? fFTP_LogAll : fFTP_LogData;
100     }
101     flag |= fFTP_UseFeatures;
102 
103     if (TEST_PORT) {
104         sprintf(buf, ":%hu", TEST_PORT);
105     } else {
106         *buf = 0;
107     }
108     CORE_LOGF(eLOG_Note, ("Connecting to ftp://%s:%s@%s%s/",
109                           TEST_USER, TEST_PASS, TEST_HOST, buf));
110     /* Run the tests */
111     connector = FTP_CreateConnectorSimple(TEST_HOST, TEST_PORT,
112                                           TEST_USER, TEST_PASS,
113                                           TEST_PATH, flag, 0);
114 
115     if (CONN_CreateEx(connector,
116                       fCONN_Supplement | fCONN_Untie, &conn) != eIO_Success) {
117         CORE_LOG(eLOG_Fatal, "Cannot create FTP download connection");
118     }
119 
120     assert(CONN_SetTimeout(conn, eIO_Open,      net_info->timeout)
121            == eIO_Success);
122     assert(CONN_SetTimeout(conn, eIO_ReadWrite, net_info->timeout)
123            == eIO_Success);
124     assert(CONN_SetTimeout(conn, eIO_Close,     net_info->timeout)
125            == eIO_Success);
126 
127     if (CONN_Read(conn, buf, sizeof(buf), &n, eIO_ReadPlain) != eIO_Closed)
128         CORE_LOG(eLOG_Fatal, "Test failed in empty READ");
129 
130     if (CONN_Write(conn, "aaa", 3, &n, eIO_WritePlain) != eIO_Success)
131         CORE_LOG(eLOG_Fatal, "Cannot write FTP command");
132 
133     if (CONN_Wait(conn, eIO_Read, net_info->timeout) != eIO_NotSupported)
134         CORE_LOG(eLOG_Fatal, "Test failed in waiting for READ");
135     CORE_LOG(eLOG_Note, "Unrecognized command correctly rejected");
136 
137     if (CONN_Write(conn, "LIST\nSIZE", 9, &n, eIO_WritePlain) != eIO_Unknown)
138         CORE_LOG(eLOG_Fatal, "Test failed to reject multiple commands");
139     CORE_LOG(eLOG_Note, "Multiple commands correctly rejected");
140 
141     status = CONN_Write(conn, "SIZE 1GB", 9, &n, eIO_WritePlain);
142     if (status == eIO_Success) {
143         char buf[128];
144         CONN_ReadLine(conn, buf, sizeof(buf) - 1, &n);
145         CORE_LOGF(eLOG_Note, ("SIZE file: %.*s", (int) n, buf));
146     } else {
147         CORE_LOGF(eLOG_Note, ("SIZE command not accepted: %s",
148                               IO_StatusStr(status)));
149     }
150 
151     if (CONN_Write(conn, "LIST", 4, &n, eIO_WritePlain) != eIO_Success)
152         CORE_LOG(eLOG_Fatal, "Cannot write LIST command");
153 
154     CORE_LOG(eLOG_Note, "LIST command output:");
155     first = 1/*true*/;
156     do {
157         status = CONN_Read(conn, buf, sizeof(buf), &n, eIO_ReadPlain);
158         if (n != 0) {
159             printf("%.*s", (int) n, buf);
160             first = 0/*false*/;
161             fflush(stdout);
162         }
163     } while (status == eIO_Success);
164     if (first  ||  status != eIO_Closed) {
165         printf("<%s>\n", status != eIO_Success ? IO_StatusStr(status) : "EOF");
166         fflush(stdout);
167     }
168 
169     if (CONN_Write(conn, "NLST\r\n", 6, &n, eIO_WritePlain) != eIO_Success)
170         CORE_LOG(eLOG_Fatal, "Cannot write NLST command");
171 
172     CORE_LOG(eLOG_Note, "NLST command output:");
173     first = 1/*true*/;
174     do {
175         status = CONN_Read(conn, buf, sizeof(buf), &n, eIO_ReadPlain);
176         if (n != 0) {
177             printf("%.*s", (int) n, buf);
178             first = 0/*false*/;
179             fflush(stdout);
180         } else
181             assert(status != eIO_Success);
182     } while (status == eIO_Success);
183     if (first  ||  status != eIO_Closed) {
184         printf("<%s>\n", status != eIO_Success ? IO_StatusStr(status) : "EOF");
185         fflush(stdout);
186     }
187 
188     if (CONN_Write(conn, "SIZE ", 5, &n, eIO_WritePersist) != eIO_Success)
189         CORE_LOG(eLOG_Fatal, "Cannot write SIZE directory command");
190     size = strlen(kChdir + 4);
191     if (CONN_Write(conn, kChdir + 4, size, &n, eIO_WritePlain) == eIO_Success){
192         CONN_ReadLine(conn, buf, sizeof(buf) - 1, &n);
193         CORE_LOGF(eLOG_Note, ("SIZE directory returned: %.*s", (int) n, buf));
194     } else {
195         CORE_LOGF(eLOG_Note, ("SIZE directory not accepted: %s",
196                               IO_StatusStr(status)));
197     }
198 
199     if (CONN_Write(conn, kChdir, sizeof(kChdir) - 1, &n, eIO_WritePlain)
200         != eIO_Success) {
201         CORE_LOGF(eLOG_Fatal, ("Cannot execute %.*s",
202                                (int) sizeof(kChdir) - 2, kChdir));
203     }
204     if (CONN_Write(conn, "PWD\n", 4, &n, eIO_WritePlain) == eIO_Success) {
205         CONN_ReadLine(conn, buf, sizeof(buf) - 1, &n);
206         CORE_LOGF(eLOG_Note, ("PWD returned: %.*s", (int) n, buf));
207     } else
208         CORE_LOG(eLOG_Fatal, "Cannot execute PWD");
209 
210     if (CONN_Write(conn, "CDUP\n", 5, &n, eIO_WritePlain) != eIO_Success)
211         CORE_LOG(eLOG_Fatal, "Cannot execute CDUP");
212     if (CONN_Write(conn, "XCUP\n", 5, &n, eIO_WritePlain) != eIO_Success)
213         CORE_LOG(eLOG_Fatal, "Cannot execute XCUP");
214 
215     if (CONN_Write(conn, kChdir, sizeof(kChdir) - 1, &n, eIO_WritePlain)
216         != eIO_Success) {
217         CORE_LOGF(eLOG_Fatal, ("Cannot re-execute %.*s",
218                                (int) sizeof(kChdir) - 2, kChdir));
219     }
220 
221     size = sizeof(kFile) - 1;
222     if ((status = CONN_Write(conn, "MDTM ", 5, &n, eIO_WritePersist))
223         == eIO_Success  &&  n == 5  &&
224         (status = CONN_Write(conn, kFile, size, &n, eIO_WritePlain))
225         == eIO_Success  &&  n == size) {
226         unsigned long val;
227         char buf[128];
228         CONN_ReadLine(conn, buf, sizeof(buf) - 1, &n);
229         if (n  &&  sscanf(buf, "%lu", &val) > 0) {
230             struct tm* tm;
231             time_t t = (time_t) val;
232             if ((tm = localtime(&t)) != 0)
233                 n = strftime(buf, sizeof(buf), "%m/%d/%Y %H:%M:%S", tm);
234         }
235         CORE_LOGF(eLOG_Note, ("MDTM returned: %.*s", (int) n, buf));
236     } else {
237         CORE_LOGF(eLOG_Note, ("MDTM command not accepted: %s",
238                               IO_StatusStr(status)));
239     }
240 
241     if ((status = CONN_Write(conn, "RETR ", 5, &n, eIO_WritePersist))
242         != eIO_Success  ||  n != 5  ||
243         (status = CONN_Write(conn, kFile, size, &n, eIO_WritePersist))
244         != eIO_Success  ||  n != size) {
245         CORE_LOGF(eLOG_Fatal, ("Cannot write 'RETR %s': %s",
246                                kFile, IO_StatusStr(status)));
247     }
248 
249     size = 0;
250     elapsed = s_GetTime();
251     do {
252         status = CONN_Read(conn, buf, sizeof(buf), &n, eIO_ReadPlain);
253         if (n != 0) {
254             fwrite(buf, n, 1, data_file);
255             fflush(data_file);
256             size += n;
257             rand();
258             if (argc > 1  &&  rand() % 100000 == 55555) {
259                 cancel = 1;
260                 break;
261             }
262         } else {
263             assert(status != eIO_Success);
264             if (status != eIO_Closed  ||  !size)
265                 CORE_LOGF(eLOG_Error, ("Read error: %s",IO_StatusStr(status)));
266         }
267     } while (status == eIO_Success);
268     if (status != eIO_Success)
269         cancel = 0;
270     elapsed = s_GetTime() - elapsed;
271 
272     if (!cancel  ||  !(rand() & 1)) {
273         if (cancel)
274             CORE_LOG(eLOG_Note, "Cancelling download by a command");
275         if (CONN_Write(conn, "NLST blah*", 10, &n, eIO_WritePlain)
276             != eIO_Success) {
277             CORE_LOG(eLOG_Fatal, "Cannot write garbled NLST command");
278         }
279 
280         CORE_LOG(eLOG_Note, "Garbled NLST command output (expected empty):");
281         first = 1/*true*/;
282         do {
283             status = CONN_Read(conn, buf, sizeof(buf), &n, eIO_ReadPlain);
284             if (n != 0) {
285                 printf("%.*s", (int) n, buf);
286                 first = 0/*false*/;
287                 fflush(stdout);
288             }
289         } while (status == eIO_Success);
290         if (first) {
291             printf("<EOF>\n");
292             fflush(stdout);
293         }
294     } else if (cancel)
295         CORE_LOG(eLOG_Note, "Closing with download still in progress");
296 
297     if (CONN_Close(conn) != eIO_Success)
298         CORE_LOG(eLOG_Fatal, "Error in closing FTP connection");
299 
300     /* Cleanup and exit */
301     fclose(data_file);
302     if (!cancel) {
303         CORE_LOGF(size ? eLOG_Note : eLOG_Fatal,
304                   ("%lu byte(s) downloaded in %.2f second(s) @ %.2fKB/s",
305                    (unsigned long) size, elapsed,
306                    (unsigned long) size / (1024 * (elapsed ? elapsed : 1.0))));
307     } else
308         remove("test_ncbi_ftp_connector.dat");
309     ConnNetInfo_Destroy(net_info);
310 
311     CORE_LOG(eLOG_Note, "TEST completed successfully");
312     CORE_SetLOG(0);
313     return 0;
314 }
315