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