1 /* Utilities for VFS modules.
3 Copyright (C) 1988, 1992 Free Software Foundation
4 Copyright (C) 1995, 1996 Miguel de Icaza
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License
8 as published by the Free Software Foundation; either version 2 of
9 the License, or (at your option) any later version.
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 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 /* Extract the hostname and username from the path */
27 /* path is in the form: [user@]hostname:port/remote-dir, e.g.:
29 * ftp://sunsite.unc.edu/pub/linux
30 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
31 * ftp://tsx-11.mit.edu:8192/
32 * ftp://joe@foo.edu:11321/private
33 * ftp://joe:password@foo.se
35 * Returns g_malloc()ed host, user and pass they are present.
36 * If the user is empty, e.g. ftp://@roxanne/private, and URL_ALLOW_ANON
37 * is not set, then the current login name is supplied.
39 * Return value is a g_malloc()ed string with the pathname relative to the
45 vfs_split_url (const char *path
, char **host
, char **user
, int *port
,
46 char **pass
, int default_port
, int flags
)
48 struct passwd
*passwd_info
;
49 char *dir
, *colon
, *inner_colon
, *at
, *rest
;
51 char *pcopy
= g_strdup (path
);
52 char *pend
= pcopy
+ strlen (pcopy
);
61 if (!(flags
& URL_NOSLASH
)) {
62 /* locate path component */
63 while (*dir
!= PATH_SEP
&& *dir
)
66 retval
= g_strdup (dir
);
69 retval
= g_strdup (PATH_SEP_STR
);
72 /* search for any possible user */
73 at
= strchr (pcopy
, '@');
75 /* We have a username */
78 inner_colon
= strchr (pcopy
, ':');
83 *pass
= g_strdup (inner_colon
);
86 *user
= g_strdup (pcopy
);
95 if (!*user
&& !(flags
& URL_ALLOW_ANON
)) {
96 passwd_info
= getpwuid (geteuid ());
97 if (passwd_info
&& passwd_info
->pw_name
)
98 *user
= g_strdup (passwd_info
->pw_name
);
100 /* This is very unlikely to happen */
101 *user
= g_strdup ("anonymous");
106 /* Check if the host comes with a port spec, if so, chop it */
107 colon
= strchr (rest
, ':');
110 if (sscanf (colon
+ 1, "%d", port
) == 1) {
111 if (*port
<= 0 || *port
>= 65536)
112 *port
= default_port
;
127 *host
= g_strdup (rest
);
132 #endif /* USE_NETCODE */
135 * Look up a user or group name from a uid/gid, maintaining a cache.
136 * FIXME, for now it's a one-entry cache.
137 * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
138 * This file should be modified for non-unix systems to do something
149 #define myuid ( my_uid < 0? (my_uid = getuid()): my_uid )
150 #define mygid ( my_gid < 0? (my_gid = getgid()): my_gid )
153 vfs_finduid (const char *uname
)
155 static int saveuid
= -993;
156 static char saveuname
[TUNMLEN
];
157 static int my_uid
= -993;
161 if (uname
[0] != saveuname
[0] /* Quick test w/o proc call */
162 ||0 != strncmp (uname
, saveuname
, TUNMLEN
)) {
163 g_strlcpy (saveuname
, uname
, TUNMLEN
);
164 pw
= getpwnam (uname
);
166 saveuid
= pw
->pw_uid
;
175 vfs_findgid (const char *gname
)
177 static int savegid
= -993;
178 static char savegname
[TGNMLEN
];
179 static int my_gid
= -993;
183 if (gname
[0] != savegname
[0] /* Quick test w/o proc call */
184 ||0 != strncmp (gname
, savegname
, TUNMLEN
)) {
185 g_strlcpy (savegname
, gname
, TUNMLEN
);
186 gr
= getgrnam (gname
);
188 savegid
= gr
->gr_gid
;
197 * Create a temporary file with a name resembling the original.
198 * This is needed e.g. for local copies requested by extfs.
199 * Some extfs scripts may look at the extension.
200 * We also protect stupid scripts agains dangerous names.
203 vfs_mkstemps (char **pname
, const char *prefix
, const char *basename
)
205 const unsigned char *p
;
210 /* Strip directories */
211 p
= strrchr (basename
, PATH_SEP
);
217 /* Protection against very long names */
218 shift
= strlen (p
) - (MC_MAXPATHLEN
- 16);
222 suffix
= g_malloc (MC_MAXPATHLEN
);
224 /* Protection against unusual characters */
226 while (*p
&& (*p
!= '#')) {
227 if (strchr (".-_@", *p
) || isalnum (*p
))
233 fd
= mc_mkstemps (pname
, prefix
, suffix
);
238 /* Parsing code is used by ftpfs, fish and extfs */
241 static char *columns
[MAXCOLS
]; /* Points to the string in column n */
242 static int column_ptr
[MAXCOLS
]; /* Index from 0 to the starting positions of the columns */
245 vfs_split_text (char *p
)
250 memset (columns
, 0, sizeof (columns
));
252 for (numcols
= 0; *p
&& numcols
< MAXCOLS
; numcols
++) {
253 while (*p
== ' ' || *p
== '\r' || *p
== '\n') {
257 columns
[numcols
] = p
;
258 column_ptr
[numcols
] = p
- original
;
259 while (*p
&& *p
!= ' ' && *p
!= '\r' && *p
!= '\n')
268 char *column
= columns
[idx
];
270 if (!column
|| column
[0] < '0' || column
[0] > '9')
276 /* Return 1 for MM-DD-YY and MM-DD-YYYY */
278 is_dos_date (const char *str
)
286 if (len
!= 8 && len
!= 10)
289 if (str
[2] != str
[5])
292 if (!strchr ("\\-/", (int) str
[2]))
299 is_week (const char *str
, struct tm
*tim
)
301 static const char *week
= "SunMonTueWedThuFriSat";
307 if ((pos
= strstr (week
, str
)) != NULL
) {
309 tim
->tm_wday
= (pos
- week
) / 3;
316 is_month (const char *str
, struct tm
*tim
)
318 static const char *month
= "JanFebMarAprMayJunJulAugSepOctNovDec";
324 if ((pos
= strstr (month
, str
)) != NULL
) {
326 tim
->tm_mon
= (pos
- month
) / 3;
333 * Check for possible locale's abbreviated month name (Jan..Dec).
334 * Any 3 bytes long string without digit and control characters.
335 * isalpha() is locale specific, so it cannot be used if current
336 * locale is "C" and ftp server use Cyrillic.
337 * TODO: Punctuation characters also cannot be part of month name.
338 * NB: It is assumed there are no whitespaces in month.
341 is_localized_month (const unsigned char *month
)
344 while ((i
< 3) && *month
&& !isdigit (*month
) && !iscntrl (*month
)) {
348 return ((i
== 3) && (*month
== 0));
352 is_time (const char *str
, struct tm
*tim
)
359 if ((p
= strchr (str
, ':')) && (p2
= strrchr (str
, ':'))) {
362 (str
, "%2d:%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
,
366 if (sscanf (str
, "%2d:%2d", &tim
->tm_hour
, &tim
->tm_min
) != 2)
376 is_year (char *str
, struct tm
*tim
)
383 if (strchr (str
, ':'))
386 if (strlen (str
) != 4)
389 if (sscanf (str
, "%ld", &year
) != 1)
392 if (year
< 1900 || year
> 3000)
395 tim
->tm_year
= (int) (year
- 1900);
401 * FIXME: this is broken. Consider following entry:
402 * -rwx------ 1 root root 1 Aug 31 10:04 2904 1234
403 * where "2904 1234" is filename. Well, this code decodes it as year :-(.
407 vfs_parse_filetype (char c
)
418 case 's': /* Socket */
422 /* If not supported, we fall through to IFIFO */
425 case 'D': /* Solaris door */
434 case 'n': /* Don't know what these are :-) */
444 vfs_parse_filemode (const char *p
)
445 { /* converts rw-rw-rw- into 0666 */
470 res
|= 0100 | S_ISUID
;
503 res
|= 0010 | S_ISGID
;
505 case 'l': /* Solaris produces these */
537 res
|= 0001 | S_ISVTX
;
550 /* This function parses from idx in the columns[] array */
552 vfs_parse_filedate (int idx
, time_t *t
)
558 int l10n
= 0; /* Locale's abbreviated month name */
560 struct tm
*local_time
;
562 /* Let's setup default time values */
563 current_time
= time (NULL
);
564 local_time
= localtime (¤t_time
);
565 tim
.tm_mday
= local_time
->tm_mday
;
566 tim
.tm_mon
= local_time
->tm_mon
;
567 tim
.tm_year
= local_time
->tm_year
;
572 tim
.tm_isdst
= -1; /* Let mktime() try to guess correct dst offset */
576 /* We eat weekday name in case of extfs */
577 if (is_week (p
, &tim
))
581 if (is_month (p
, &tim
)) {
582 /* And we expect, it followed by day number */
584 tim
.tm_mday
= (int) atol (columns
[idx
++]);
586 return 0; /* No day */
589 /* We usually expect:
592 But in case of extfs we allow these date formats:
595 Wek Mon DD hh:mm:ss YYYY
597 where Mon is Jan-Dec, DD, MM, YY two digit day, month, year,
598 YYYY four digit year, hh, mm, ss two digit hour, minute or second. */
600 /* Special case with MM-DD-YY or MM-DD-YYYY */
601 if (is_dos_date (p
)) {
604 if (sscanf (p
, "%2d-%2d-%d", &d
[0], &d
[1], &d
[2]) == 3) {
605 /* Months are zero based */
622 return 0; /* sscanf failed */
624 /* Locale's abbreviated month name followed by day number */
625 if (is_localized_month (p
) && (is_num (idx
++)))
628 return 0; /* unsupported format */
632 /* Here we expect to find time and/or year */
635 if (is_time (columns
[idx
], &tim
)
636 || (got_year
= is_year (columns
[idx
], &tim
))) {
639 /* This is a special case for ctime() or Mon DD YYYY hh:mm */
640 if (is_num (idx
) && (columns
[idx
+ 1][0])) {
642 if (is_time (columns
[idx
], &tim
))
643 idx
++; /* time also */
645 if ((got_year
= is_year (columns
[idx
], &tim
)))
646 idx
++; /* year also */
649 } /* only time or date */
651 return 0; /* Nor time or date */
654 * If the date is less than 6 months in the past, it is shown without year
655 * other dates in the past or future are shown with year but without time
656 * This does not check for years before 1900 ... I don't know, how
657 * to represent them at all
659 if (!got_year
&& local_time
->tm_mon
< 6
660 && local_time
->tm_mon
< tim
.tm_mon
661 && tim
.tm_mon
- local_time
->tm_mon
>= 6)
665 if (l10n
|| (*t
= mktime (&tim
)) < 0)
671 vfs_parse_ls_lga (const char *p
, struct stat
*s
, char **filename
,
674 int idx
, idx2
, num_cols
;
678 const char *line
= p
;
680 if (strncmp (p
, "total", 5) == 0)
683 if ((i
= vfs_parse_filetype (*(p
++))) == -1)
687 if (*p
== ' ') /* Notwell 4 */
690 if (strlen (p
) <= 8 || p
[8] != ']')
692 /* Should parse here the Notwell permissions :) */
693 if (S_ISDIR (s
->st_mode
))
695 (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
| S_IXUSR
| S_IXGRP
698 s
->st_mode
|= (S_IRUSR
| S_IRGRP
| S_IROTH
| S_IWUSR
);
701 if ((i
= vfs_parse_filemode (p
)) == -1)
706 /* This is for an extra ACL attribute (HP-UX) */
711 p_copy
= g_strdup (p
);
712 num_cols
= vfs_split_text (p_copy
);
714 s
->st_nlink
= atol (columns
[0]);
715 if (s
->st_nlink
<= 0)
719 s
->st_uid
= vfs_finduid (columns
[1]);
721 s
->st_uid
= (uid_t
) atol (columns
[1]);
723 /* Mhm, the ls -lg did not produce a group field */
724 for (idx
= 3; idx
<= 5; idx
++)
725 if (is_month (columns
[idx
], NULL
) || is_week (columns
[idx
], NULL
)
726 || is_dos_date (columns
[idx
])
727 || is_localized_month (columns
[idx
]))
731 || (idx
== 5 && !S_ISCHR (s
->st_mode
) && !S_ISBLK (s
->st_mode
)))
734 /* We don't have gid */
736 || (idx
== 4 && (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
))))
739 /* We have gid field */
741 s
->st_gid
= (gid_t
) atol (columns
[2]);
743 s
->st_gid
= vfs_findgid (columns
[2]);
748 if (S_ISCHR (s
->st_mode
) || S_ISBLK (s
->st_mode
)) {
751 /* Corner case: there is no whitespace(s) between maj & min */
752 if (!is_num (idx2
) && idx2
== 2) {
753 if (!is_num (++idx2
) || sscanf (columns
[idx2
], " %d,%d", &min
, &min
) != 2)
756 if (!is_num (idx2
) || sscanf (columns
[idx2
], " %d,", &maj
) != 1)
759 if (!is_num (++idx2
) || sscanf (columns
[idx2
], " %d", &min
) != 1)
762 #ifdef HAVE_STRUCT_STAT_ST_RDEV
763 s
->st_rdev
= ((maj
& 0xff) << 8) | (min
& 0xffff00ff);
768 /* Common file size */
772 s
->st_size
= (size_t) atol (columns
[idx2
]);
773 #ifdef HAVE_STRUCT_STAT_ST_RDEV
778 idx
= vfs_parse_filedate (idx
, &s
->st_mtime
);
781 /* Use resulting time value */
782 s
->st_atime
= s
->st_ctime
= s
->st_mtime
;
783 /* s->st_dev and s->st_ino must be initialized by vfs_s_new_inode () */
784 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
787 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
788 s
->st_blocks
= (s
->st_size
+ 511) / 512;
791 for (i
= idx
+ 1, idx2
= 0; i
< num_cols
; i
++)
792 if (strcmp (columns
[i
], "->") == 0) {
797 if (((S_ISLNK (s
->st_mode
) || (num_cols
== idx
+ 3 && s
->st_nlink
> 1))) /* Maybe a hardlink? (in extfs) */
802 g_strndup (p
+ column_ptr
[idx
],
803 column_ptr
[idx2
] - column_ptr
[idx
] - 1);
806 t
= g_strdup (p
+ column_ptr
[idx2
+ 1]);
810 /* Extract the filename from the string copy, not from the columns
811 * this way we have a chance of entering hidden directories like ". ."
815 * filename = g_strdup (columns [idx++]);
818 t
= g_strdup (p
+ column_ptr
[idx
]);
827 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
829 if ((--p
> 0) && (t
[p
] == '\r' || t
[p
] == '\n'))
838 static int errorcount
= 0;
840 if (++errorcount
< 5) {
841 message (1, _("Cannot parse:"), "%s",
842 (p_copy
&& *p_copy
) ? p_copy
: line
);
843 } else if (errorcount
== 5)
844 message (1, MSG_ERROR
,
845 _("More parsing errors will be ignored."));
853 vfs_die (const char *m
)
855 message (1, _("Internal error:"), "%s", m
);
860 vfs_get_password (const char *msg
)
862 return input_dialog (msg
, _("Password:"), INPUT_PASSWORD
);