1 /*
2 Utilities for VFS modules.
3
4 Copyright (C) 1988-2021
5 Free Software Foundation, Inc.
6
7 Copyright (C) 1995, 1996 Miguel de Icaza
8
9 This file is part of the Midnight Commander.
10
11 The Midnight Commander is free software: you can redistribute it
12 and/or modify it under the terms of the GNU General Public License as
13 published by the Free Software Foundation, either version 3 of the License,
14 or (at your option) any later version.
15
16 The Midnight Commander is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /**
26 * \file
27 * \brief Source: Utilities for VFS modules
28 * \author Miguel de Icaza
29 * \date 1995, 1996
30 */
31
32 #include <config.h>
33
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <pwd.h>
37 #include <grp.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "lib/global.h"
42 #include "lib/unixcompat.h"
43 #include "lib/util.h" /* mc_mkstemps() */
44 #include "lib/widget.h" /* message() */
45 #include "lib/strutil.h" /* INVALID_CONV */
46
47 #include "vfs.h"
48 #include "utilvfs.h"
49
50 /*** global variables ****************************************************************************/
51
52 /*** file scope macro definitions ****************************************************************/
53
54 #ifndef TUNMLEN
55 #define TUNMLEN 256
56 #endif
57 #ifndef TGNMLEN
58 #define TGNMLEN 256
59 #endif
60
61 #define MC_HISTORY_VFS_PASSWORD "mc.vfs.password"
62
63 /*
64 * FIXME2, the "-993" is to reduce the chance of a hit on the first lookup.
65 */
66 #define GUID_DEFAULT_CONST -993
67
68 /*** file scope type declarations ****************************************************************/
69
70 /*** file scope variables ************************************************************************/
71
72 /*** file scope functions ************************************************************************/
73 /* --------------------------------------------------------------------------------------------- */
74
75
76 /* --------------------------------------------------------------------------------------------- */
77 /*** public functions ****************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
79 /** Get current username
80 *
81 * @return g_malloc()ed string with the name of the currently logged in
82 * user ("anonymous" if uid is not registered in the system)
83 */
84
85 char *
vfs_get_local_username(void)86 vfs_get_local_username (void)
87 {
88 struct passwd *p_i;
89
90 p_i = getpwuid (geteuid ());
91
92 return (p_i && p_i->pw_name) ? g_strdup (p_i->pw_name) : g_strdup ("anonymous"); /* Unknown UID, strange */
93 }
94
95 /* --------------------------------------------------------------------------------------------- */
96 /**
97 * Look up a user or group name from a uid/gid, maintaining a cache.
98 * FIXME, for now it's a one-entry cache.
99 * This file should be modified for non-unix systems to do something
100 * reasonable.
101 */
102
103 /* --------------------------------------------------------------------------------------------- */
104
105 int
vfs_finduid(const char * uname)106 vfs_finduid (const char *uname)
107 {
108 static int saveuid = GUID_DEFAULT_CONST;
109 static char saveuname[TUNMLEN] = "\0";
110
111 size_t uname_len;
112
113 uname_len = strlen (uname);
114
115 if (uname[0] != saveuname[0] /* Quick test w/o proc call */
116 || strncmp (uname, saveuname, MIN (uname_len, TUNMLEN - 1)) != 0)
117 {
118 struct passwd *pw;
119
120 g_strlcpy (saveuname, uname, TUNMLEN);
121 pw = getpwnam (uname);
122 if (pw)
123 {
124 saveuid = pw->pw_uid;
125 }
126 else
127 {
128 static int my_uid = GUID_DEFAULT_CONST;
129
130 if (my_uid < 0)
131 my_uid = getuid ();
132
133 saveuid = my_uid;
134 }
135 }
136 return saveuid;
137 }
138
139 /* --------------------------------------------------------------------------------------------- */
140
141 int
vfs_findgid(const char * gname)142 vfs_findgid (const char *gname)
143 {
144 static int savegid = GUID_DEFAULT_CONST;
145 static char savegname[TGNMLEN] = "\0";
146
147 size_t gname_len;
148
149 gname_len = strlen (gname);
150
151 if (gname[0] != savegname[0] /* Quick test w/o proc call */
152 || strncmp (gname, savegname, MIN (gname_len, TGNMLEN - 1)) != 0)
153 {
154 struct group *gr;
155
156 g_strlcpy (savegname, gname, TGNMLEN);
157 gr = getgrnam (gname);
158 if (gr)
159 {
160 savegid = gr->gr_gid;
161 }
162 else
163 {
164 static int my_gid = GUID_DEFAULT_CONST;
165
166 if (my_gid < 0)
167 my_gid = getgid ();
168
169 savegid = my_gid;
170 }
171 }
172 return savegid;
173 }
174
175 /* --------------------------------------------------------------------------------------------- */
176 /**
177 * Create a temporary file with a name resembling the original.
178 * This is needed e.g. for local copies requested by extfs.
179 * Some extfs scripts may look at the extension.
180 * We also protect stupid scripts agains dangerous names.
181 */
182
183 int
vfs_mkstemps(vfs_path_t ** pname_vpath,const char * prefix,const char * param_basename)184 vfs_mkstemps (vfs_path_t ** pname_vpath, const char *prefix, const char *param_basename)
185 {
186 const char *p;
187 GString *suffix;
188 int shift;
189 int fd;
190
191 /* Strip directories */
192 p = strrchr (param_basename, PATH_SEP);
193 if (p == NULL)
194 p = param_basename;
195 else
196 p++;
197
198 /* Protection against very long names */
199 shift = strlen (p) - (MC_MAXPATHLEN - 16);
200 if (shift > 0)
201 p += shift;
202
203 suffix = g_string_sized_new (32);
204
205 /* Protection against unusual characters */
206 for (; *p != '\0' && *p != '#'; p++)
207 if (strchr (".-_@", *p) != NULL || g_ascii_isalnum (*p))
208 g_string_append_c (suffix, *p);
209
210 fd = mc_mkstemps (pname_vpath, prefix, suffix->str);
211 g_string_free (suffix, TRUE);
212
213 return fd;
214 }
215
216 /* --------------------------------------------------------------------------------------------- */
217 /** Extract the hostname and username from the path
218 *
219 * Format of the path is [user@]hostname:port/remote-dir, e.g.:
220 *
221 * ftp://sunsite.unc.edu/pub/linux
222 * ftp://miguel@sphinx.nuclecu.unam.mx/c/nc
223 * ftp://tsx-11.mit.edu:8192/
224 * ftp://joe@foo.edu:11321/private
225 * ftp://joe:password@foo.se
226 *
227 * @param path is an input string to be parsed
228 * @param default_port is an input default port
229 * @param flags are parsing modifier flags (@see vfs_url_flags_t)
230 *
231 * @return g_malloc()ed url info.
232 * If the user is empty, e.g. ftp://@roxanne/private, and URL_USE_ANONYMOUS
233 * is not set, then the current login name is supplied.
234 * Return value is a g_malloc()ed structure with the pathname relative to the
235 * host.
236 */
237
238 vfs_path_element_t *
vfs_url_split(const char * path,int default_port,vfs_url_flags_t flags)239 vfs_url_split (const char *path, int default_port, vfs_url_flags_t flags)
240 {
241 vfs_path_element_t *path_element;
242
243 char *pcopy;
244 size_t pcopy_len;
245 const char *pend;
246 char *colon, *at, *rest;
247
248 path_element = g_new0 (vfs_path_element_t, 1);
249 path_element->port = default_port;
250
251 pcopy_len = strlen (path);
252 pcopy = g_strndup (path, pcopy_len);
253 pend = pcopy + pcopy_len;
254
255 if ((flags & URL_NOSLASH) == 0)
256 {
257 char *dir = pcopy;
258
259 /* locate path component */
260 while (!IS_PATH_SEP (*dir) && *dir != '\0')
261 dir++;
262 if (*dir == '\0')
263 path_element->path = g_strdup (PATH_SEP_STR);
264 else
265 {
266 path_element->path = g_strndup (dir, pcopy_len - (size_t) (dir - pcopy));
267 *dir = '\0';
268 }
269 }
270
271 /* search for any possible user */
272 at = strrchr (pcopy, '@');
273
274 /* We have a username */
275 if (at == NULL)
276 rest = pcopy;
277 else
278 {
279 char *inner_colon;
280
281 *at = '\0';
282 inner_colon = strchr (pcopy, ':');
283 if (inner_colon != NULL)
284 {
285 *inner_colon = '\0';
286 inner_colon++;
287 path_element->password = g_strdup (inner_colon);
288 }
289
290 if (*pcopy != '\0')
291 path_element->user = g_strdup (pcopy);
292
293 if (pend == at + 1)
294 rest = at;
295 else
296 rest = at + 1;
297 }
298
299 if ((flags & URL_USE_ANONYMOUS) == 0)
300 {
301 g_free (path_element->user);
302 path_element->user = vfs_get_local_username ();
303 }
304 /* Check if the host comes with a port spec, if so, chop it */
305 if (*rest != '[')
306 colon = strchr (rest, ':');
307 else
308 {
309 colon = strchr (++rest, ']');
310 if (colon != NULL)
311 {
312 colon[0] = '\0';
313 colon[1] = '\0';
314 colon++;
315 }
316 else
317 {
318 vfs_path_element_free (path_element);
319 g_free (pcopy);
320 return NULL;
321 }
322 }
323
324 if (colon != NULL)
325 {
326 *colon = '\0';
327 /* cppcheck-suppress invalidscanf */
328 if (sscanf (colon + 1, "%d", &path_element->port) == 1)
329 {
330 if (path_element->port <= 0 || path_element->port >= 65536)
331 path_element->port = default_port;
332 }
333 else
334 while (*(++colon) != '\0')
335 {
336 switch (*colon)
337 {
338 case 'C':
339 path_element->port = 1;
340 break;
341 case 'r':
342 path_element->port = 2;
343 break;
344 default:
345 break;
346 }
347 }
348 }
349
350 path_element->host = g_strdup (rest);
351 g_free (pcopy);
352 #ifdef HAVE_CHARSET
353 path_element->dir.converter = INVALID_CONV;
354 #endif
355
356 return path_element;
357 }
358
359 /* --------------------------------------------------------------------------------------------- */
360
vfs_die(const char * m)361 void __attribute__ ((noreturn)) vfs_die (const char *m)
362 {
363 message (D_ERROR, _("Internal error:"), "%s", m);
364 exit (EXIT_FAILURE);
365 }
366
367 /* --------------------------------------------------------------------------------------------- */
368
369 char *
vfs_get_password(const char * msg)370 vfs_get_password (const char *msg)
371 {
372 return input_dialog (msg, _("Password:"), MC_HISTORY_VFS_PASSWORD, INPUT_PASSWORD,
373 INPUT_COMPLETE_NONE);
374 }
375
376 /* --------------------------------------------------------------------------------------------- */
377