60c8c16940bcf4407f399edbf213f4f1a51e1c06
[debian/amanda] / common-src / fileheader.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: fileheader.c 6512 2007-05-24 17:00:24Z ian $
28  */
29
30 #include "amanda.h"
31 #include "fileheader.h"
32 #include "match.h"
33 #include <glib.h>
34 #include "util.h"
35
36 static const char *     filetype2str(filetype_t);
37 static filetype_t       str2filetype(const char *);
38 static void             strange_header(dumpfile_t *, const char *,
39                                 size_t, const char *, const char *);
40 static ssize_t          hexdump(const char *buffer, size_t len);
41 static char            *quote_heredoc(char *text, char *delimiter_prefix);
42 static char            *parse_heredoc(char *line, char **saveptr);
43
44 void
45 fh_init(
46     dumpfile_t *file)
47 {
48     memset(file, '\0', SIZEOF(*file));
49     file->type = F_EMPTY;
50     file->blocksize = 0;
51 }
52
53 static void
54 strange_header(
55     dumpfile_t *file,
56     const char *buffer,
57     size_t      buflen,
58     const char *expected,
59     const char *actual)
60 {
61     if (actual == NULL)
62         actual = "<null>";
63     if (expected == NULL)
64         expected = "<null>";
65
66     g_fprintf(stderr, _("%s: strange amanda header: \"%.*s\"\n"), get_pname(),
67                 (int)buflen, buffer);
68
69     g_fprintf(stderr, _("%s: Expected: \"%s\"  Actual: \"%s\"\n"), get_pname(),
70                 expected, actual);
71
72     file->type = F_WEIRD;
73 }
74
75 /* chop whitespace off of a string, in place */
76 static void
77 chomp(char *str)
78 {
79     char *s = str;
80
81     if (!str)
82         return;
83
84     /* trim leading space */
85     while (g_ascii_isspace(*s)) { s++; }
86     if (s != str)
87         memmove(str, s, strlen(s)+1);
88
89     /* trim trailing space */
90     if (*str) {
91         for (s = str+strlen(str)-1; s >= str; s--) {
92             if (!g_ascii_isspace(*s))
93                 break;
94             *s = '\0';
95         }
96     }
97 }
98
99 void
100 parse_file_header(
101     const char *buffer,
102     dumpfile_t *file,
103     size_t      buflen)
104 {
105     char *buf, *line, *tok, *line1;
106     size_t lsize;
107     char *uqname;
108     int in_quotes;
109     char *saveptr = NULL;
110
111     /* put the buffer into a writable chunk of memory and nul-term it */
112     buf = alloc(buflen + 1);
113     memcpy(buf, buffer, buflen);
114     buf[buflen] = '\0';
115     fh_init(file); 
116
117     /* extract the first unquoted line */
118     in_quotes = 0;
119     for (line = buf, lsize = 0; lsize < buflen; line++) {
120         if ((*line == '\n') && !in_quotes)
121             break;
122
123         if (*line == '"') {
124             in_quotes = !in_quotes;
125         } else if ((*line == '\\') && (*(line + 1) == '"')) {
126             line++;
127             lsize++;
128         }
129         lsize++;
130     }
131     *line = '\0';
132     line1 = alloc(lsize + 1);
133     strncpy(line1, buf, lsize);
134     line1[lsize] = '\0';
135     *line = '\n';
136
137     tok = strtok_r(line1, " ", &saveptr);
138     if (tok == NULL) {
139         g_fprintf(stderr, _("%s: Empty amanda header: buflen=%zu lsize=%zu\n"), get_pname(),
140             buflen, 
141             lsize);
142         hexdump(buffer, lsize);
143         strange_header(file, buffer, buflen, _("<Non-empty line>"), tok);
144         goto out;
145     }
146
147     if (strcmp(tok, "NETDUMP:") != 0 && strcmp(tok, "AMANDA:") != 0) {
148         amfree(buf);
149         file->type = F_UNKNOWN;
150         amfree(line1);
151         return;
152     }
153
154     tok = strtok_r(NULL, " ", &saveptr);
155     if (tok == NULL) {
156         strange_header(file, buffer, buflen, _("<file type>"), tok);
157         goto out;
158     }
159     file->type = str2filetype(tok);
160     
161     switch (file->type) {
162     case F_TAPESTART:
163         tok = strtok_r(NULL, " ", &saveptr);
164         if ((tok == NULL) || (strcmp(tok, "DATE") != 0)) {
165             strange_header(file, buffer, buflen, "DATE", tok);
166             goto out;
167         }
168
169         tok = strtok_r(NULL, " ", &saveptr);
170         if (tok == NULL) {
171             strange_header(file, buffer, buflen, _("<date stamp>"), tok);
172             goto out;
173         }
174         strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
175
176         tok = strtok_r(NULL, " ", &saveptr);
177         if ((tok == NULL) || (strcmp(tok, "TAPE") != 0)) {
178             strange_header(file, buffer, buflen, "TAPE", tok);
179             goto out;
180         }
181
182         tok = strtok_r(NULL, " ", &saveptr);
183         if (tok == NULL) {
184             strange_header(file, buffer, buflen, _("<file type>"), tok);
185             goto out;
186         }
187         strncpy(file->name, tok, SIZEOF(file->name) - 1);
188         break;
189
190     case F_DUMPFILE:
191     case F_CONT_DUMPFILE:
192     case F_SPLIT_DUMPFILE:
193         tok = strtok_r(NULL, " ", &saveptr);
194         if (tok == NULL) {
195             strange_header(file, buffer, buflen, _("<date stamp>"), tok);
196             goto out;
197         }
198         strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
199
200         tok = strtok_r(NULL, " ", &saveptr);
201         if (tok == NULL) {
202             strange_header(file, buffer, buflen, _("<file name>"), tok);
203             goto out;
204         }
205         strncpy(file->name, tok, SIZEOF(file->name) - 1);
206
207         tok = strquotedstr(&saveptr);
208         if (tok == NULL) {
209             strange_header(file, buffer, buflen, _("<disk name>"), tok);
210             goto out;
211         }
212         uqname = unquote_string(tok);
213         strncpy(file->disk, uqname, SIZEOF(file->disk) - 1);
214         amfree(uqname);
215         
216         if(file->type == F_SPLIT_DUMPFILE) {
217             tok = strtok_r(NULL, " ", &saveptr);
218             if (tok == NULL || strcmp(tok, "part") != 0) {
219                 strange_header(file, buffer, buflen, "part", tok);
220                 goto out;
221             }
222
223             tok = strtok_r(NULL, "/", &saveptr);
224             if ((tok == NULL) || (sscanf(tok, "%d", &file->partnum) != 1)) {
225                 strange_header(file, buffer, buflen, _("<part num param>"), tok);
226                 goto out;
227             }
228
229             /* If totalparts == -1, then the original dump was done in 
230                streaming mode (no holding disk), thus we don't know how 
231                many parts there are. */
232             tok = strtok_r(NULL, " ", &saveptr);
233             if((tok == NULL) || (sscanf(tok, "%d", &file->totalparts) != 1)) {
234                 strange_header(file, buffer, buflen, _("<total parts param>"), tok);
235                 goto out;
236             }
237         }
238
239         tok = strtok_r(NULL, " ", &saveptr);
240         if ((tok == NULL) || (strcmp(tok, "lev") != 0)) {
241             strange_header(file, buffer, buflen, "lev", tok);
242             goto out;
243         }
244
245         tok = strtok_r(NULL, " ", &saveptr);
246         if ((tok == NULL) || (sscanf(tok, "%d", &file->dumplevel) != 1)) {
247             strange_header(file, buffer, buflen, _("<dump level param>"), tok);
248             goto out;
249         }
250
251         tok = strtok_r(NULL, " ", &saveptr);
252         if ((tok == NULL) || (strcmp(tok, "comp") != 0)) {
253             strange_header(file, buffer, buflen, "comp", tok);
254             goto out;
255         }
256
257         tok = strtok_r(NULL, " ", &saveptr);
258         if (tok == NULL) {
259             strange_header(file, buffer, buflen, _("<comp param>"), tok);
260             goto out;
261         }
262         strncpy(file->comp_suffix, tok, SIZEOF(file->comp_suffix) - 1);
263
264         file->compressed = (0 != strcmp(file->comp_suffix, "N"));
265         if (file->compressed) {
266             /* compatibility with pre-2.2 amanda */
267             if (strcmp(file->comp_suffix, "C") == 0)
268                 strncpy(file->comp_suffix, ".Z", SIZEOF(file->comp_suffix) - 1);
269         } else {
270             strcpy(file->comp_suffix, "");
271         }
272
273         tok = strtok_r(NULL, " ", &saveptr);
274         /* "program" is optional */
275         if (tok == NULL || strcmp(tok, "program") != 0) {
276             break;
277         }
278
279         tok = strtok_r(NULL, " ", &saveptr);
280         if (tok == NULL) {
281             strange_header(file, buffer, buflen, _("<program name>"), tok);
282             goto out;
283         }
284         strncpy(file->program, tok, SIZEOF(file->program) - 1);
285
286         if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
287              break;          /* reached the end of the buffer */
288
289         /* encryption is optional */
290         if (BSTRNCMP(tok, "crypt") == 0) {
291             tok = strtok_r(NULL, " ", &saveptr);
292             if (tok == NULL) {
293                 strange_header(file, buffer, buflen, _("<crypt param>"), tok);
294                 goto out;
295             }
296             strncpy(file->encrypt_suffix, tok,
297                     SIZEOF(file->encrypt_suffix) - 1);
298             file->encrypted = 1;
299
300             /* for compatibility with who-knows-what, allow "comp N" to be
301              * equivalent to no compression */
302             if (0 == BSTRNCMP(file->encrypt_suffix, "N")) {
303                 file->encrypted = 0;
304                 strcpy(file->encrypt_suffix, "");
305             }
306
307             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
308                 break;
309         }
310
311         /* "srvcompprog" is optional */
312         if (BSTRNCMP(tok, "server_custom_compress") == 0) {
313             tok = strtok_r(NULL, " ", &saveptr);
314             if (tok == NULL) {
315                 strange_header(file, buffer, buflen,
316                                 _("<server custom compress param>"), tok);
317                 goto out;
318             }
319             strncpy(file->srvcompprog, tok, SIZEOF(file->srvcompprog) - 1);
320             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
321                 break;      
322         }
323
324         /* "clntcompprog" is optional */
325         if (BSTRNCMP(tok, "client_custom_compress") == 0) {
326             tok = strtok_r(NULL, " ", &saveptr);
327             if (tok == NULL) {
328                 strange_header(file, buffer, buflen,
329                                 _("<client custom compress param>"), tok);
330                 goto out;
331             }
332             strncpy(file->clntcompprog, tok, SIZEOF(file->clntcompprog) - 1);
333             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL)
334                 break;
335         }
336
337         /* "srv_encrypt" is optional */
338         if (BSTRNCMP(tok, "server_encrypt") == 0) {
339             tok = strtok_r(NULL, " ", &saveptr);
340             if (tok == NULL) {
341                 strange_header(file, buffer, buflen,
342                                 _("<server encrypt param>"), tok);
343                 goto out;
344             }
345             strncpy(file->srv_encrypt, tok, SIZEOF(file->srv_encrypt) - 1);
346             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) 
347                 break;
348         }
349
350         /* "clnt_encrypt" is optional */
351         if (BSTRNCMP(tok, "client_encrypt") == 0) {
352             tok = strtok_r(NULL, " ", &saveptr);
353             if (tok == NULL) {
354                 strange_header(file, buffer, buflen,
355                                 _("<client encrypt param>"), tok);
356                 goto out;
357             }
358             strncpy(file->clnt_encrypt, tok, SIZEOF(file->clnt_encrypt) - 1);
359             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) 
360                 break;
361         }
362
363         /* "srv_decrypt_opt" is optional */
364         if (BSTRNCMP(tok, "server_decrypt_option") == 0) {
365             tok = strtok_r(NULL, " ", &saveptr);
366             if (tok == NULL) {
367                 strange_header(file, buffer, buflen,
368                                 _("<server decrypt param>"), tok);
369                 goto out;
370             }
371             strncpy(file->srv_decrypt_opt, tok,
372                     SIZEOF(file->srv_decrypt_opt) - 1);
373             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) 
374                 break;
375         }
376
377         /* "clnt_decrypt_opt" is optional */
378         if (BSTRNCMP(tok, "client_decrypt_option") == 0) {
379             tok = strtok_r(NULL, " ", &saveptr);
380             if (tok == NULL) {
381                 strange_header(file, buffer, buflen,
382                                 _("<client decrypt param>"), tok);
383                 goto out;
384             }
385             strncpy(file->clnt_decrypt_opt, tok,
386                     SIZEOF(file->clnt_decrypt_opt) - 1);
387             if ((tok = strtok_r(NULL, " ", &saveptr)) == NULL) 
388                 break;
389         }
390       break;
391       
392
393     case F_TAPEEND:
394         tok = strtok_r(NULL, " ", &saveptr);
395         /* DATE is optional */
396         if (tok != NULL) {
397             if (strcmp(tok, "DATE") == 0) {
398                 tok = strtok_r(NULL, " ", &saveptr);
399                 if(tok == NULL)
400                     file->datestamp[0] = '\0';
401                 else
402                     strncpy(file->datestamp, tok, SIZEOF(file->datestamp) - 1);
403             } else {
404                 strange_header(file, buffer, buflen, _("<DATE>"), tok);
405            }
406         } else {
407             file->datestamp[0] = '\0';
408         }
409         break;
410
411     case F_NOOP:
412         /* nothing follows */
413         break;
414
415     default:
416         strange_header(file, buffer, buflen,
417                 _("TAPESTART|DUMPFILE|CONT_DUMPFILE|SPLIT_DUMPFILE|TAPEEND|NOOP"), tok);
418         goto out;
419     }
420
421     (void)strtok_r(buf, "\n", &saveptr); /* this is the first line */
422     /* iterate through the rest of the lines */
423     while ((line = strtok_r(NULL, "\n", &saveptr)) != NULL) {
424 #define SC "CONT_FILENAME="
425         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
426             line += SIZEOF(SC) - 1;
427             strncpy(file->cont_filename, line,
428                     SIZEOF(file->cont_filename) - 1);
429             continue;
430         }
431 #undef SC
432
433 #define SC "PARTIAL="
434         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
435             line += SIZEOF(SC) - 1;
436             file->is_partial = !strcasecmp(line, "yes");
437             continue;
438         }
439 #undef SC
440 #define SC "APPLICATION="
441         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
442             line += SIZEOF(SC) - 1;
443             strncpy(file->application, line,
444                     SIZEOF(file->application) - 1);
445             continue;
446         }
447 #undef SC
448
449 #define SC "ORIGSIZE="
450         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
451             line += SIZEOF(SC) - 1;
452             file->orig_size = OFF_T_ATOI(line);
453         }
454 #undef SC
455
456 #define SC "DLE="
457         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
458             line += SIZEOF(SC) - 1;
459             file->dle_str = parse_heredoc(line, &saveptr);
460         }
461 #undef SC
462
463 #define SC _("To restore, position tape at start of file and run:")
464         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0)
465             continue;
466 #undef SC
467
468 #define SC "\tdd if=<tape> "
469         if (strncmp(line, SC, SIZEOF(SC) - 1) == 0) {
470             char *cmd1, *cmd2, *cmd3=NULL;
471
472             /* skip over dd command */
473             if ((cmd1 = strchr(line, '|')) == NULL) {
474
475                 strncpy(file->recover_cmd, "BUG",
476                         SIZEOF(file->recover_cmd) - 1);
477                 continue;
478             }
479             *cmd1++ = '\0';
480
481             /* block out first pipeline command */
482             if ((cmd2 = strchr(cmd1, '|')) != NULL) {
483               *cmd2++ = '\0';
484               if ((cmd3 = strchr(cmd2, '|')) != NULL)
485                 *cmd3++ = '\0';
486             }
487            
488             /* clean up some extra spaces in various fields */
489             chomp(cmd1);
490             chomp(cmd2);
491             chomp(cmd3);
492
493             /* three cmds: decrypt    | uncompress | recover
494              * two   cmds: uncompress | recover
495              * XXX note that if there are two cmds, the first one 
496              * XXX could be either uncompress or decrypt. Since no
497              * XXX code actually call uncompress_cmd/decrypt_cmd, it's ok
498              * XXX for header information.
499              * one   cmds: recover
500              */
501
502             if ( cmd3 == NULL) {
503               if (cmd2 == NULL) {
504                 strncpy(file->recover_cmd, cmd1,
505                         SIZEOF(file->recover_cmd) - 1);
506               } else {
507                 g_snprintf(file->uncompress_cmd,
508                          SIZEOF(file->uncompress_cmd), "%s |", cmd1);
509                 strncpy(file->recover_cmd, cmd2,
510                         SIZEOF(file->recover_cmd) - 1);
511               }
512             } else {    /* cmd3 presents:  decrypt | uncompress | recover */
513               g_snprintf(file->decrypt_cmd,
514                        SIZEOF(file->decrypt_cmd), "%s |", cmd1);
515               g_snprintf(file->uncompress_cmd,
516                        SIZEOF(file->uncompress_cmd), "%s |", cmd2);
517               strncpy(file->recover_cmd, cmd3,
518                       SIZEOF(file->recover_cmd) - 1);
519             }
520             continue;
521         }
522 #undef SC
523         /* XXX complain about weird lines? */
524     }
525
526 out:
527     amfree(buf);
528     amfree(line1);
529 }
530
531 void
532 dump_dumpfile_t(
533     const dumpfile_t *file)
534 {
535         g_debug(_("Contents of *(dumpfile_t *)%p:"), file);
536         g_debug(_("    type             = %d (%s)"),
537                         file->type, filetype2str(file->type));
538         g_debug(_("    datestamp        = '%s'"), file->datestamp);
539         g_debug(_("    dumplevel        = %d"), file->dumplevel);
540         g_debug(_("    compressed       = %d"), file->compressed);
541         g_debug(_("    encrypted        = %d"), file->encrypted);
542         g_debug(_("    comp_suffix      = '%s'"), file->comp_suffix);
543         g_debug(_("    encrypt_suffix   = '%s'"), file->encrypt_suffix);
544         g_debug(_("    name             = '%s'"), file->name);
545         g_debug(_("    disk             = '%s'"), file->disk);
546         g_debug(_("    program          = '%s'"), file->program);
547         g_debug(_("    application      = '%s'"), file->application);
548         g_debug(_("    srvcompprog      = '%s'"), file->srvcompprog);
549         g_debug(_("    clntcompprog     = '%s'"), file->clntcompprog);
550         g_debug(_("    srv_encrypt      = '%s'"), file->srv_encrypt);
551         g_debug(_("    clnt_encrypt     = '%s'"), file->clnt_encrypt);
552         g_debug(_("    recover_cmd      = '%s'"), file->recover_cmd);
553         g_debug(_("    uncompress_cmd   = '%s'"), file->uncompress_cmd);
554         g_debug(_("    decrypt_cmd      = '%s'"), file->decrypt_cmd);
555         g_debug(_("    srv_decrypt_opt  = '%s'"), file->srv_decrypt_opt);
556         g_debug(_("    clnt_decrypt_opt = '%s'"), file->clnt_decrypt_opt);
557         g_debug(_("    cont_filename    = '%s'"), file->cont_filename);
558         if (file->dle_str)
559             g_debug(_("    dle_str          = %s"), file->dle_str);
560         else
561             g_debug(_("    dle_str          = (null)"));
562         g_debug(_("    is_partial       = %d"), file->is_partial);
563         g_debug(_("    partnum          = %d"), file->partnum);
564         g_debug(_("    totalparts       = %d"), file->totalparts);
565         if (file->blocksize)
566             g_debug(_("    blocksize        = %zu"), file->blocksize);
567 }
568
569 static void
570 validate_nonempty_str(
571     const char *val,
572     const char *name)
573 {
574     if (strlen(val) == 0) {
575         error(_("Invalid %s '%s'\n"), name, val);
576         /*NOTREACHED*/
577     }
578 }
579
580 static void
581 validate_not_both(
582     const char *val1, const char *val2,
583     const char *name1, const char *name2)
584 {
585     if (*val1 && *val2) {
586         error("cannot set both %s and %s\n", name1, name2);
587     }
588 }
589
590 static void
591 validate_no_space(
592     const char *val,
593     const char *name)
594 {
595     if (strchr(val, ' ') != NULL) {
596         error(_("%s cannot contain spaces\n"), name);
597         /*NOTREACHED*/
598     }
599 }
600
601 static void
602 validate_pipe_cmd(
603         const char *cmd,
604         const char *name)
605 {
606     if (strlen(cmd) && cmd[strlen(cmd)-1] != '|') {
607         error("invalid %s (must end with '|'): '%s'\n", name, cmd);
608     }
609 }
610
611 static void
612 validate_encrypt_suffix(
613         int encrypted,
614         const char *suff)
615 {
616     if (encrypted) {
617         if (!suff[0] || (0 == strcmp(suff, "N"))) {
618             error(_("Invalid encrypt_suffix '%s'\n"), suff);
619         }
620     } else {
621         if (suff[0] && (0 != strcmp(suff, "N"))) {
622             error(_("Invalid header: encrypt_suffix '%s' specified but not encrypted\n"), suff);
623         }
624     }
625 }
626
627 static void
628 validate_datestamp(
629     const char *datestamp)
630 {
631         if (strcmp(datestamp, "X") == 0) {
632             return;
633         }
634
635         if ((strlen(datestamp) == 8) && match("^[0-9]{8}$", datestamp)) {
636             return;
637         }
638         if ((strlen(datestamp) == 14) && match("^[0-9]{14}$", datestamp)) {
639             return;
640         }
641         error(_("Invalid datestamp '%s'\n"), datestamp);
642         /*NOTREACHED*/
643 }
644
645 static void
646 validate_parts(
647     const int partnum,
648     const int totalparts)
649 {
650         if (partnum < 1) {
651             error(_("Invalid partnum (%d)\n"), partnum);
652             /*NOTREACHED*/
653         }
654
655         if (partnum > totalparts && totalparts >= 0) {
656             error(_("Invalid partnum (%d) > totalparts (%d)\n"),
657                         partnum, totalparts);
658             /*NOTREACHED*/
659         }
660 }
661
662 char *
663 build_header(const dumpfile_t * file, size_t *size, size_t max_size)
664 {
665     GString *rval, *split_data;
666     char *qname;
667     char *program;
668     size_t min_size;
669
670     min_size = size? *size : max_size;
671     g_debug(_("Building type %s header of %zu-%zu bytes with name='%s' disk='%s' dumplevel=%d and blocksize=%zu"),
672             filetype2str(file->type), min_size, max_size,
673             file->name, file->disk, file->dumplevel, file->blocksize);
674
675     rval = g_string_sized_new(min_size);
676     split_data = g_string_sized_new(10);
677
678     switch (file->type) {
679     case F_TAPESTART:
680         validate_nonempty_str(file->name, "name");
681         validate_datestamp(file->datestamp);
682         g_string_printf(rval,
683                         "AMANDA: TAPESTART DATE %s TAPE %s\n\014\n",
684                         file->datestamp, file->name);
685         break;
686
687     case F_SPLIT_DUMPFILE:
688         validate_parts(file->partnum, file->totalparts);
689         g_string_printf(split_data,
690                         " part %d/%d ", file->partnum, file->totalparts);
691         /* FALLTHROUGH */
692
693     case F_CONT_DUMPFILE:
694     case F_DUMPFILE :
695         validate_nonempty_str(file->name, "name");
696         validate_nonempty_str(file->program, "program");
697         validate_datestamp(file->datestamp);
698         validate_encrypt_suffix(file->encrypted, file->encrypt_suffix);
699         qname = quote_string(file->disk);
700         program = stralloc(file->program);
701         if (match("^.*[.][Ee][Xx][Ee]$", program)) {
702                 /* Trim ".exe" from program name */
703                 program[strlen(program) - strlen(".exe")] = '\0';
704         }
705         g_string_printf(rval, 
706                         "AMANDA: %s %s %s %s %s lev %d comp %s program %s",
707                         filetype2str(file->type),
708                         file->datestamp, file->name, qname,
709                         split_data->str,
710                         file->dumplevel,
711                         file->compressed? file->comp_suffix : "N",
712                         program); 
713         amfree(program);
714         amfree(qname);
715
716         /* only output crypt if it's enabled */
717         if (file->encrypted) {
718             g_string_append_printf(rval, " crypt %s", file->encrypt_suffix);
719         }
720
721         validate_not_both(file->srvcompprog, file->clntcompprog,
722                             "srvcompprog", "clntcompprog");
723         if (*file->srvcompprog) {
724             validate_no_space(file->srvcompprog, "srvcompprog");
725             g_string_append_printf(rval, " server_custom_compress %s",
726                                    file->srvcompprog);
727         } else if (*file->clntcompprog) {
728             validate_no_space(file->clntcompprog, "clntcompprog");
729             g_string_append_printf(rval, " client_custom_compress %s",
730                                    file->clntcompprog);
731         }
732
733         validate_not_both(file->srv_encrypt, file->clnt_encrypt,
734                             "srv_encrypt", "clnt_encrypt");
735         if (*file->srv_encrypt) {
736             validate_no_space(file->srv_encrypt, "srv_encrypt");
737             g_string_append_printf(rval, " server_encrypt %s",
738                                    file->srv_encrypt);
739         } else if (*file->clnt_encrypt) {
740             validate_no_space(file->clnt_encrypt, "clnt_encrypt");
741             g_string_append_printf(rval, " client_encrypt %s",
742                                    file->clnt_encrypt);
743         }
744
745         validate_not_both(file->srv_decrypt_opt, file->clnt_decrypt_opt,
746                             "srv_decrypt_opt", "clnt_decrypt_opt");
747         if (*file->srv_decrypt_opt) {
748             validate_no_space(file->srv_decrypt_opt, "srv_decrypt_opt");
749             g_string_append_printf(rval, " server_decrypt_option %s",
750                                    file->srv_decrypt_opt);
751         } else if (*file->clnt_decrypt_opt) {
752             g_string_append_printf(rval, " client_decrypt_option %s",
753                                    file->clnt_decrypt_opt);
754         } 
755         
756         g_string_append_printf(rval, "\n");
757         
758         if (file->cont_filename[0] != '\0') {
759             validate_no_space(file->cont_filename, "cont_filename");
760             g_string_append_printf(rval, "CONT_FILENAME=%s\n",
761                                    file->cont_filename);
762         }
763         if (file->application[0] != '\0') {
764             validate_no_space(file->application, "application");
765             g_string_append_printf(rval, "APPLICATION=%s\n", file->application);
766         }
767         if (file->is_partial != 0) {
768             g_string_append_printf(rval, "PARTIAL=YES\n");
769         }
770         if (file->orig_size > 0) {
771             g_string_append_printf(rval, "ORIGSIZE=%jd\n",
772                                          (intmax_t)file->orig_size);
773         }
774         if (file->dle_str && strlen(file->dle_str) < max_size-2048) {
775             char *heredoc = quote_heredoc(file->dle_str, "ENDDLE");
776             g_string_append_printf(rval, "DLE=%s\n", heredoc);
777             amfree(heredoc);
778         }
779         
780         g_string_append_printf(rval,
781             _("To restore, position tape at start of file and run:\n"));
782
783         g_string_append_printf(rval, "\tdd if=<tape> ");
784         if (file->blocksize)
785             g_string_append_printf(rval, "bs=%zuk ",
786                                    file->blocksize / 1024);
787         g_string_append_printf(rval, "skip=1 | ");
788         if (*file->recover_cmd) {
789             if (*file->decrypt_cmd) {
790                 validate_pipe_cmd(file->decrypt_cmd, "decrypt_cmd");
791                 g_string_append_printf(rval, "%s ", file->decrypt_cmd);
792             }
793             if (*file->uncompress_cmd) {
794                 validate_pipe_cmd(file->uncompress_cmd, "uncompress_cmd");
795                 g_string_append_printf(rval, "%s ", file->uncompress_cmd);
796             }
797             g_string_append_printf(rval, "%s ", file->recover_cmd);
798         } else {
799             if (*file->uncompress_cmd || *file->decrypt_cmd)
800                 error("cannot specify uncompress_cmd or decrypt_cmd without recover_cmd\n");
801         }
802         /* \014 == ^L == form feed */
803         g_string_append_printf(rval, "\n\014\n");
804         break;
805
806     case F_TAPEEND:
807         validate_datestamp(file->datestamp);
808         g_string_printf(rval, "AMANDA: TAPEEND DATE %s\n\014\n",
809                         file->datestamp);
810         break;
811
812     case F_NOOP:
813         g_string_printf(rval, "AMANDA: NOOP\n\014\n");
814         break;
815
816     case F_UNKNOWN:
817     case F_EMPTY:
818     case F_WEIRD:
819     default:
820         error(_("Invalid header type: %d (%s)"),
821                 file->type, filetype2str(file->type));
822         /*NOTREACHED*/
823     }
824     
825     g_string_free(split_data, TRUE);
826
827     /* is it too big? */
828     if (rval->len > max_size) {
829         g_debug("header is larger than %zu bytes -- cannot create", max_size);
830         g_string_free(rval, TRUE);
831         return NULL;
832     }
833
834     /* Clear extra bytes. */
835     if (rval->len < min_size) {
836         bzero(rval->str + rval->len, rval->allocated_len - rval->len);
837     }
838     if (size) {
839         *size = MAX(min_size, (size_t)rval->len);
840     }
841     return g_string_free(rval, FALSE);
842 }
843
844 void
845 print_header(
846     FILE *              outf,
847     const dumpfile_t *  file)
848 {
849     char *summ = summarize_header(file);
850     g_fprintf(outf, "%s\n", summ);
851     g_free(summ);
852 }
853
854 /*
855  * Prints the contents of the file structure.
856  */
857 char *
858 summarize_header(
859     const dumpfile_t *  file)
860 {
861     char *qdisk;
862     GString *summ;
863
864     switch(file->type) {
865     case F_EMPTY:
866         return g_strdup(_("EMPTY file"));
867
868     case F_UNKNOWN:
869         return g_strdup(_("UNKNOWN file"));
870
871     default:
872     case F_WEIRD:
873         return g_strdup(_("WEIRD file"));
874
875     case F_TAPESTART:
876         return g_strdup_printf(_("start of tape: date %s label %s"),
877                file->datestamp, file->name);
878
879     case F_NOOP:
880         return g_strdup(_("NOOP file"));
881
882     case F_DUMPFILE:
883     case F_CONT_DUMPFILE:
884         qdisk = quote_string(file->disk);
885         summ = g_string_new("");
886         g_string_printf(summ, "%s: date %s host %s disk %s lev %d comp %s",
887             filetype2str(file->type), file->datestamp, file->name,
888             qdisk, file->dumplevel,
889             file->compressed? file->comp_suffix : "N");
890         amfree(qdisk);
891         goto add_suffixes;
892
893     case F_SPLIT_DUMPFILE: {
894         char totalparts[NUM_STR_SIZE*2];
895         if(file->totalparts > 0)
896             g_snprintf(totalparts, SIZEOF(totalparts), "%d", file->totalparts);
897         else
898             g_snprintf(totalparts, SIZEOF(totalparts), "UNKNOWN");
899         qdisk = quote_string(file->disk);
900         summ = g_string_new("");
901         g_string_printf(summ, "split dumpfile: date %s host %s disk %s"
902                       " part %d/%s lev %d comp %s",
903                       file->datestamp, file->name, qdisk, file->partnum,
904                       totalparts, file->dumplevel,
905                       file->compressed? file->comp_suffix : "N");
906         amfree(qdisk);
907         goto add_suffixes;
908     }
909
910     add_suffixes:
911         if (*file->program)
912             g_string_append_printf(summ, " program %s", file->program);
913         if (strcmp(file->encrypt_suffix, "enc") == 0)
914             g_string_append_printf(summ, " crypt %s", file->encrypt_suffix);
915         if (*file->srvcompprog)
916             g_string_append_printf(summ, " server_custom_compress %s", file->srvcompprog);
917         if (*file->clntcompprog)
918             g_string_append_printf(summ, " client_custom_compress %s", file->clntcompprog);
919         if (*file->srv_encrypt)
920             g_string_append_printf(summ, " server_encrypt %s", file->srv_encrypt);
921         if (*file->clnt_encrypt)
922             g_string_append_printf(summ, " client_encrypt %s", file->clnt_encrypt);
923         if (*file->srv_decrypt_opt)
924             g_string_append_printf(summ, " server_decrypt_option %s", file->srv_decrypt_opt);
925         if (*file->clnt_decrypt_opt)
926             g_string_append_printf(summ, " client_decrypt_option %s", file->clnt_decrypt_opt);
927         return g_string_free(summ, FALSE);
928
929     case F_TAPEEND:
930         return g_strdup_printf("end of tape: date %s", file->datestamp);
931         break;
932     }
933 }
934
935 int
936 known_compress_type(
937     const dumpfile_t *  file)
938 {
939     if(strcmp(file->comp_suffix, ".Z") == 0)
940         return 1;
941 #ifdef HAVE_GZIP
942     if(strcmp(file->comp_suffix, ".gz") == 0)
943         return 1;
944 #endif
945     if(strcmp(file->comp_suffix, "cust") == 0)
946         return 1;
947     return 0;
948 }
949
950 static const struct {
951     filetype_t type;
952     const char *str;
953 } filetypetab[] = {
954     { F_UNKNOWN, "UNKNOWN" },
955     { F_WEIRD, "WEIRD" },
956     { F_TAPESTART, "TAPESTART" },
957     { F_TAPEEND,  "TAPEEND" },
958     { F_DUMPFILE, "FILE" },
959     { F_CONT_DUMPFILE, "CONT_FILE" },
960     { F_SPLIT_DUMPFILE, "SPLIT_FILE" },
961     { F_NOOP, "NOOP" }
962 };
963 #define NFILETYPES      (size_t)(sizeof(filetypetab) / sizeof(filetypetab[0]))
964
965 static const char *
966 filetype2str(
967     filetype_t  type)
968 {
969     int i;
970
971     for (i = 0; i < (int)NFILETYPES; i++)
972         if (filetypetab[i].type == type)
973             return (filetypetab[i].str);
974     return ("UNKNOWN");
975 }
976
977 static filetype_t
978 str2filetype(
979     const char *str)
980 {
981     int i;
982
983     for (i = 0; i < (int)NFILETYPES; i++)
984         if (strcmp(filetypetab[i].str, str) == 0)
985             return (filetypetab[i].type);
986     return (F_UNKNOWN);
987 }
988
989 gboolean headers_are_equal(dumpfile_t * a, dumpfile_t * b) {
990     if (a == NULL && b == NULL)
991         return TRUE;
992
993     if (a == NULL || b == NULL)
994         return FALSE;
995
996     if (a->type != b->type) return FALSE;
997     if (strcmp(a->datestamp, b->datestamp)) return FALSE;
998     if (a->dumplevel != b->dumplevel) return FALSE;
999     if (a->compressed != b->compressed) return FALSE;
1000     if (a->encrypted != b->encrypted) return FALSE;
1001     if (strcmp(a->comp_suffix, b->comp_suffix)) return FALSE;
1002     if (strcmp(a->encrypt_suffix, b->encrypt_suffix)) return FALSE;
1003     if (strcmp(a->name, b->name)) return FALSE;
1004     if (strcmp(a->disk, b->disk)) return FALSE;
1005     if (strcmp(a->program, b->program)) return FALSE;
1006     if (strcmp(a->application, b->application)) return FALSE;
1007     if (strcmp(a->srvcompprog, b->srvcompprog)) return FALSE;
1008     if (strcmp(a->clntcompprog, b->clntcompprog)) return FALSE;
1009     if (strcmp(a->srv_encrypt, b->srv_encrypt)) return FALSE;
1010     if (strcmp(a->clnt_encrypt, b->clnt_encrypt)) return FALSE;
1011     if (strcmp(a->recover_cmd, b->recover_cmd)) return FALSE;
1012     if (strcmp(a->uncompress_cmd, b->uncompress_cmd)) return FALSE;
1013     if (strcmp(a->decrypt_cmd, b->decrypt_cmd)) return FALSE;
1014     if (strcmp(a->srv_decrypt_opt, b->srv_decrypt_opt)) return FALSE;
1015     if (strcmp(a->clnt_decrypt_opt, b->clnt_decrypt_opt)) return FALSE;
1016     if (strcmp(a->cont_filename, b->cont_filename)) return FALSE;
1017     if (a->dle_str != b->dle_str && a->dle_str && b->dle_str
1018         && strcmp(a->dle_str, b->dle_str)) return FALSE;
1019     if (a->is_partial != b->is_partial) return FALSE;
1020     if (a->partnum != b->partnum) return FALSE;
1021     if (a->totalparts != b->totalparts) return FALSE;
1022     if (a->blocksize != b->blocksize) return FALSE;
1023
1024     return TRUE; /* ok, they're the same */
1025 }
1026
1027 dumpfile_t * dumpfile_copy(dumpfile_t* source) {
1028     dumpfile_t* rval = malloc(sizeof(dumpfile_t));
1029     memcpy(rval, source, sizeof(dumpfile_t));
1030     if (rval->dle_str) rval->dle_str = stralloc(rval->dle_str);
1031     return rval;
1032 }
1033
1034 void
1035 dumpfile_copy_in_place(
1036     dumpfile_t *dest,
1037     dumpfile_t* source)
1038 {
1039     memcpy(dest, source, sizeof(dumpfile_t));
1040     if (dest->dle_str) dest->dle_str = stralloc(dest->dle_str);
1041 }
1042
1043 void dumpfile_free_data(dumpfile_t* info) {
1044     if (info) {
1045         amfree(info->dle_str);
1046     }
1047 }
1048
1049 void dumpfile_free(dumpfile_t* info) {
1050     dumpfile_free_data(info);
1051     amfree(info);
1052 }
1053
1054 static ssize_t
1055 hexdump(
1056     const char *buffer,
1057     size_t      len)
1058 {
1059     ssize_t rc = -1;
1060
1061     FILE *stream = popen("od -c -x -", "w");
1062         
1063     if (stream != NULL) {
1064         fflush(stdout);
1065         rc = (ssize_t)fwrite(buffer, len, 1, stream);
1066         if (ferror(stream))
1067             rc = -1;
1068         pclose(stream);
1069     }
1070     return rc;
1071 }
1072
1073 static char *quote_heredoc(
1074     char  *text,
1075     char  *delimiter_prefix)
1076 {
1077     char *delimiter = stralloc(delimiter_prefix);
1078     int delimiter_n = 0;
1079     int delimiter_len = strlen(delimiter);
1080     char *quoted;
1081
1082     /* keep picking delimiters until we find one that's not a line in TEXT */
1083     while (1) {
1084         char *line = text;
1085         char *c = text;
1086         gboolean found_delimiter = FALSE;
1087
1088         while (1) {
1089             if (*c == '\n' || *c == '\0') {
1090                 int linelen = c - line;
1091                 if (linelen == delimiter_len && 0 == strncmp(line, delimiter, linelen)) {
1092                     found_delimiter = TRUE;
1093                     break;
1094                 }
1095                 line = c+1;
1096             }
1097             if (!*c) break;
1098             c++;
1099         }
1100
1101         if (!found_delimiter)
1102             break;
1103
1104         delimiter = newvstrallocf(delimiter, "%s%d", delimiter_prefix, ++delimiter_n);
1105         delimiter_len = strlen(delimiter);
1106     }
1107
1108     /* we have a delimiter .. now use it */
1109     quoted = vstrallocf("<<%s\n%s\n%s", delimiter, text, delimiter);
1110     amfree(delimiter);
1111     return quoted;
1112 }
1113
1114 static char *parse_heredoc(
1115     char  *line,
1116     char **saveptr)
1117 {
1118     char *result = NULL;
1119
1120     if (strncmp(line, "<<", 2) == 0) {
1121         char *keyword = line+2;
1122         char *new_line;
1123
1124         while((new_line = strtok_r(NULL, "\n", saveptr)) != NULL &&
1125               strcmp(new_line, keyword) != 0) {
1126             result = vstrextend(&result, new_line, "\n", NULL);
1127         }
1128         /* make sure we have something */
1129         if (!result)
1130             result = g_strdup("");
1131         /* remove latest '\n' */
1132         else if (strlen(result) > 0)
1133             result[strlen(result)-1] = '\0';
1134     } else {
1135         result = stralloc(line);
1136     }
1137     return result;
1138 }