1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: amrecover.c,v 1.73 2006/07/25 18:27:57 martinea Exp $
29  *
30  * an interactive program for recovering backed-up files
31  */
32 
33 #include "amanda.h"
34 #include "stream.h"
35 #include "amfeatures.h"
36 #include "amrecover.h"
37 #include "getfsent.h"
38 #include "dgram.h"
39 #include "util.h"
40 #include "conffile.h"
41 #include "protocol.h"
42 #include "event.h"
43 #include "security.h"
44 #include "conffile.h"
45 #include "getopt.h"
46 
47 #define amrecover_debug(i, ...) do {	\
48 	if ((i) <= debug_amrecover) {	\
49 	    dbprintf(__VA_ARGS__);	\
50 	}				\
51 } while (0)
52 
53 static struct option long_options[] = {
54     {"version"         , 0, NULL,  1},
55     {NULL, 0, NULL, 0}
56 };
57 
58 extern int process_line(char *line);
59 int get_line(void);
60 int grab_reply(int show);
61 void sigint_handler(int signum);
62 int main(int argc, char **argv);
63 
64 #define USAGE _("Usage: amrecover [--version] [[-C] <config>] [-s <index-server>] [-t <tape-server>] [-d <tape-device>] [-o <clientconfigoption>]*\n")
65 
66 char *server_name = NULL;
67 int server_socket;
68 char *server_line = NULL;
69 char *dump_datestamp = NULL;		/* date we are restoring */
70 char *dump_hostname;			/* which machine we are restoring */
71 char *disk_name = NULL;			/* disk we are restoring */
72 dle_t *dump_dle = NULL;
73 char *mount_point = NULL;		/* where disk was mounted */
74 char *disk_path = NULL;			/* path relative to mount point */
75 char *disk_tpath = NULL;		/* translated path relative to mount point */
76 char dump_date[STR_SIZE];		/* date on which we are restoring */
77 int quit_prog;				/* set when time to exit parser */
78 char *tape_server_name = NULL;
79 int tape_server_socket;
80 char *tape_device_name = NULL;
81 am_feature_t *our_features = NULL;
82 char *our_features_string = NULL;
83 am_feature_t *indexsrv_features = NULL;
84 am_feature_t *tapesrv_features = NULL;
85 proplist_t proplist = NULL;
86 static char *errstr = NULL;
87 char *authopt;
88 int amindexd_alive = 0;
89 
90 static struct {
91     const char *name;
92     security_stream_t *fd;
93 } streams[] = {
94 #define MESGFD  0
95     { "MESG", NULL },
96 };
97 #define NSTREAMS        (int)(sizeof(streams) / sizeof(streams[0]))
98 
99 static void amindexd_response(void *, pkt_t *, security_handle_t *);
100 void stop_amindexd(void);
101 
102 static char* mesg_buffer = NULL;
103 /* gets a "line" from server and put in server_line */
104 /* server_line is terminated with \0, \r\n is striped */
105 /* returns -1 if error */
106 
107 int
get_line(void)108 get_line(void)
109 {
110     ssize_t size;
111     char *newbuf, *s;
112     void *buf;
113 
114     if (!mesg_buffer)
115 	mesg_buffer = stralloc("");
116 
117     while (!strstr(mesg_buffer,"\r\n")) {
118 	buf = NULL;
119 	size = security_stream_read_sync(streams[MESGFD].fd, &buf);
120 	if(size < 0) {
121 	    amrecover_debug(1, "amrecover: get_line size < 0 (%zd)\n", size);
122 	    return -1;
123 	}
124 	else if(size == 0) {
125 	    amrecover_debug(1, "amrecover: get_line size == 0 (%zd)\n", size);
126 	    return -1;
127 	}
128 	else if (buf == NULL) {
129 	    amrecover_debug(1, "amrecover: get_line buf == NULL\n");
130 	    return -1;
131 	}
132         amrecover_debug(1, "amrecover: get_line size = %zd\n", size);
133 	newbuf = alloc(strlen(mesg_buffer)+size+1);
134 	strncpy(newbuf, mesg_buffer, (size_t)(strlen(mesg_buffer) + size));
135 	memcpy(newbuf+strlen(mesg_buffer), buf, (size_t)size);
136 	newbuf[strlen(mesg_buffer)+size] = '\0';
137 	amfree(mesg_buffer);
138 	mesg_buffer = newbuf;
139 	amfree(buf);
140     }
141 
142     s = strstr(mesg_buffer,"\r\n");
143     *s = '\0';
144     newbuf = stralloc(s+2);
145     server_line = newstralloc(server_line, mesg_buffer);
146     amfree(mesg_buffer);
147     mesg_buffer = newbuf;
148     amrecover_debug(1, "server_line: %s\n", server_line);
149     amrecover_debug(1, "get: %s\n", mesg_buffer);
150     return 0;
151 }
152 
153 
154 /* get reply from server and print to screen */
155 /* handle multi-line reply */
156 /* return -1 if error */
157 /* return code returned by server always occupies first 3 bytes of global
158    variable server_line */
159 /* show == 0: Print the reply if it is an error */
160 /* show == 1: Always print the reply            */
161 int
grab_reply(int show)162 grab_reply(
163     int show)
164 {
165     do {
166 	if (get_line() == -1) {
167 	    return -1;
168 	}
169 	if (show || server_line[0] == '5') {
170 	    puts(server_line);
171 	}
172     } while (server_line[3] == '-');
173     if(show) fflush(stdout);
174 
175     return 0;
176 }
177 
178 
179 /* get 1 line of reply */
180 /* returns -1 if error, 0 if last (or only) line, 1 if more to follow */
181 int
get_reply_line(void)182 get_reply_line(void)
183 {
184     if (get_line() == -1)
185 	return -1;
186     return server_line[3] == '-';
187 }
188 
189 
190 /* returns pointer to returned line */
191 char *
reply_line(void)192 reply_line(void)
193 {
194     return server_line;
195 }
196 
197 
198 
199 /* returns 0 if server returned an error code (ie code starting with 5)
200    and non-zero otherwise */
201 int
server_happy(void)202 server_happy(void)
203 {
204     return server_line[0] != '5';
205 }
206 
207 
208 int
send_command(char * cmd)209 send_command(
210     char *	cmd)
211 {
212     /*
213      * NOTE: this routine is called from sigint_handler, so we must be
214      * **very** careful about what we do since there is no way to know
215      * our state at the time the interrupt happened.  For instance,
216      * do not use any stdio or malloc routines here.
217      */
218     char *buffer;
219 
220     buffer = alloc(strlen(cmd)+3);
221     strncpy(buffer, cmd, strlen(cmd));
222     buffer[strlen(cmd)] = '\r';
223     buffer[strlen(cmd)+1] = '\n';
224     buffer[strlen(cmd)+2] = '\0';
225 
226     g_debug("sending: %s\n", buffer);
227     if(security_stream_write(streams[MESGFD].fd, buffer, strlen(buffer)) < 0) {
228 	return -1;
229     }
230     amfree(buffer);
231     return (0);
232 }
233 
234 
235 /* send a command to the server, get reply and print to screen */
236 int
converse(char * cmd)237 converse(
238     char *	cmd)
239 {
240     if (send_command(cmd) == -1) return -1;
241     if (grab_reply(1) == -1) return -1;
242     return 0;
243 }
244 
245 
246 /* same as converse() but reply not echoed to stdout */
247 int
exchange(char * cmd)248 exchange(
249     char *	cmd)
250 {
251     if (send_command(cmd) == -1) return -1;
252     if (grab_reply(0) == -1) return -1;
253     return 0;
254 }
255 
256 
257 /* basic interrupt handler for when user presses ^C */
258 /* Bale out, letting server know before doing so */
259 void
sigint_handler(int signum)260 sigint_handler(
261     int	signum)
262 {
263     /*
264      * NOTE: we must be **very** careful about what we do here since there
265      * is no way to know our state at the time the interrupt happened.
266      * For instance, do not use any stdio routines here or in any called
267      * routines.  Also, use _exit() instead of exit() to make sure stdio
268      * buffer flushing is not attempted.
269      */
270     (void)signum;	/* Quiet unused parameter warning */
271 
272     if (extract_restore_child_pid != -1)
273 	(void)kill(extract_restore_child_pid, SIGKILL);
274     extract_restore_child_pid = -1;
275 
276     if(amindexd_alive)
277 	(void)send_command("QUIT");
278 
279     _exit(1);
280 }
281 
282 
283 char *
clean_pathname(char * s)284 clean_pathname(
285     char *	s)
286 {
287     size_t length;
288     length = strlen(s);
289 
290     /* remove "/" at end of path */
291     if(length>1 && s[length-1]=='/')
292 	s[length-1]='\0';
293 
294     /* change "/." to "/" */
295     if(strcmp(s,"/.")==0)
296 	s[1]='\0';
297 
298     /* remove "/." at end of path */
299     if(strcmp(&(s[length-2]),"/.")==0)
300 	s[length-2]='\0';
301 
302     return s;
303 }
304 
305 
306 void
quit(void)307 quit(void)
308 {
309     quit_prog = 1;
310     (void)converse("QUIT");
311     stop_amindexd();
312 }
313 
314 #ifdef DEFAULT_TAPE_SERVER
315 # define DEFAULT_TAPE_SERVER_FAILOVER (DEFAULT_TAPE_SERVER)
316 #else
317 # define DEFAULT_TAPE_SERVER_FAILOVER (NULL)
318 #endif
319 
320 int
main(int argc,char ** argv)321 main(
322     int		argc,
323     char **	argv)
324 {
325     int i;
326     time_t timer;
327     char *lineread = NULL;
328     struct sigaction act, oact;
329     extern char *optarg;
330     extern int optind;
331     char *line = NULL;
332     const security_driver_t *secdrv;
333     char *req = NULL;
334     int response_error;
335     struct tm *tm;
336     config_overrides_t *cfg_ovr;
337     char *starting_hostname = NULL;
338 
339     /*
340      * Configure program for internationalization:
341      *   1) Only set the message locale for now.
342      *   2) Set textdomain for all amanda related programs to "amanda"
343      *      We don't want to be forced to support dozens of message catalogs.
344      */
345     setlocale(LC_MESSAGES, "C");
346     textdomain("amanda");
347 
348     safe_fd(-1, 0);
349 
350     set_pname("amrecover");
351 
352     /* Don't die when child closes pipe */
353     signal(SIGPIPE, SIG_IGN);
354 
355     dbopen(DBG_SUBDIR_CLIENT);
356 
357     /* treat amrecover-specific command line options as the equivalent
358      * -o command-line options to set configuration values */
359     cfg_ovr = new_config_overrides(argc/2);
360 
361     /* If the first argument is not an option flag, then we assume
362      * it is a configuration name to match the syntax of the other
363      * Amanda utilities. */
364     if (argc > 1 && argv[1][0] != '-') {
365 	add_config_override(cfg_ovr, "conf", argv[1]);
366 
367 	/* remove that option from the command line */
368 	argv[1] = argv[0];
369 	argv++; argc--;
370     }
371 
372     /* now parse regular command-line '-' options */
373     while ((i = getopt_long(argc, argv, "o:C:s:t:d:Uh:", long_options, NULL)) != EOF) {
374 	switch (i) {
375 	    case 1:
376 		printf("amrecover-%s\n", VERSION);
377 		return(0);
378 		break;
379 	    case 'C':
380 		add_config_override(cfg_ovr, "conf", optarg);
381 		break;
382 
383 	    case 's':
384 		add_config_override(cfg_ovr, "index_server", optarg);
385 		break;
386 
387 	    case 't':
388 		add_config_override(cfg_ovr, "tape_server", optarg);
389 		break;
390 
391 	    case 'd':
392 		add_config_override(cfg_ovr, "tapedev", optarg);
393 		break;
394 
395 	    case 'o':
396 		add_config_override_opt(cfg_ovr, optarg);
397 		break;
398 
399 	    case 'h':
400 		starting_hostname = g_strdup(optarg);
401 		break;
402 
403 	    case 'U':
404 	    case '?':
405 		(void)g_printf(USAGE);
406 		return 0;
407 	}
408     }
409     if (optind != argc) {
410 	(void)g_fprintf(stderr, USAGE);
411 	exit(1);
412     }
413 
414     /* load the base client configuration */
415     set_config_overrides(cfg_ovr);
416     config_init(CONFIG_INIT_CLIENT, NULL);
417 
418     if (config_errors(NULL) >= CFGERR_WARNINGS) {
419 	config_print_errors();
420 	if (config_errors(NULL) >= CFGERR_ERRORS) {
421 	    g_critical(_("errors processing config file"));
422 	}
423     }
424 
425     /* and now try to load the configuration named in that file */
426     config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
427 		getconf_str(CNF_CONF));
428 
429     check_running_as(RUNNING_AS_ROOT);
430 
431     dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
432 
433     our_features = am_init_feature_set();
434     our_features_string = am_feature_to_string(our_features);
435 
436     if (!starting_hostname) {
437 	starting_hostname = alloc(MAX_HOSTNAME_LENGTH+1);
438 	if (gethostname(starting_hostname, MAX_HOSTNAME_LENGTH) != 0) {
439 	    error(_("cannot determine local host name\n"));
440 	    /*NOTREACHED*/
441 	}
442 	starting_hostname[MAX_HOSTNAME_LENGTH] = '\0';
443     }
444 
445     server_name = NULL;
446     if (getconf_seen(CNF_INDEX_SERVER) == -2) { /* command line argument */
447 	server_name = getconf_str(CNF_INDEX_SERVER);
448     }
449     if (!server_name) {
450 	server_name = getenv("AMANDA_SERVER");
451 	if (server_name) {
452 	    g_printf(_("Using index server from environment AMANDA_SERVER (%s)\n"), server_name);
453 	}
454     }
455     if (!server_name) {
456 	server_name = getconf_str(CNF_INDEX_SERVER);
457     }
458     if (!server_name) {
459 	error(_("No index server set"));
460 	/*NOTREACHED*/
461     }
462     server_name = stralloc(server_name);
463 
464     tape_server_name = NULL;
465     if (getconf_seen(CNF_TAPE_SERVER) == -2) { /* command line argument */
466 	tape_server_name = getconf_str(CNF_TAPE_SERVER);
467     }
468     if (!tape_server_name) {
469 	tape_server_name = getenv("AMANDA_TAPE_SERVER");
470 	if (!tape_server_name) {
471 	    tape_server_name = getenv("AMANDA_TAPESERVER");
472 	    if (tape_server_name) {
473 		g_printf(_("Using tape server from environment AMANDA_TAPESERVER (%s)\n"), tape_server_name);
474 	    }
475 	} else {
476 	    g_printf(_("Using tape server from environment AMANDA_TAPE_SERVER (%s)\n"), tape_server_name);
477 	}
478     }
479     if (!tape_server_name) {
480 	tape_server_name = getconf_str(CNF_TAPE_SERVER);
481     }
482     if (!tape_server_name) {
483 	error(_("No tape server set"));
484 	/*NOTREACHED*/
485     }
486     tape_server_name = stralloc(tape_server_name);
487 
488     amfree(tape_device_name);
489     tape_device_name = getconf_str(CNF_TAPEDEV);
490     if (!tape_device_name ||
491 	strlen(tape_device_name) == 0 ||
492 	!getconf_seen(CNF_TAPEDEV)) {
493 	tape_device_name = NULL;
494     } else {
495 	tape_device_name = stralloc(tape_device_name);
496     }
497 
498     authopt = stralloc(getconf_str(CNF_AUTH));
499 
500 
501     amfree(disk_name);
502     amfree(mount_point);
503     amfree(disk_path);
504     amfree(disk_tpath);
505     dump_date[0] = '\0';
506 
507     /* Don't die when child closes pipe */
508     signal(SIGPIPE, SIG_IGN);
509 
510     /* set up signal handler */
511     act.sa_handler = sigint_handler;
512     sigemptyset(&act.sa_mask);
513     act.sa_flags = 0;
514     if (sigaction(SIGINT, &act, &oact) != 0) {
515 	error(_("error setting signal handler: %s"), strerror(errno));
516 	/*NOTREACHED*/
517     }
518 
519     proplist = g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &free_property_t);
520 
521     protocol_init();
522 
523     /* We assume that amindexd support fe_amindexd_options_features */
524     /*                             and fe_amindexd_options_auth     */
525     /* We should send a noop to really know                         */
526     req = vstrallocf("SERVICE amindexd\n"
527 		    "OPTIONS features=%s;auth=%s;\n",
528 		    our_features_string, authopt);
529 
530     secdrv = security_getdriver(authopt);
531     if (secdrv == NULL) {
532 	error(_("no '%s' security driver available for host '%s'"),
533 	    authopt, server_name);
534 	/*NOTREACHED*/
535     }
536 
537     protocol_sendreq(server_name, secdrv, generic_client_get_security_conf,
538 		     req, STARTUP_TIMEOUT, amindexd_response, &response_error);
539 
540     amfree(req);
541     protocol_run();
542 
543     g_printf(_("AMRECOVER Version %s. Contacting server on %s ...\n"),
544 	   VERSION, server_name);
545 
546     if(response_error != 0) {
547 	g_fprintf(stderr,"%s\n",errstr);
548 	exit(1);
549     }
550 
551     /* get server's banner */
552     if (grab_reply(1) == -1) {
553         aclose(server_socket);
554 	exit(1);
555     }
556     if (!server_happy()) {
557 	dbclose();
558 	aclose(server_socket);
559 	exit(1);
560     }
561 
562     /* try to get the features from the server */
563     {
564 	char *their_feature_string = NULL;
565 
566 	indexsrv_features = NULL;
567 
568 	line = vstrallocf("FEATURES %s", our_features_string);
569 	if(exchange(line) == 0) {
570 	    their_feature_string = stralloc(server_line+13);
571 	    indexsrv_features = am_string_to_feature(their_feature_string);
572 	    if (!indexsrv_features)
573 		g_printf(_("Bad feature string from server: %s"), their_feature_string);
574 	}
575 	if (!indexsrv_features)
576 	    indexsrv_features = am_set_default_feature_set();
577 
578 	amfree(their_feature_string);
579 	amfree(line);
580     }
581 
582     /* set the date of extraction to be today */
583     (void)time(&timer);
584     tm = localtime(&timer);
585     if (tm)
586 	strftime(dump_date, sizeof(dump_date), "%Y-%m-%d", tm);
587     else
588 	error(_("BAD DATE"));
589 
590     g_printf(_("Setting restore date to today (%s)\n"), dump_date);
591     line = vstrallocf("DATE %s", dump_date);
592     if (converse(line) == -1) {
593         aclose(server_socket);
594 	exit(1);
595     }
596     amfree(line);
597 
598     line = vstrallocf("SCNF %s", get_config_name());
599     if (converse(line) == -1) {
600         aclose(server_socket);
601 	exit(1);
602     }
603     amfree(line);
604 
605     if (server_happy()) {
606 	/* set host we are restoring to this host by default */
607 	amfree(dump_hostname);
608 	set_host(starting_hostname);
609 	if (dump_hostname)
610 	    g_printf(_("Use the setdisk command to choose dump disk to recover\n"));
611 	else
612 	    g_printf(_("Use the sethost command to choose a host to recover\n"));
613 
614     }
615 
616     quit_prog = 0;
617     do {
618 	if ((lineread = readline("amrecover> ")) == NULL) {
619 	    clearerr(stdin);
620 	    putchar('\n');
621 	    break;
622 	}
623 	if (lineread[0] != '\0')
624 	{
625 	    add_history(lineread);
626 	    dbprintf(_("user command: '%s'\n"), lineread);
627 	    process_line(lineread);	/* act on line's content */
628 	}
629 	amfree(lineread);
630     } while (!quit_prog);
631 
632     dbclose();
633 
634     aclose(server_socket);
635     return 0;
636 }
637 
638 static void
amindexd_response(void * datap,pkt_t * pkt,security_handle_t * sech)639 amindexd_response(
640     void *datap,
641     pkt_t *pkt,
642     security_handle_t *sech)
643 {
644     int ports[NSTREAMS], *response_error = datap, i;
645     char *p;
646     char *tok;
647     char *extra = NULL;
648 
649     assert(response_error != NULL);
650     assert(sech != NULL);
651 
652     if (pkt == NULL) {
653 	errstr = newvstrallocf(errstr, _("[request failed: %s]"),
654 			     security_geterror(sech));
655 	*response_error = 1;
656 	return;
657     }
658 
659     if (pkt->type == P_NAK) {
660 #if defined(PACKET_DEBUG)
661 	dbprintf(_("got nak response:\n----\n%s\n----\n\n"), pkt->body);
662 #endif
663 
664 	tok = strtok(pkt->body, " ");
665 	if (tok == NULL || strcmp(tok, "ERROR") != 0)
666 	    goto bad_nak;
667 
668 	tok = strtok(NULL, "\n");
669 	if (tok != NULL) {
670 	    errstr = newvstrallocf(errstr, "NAK: %s", tok);
671 	    *response_error = 1;
672 	} else {
673 bad_nak:
674 	    errstr = newvstrallocf(errstr, _("request NAK"));
675 	    *response_error = 2;
676 	}
677 	return;
678     }
679 
680     if (pkt->type != P_REP) {
681 	errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
682 			      pkt_type2str(pkt->type), pkt->body);
683 	*response_error = 1;
684 	return;
685     }
686 
687 #if defined(PACKET_DEBUG)
688     g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
689 #endif
690 
691     for(i = 0; i < NSTREAMS; i++) {
692         ports[i] = -1;
693         streams[i].fd = NULL;
694     }
695 
696     p = pkt->body;
697     while((tok = strtok(p, " \n")) != NULL) {
698 	p = NULL;
699 
700 	/*
701 	 * Error response packets have "ERROR" followed by the error message
702 	 * followed by a newline.
703 	 */
704 	if (strcmp(tok, "ERROR") == 0) {
705 	    tok = strtok(NULL, "\n");
706 	    if (tok == NULL) {
707 	        errstr = newvstrallocf(errstr, _("[bogus error packet]"));
708 	    } else {
709 		errstr = newvstrallocf(errstr, "%s", tok);
710 	    }
711 	    *response_error = 2;
712 	    return;
713 	}
714 
715 
716         /*
717          * Regular packets have CONNECT followed by three streams
718          */
719         if (strcmp(tok, "CONNECT") == 0) {
720 
721 	    /*
722 	     * Parse the three stream specifiers out of the packet.
723 	     */
724 	    for (i = 0; i < NSTREAMS; i++) {
725 		tok = strtok(NULL, " ");
726 		if (tok == NULL || strcmp(tok, streams[i].name) != 0) {
727 		    extra = vstrallocf(
728 			   _("CONNECT token is \"%s\": expected \"%s\""),
729 			   tok ? tok : _("(null)"), streams[i].name);
730 		    goto parse_error;
731 		}
732 		tok = strtok(NULL, " \n");
733 		if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
734 		    extra = vstrallocf(
735 			   _("CONNECT %s token is \"%s\" expected a port number"),
736 			   streams[i].name, tok ? tok : _("(null)"));
737 		    goto parse_error;
738 		}
739 	    }
740 	    continue;
741 	}
742 
743 	/*
744 	 * OPTIONS [options string] '\n'
745 	 */
746 	if (strcmp(tok, "OPTIONS") == 0) {
747 	    tok = strtok(NULL, "\n");
748 	    if (tok == NULL) {
749 		extra = vstrallocf(_("OPTIONS token is missing"));
750 		goto parse_error;
751 	    }
752 #if 0
753 	    tok_end = tok + strlen(tok);
754 	    while((p = strchr(tok, ';')) != NULL) {
755 		*p++ = '\0';
756 		if(strncmp_const(tok, "features=") == 0) {
757 		    tok += SIZEOF("features=") - 1;
758 		    am_release_feature_set(their_features);
759 		    if((their_features = am_string_to_feature(tok)) == NULL) {
760 			errstr = newvstrallocf(errstr,
761 				      _("OPTIONS: bad features value: %s"),
762 				      tok);
763 			goto parse_error;
764 		    }
765 		}
766 		tok = p;
767 	    }
768 #endif
769 	    continue;
770 	}
771 #if 0
772 	extra = vstrallocf(_("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""), tok ? tok : _("(null)"));
773 	goto parse_error;
774 #endif
775     }
776 
777     /*
778      * Connect the streams to their remote ports
779      */
780     for (i = 0; i < NSTREAMS; i++) {
781 /*@i@*/	if (ports[i] == -1)
782 	    continue;
783 	streams[i].fd = security_stream_client(sech, ports[i]);
784 	if (streams[i].fd == NULL) {
785 	    errstr = newvstrallocf(errstr,
786 			_("[could not connect %s stream: %s]"),
787 			streams[i].name, security_geterror(sech));
788 	    goto connect_error;
789 	}
790     }
791     /*
792      * Authenticate the streams
793      */
794     for (i = 0; i < NSTREAMS; i++) {
795 	if (streams[i].fd == NULL)
796 	    continue;
797 	if (security_stream_auth(streams[i].fd) < 0) {
798 	    errstr = newvstrallocf(errstr,
799 		_("[could not authenticate %s stream: %s]"),
800 		streams[i].name, security_stream_geterror(streams[i].fd));
801 	    goto connect_error;
802 	}
803     }
804 
805     /*
806      * The MESGFD and DATAFD streams are mandatory.  If we didn't get
807      * them, complain.
808      */
809     if (streams[MESGFD].fd == NULL) {
810         errstr = newvstrallocf(errstr, _("[couldn't open MESG streams]"));
811         goto connect_error;
812     }
813 
814     /* everything worked */
815     *response_error = 0;
816     amindexd_alive = 1;
817     return;
818 
819 parse_error:
820     errstr = newvstrallocf(errstr,
821 			  _("[parse of reply message failed: %s]"),
822 			  extra ? extra : _("(no additional information)"));
823     amfree(extra);
824     *response_error = 2;
825     return;
826 
827 connect_error:
828     stop_amindexd();
829     *response_error = 1;
830 }
831 
832 /*
833  * This is called when everything needs to shut down so event_loop()
834  * will exit.
835  */
836 void
stop_amindexd(void)837 stop_amindexd(void)
838 {
839     int i;
840 
841     amindexd_alive = 0;
842     for (i = 0; i < NSTREAMS; i++) {
843         if (streams[i].fd != NULL) {
844             security_stream_close(streams[i].fd);
845             streams[i].fd = NULL;
846         }
847     }
848 }
849 
850 
851 char *
translate_octal(char * line)852 translate_octal(
853     char *line)
854 {
855     char *s = line, *s1, *s2;
856     char *p = line;
857     int i;
858 
859     if (!translate_mode)
860 	return strdup(line);
861 
862     while(*s != '\0') {
863 	if ((s == line || *(s-1) != '\\') && *s == '\\') {
864 	    s++;
865 	    s1 = s+1;
866 	    s2 = s+2;
867 	    if (g_ascii_isdigit(*s) && *s1 != '\0' &&
868 		g_ascii_isdigit(*s1) &&
869 		g_ascii_isdigit(*s2)) {
870 		i = ((*s)-'0')*64 + ((*s1)-'0')*8 + ((*s2)-'0');
871 		*p++ = i;
872 		s += 3;
873 	    } else {
874 		*p++ = *s++;
875 	    }
876 
877 	} else {
878 	    *p++ = *s++;
879 	}
880     }
881     *p = '\0';
882 
883     return line;
884 }
885 
886