7b859c754782b5df789edbddf06995a7ad98caf4
[erwise] / ClFTP.c
1 /*
2  * ClFTP.c --
3  *
4  * Author: Teemu Rantanen <tvr@cs.hut.fi>
5  * Copyright (c) 1992 Teemu Rantanen
6  *                    All rights reserved
7  *
8  * Created: Sun Apr 19 22:42:51 1992 tvr
9  * Last modified: Mon May 11 23:30:15 1992 tvr
10  *
11  */
12
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/time.h>
16
17 #include "Cl.h"
18
19 #include "HTParse.h"
20 #include "HTUtils.h"
21 #include "tcp.h"
22 #include "HTAnchor.h"
23
24 #define IPPORT_FTP 21
25
26 /*
27  * Erwise's own ftp load function. Setup all things to be done
28  */
29
30 PUBLIC int HTFTP_open_file_read
31 ARGS2 (
32         char *, name,
33         HTParentAnchor *, anchor
34 )
35 {
36   int status;
37
38   /*
39    * Set up a list of things to do
40    */
41
42   {
43     static void (*functions[]) () =
44     {
45       WWWErwiseConnect,
46       WWWErwiseSetSelect,
47       WWWErwiseFtpGetCommand,   /* get junk */
48
49       WWWErwiseSetPoll,
50       WWWErwiseFtpUser,         /* Send user */
51       WWWErwiseSendCommand,
52       WWWErwiseSetSelect,
53       WWWErwiseFtpGetCommand,
54
55       WWWErwiseSetPoll,
56       WWWErwiseFtpPass,         /* send pass */
57       WWWErwiseSendCommand,
58       WWWErwiseSetSelect,
59       WWWErwiseFtpGetCommand,
60
61       WWWErwiseSetPoll,
62       WWWErwiseFtpBinary,       /* set binary mode */
63       WWWErwiseSendCommand,
64       WWWErwiseSetSelect,
65       WWWErwiseFtpGetCommand,
66
67       WWWErwiseSetPoll,
68       WWWErwiseFtpPassive,      /* set passive mode */
69       WWWErwiseSendCommand,
70       WWWErwiseSetSelect,
71
72       WWWErwiseFtpGetPassive,   /* Get reply and request a file */
73       WWWErwiseSetPoll,
74       WWWErwiseSendCommand,
75
76       WWWErwiseFtpDataChannel,  /* make connection */
77       WWWErwiseConnect,
78
79       WWWErwiseFtpCheckForError,/* check for file not found etc ... */
80
81       WWWErwiseSetSelect,       /* read data */
82       WWWErwiseReadData,
83
84       WWWErwiseSetPoll,         /* parse stuff */
85       WWWErwiseTerminateIfLoadToFile,
86       WWWErwiseParse,
87       NULL,
88     };
89     WWWErwiseConnection->function = functions;
90   }
91
92   /*
93    * Set up information needed later
94    */
95
96   WWWErwiseConnection->ftphost = HTParse (name, "", PARSE_HOST);
97
98   WWWErwiseConnection->anAnchor = anchor;
99
100   /*
101    * Start connecting to data port
102    */
103
104   status = cl_start_connection (WWWErwiseConnection->ftphost,
105                                 WWWErwiseConnection,
106                                 IPPORT_FTP);
107
108   /*
109    * FTP mode on formatting is PLAINTEXT
110    */
111
112   WWWErwiseConnection->diag = 1;
113
114   return status;
115 }
116
117
118 /*
119  * Send misc commands to data flow
120  */
121
122 void
123 WWWErwiseFtpUser ()
124 {
125   CL_DEBUG (("FTP: send user\n"));
126
127   WWWErwiseConnection->command = strdup ("USER ftp\r\n");
128
129   WWWErwiseConnection->function++;
130 }
131
132 void
133 WWWErwiseFtpPass ()
134 {
135   char tmp[8192], hostname[1024];
136
137   CL_DEBUG (("FTP: send pass\n"));
138
139   if (!gethostname (hostname, 1024))
140     {
141       strcpy (hostname, "noname.rupu");
142     }
143
144   sprintf (tmp, "PASS erwise@%s\r\n", hostname);
145
146   WWWErwiseConnection->command = strdup (tmp);
147
148   WWWErwiseConnection->function++;
149 }
150
151 void
152 WWWErwiseFtpPassive ()
153 {
154   CL_DEBUG (("FTP: send pasv\n"));
155
156   WWWErwiseConnection->command = strdup ("PASV\r\n");
157
158   WWWErwiseConnection->function++;
159 }
160
161 void
162 WWWErwiseFtpBinary ()
163 {
164   CL_DEBUG (("FTP: set binary\n"));
165
166   WWWErwiseConnection->command = strdup ("TYPE I\r\n");
167
168   WWWErwiseConnection->function++;
169 }
170
171
172 /*
173  * Get junk commands return when the are accomplished
174  */
175
176 #define ERWISE_BL_SIZE   8192
177
178 void
179 WWWErwiseFtpGetCommand ()
180 {
181   char tmp[ERWISE_BL_SIZE];
182
183   int i;
184
185   i = NETREAD (WWWErwiseConnection->fd, tmp, ERWISE_BL_SIZE);
186
187   if ((i == -1) && (errno == EWOULDBLOCK))
188     {
189       return;
190     }
191
192   CL_DEBUG (("FTP: got %s\n", tmp));
193
194   if ((tmp[0] < '2') || (tmp[0] > '3'))
195     {
196
197       WWWErwiseStatus = CL_FAILED;
198
199       return;
200
201     }
202
203   WWWErwiseConnection->function++;
204
205 }
206 /*
207  * Get return code from passive command
208  */
209
210 void
211 WWWErwiseFtpGetPassive ()
212 {
213   char tmp[1024], *p;
214   int a1, a2, a3, a4;
215   int p1, p2, port;
216
217   int i;
218
219   i = NETREAD (WWWErwiseConnection->fd, tmp, 1024);
220
221   if ((i == -1) && (errno == EWOULDBLOCK))
222     {
223       return;
224     }
225
226   tmp[i] = 0;
227
228   CL_DEBUG (("FTP: got passive %s\n", tmp));
229
230   /*
231    * Get to the line which is reply from PASV command.
232    * Check for errors on the way.
233    */
234
235   p = tmp;
236
237   i = 1;
238
239   while (i)
240     {
241       if ((*p >= '0') && (*p <= '9'))
242         {
243           if (!strncmp ("227", p, 3))
244             {
245               i = 0;
246               continue;
247             }
248
249           if ((p[0] < '2') || (tmp[0] > '3'))
250             {
251               WWWErwiseStatus = CL_FAILED;
252               return;
253             }
254         }
255
256       /*
257        * Get next line
258        */
259       while (*p && (*p != '\n'))
260         p++;
261
262       p++;
263
264       if (!*p)
265         {
266           /*
267            * no line but no errors yet
268            */
269           return;
270         }
271     }
272
273   /*
274    * Correct line, get port
275    */
276
277   while (*p && (*p != '('))
278     p++;
279
280   if (!*p)
281     {
282       /*
283        * 227 but no address / port ??
284        */
285
286       WWWErwiseStatus = CL_FAILED;
287
288       return;
289     }
290
291   sscanf (p, "(%d,%d,%d,%d,%d,%d)", &a1, &a2, &a3, &a4, &p1, &p2);
292
293   port = p1 * 256 + p2;
294
295   printf ("ErwiseFTP: got connection to %d.%d.%d.%d on port %d\n",
296           a1, a2, a3, a4, port);
297
298   /*
299    * send receive command too
300    */
301
302   {
303     char *filename = HTParse (WWWErwiseConnection->address, "",
304                               PARSE_PATH + PARSE_PUNCTUATION);
305
306     sprintf (tmp, "RETR %s\r\n", filename);
307
308     WWWErwiseConnection->command = strdup (tmp);
309   }
310
311   WWWErwiseConnection->port = port;
312
313   WWWErwiseConnection->function++;
314 }
315
316
317 /*
318  * Make data channel connection
319  */
320 void
321 WWWErwiseFtpDataChannel ()
322 {
323   /*
324    * Set data flow port to secondary so that it can be closed properly
325    */
326
327   WWWErwiseConnection->secondary_fd = WWWErwiseConnection->fd;
328
329   cl_start_connection (WWWErwiseConnection->ftphost,
330                        WWWErwiseConnection,
331                        WWWErwiseConnection->port);
332
333   WWWErwiseConnection->function++;
334 }
335
336
337
338 /*
339  * Start connection. Make sure it is nonblocking one.
340  */
341
342 int
343 cl_start_connection (host, connection, port)
344      char *host;
345      ClConnection_t *connection;
346      int port;
347 {
348   /*
349    * Code partly from get_connection()
350    */
351
352   int s;
353
354   struct hostent *phost;        /* Pointer to host -- See netdb.h */
355   struct sockaddr_in soc_address;       /* Binary network address */
356   struct sockaddr_in *sin = &soc_address;
357
358 /*  Set up defaults:
359 */
360   sin->sin_family = AF_INET;    /* Family, host order  */
361   sin->sin_port = htons (port);
362
363 /* Get node name:
364 */
365   {
366     if (*host >= '0' && *host <= '9')
367       {                         /* Numeric node address: */
368         sin->sin_addr.s_addr = inet_addr (host);        /* See arpa/inet.h */
369
370       }
371     else
372       {                         /* Alphanumeric node name: */
373         phost = gethostbyname (host);   /* See netdb.h */
374         if (!phost)
375           {
376             if (TRACE)
377               printf (
378                        "FTP: Can't find internet node name `%s'.\n",
379                        host);
380
381             WWWErwiseStatus = CL_FAILED;
382
383             return -1;
384           }
385         memcpy (&sin->sin_addr, phost->h_addr, phost->h_length);
386       }
387
388     if (TRACE)
389       printf (
390                "FTP: Parsed remote address as port %d, inet %d.%d.%d.%d\n",
391                (unsigned int) ntohs (sin->sin_port),
392                (int) *((unsigned char *) (&sin->sin_addr) + 0),
393                (int) *((unsigned char *) (&sin->sin_addr) + 1),
394                (int) *((unsigned char *) (&sin->sin_addr) + 2),
395                (int) *((unsigned char *) (&sin->sin_addr) + 3));
396   }                             /* scope of p1 */
397
398
399   {
400     int status;
401
402     s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
403
404     if (s < 0)
405       {
406         WWWErwiseStatus = CL_FAILED;
407
408         return -1;
409       }
410
411     connection->fd = s;
412
413     /*
414      * Must NOT select connection now. Must poll.
415      */
416     connection->select_fd = 0;
417
418     (void) fcntl (s, F_SETFL, O_NONBLOCK);
419     (void) fcntl (s, F_SETFL, FNDELAY);
420
421     status = erwise_connect (s,
422                              (struct sockaddr *) & soc_address,
423                              sizeof (soc_address));
424   }
425
426   /*
427    * everything done
428    */
429
430   return s;
431 }
432
433
434
435 /*
436  * Check for errors when retr is sent
437  */
438
439 void
440 WWWErwiseFtpCheckForError ()
441 {
442   fd_set readfds;
443   fd_set exceptionfds;
444
445   struct timeval tv;
446
447   int fd, fd2;
448
449   fd = WWWErwiseConnection->fd;
450   fd2 = WWWErwiseConnection->secondary_fd;
451
452   tv.tv_sec = tv.tv_usec = 0;
453
454   FD_ZERO (&readfds);
455   FD_SET (fd, &readfds);
456   FD_SET (fd2, &readfds);
457
458   FD_ZERO (&exceptionfds);
459   FD_SET (fd, &exceptionfds);
460   FD_SET (fd2, &exceptionfds);
461
462   select ((fd > fd2 ? fd : fd2) + 1, &readfds, NULL, &exceptionfds, &tv);
463
464   /*
465    * Error on either data or command channel
466    */
467   if (FD_ISSET (fd, &exceptionfds) || FD_ISSET (fd2, &exceptionfds))
468     {
469
470       WWWErwiseStatus = CL_FAILED;
471
472       return;
473     }
474
475   /*
476    * Data is flowing (OK)
477    */
478   if (FD_ISSET (fd, &readfds))
479     {
480
481       WWWErwiseConnection->function++;
482
483       return;
484     }
485
486   /*
487    * Anything here is considered an error
488    */
489   if (FD_ISSET (fd2, &readfds))
490     {
491
492       WWWErwiseStatus = CL_FAILED;
493
494       return;
495     }
496 }