1 /*
2 * rc.c -- config file parser + autologin lookup
3 *
4 * Yet Another FTP Client
5 * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version. See COPYING for more details.
11 */
12
13 #include "syshdr.h"
14 #include "ftp.h"
15 #include "strq.h"
16 #include "gvars.h"
17 #include "args.h"
18 #include "alias.h"
19 #include "commands.h"
20 #include "transfer.h"
21 #include "utils.h"
22
23 static void errp(char *str, ...) YAFC_PRINTF(1, 2);
24
25 static int nerr = 0;
26 static char *current_rcfile = NULL;
27 static char *ungetstr = 0;
28
errp(char * str,...)29 static void errp(char *str, ...)
30 {
31 va_list ap;
32
33 if(nerr == 0)
34 fprintf(stderr, _("Error(s) while parsing '%s':\n"), current_rcfile);
35
36 va_start(ap, str);
37 vfprintf(stderr, str, ap);
38 va_end(ap);
39 nerr++;
40 }
41
_nextstr(FILE * fp)42 static char *_nextstr(FILE *fp)
43 {
44 static char tmp[257];
45 char *e;
46 int c;
47
48 if(ungetstr) {
49 e = ungetstr;
50 ungetstr = 0;
51 return e;
52 }
53
54 if(fscanf(fp, "%256s", tmp) == EOF) {
55 if(ferror(fp))
56 perror(current_rcfile);
57 return 0;
58 }
59 if(tmp[0] == '#') { /* skip comments */
60 while((c = getc(fp)) != EOF) {
61 if(c == '\n')
62 break;
63 }
64 return _nextstr(fp);
65 }
66 return tmp;
67 }
68
nextstr(FILE * fp)69 static char *nextstr(FILE *fp)
70 {
71 static char tmp[257];
72 char *e;
73 int i;
74
75 e = _nextstr(fp);
76 if(!e)
77 return 0;
78 if(*e=='\"' || *e=='\'') {
79 strlcpy(tmp, e+1, sizeof(tmp));
80 i = strlen(tmp);
81 if(i >= 1 && (tmp[i-1] == '\'' || tmp[i-1] == '\"')) {
82 tmp[i-1] = 0;
83 return tmp;
84 }
85 while(true) {
86 int c;
87
88 tmp[i] = 0;
89
90 c = fgetc(fp);
91 if(c == EOF) {
92 errp(_("unmatched quote\n"));
93 break;
94 }
95 if((i == 0 || tmp[i-1] != '\\') && (c == '\"' || c == '\''))
96 break;
97 tmp[i++] = c;
98 if(i == 256) {
99 errp(_("string too long or unmatched quote, truncated\n"));
100 break;
101 }
102 }
103 return tmp;
104 }
105 return e;
106 }
107
nextbool(FILE * fp)108 static bool nextbool(FILE *fp)
109 {
110 char *e;
111 int b;
112
113 if((e=nextstr(fp)) == NULL) {
114 errp(_("Unexpected end of file encountered\n"));
115 return false;
116 }
117
118 if((b=str2bool(e)) != -1)
119 return (bool)b;
120 else {
121 errp(_("Expected boolean value, but got '%s'\n"), e);
122 ungetstr = e;
123 return false;
124 }
125 }
126
127 #define NEXTSTR if((e=nextstr(fp)) == 0) break
128
129 #define TRIG_MACHINE 1
130 #define TRIG_LOCAL 2
131 #define TRIG_DEFAULT 3
132
parse_host(int trig,FILE * fp)133 static void parse_host(int trig, FILE *fp)
134 {
135 static bool has_warned_about_passwd = false;
136 char *e;
137 url_t *up;
138
139 up = url_create();
140
141 if(trig == TRIG_MACHINE) {
142 if((e=nextstr(fp)) == 0)
143 return;
144
145 if(e[0] == 0) {
146 errp(_("'machine' directive needs a hostname\n"));
147 url_destroy(up);
148 return;
149 }
150 url_parse(up, e);
151 }
152
153 while(!feof(fp)) {
154 NEXTSTR;
155
156 if(strcasecmp(e, "login") == 0) {
157 NEXTSTR;
158 url_setusername(up, e);
159 } else if(strcasecmp(e, "alias") == 0) {
160 NEXTSTR;
161 if(up->hostname[0] == '.')
162 printf(_("'alias' directive not useful with domains\n"));
163 else {
164 unquote(e);
165 url_setalias(up, e);
166 }
167 } else if(strcasecmp(e, "password") == 0) {
168 NEXTSTR;
169 url_setpassword(up, e);
170 if(!has_warned_about_passwd) {
171 struct stat sb;
172 has_warned_about_passwd = true;
173 if(fstat(fileno(fp), &sb)==0 && (sb.st_mode & 077)!=0) {
174 printf(_("WARNING! Config file contains passwords "
175 "but is readable by others (mode %03o)\n"),
176 sb.st_mode&0777);
177 sleep(3);
178 }
179 }
180
181 } else if(strcasecmp(e, "anonymous") == 0) {
182 url_setusername(up, e);
183 url_setpassword(up, gvAnonPasswd); /* could be NULL */
184 } else if(strcasecmp(e, "account") == 0) {
185 NEXTSTR;
186 /* FIXME: account skipped in parse_host() */
187 } else if(strcasecmp(e, "cwd") == 0) {
188 NEXTSTR;
189 url_setdirectory(up, e);
190 } else if(strcasecmp(e, "port") == 0) {
191 NEXTSTR;
192 url_setport(up, atoi(e));
193 } else if(strcasecmp(e, "mech") == 0) {
194 NEXTSTR;
195 url_setmech(up, e);
196 } else if(strcasecmp(e, "prot") == 0) {
197 NEXTSTR;
198 url_setprotlevel(up, e);
199 } else if(strcasecmp(e, "passive") == 0) {
200 url_setpassive(up, nextbool(fp));
201 } else if(strcasecmp(e, "sftp") == 0) {
202 NEXTSTR;
203 url_setsftp(up, e);
204 } else if(strcasecmp(e, "noupdate") == 0) {
205 up->noupdate = true;
206 } else if(strcasecmp(e, "macdef") == 0) {
207 while(e) { /* FIXME: macdef: this is not really true */
208 NEXTSTR;
209 }
210 break;
211 } else {
212 ungetstr = e;
213 clearerr(fp);
214 break;
215 }
216 }
217
218 if(trig == TRIG_MACHINE) {
219 listitem *li;
220 li = list_search(gvBookmarks, (listsearchfunc)urlcmp, up);
221 if(li)
222 /* bookmark already exists, overwrite it (delete and create new) */
223 list_delitem(gvBookmarks, li);
224 list_additem(gvBookmarks, up);
225 } else if(trig == TRIG_LOCAL) {
226 if(gvLocalUrl)
227 url_destroy(gvLocalUrl);
228 gvLocalUrl = up;
229 } else { /* trig == TRIG_DEFAULT */
230 if(gvDefaultUrl)
231 url_destroy(gvDefaultUrl);
232 gvDefaultUrl = up;
233 }
234 }
235
parse_rc(const char * file,bool warn)236 int parse_rc(const char *file, bool warn)
237 {
238 FILE *fp;
239 char *e;
240
241 e = tilde_expand_home(file, gvLocalHomeDir);
242 fp = fopen(e, "r");
243 if(!fp) {
244 if(warn)
245 perror(e);
246 free(e);
247 return -1;
248 }
249 current_rcfile = e;
250
251 nerr = 0;
252
253 while(!feof(fp)) {
254 if(nerr>20) {
255 errp(_("As a computer, I find your faith in technology amusing..."
256 "\nToo many errors\n"));
257 fclose(fp);
258 free(current_rcfile);
259 return -1;
260 }
261
262 NEXTSTR;
263
264 if(strcasecmp(e, "autologin") == 0)
265 gvAutologin = nextbool(fp);
266 else if(strcasecmp(e, "autoreconnect") == 0)
267 gvAutoReconnect = nextbool(fp);
268 else if(strcasecmp(e, "verbose") == 0)
269 gvVerbose = nextbool(fp);
270 else if(strcasecmp(e, "debug") == 0)
271 gvDebug = nextbool(fp);
272 else if(strcasecmp(e, "trace") == 0)
273 gvTrace = nextbool(fp);
274 else if(strcasecmp(e, "inhibit_startup_syst") == 0
275 || strcasecmp(e, "no_startup_syst") == 0)
276 gvStartupSyst = !nextbool(fp);
277 else if(strcasecmp(e, "prompt_on_disconnect") == 0)
278 /* ignored for backward compat. */ nextbool(fp);
279 else if(strcasecmp(e, "remote_completion") == 0)
280 gvRemoteCompletion = nextbool(fp);
281 else if(strcasecmp(e, "read_netrc") == 0)
282 gvReadNetrc = nextbool(fp);
283 else if(strcasecmp(e, "quit_on_eof") == 0)
284 gvQuitOnEOF = nextbool(fp);
285 else if(strcasecmp(e, "use_passive_mode") == 0)
286 gvPasvmode = nextbool(fp);
287 else if(strcasecmp(e, "use_history") == 0)
288 gvUseHistory = nextbool(fp);
289 else if(strcasecmp(e, "beep_after_long_command") == 0)
290 gvBeepLongCommand = nextbool(fp);
291 else if(strcasecmp(e, "auto_bookmark_save_passwd") == 0)
292 gvAutoBookmarkSavePasswd = nextbool(fp);
293 else if(strcasecmp(e, "auto_bookmark_silent") == 0)
294 gvAutoBookmarkSilent = nextbool(fp);
295 else if(strcasecmp(e, "tilde") == 0)
296 gvTilde = nextbool(fp);
297 else if(strcasecmp(e, "reverse_dns") == 0)
298 gvReverseDNS = nextbool(fp);
299 else if(strcasecmp(e, "waiting_dots") == 0)
300 gvWaitingDots = nextbool(fp);
301 else if(strcasecmp(e, "use_env_string") == 0)
302 gvUseEnvString = nextbool(fp);
303 else if(strcasecmp(e, "auto_bookmark") == 0) {
304 NEXTSTR;
305
306 if(strcasecmp(e, "ask") == 0)
307 gvAutoBookmark = 2;
308 else {
309 gvAutoBookmark = str2bool(e);
310 if(gvAutoBookmark == -1) {
311 errp(_("Expected boolean value or 'ask', but got '%s'\n"),
312 e);
313 gvAutoBookmark = 0;
314 }
315 }
316 } else if(strcasecmp(e, "auto_bookmark_update") == 0) {
317 NEXTSTR;
318
319 if(strcasecmp(e, "ask") == 0)
320 gvAutoBookmarkUpdate = 2;
321 else {
322 gvAutoBookmarkUpdate = str2bool(e);
323 if(gvAutoBookmarkUpdate == -1) {
324 errp(_("Expected boolean value or 'ask', but got '%s'\n"),
325 e);
326 gvAutoBookmarkUpdate = 0;
327 }
328 }
329 } else if(strcasecmp(e, "load_taglist") == 0) {
330 NEXTSTR;
331
332 if(strcasecmp(e, "ask") == 0)
333 gvLoadTaglist = 2;
334 else {
335 gvLoadTaglist = str2bool(e);
336 if(gvLoadTaglist == -1) {
337 errp(_("Expected boolean value or 'ask', but got '%s'\n"),
338 e);
339 gvLoadTaglist = 2;
340 }
341 }
342 } else if(strcasecmp(e, "default_type") == 0) {
343 NEXTSTR;
344 if(strcasecmp(e, "binary") == 0 || strcasecmp(e, "I") == 0)
345 gvDefaultType = tmBinary;
346 else if(strcasecmp(e, "ascii") == 0 || strcasecmp(e, "A") == 0)
347 gvDefaultType = tmAscii;
348 else
349 errp(_("Unknown default_type parameter '%s'..."
350 " (use 'ascii' or 'binary')\n"), e);
351 } else if(strcasecmp(e, "default_mechanism") == 0) {
352 NEXTSTR;
353 list_free(gvDefaultMechanism);
354 gvDefaultMechanism = list_new((listfunc)free);
355 listify_string(e, gvDefaultMechanism);
356 } else if(strcasecmp(e, "anon_password") == 0) {
357 NEXTSTR;
358 free(gvAnonPasswd);
359 gvAnonPasswd = xstrdup(e);
360 } else if(strcasecmp(e, "long_command_time") == 0) {
361 NEXTSTR;
362 gvLongCommandTime = atoi(e);
363 if(gvLongCommandTime < 0) {
364 errp(_("Invalid value for long_command_time: %d\n"),
365 gvLongCommandTime);
366 gvLongCommandTime = 30;
367 }
368 } else if(strcasecmp(e, "connect_wait_time") == 0) {
369 NEXTSTR;
370 gvConnectWaitTime = atoi(e);
371 if(gvConnectWaitTime < 0) {
372 errp(_("Invalid value for connect_wait_time: %d\n"),
373 gvConnectWaitTime);
374 gvConnectWaitTime = 30;
375 }
376 } else if(strcasecmp(e, "cache_timeout") == 0) {
377 NEXTSTR;
378 gvCacheTimeout = atoi(e);
379 if(gvCacheTimeout < 0) {
380 errp(_("Invalid value for cache_timeout: %d\n"),
381 gvCacheTimeout);
382 gvCacheTimeout = 0;
383 }
384 } else if(strcasecmp(e, "connect_attempts") == 0) {
385 NEXTSTR;
386 gvConnectAttempts = (unsigned)atoi(e);
387 if(gvConnectAttempts == 0)
388 gvConnectAttempts = 1;
389 } else if(strcasecmp(e, "command_timeout") == 0) {
390 NEXTSTR;
391 gvCommandTimeout = (unsigned)atoi(e);
392 } else if(strcasecmp(e, "connection_timeout") == 0) {
393 NEXTSTR;
394 gvConnectionTimeout = (unsigned)atoi(e);
395 } else if(strcasecmp(e, "include") == 0) {
396 char *rcfile;
397 NEXTSTR;
398 rcfile = tilde_expand_home(e, gvLocalHomeDir);
399 if(strcmp(rcfile, current_rcfile) == 0) {
400 free(rcfile);
401 errp(_("Skipping circular include statement: %s\n"), e);
402 } else {
403 free(current_rcfile);
404 parse_rc(e, true);
405 current_rcfile = rcfile;
406 }
407 } else if(strcasecmp(e, "prompt1") == 0) {
408 NEXTSTR;
409 free(gvPrompt1);
410 gvPrompt1 = xstrdup(e);
411 } else if(strcasecmp(e, "prompt2") == 0) {
412 NEXTSTR;
413 free(gvPrompt2);
414 gvPrompt2 = xstrdup(e);
415 } else if(strcasecmp(e, "prompt3") == 0) {
416 NEXTSTR;
417 free(gvPrompt3);
418 gvPrompt3 = xstrdup(e);
419 } else if(strcasecmp(e, "ssh_options") == 0) {
420 NEXTSTR;
421 free(gvSSHOptions);
422 gvSSHOptions = xstrdup(e);
423 } else if (strcasecmp(e, "ssh_try_scp") == 0) {
424 gvSSHTrySCP = nextbool(fp);
425 } else if(strcasecmp(e, "xterm_title_terms") == 0) {
426 NEXTSTR;
427 free(gvXtermTitleTerms);
428 gvXtermTitleTerms = xstrdup(e);
429 } else if(strcasecmp(e, "xterm_title1") == 0) {
430 NEXTSTR;
431 free(gvXtermTitle1);
432 gvXtermTitle1 = xstrdup(e);
433 } else if(strcasecmp(e, "xterm_title2") == 0) {
434 NEXTSTR;
435 free(gvXtermTitle2);
436 gvXtermTitle2 = xstrdup(e);
437 } else if(strcasecmp(e, "xterm_title3") == 0) {
438 NEXTSTR;
439 free(gvXtermTitle3);
440 gvXtermTitle3 = xstrdup(e);
441 } else if(strcasecmp(e, "transfer_begin_string") == 0) {
442 NEXTSTR;
443 free(gvTransferBeginString);
444 gvTransferBeginString = xstrdup(e);
445 unquote_escapes(gvTransferBeginString);
446 } else if(strcasecmp(e, "transfer_string") == 0) {
447 NEXTSTR;
448 free(gvTransferString);
449 gvTransferString = xstrdup(e);
450 unquote_escapes(gvTransferString);
451 } else if(strcasecmp(e, "transfer_xterm_string") == 0) {
452 NEXTSTR;
453 free(gvTransferXtermString);
454 gvTransferXtermString = xstrdup(e);
455 unquote_escapes(gvTransferXtermString);
456 } else if(strcasecmp(e, "transfer_end_string") == 0) {
457 NEXTSTR;
458 free(gvTransferEndString);
459 gvTransferEndString = xstrdup(e);
460 unquote_escapes(gvTransferEndString);
461 } else if(strcasecmp(e, "nohup_mailaddress") == 0) {
462 NEXTSTR;
463 free(gvNohupMailAddress);
464 gvNohupMailAddress = xstrdup(e);
465 } else if(strcasecmp(e, "sendmail_path") == 0) {
466 NEXTSTR;
467 free(gvSendmailPath);
468 gvSendmailPath = xstrdup(e);
469 } else if(strcasecmp(e, "history_max") == 0) {
470 NEXTSTR;
471 gvHistoryMax = atoi(e);
472 if(gvHistoryMax <= 0) {
473 errp(_("Invalid value for history_max: %d\n"), gvHistoryMax);
474 gvHistoryMax = 256;
475 }
476 } else if(strcasecmp(e, "ascii_transfer_mask") == 0) {
477 NEXTSTR;
478 listify_string(e, gvAsciiMasks);
479 } else if(strcasecmp(e, "transfer_first_mask") == 0) {
480 NEXTSTR;
481 listify_string(e, gvTransferFirstMasks);
482 } else if(strcasecmp(e, "ignore_mask") == 0) {
483 NEXTSTR;
484 listify_string(e, gvIgnoreMasks);
485 } else if(strcasecmp(e, "stats_threshold") == 0) {
486 NEXTSTR;
487 gvStatsThreshold = atoi(e);
488 if(gvStatsThreshold < 0) {
489 errp(_("Invalid value for stats_threshold: %i\n"),
490 gvStatsThreshold);
491 gvStatsThreshold = 20;
492 }
493 } else if(strcasecmp(e, "startup_local_directory") == 0) {
494 NEXTSTR;
495 e = tilde_expand_home(e, gvLocalHomeDir);
496 if(chdir(e) == -1)
497 perror(e);
498 else
499 cmd_lpwd(0, 0);
500 free(e);
501 } else if(strcasecmp(e, "alias") == 0) {
502 args_t *args;
503 char *name;
504
505 NEXTSTR;
506 name = xstrdup(e);
507
508 NEXTSTR;
509
510 args = args_create();
511 args_push_back(args, e);
512 alias_define(name, args);
513 free(name);
514 }
515 else if(strcasecmp(e, "proxy_type") == 0) {
516 NEXTSTR;
517
518 gvProxyType = atoi(e);
519 if(gvProxyType < 0 || gvProxyType > 7) {
520 errp(_("Invalid value for proxy_type: %d\n"), gvProxyType);
521 gvProxyType = 0;
522 }
523 } else if(strcasecmp(e, "proxy_host") == 0) {
524 NEXTSTR;
525 url_destroy(gvProxyUrl);
526 gvProxyUrl = url_init(e);
527 } else if(strcasecmp(e, "proxy_exclude") == 0) {
528 NEXTSTR;
529 listify_string(e, gvProxyExclude);
530 }
531 else if(strcasecmp(e, "machine") == 0)
532 parse_host(TRIG_MACHINE, fp);
533 else if(strcasecmp(e, "default") == 0)
534 parse_host(TRIG_DEFAULT, fp);
535 else if(strcasecmp(e, "local") == 0)
536 parse_host(TRIG_LOCAL, fp);
537 else
538 errp(_("Config parse error: '%s'\n"), e);
539 }
540 fclose(fp);
541 free(current_rcfile);
542 return 0;
543 }
544
get_autologin_url_short(const char * host)545 static url_t *get_autologin_url_short(const char *host)
546 {
547 url_t *x, *found = 0;
548 listitem *li;
549
550 li = gvBookmarks->first;
551 while(li) {
552 x = (url_t *)li->data;
553 li = li->next;
554 /* compare only strlen(host) chars, allowing aliases
555 * to be shortened, as long as they're not ambiguous
556 */
557 if(x->alias && strncasecmp(x->alias, host, strlen(host)) == 0) {
558 if(strlen(x->alias) == strlen(host))
559 /* exact match */
560 return x;
561 if(found)
562 found = (url_t *)-1;
563 else
564 found = x;
565 }
566 }
567 if(found)
568 return found;
569
570 /* now do the same for hostnames (skip aliases) */
571 li = gvBookmarks->first;
572 while(li) {
573 x = (url_t *)li->data;
574 li = li->next;
575 /* compare only strlen(host) chars, allowing hostnames
576 * to be shortened, as long as they're not ambiguous
577 */
578 if(!x->alias && strncasecmp(x->hostname, host, strlen(host)) == 0) {
579 if(strlen(x->hostname) == strlen(host))
580 /* exact match */
581 return x;
582 if(found)
583 found = (url_t *)-1;
584 else
585 found = x;
586 }
587 }
588
589 return found;
590 }
591
592 /* returns a copy of the found URL, should be deleted
593 * returns 0 if not found, and -1 if ambiguous
594 */
get_autologin_url(const char * host)595 url_t *get_autologin_url(const char *host)
596 {
597 listitem *li;
598 const char *dot = host;
599 url_t *x;
600
601 x = get_autologin_url_short(host);
602 if(x == (url_t *)-1)
603 return x;
604 if(x)
605 return url_clone(x);
606
607 /* try to match the 'local' directive */
608 if(gvLocalUrl && strchr(host, '.') == 0) {
609 x = url_clone(gvLocalUrl);
610 url_sethostname(x, host);
611 return x;
612 }
613
614 /* try to match as much as possible of the domain name */
615 while((dot = strchr(dot, '.')) != 0) {
616 li = gvBookmarks->first;
617 while(li) {
618 x = (url_t *)li->data;
619
620 if(x->hostname && strcasecmp(dot, x->hostname) == 0) {
621 x = url_clone(x);
622 url_sethostname(x, host);
623 return x;
624 }
625 li = li->next;
626 }
627 dot++;
628 }
629
630 /* return default, or NULL if no default */
631 if(gvDefaultUrl) {
632 x = url_clone(gvDefaultUrl);
633 url_sethostname(x, host);
634 return x;
635 }
636 return 0;
637 }
638