comparison lib/parse-dir-listing.c @ 950:c7d7a081cd9c

2008-03-04 Brian Masney <masneyb@gftp.org> * lib/gftp.h lib/socket-connect.c lib/sockutils.c lib/protocols.c lib/Makefile.am lib/charset-conv.c lib/parse-dir-listing.c - split protocols.c into smaller files. No changes were made to the moved functions.
author masneyb
date Tue, 04 Mar 2008 12:28:40 +0000
parents
children
comparison
equal deleted inserted replaced
949:9a6571938f89 950:c7d7a081cd9c
1 /*****************************************************************************/
2 /* parse-dir-listing.c - contains functions for parsing the different types */
3 /* of directory listings. */
4 /* Copyright (C) 1998-2008 Brian Masney <masneyb@gftp.org> */
5 /* */
6 /* This program is free software; you can redistribute it and/or modify */
7 /* it under the terms of the GNU General Public License as published by */
8 /* the Free Software Foundation; either version 2 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* This program is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU General Public License */
17 /* along with this program; if not, write to the Free Software */
18 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */
19 /*****************************************************************************/
20
21 #include "gftp.h"
22 static const char cvsid[] = "$Id: protocols.c 952 2008-01-24 23:31:26Z masneyb $";
23
24 static char *
25 copy_token (/*@out@*/ char **dest, char *source)
26 {
27 char *endpos, savepos;
28
29 endpos = source;
30 while (*endpos != ' ' && *endpos != '\t' && *endpos != '\0')
31 endpos++;
32 if (*endpos == '\0')
33 {
34 *dest = NULL;
35 return (NULL);
36 }
37
38 savepos = *endpos;
39 *endpos = '\0';
40 *dest = g_malloc0 ((gulong) (endpos - source + 1));
41 strcpy (*dest, source);
42 *endpos = savepos;
43
44 /* Skip the blanks till we get to the next entry */
45 source = endpos + 1;
46 while ((*source == ' ' || *source == '\t') && *source != '\0')
47 source++;
48 return (source);
49 }
50
51
52 static char *
53 goto_next_token (char *pos)
54 {
55 while (*pos != ' ' && *pos != '\t' && *pos != '\0')
56 pos++;
57
58 while (*pos == ' ' || *pos == '\t')
59 pos++;
60
61 return (pos);
62 }
63
64
65 static time_t
66 parse_vms_time (char *str, char **endpos)
67 {
68 struct tm curtime;
69 time_t ret;
70
71 /* 8-JUN-2004 13:04:14 */
72 memset (&curtime, 0, sizeof (curtime));
73
74 *endpos = strptime (str, "%d-%b-%Y %H:%M:%S", &curtime);
75 if (*endpos == NULL)
76 *endpos = strptime (str, "%d-%b-%Y %H:%M", &curtime);
77
78 if (*endpos != NULL)
79 {
80 ret = mktime (&curtime);
81 for (; **endpos == ' ' || **endpos == '\t'; (*endpos)++);
82 }
83 else
84 {
85 ret = 0;
86 *endpos = goto_next_token (str);
87 if (*endpos != NULL)
88 *endpos = goto_next_token (*endpos);
89 }
90
91 return (ret);
92 }
93
94
95 time_t
96 parse_time (char *str, char **endpos)
97 {
98 struct tm curtime, *loctime;
99 time_t t, ret;
100 char *tmppos;
101 size_t slen;
102 int i, num;
103
104 slen = strlen (str);
105 memset (&curtime, 0, sizeof (curtime));
106 curtime.tm_isdst = -1;
107
108 if (slen > 4 && isdigit ((int) str[0]) && str[2] == '-' &&
109 isdigit ((int) str[3]))
110 {
111 /* This is how DOS will return the date/time */
112 /* 07-06-99 12:57PM */
113
114 tmppos = strptime (str, "%m-%d-%y %I:%M%p", &curtime);
115 }
116 else if (slen > 4 && isdigit ((int) str[0]) && str[2] == '-' &&
117 isalpha (str[3]))
118 {
119 /* 10-Jan-2003 09:14 */
120 tmppos = strptime (str, "%d-%h-%Y %H:%M", &curtime);
121 }
122 else if (slen > 4 && isdigit ((int) str[0]) && str[4] == '/')
123 {
124 /* 2003/12/25 */
125 tmppos = strptime (str, "%Y/%m/%d", &curtime);
126 }
127 else
128 {
129 /* This is how most UNIX, Novell, and MacOS ftp servers send their time */
130 /* Jul 06 12:57 or Jul 6 1999 */
131
132 if (strchr (str, ':') != NULL)
133 {
134 tmppos = strptime (str, "%h %d %H:%M", &curtime);
135 t = time (NULL);
136 loctime = localtime (&t);
137
138 if (curtime.tm_mon > loctime->tm_mon)
139 curtime.tm_year = loctime->tm_year - 1;
140 else
141 curtime.tm_year = loctime->tm_year;
142 }
143 else
144 tmppos = strptime (str, "%h %d %Y", &curtime);
145 }
146
147 if (tmppos != NULL)
148 ret = mktime (&curtime);
149 else
150 ret = 0;
151
152 if (endpos != NULL)
153 {
154 if (tmppos == NULL)
155 {
156 /* We cannot parse this date format. So, just skip this date field
157 and continue to the next token. This is mainly for the HTTP
158 support */
159
160 *endpos = str;
161 for (num = 0; num < 2 && **endpos != '\0'; num++)
162 {
163 for (i=0;
164 (*endpos)[i] != ' ' && (*endpos)[i] != '\t' &&
165 (*endpos)[i] != '\0';
166 i++);
167 *endpos += i;
168
169 for (i=0; (*endpos)[i] == ' ' || (*endpos)[i] == '\t'; i++);
170 *endpos += i;
171 }
172 }
173 else
174 *endpos = tmppos;
175 }
176
177 return (ret);
178 }
179
180
181 static mode_t
182 gftp_parse_vms_attribs (char **src, mode_t mask)
183 {
184 char *endpos;
185 mode_t ret;
186
187 if (*src == NULL)
188 return (0);
189
190 if ((endpos = strchr (*src, ',')) != NULL)
191 *endpos = '\0';
192
193 ret = 0;
194 if (strchr (*src, 'R') != NULL)
195 ret |= S_IRUSR | S_IRGRP | S_IROTH;
196 if (strchr (*src, 'W') != NULL)
197 ret |= S_IWUSR | S_IWGRP | S_IWOTH;
198 if (strchr (*src, 'E') != NULL)
199 ret |= S_IXUSR | S_IXGRP | S_IXOTH;
200
201 *src = endpos + 1;
202
203 return (ret & mask);
204 }
205
206
207 static int
208 gftp_parse_ls_vms (gftp_request * request, int fd, char *str, gftp_file * fle)
209 {
210 char *curpos, *endpos, tempstr[1024];
211 int multiline;
212 ssize_t len;
213
214 /* .PINE-DEBUG1;1 9 21-AUG-2002 20:06 [MYERSRG] (RWED,RWED,,) */
215 /* WWW.DIR;1 1 23-NOV-1999 05:47 [MYERSRG] (RWE,RWE,RE,E) */
216
217 /* Multiline VMS
218 $MAIN.TPU$JOURNAL;1
219 1/18 8-JUN-2004 13:04:14 [NUCLEAR,FISSION] (RWED,RWED,RE,)
220 TCPIP$FTP_SERVER.LOG;29
221 0/18 8-JUN-2004 14:42:04 [NUCLEAR,FISSION] (RWED,RWED,RE,)
222 TCPIP$FTP_SERVER.LOG;28
223 5/18 8-JUN-2004 13:05:11 [NUCLEAR,FISSION] (RWED,RWED,RE,)
224 TCPIP$FTP_SERVER.LOG;27
225 5/18 8-JUN-2004 13:03:51 [NUCLEAR,FISSION] (RWED,RWED,RE,) */
226
227 if ((curpos = strchr (str, ';')) == NULL)
228 return (GFTP_EFATAL);
229
230 multiline = strchr (str, ' ') == NULL;
231
232 *curpos = '\0';
233 if (strlen (str) > 4 && strcmp (curpos - 4, ".DIR") == 0)
234 {
235 fle->st_mode |= S_IFDIR;
236 *(curpos - 4) = '\0';
237 }
238
239 fle->file = g_strdup (str);
240
241 if (multiline)
242 {
243 if (request->get_next_dirlist_line == NULL)
244 return (GFTP_EFATAL);
245
246 len = request->get_next_dirlist_line (request, fd, tempstr,
247 sizeof (tempstr));
248 if (len <= 0)
249 return ((int) len);
250
251 for (curpos = tempstr; *curpos == ' ' || *curpos == '\t'; curpos++);
252 }
253 else
254 curpos = goto_next_token (curpos + 1);
255
256 fle->size = gftp_parse_file_size (curpos) * 512; /* Is this correct? */
257
258 curpos = goto_next_token (curpos);
259
260 fle->datetime = parse_vms_time (curpos, &curpos);
261
262 if (*curpos != '[')
263 return (GFTP_EFATAL);
264
265 if ((endpos = strchr (curpos, ']')) == NULL)
266 return (GFTP_EFATAL);
267
268 curpos = goto_next_token (endpos + 1);
269 if ((curpos = strchr (curpos, ',')) == NULL)
270 return (0);
271 curpos++;
272
273 fle->st_mode = gftp_parse_vms_attribs (&curpos, S_IRWXU);
274 fle->st_mode |= gftp_parse_vms_attribs (&curpos, S_IRWXG);
275 fle->st_mode |= gftp_parse_vms_attribs (&curpos, S_IRWXO);
276
277 fle->user = g_strdup ("");
278 fle->group = g_strdup ("");
279
280 return (0);
281 }
282
283
284 static int
285 gftp_parse_ls_mvs (char *str, gftp_file * fle)
286 {
287 char *curpos;
288
289 /* Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname */
290 /* SVI52A 3390 2003/12/10 8 216 FB 80 27920 PS CARDS.DELETES */
291 /* SVI528 3390 2003/12/12 1 5 FB 80 24000 PO CLIST */
292
293 curpos = goto_next_token (str + 1);
294 if (curpos == NULL)
295 return (GFTP_EFATAL);
296
297 curpos = goto_next_token (curpos + 1);
298 if (curpos == NULL)
299 return (GFTP_EFATAL);
300
301 fle->datetime = parse_time (curpos, &curpos);
302
303 curpos = goto_next_token (curpos);
304 if (curpos == NULL)
305 return (GFTP_EFATAL);
306
307 curpos = goto_next_token (curpos + 1);
308 if (curpos == NULL)
309 return (GFTP_EFATAL);
310
311 fle->size = gftp_parse_file_size (curpos) * 55996;
312 curpos = goto_next_token (curpos + 1);
313 if (curpos == NULL)
314 return (GFTP_EFATAL);
315
316 curpos = goto_next_token (curpos + 1);
317 if (curpos == NULL)
318 return (GFTP_EFATAL);
319
320 curpos = goto_next_token (curpos + 1);
321 if (curpos == NULL)
322 return (GFTP_EFATAL);
323
324 curpos = goto_next_token (curpos + 1);
325 if (curpos == NULL)
326 return (GFTP_EFATAL);
327
328 if (strncmp (curpos, "PS", 2) == 0)
329 fle->st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
330 else if (strncmp (curpos, "PO", 2) == 0)
331 fle->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
332 else
333 return (GFTP_EFATAL);
334
335 curpos = goto_next_token (curpos + 1);
336
337 fle->user = g_strdup (_("unknown"));
338 fle->group = g_strdup (_("unknown"));
339 fle->file = g_strdup (curpos);
340
341 return (0);
342 }
343
344
345 static int
346 gftp_parse_ls_eplf (char *str, gftp_file * fle)
347 {
348 char *startpos;
349 int isdir = 0;
350
351 startpos = str;
352 while (startpos)
353 {
354 startpos++;
355 switch (*startpos)
356 {
357 case '/':
358 isdir = 1;
359 break;
360 case 's':
361 fle->size = gftp_parse_file_size (startpos + 1);
362 break;
363 case 'm':
364 fle->datetime = strtol (startpos + 1, NULL, 10);
365 break;
366 }
367 startpos = strchr (startpos, ',');
368 }
369
370 if ((startpos = strchr (str, 9)) == NULL)
371 return (GFTP_EFATAL);
372
373 if (isdir)
374 fle->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
375 else
376 fle->st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
377
378 fle->file = g_strdup (startpos + 1);
379 fle->user = g_strdup (_("unknown"));
380 fle->group = g_strdup (_("unknown"));
381 return (0);
382 }
383
384
385 static int
386 gftp_parse_ls_unix (gftp_request * request, char *str, size_t slen,
387 gftp_file * fle)
388 {
389 char *endpos, *startpos, *pos, *attribs;
390 int cols;
391
392 /* If there is no space between the attribs and links field, just make one */
393 if (slen > 10)
394 str[10] = ' ';
395
396 /* Determine the number of columns */
397 cols = 0;
398 pos = str;
399 while (*pos != '\0')
400 {
401 while (*pos != '\0' && *pos != ' ' && *pos != '\t')
402 {
403 if (*pos == ':')
404 break;
405 pos++;
406 }
407
408 cols++;
409
410 if (*pos == ':')
411 {
412 cols++;
413 break;
414 }
415
416 while (*pos == ' ' || *pos == '\t')
417 pos++;
418 }
419
420 startpos = str;
421 /* Copy file attributes */
422 if ((startpos = copy_token (&attribs, startpos)) == NULL)
423 return (GFTP_EFATAL);
424
425 if (strlen (attribs) < 10)
426 return (GFTP_EFATAL);
427
428 fle->st_mode = gftp_convert_attributes_to_mode_t (attribs);
429 g_free (attribs);
430
431 if (cols >= 9)
432 {
433 /* Skip the number of links */
434 startpos = goto_next_token (startpos);
435
436 /* Copy the user that owns this file */
437 if ((startpos = copy_token (&fle->user, startpos)) == NULL)
438 return (GFTP_EFATAL);
439
440 /* Copy the group that owns this file */
441 if ((startpos = copy_token (&fle->group, startpos)) == NULL)
442 return (GFTP_EFATAL);
443 }
444 else
445 {
446 fle->group = g_strdup (_("unknown"));
447 if (cols == 8)
448 {
449 if ((startpos = copy_token (&fle->user, startpos)) == NULL)
450 return (GFTP_EFATAL);
451 }
452 else
453 fle->user = g_strdup (_("unknown"));
454 startpos = goto_next_token (startpos);
455 }
456
457 if (request->server_type == GFTP_DIRTYPE_CRAY)
458 {
459 /* See if this is a Cray directory listing. It has the following format:
460 drwx------ 2 feiliu g913 DK common 4096 Sep 24 2001 wv */
461 if (cols == 11 && strstr (str, "->") == NULL)
462 {
463 startpos = goto_next_token (startpos);
464 startpos = goto_next_token (startpos);
465 }
466 }
467
468 /* See if this is a block or character device. We will store the major number
469 in the high word and the minor number in the low word. */
470 if (GFTP_IS_SPECIAL_DEVICE (fle->st_mode) &&
471 (endpos = strchr (startpos, ',')) != NULL)
472 {
473 fle->size = (unsigned long) strtol (startpos, NULL, 10) << 16;
474
475 startpos = endpos + 1;
476 while (*startpos == ' ')
477 startpos++;
478
479 /* Get the minor number */
480 if ((endpos = strchr (startpos, ' ')) == NULL)
481 return (GFTP_EFATAL);
482 fle->size |= strtol (startpos, NULL, 10) & 0xFF;
483 }
484 else
485 {
486 /* This is a regular file */
487 if ((endpos = strchr (startpos, ' ')) == NULL)
488 return (GFTP_EFATAL);
489 fle->size = gftp_parse_file_size (startpos);
490 }
491
492 /* Skip the blanks till we get to the next entry */
493 startpos = endpos + 1;
494 while (*startpos == ' ')
495 startpos++;
496
497 fle->datetime = parse_time (startpos, &startpos);
498
499 /* Skip the blanks till we get to the next entry */
500 startpos = goto_next_token (startpos);
501
502 /* Parse the filename. If this file is a symbolic link, remove the -> part */
503 if (S_ISLNK (fle->st_mode) && ((endpos = strstr (startpos, "->")) != NULL))
504 *(endpos - 1) = '\0';
505
506 fle->file = g_strdup (startpos);
507
508 /* Uncomment this if you want to strip the spaces off of the end of the file.
509 I don't want to do this by default since there are valid filenames with
510 spaces at the end of them. Some broken FTP servers like the Paradyne IPC
511 DSLAMS append a bunch of spaces at the end of the file.
512 for (endpos = fle->file + strlen (fle->file) - 1;
513 *endpos == ' ';
514 *endpos-- = '\0');
515 */
516
517 return (0);
518 }
519
520
521 static int
522 gftp_parse_ls_nt (char *str, gftp_file * fle)
523 {
524 char *startpos;
525
526 startpos = str;
527 fle->datetime = parse_time (startpos, &startpos);
528
529 fle->user = g_strdup (_("unknown"));
530 fle->group = g_strdup (_("unknown"));
531
532 startpos = goto_next_token (startpos);
533
534 if (startpos[0] == '<')
535 fle->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
536 else
537 {
538 fle->st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
539 fle->size = gftp_parse_file_size (startpos);
540 }
541
542 startpos = goto_next_token (startpos);
543 fle->file = g_strdup (startpos);
544 return (0);
545 }
546
547
548 static int
549 gftp_parse_ls_novell (char *str, gftp_file * fle)
550 {
551 char *startpos;
552
553 if (str[12] != ' ')
554 return (GFTP_EFATAL);
555
556 str[12] = '\0';
557 fle->st_mode = gftp_convert_attributes_to_mode_t (str);
558 startpos = str + 13;
559
560 while ((*startpos == ' ' || *startpos == '\t') && *startpos != '\0')
561 startpos++;
562
563 if ((startpos = copy_token (&fle->user, startpos)) == NULL)
564 return (GFTP_EFATAL);
565
566 fle->group = g_strdup (_("unknown"));
567
568 while (*startpos != '\0' && !isdigit (*startpos))
569 startpos++;
570
571 fle->size = gftp_parse_file_size (startpos);
572
573 startpos = goto_next_token (startpos);
574 fle->datetime = parse_time (startpos, &startpos);
575
576 startpos = goto_next_token (startpos);
577 fle->file = g_strdup (startpos);
578 return (0);
579 }
580
581
582 int
583 gftp_parse_ls (gftp_request * request, const char *lsoutput, gftp_file * fle,
584 int fd)
585 {
586 char *str, *endpos, tmpchar;
587 int result, is_vms;
588 size_t len;
589
590 g_return_val_if_fail (lsoutput != NULL, GFTP_EFATAL);
591 g_return_val_if_fail (fle != NULL, GFTP_EFATAL);
592
593 str = g_strdup (lsoutput);
594 memset (fle, 0, sizeof (*fle));
595
596 len = strlen (str);
597 if (len > 0 && str[len - 1] == '\n')
598 str[--len] = '\0';
599 if (len > 0 && str[len - 1] == '\r')
600 str[--len] = '\0';
601
602 switch (request->server_type)
603 {
604 case GFTP_DIRTYPE_CRAY:
605 case GFTP_DIRTYPE_UNIX:
606 result = gftp_parse_ls_unix (request, str, len, fle);
607 break;
608 case GFTP_DIRTYPE_EPLF:
609 result = gftp_parse_ls_eplf (str, fle);
610 break;
611 case GFTP_DIRTYPE_NOVELL:
612 result = gftp_parse_ls_novell (str, fle);
613 break;
614 case GFTP_DIRTYPE_DOS:
615 result = gftp_parse_ls_nt (str, fle);
616 break;
617 case GFTP_DIRTYPE_VMS:
618 result = gftp_parse_ls_vms (request, fd, str, fle);
619 break;
620 case GFTP_DIRTYPE_MVS:
621 result = gftp_parse_ls_mvs (str, fle);
622 break;
623 default: /* autodetect */
624 if (*lsoutput == '+')
625 result = gftp_parse_ls_eplf (str, fle);
626 else if (isdigit ((int) str[0]) && str[2] == '-')
627 result = gftp_parse_ls_nt (str, fle);
628 else if (str[1] == ' ' && str[2] == '[')
629 result = gftp_parse_ls_novell (str, fle);
630 else
631 {
632 if ((endpos = strchr (str, ' ')) != NULL)
633 {
634 /* If the first token in the string has a ; in it, then */
635 /* we'll assume that this is a VMS directory listing */
636 tmpchar = *endpos;
637 *endpos = '\0';
638 is_vms = strchr (str, ';') != NULL;
639 *endpos = tmpchar;
640 }
641 else
642 is_vms = 0;
643
644 if (is_vms)
645 result = gftp_parse_ls_vms (request, fd, str, fle);
646 else
647 result = gftp_parse_ls_unix (request, str, len, fle);
648 }
649 break;
650 }
651 g_free (str);
652
653 return (result);
654 }
655