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