]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_restore.c
Merge branch 'master' into basejobv3
[bacula/bacula] / bacula / src / dird / ua_restore.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *
30  *   Bacula Director -- User Agent Database restore Command
31  *      Creates a bootstrap file for restoring files and
32  *      starts the restore job.
33  *
34  *      Tree handling routines split into ua_tree.c July MMIII.
35  *      BSR (bootstrap record) handling routines split into
36  *        bsr.c July MMIII
37  *
38  *     Kern Sibbald, July MMII
39  *
40  *   Version $Id$
41  */
42
43
44 #include "bacula.h"
45 #include "dird.h"
46
47 /* Imported functions */
48 extern void print_bsr(UAContext *ua, RBSR *bsr);
49
50
51
52 /* Forward referenced functions */
53 static int last_full_handler(void *ctx, int num_fields, char **row);
54 static int jobid_handler(void *ctx, int num_fields, char **row);
55 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
56 static int fileset_handler(void *ctx, int num_fields, char **row);
57 static void free_name_list(NAME_LIST *name_list);
58 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
59 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
60 static void free_rx(RESTORE_CTX *rx);
61 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *fname);
62 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
63 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
64                                          char *date);
65 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
66                                         char *date);
67 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir);
68 static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
69 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx);
70 static int get_date(UAContext *ua, char *date, int date_len);
71 static int restore_count_handler(void *ctx, int num_fields, char **row);
72 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
73 static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx);
74
75 /*
76  *   Restore files
77  *
78  */
79 int restore_cmd(UAContext *ua, const char *cmd)
80 {
81    RESTORE_CTX rx;                    /* restore context */
82    JOB *job;
83    int i;
84    JCR *jcr = ua->jcr;
85    char *escaped_bsr_name = NULL;
86    char *escaped_where_name = NULL;
87    char *strip_prefix, *add_prefix, *add_suffix, *regexp;
88    strip_prefix = add_prefix = add_suffix = regexp = NULL;
89
90    memset(&rx, 0, sizeof(rx));
91    rx.path = get_pool_memory(PM_FNAME);
92    rx.fname = get_pool_memory(PM_FNAME);
93    rx.JobIds = get_pool_memory(PM_FNAME);
94    rx.BaseJobIds = get_pool_memory(PM_FNAME);
95    rx.query = get_pool_memory(PM_FNAME);
96    rx.bsr = new_bsr();
97
98    i = find_arg_with_value(ua, "where");
99    if (i >= 0) {
100       rx.where = ua->argv[i];
101    }
102
103    i = find_arg_with_value(ua, "strip_prefix");
104    if (i >= 0) {
105       strip_prefix = ua->argv[i];
106    }
107
108    i = find_arg_with_value(ua, "add_prefix");
109    if (i >= 0) {
110       add_prefix = ua->argv[i];
111    }
112
113    i = find_arg_with_value(ua, "add_suffix");
114    if (i >= 0) {
115       add_suffix = ua->argv[i];
116    }
117
118    i = find_arg_with_value(ua, "regexwhere");
119    if (i >= 0) {
120       rx.RegexWhere = ua->argv[i];
121    }
122
123    if (strip_prefix || add_suffix || add_prefix) {
124       int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix);
125       regexp = (char *)bmalloc(len * sizeof(char));
126
127       bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix);
128       rx.RegexWhere = regexp;
129    }
130
131    /* TODO: add acl for regexwhere ? */
132
133    if (rx.RegexWhere) {
134       if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere)) {
135          ua->error_msg(_("\"RegexWhere\" specification not authorized.\n"));
136          goto bail_out;
137       }
138    }
139
140    if (rx.where) {
141       if (!acl_access_ok(ua, Where_ACL, rx.where)) {
142          ua->error_msg(_("\"where\" specification not authorized.\n"));
143          goto bail_out;
144       }
145    }
146
147    if (!open_client_db(ua)) {
148       goto bail_out;
149    }
150
151    /* Ensure there is at least one Restore Job */
152    LockRes();
153    foreach_res(job, R_JOB) {
154       if (job->JobType == JT_RESTORE) {
155          if (!rx.restore_job) {
156             rx.restore_job = job;
157          }
158          rx.restore_jobs++;
159       }
160    }
161    UnlockRes();
162    if (!rx.restore_jobs) {
163       ua->error_msg(_(
164          "No Restore Job Resource found in bacula-dir.conf.\n"
165          "You must create at least one before running this command.\n"));
166       goto bail_out;
167    }
168
169    /*
170     * Request user to select JobIds or files by various different methods
171     *  last 20 jobs, where File saved, most recent backup, ...
172     *  In the end, a list of files are pumped into
173     *  add_findex()
174     */
175    switch (user_select_jobids_or_files(ua, &rx)) {
176    case 0:                            /* error */
177       goto bail_out;
178    case 1:                            /* selected by jobid */
179       get_and_display_basejobs(ua, &rx);
180       if (!build_directory_tree(ua, &rx)) {
181          ua->send_msg(_("Restore not done.\n"));
182          goto bail_out;
183       }
184       break;
185    case 2:                            /* selected by filename, no tree needed */
186       break;
187    }
188
189    if (rx.bsr->JobId) {
190       uint32_t selected_files;
191       char ed1[50];
192       if (!complete_bsr(ua, rx.bsr)) {   /* find Vol, SessId, SessTime from JobIds */
193          ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n"));
194          goto bail_out;
195       }
196       if (!(selected_files = write_bsr_file(ua, rx))) {
197          ua->warning_msg(_("No files selected to be restored.\n"));
198          goto bail_out;
199       }
200       display_bsr_info(ua, rx);          /* display vols needed, etc */
201
202       /* If no count of files, use bsr generated value (often wrong) */
203       if (rx.selected_files == 0) {
204          rx.selected_files = selected_files;
205       }
206       if (rx.selected_files==1) {
207          ua->info_msg(_("\n1 file selected to be restored.\n\n"));
208       }
209       else {
210          ua->info_msg(_("\n%s files selected to be restored.\n\n"), 
211             edit_uint64_with_commas(rx.selected_files, ed1));
212       }
213    } else {
214       ua->warning_msg(_("No files selected to be restored.\n"));
215       goto bail_out;
216    }
217
218    if (rx.restore_jobs == 1) {
219       job = rx.restore_job;
220    } else {
221       job = select_restore_job_resource(ua);
222    }
223    if (!job) {
224       goto bail_out;
225    }
226
227    get_client_name(ua, &rx);
228    if (!rx.ClientName) {
229       ua->error_msg(_("No Client resource found!\n"));
230       goto bail_out;
231    }
232    get_restore_client_name(ua, rx);
233
234    escaped_bsr_name = escape_filename(jcr->RestoreBootstrap);
235
236    /* Build run command */
237    if (rx.RegexWhere) {
238       escaped_where_name = escape_filename(rx.RegexWhere);
239       Mmsg(ua->cmd,
240           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
241           " bootstrap=\"%s\" regexwhere=\"%s\" files=%u catalog=\"%s\"",
242           job->name(), rx.ClientName, rx.RestoreClientName, 
243           rx.store?rx.store->name():"",
244           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
245           escaped_where_name ? escaped_where_name : rx.RegexWhere,
246           rx.selected_files, ua->catalog->name());
247
248    } else if (rx.where) {
249       escaped_where_name = escape_filename(rx.where);
250       Mmsg(ua->cmd,
251           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
252           " bootstrap=\"%s\" where=\"%s\" files=%u catalog=\"%s\"",
253           job->name(), rx.ClientName, rx.RestoreClientName,
254           rx.store?rx.store->name():"",
255           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
256           escaped_where_name ? escaped_where_name : rx.where,
257           rx.selected_files, ua->catalog->name());
258
259    } else {
260       Mmsg(ua->cmd,
261           "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\""
262           " bootstrap=\"%s\" files=%u catalog=\"%s\"",
263           job->name(), rx.ClientName, rx.RestoreClientName,
264           rx.store?rx.store->name():"",
265           escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap,
266           rx.selected_files, ua->catalog->name());
267    }
268
269    if (escaped_bsr_name != NULL) {
270       bfree(escaped_bsr_name);
271    }
272
273    if (escaped_where_name != NULL) {
274       bfree(escaped_where_name);
275    }
276    
277    if (regexp) {
278       bfree(regexp);
279    }
280
281    if (find_arg(ua, NT_("yes")) > 0) {
282       pm_strcat(ua->cmd, " yes");    /* pass it on to the run command */
283    }
284    Dmsg1(200, "Submitting: %s\n", ua->cmd);
285    parse_ua_args(ua);
286    run_cmd(ua, ua->cmd);
287    free_rx(&rx);
288    return 1;
289
290 bail_out:
291    if (escaped_bsr_name != NULL) {
292       bfree(escaped_bsr_name);
293    }
294
295    if (escaped_where_name != NULL) {
296       bfree(escaped_where_name);
297    }
298
299    if (regexp) {
300       bfree(regexp);
301    }
302
303    free_rx(&rx);
304    return 0;
305
306 }
307
308 /* 
309  * Fill the rx->BaseJobIds and display the list
310  */
311 static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx)
312 {
313    rx->BaseJobIds[0] = '\0';
314
315    if (!db_get_used_base_jobids(ua->jcr, ua->db, rx->JobIds, rx->BaseJobIds)) {
316       ua->warning_msg("%s", db_strerror(ua->db));
317    }
318    
319    if (*rx->BaseJobIds) {
320       POOL_MEM q;
321       Mmsg(q, uar_print_jobs, rx->BaseJobIds);
322       ua->send_msg(_("The restore will use the following job(s) as Base\n"));
323       db_list_sql_query(ua->jcr, ua->db, q.c_str(), prtit, ua, 1, HORZ_LIST);
324    }
325 }
326
327 static void free_rx(RESTORE_CTX *rx)
328 {
329    free_bsr(rx->bsr);
330    rx->bsr = NULL;
331    free_and_null_pool_memory(rx->JobIds);
332    free_and_null_pool_memory(rx->BaseJobIds);
333    free_and_null_pool_memory(rx->fname);
334    free_and_null_pool_memory(rx->path);
335    free_and_null_pool_memory(rx->query);
336    free_name_list(&rx->name_list);
337 }
338
339 static bool has_value(UAContext *ua, int i)
340 {
341    if (!ua->argv[i]) {
342       ua->error_msg(_("Missing value for keyword: %s\n"), ua->argk[i]);
343       return false;
344    }
345    return true;
346 }
347
348 /*
349  * This gets the client name from which the backup was made
350  */
351 static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
352 {
353    /* If no client name specified yet, get it now */
354    if (!rx->ClientName[0]) {
355       CLIENT_DBR cr;
356       /* try command line argument */
357       int i = find_arg_with_value(ua, NT_("client"));
358       if (i < 0) {
359          i = find_arg_with_value(ua, NT_("backupclient"));
360       }
361       if (i >= 0) {
362          if (!has_value(ua, i)) {
363             return 0;
364          }
365          bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
366          return 1;
367       }
368       memset(&cr, 0, sizeof(cr));
369       if (!get_client_dbr(ua, &cr)) {
370          return 0;
371       }
372       bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
373    }
374    return 1;
375 }
376
377 /*
378  * This is where we pick up a client name to restore to.
379  */
380 static int get_restore_client_name(UAContext *ua, RESTORE_CTX &rx)
381 {
382    /* Start with same name as backup client */
383    bstrncpy(rx.RestoreClientName, rx.ClientName, sizeof(rx.RestoreClientName));    
384
385    /* try command line argument */
386    int i = find_arg_with_value(ua, NT_("restoreclient"));
387    if (i >= 0) {
388       if (!has_value(ua, i)) {
389          return 0;
390       }
391       bstrncpy(rx.RestoreClientName, ua->argv[i], sizeof(rx.RestoreClientName));
392       return 1;
393    }
394    return 1;
395 }
396
397
398
399 /*
400  * The first step in the restore process is for the user to
401  *  select a list of JobIds from which he will subsequently
402  *  select which files are to be restored.
403  *
404  *  Returns:  2  if filename list made
405  *            1  if jobid list made
406  *            0  on error
407  */
408 static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx)
409 {
410    char *p;
411    char date[MAX_TIME_LENGTH];
412    bool have_date = false;
413    /* Include current second if using current time */
414    utime_t now = time(NULL) + 1;
415    JobId_t JobId;
416    JOB_DBR jr = { (JobId_t)-1 };
417    bool done = false;
418    int i, j;
419    const char *list[] = {
420       _("List last 20 Jobs run"),
421       _("List Jobs where a given File is saved"),
422       _("Enter list of comma separated JobIds to select"),
423       _("Enter SQL list command"),
424       _("Select the most recent backup for a client"),
425       _("Select backup for a client before a specified time"),
426       _("Enter a list of files to restore"),
427       _("Enter a list of files to restore before a specified time"),
428       _("Find the JobIds of the most recent backup for a client"),
429       _("Find the JobIds for a backup for a client before a specified time"),
430       _("Enter a list of directories to restore for found JobIds"),
431       _("Select full restore to a specified JobId"),
432       _("Cancel"),
433       NULL };
434
435    const char *kw[] = {
436        /* These keywords are handled in a for loop */
437       "jobid",       /* 0 */
438       "current",     /* 1 */
439       "before",      /* 2 */
440       "file",        /* 3 */
441       "directory",   /* 4 */
442       "select",      /* 5 */
443       "pool",        /* 6 */
444       "all",         /* 7 */
445
446       /* The keyword below are handled by individual arg lookups */
447       "client",       /* 8 */
448       "storage",      /* 9 */
449       "fileset",      /* 10 */
450       "where",        /* 11 */
451       "yes",          /* 12 */
452       "bootstrap",    /* 13 */
453       "done",         /* 14 */
454       "strip_prefix", /* 15 */
455       "add_prefix",   /* 16 */
456       "add_suffix",   /* 17 */
457       "regexwhere",   /* 18 */
458       "restoreclient", /* 19 */
459       "copies",        /* 20 */
460       NULL
461    };
462
463    rx->JobIds[0] = 0;
464
465    for (i=1; i<ua->argc; i++) {       /* loop through arguments */
466       bool found_kw = false;
467       for (j=0; kw[j]; j++) {         /* loop through keywords */
468          if (strcasecmp(kw[j], ua->argk[i]) == 0) {
469             found_kw = true;
470             break;
471          }
472       }
473       if (!found_kw) {
474          ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]);
475          return 0;
476       }
477       /* Found keyword in kw[] list, process it */
478       switch (j) {
479       case 0:                            /* jobid */
480          if (!has_value(ua, i)) {
481             return 0;
482          }
483          if (*rx->JobIds != 0) {
484             pm_strcat(rx->JobIds, ",");
485          }
486          pm_strcat(rx->JobIds, ua->argv[i]);
487          done = true;
488          break;
489       case 1:                            /* current */
490          /*
491           * Note, we add one second here just to include any job
492           *  that may have finished within the current second,
493           *  which happens a lot in scripting small jobs.
494           */
495          bstrutime(date, sizeof(date), now);
496          have_date = true;
497          break;
498       case 2:                            /* before */
499          if (have_date || !has_value(ua, i)) {
500             return 0;
501          }
502          if (str_to_utime(ua->argv[i]) == 0) {
503             ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]);
504             return 0;
505          }
506          bstrncpy(date, ua->argv[i], sizeof(date));
507          have_date = true;
508          break;
509       case 3:                            /* file */
510       case 4:                            /* dir */
511          if (!has_value(ua, i)) {
512             return 0;
513          }
514          if (!have_date) {
515             bstrutime(date, sizeof(date), now);
516          }
517          if (!get_client_name(ua, rx)) {
518             return 0;
519          }
520          pm_strcpy(ua->cmd, ua->argv[i]);
521          insert_one_file_or_dir(ua, rx, date, j==4);
522          return 2;
523       case 5:                            /* select */
524          if (!have_date) {
525             bstrutime(date, sizeof(date), now);
526          }
527          if (!select_backups_before_date(ua, rx, date)) {
528             return 0;
529          }
530          done = true;
531          break;
532       case 6:                            /* pool specified */
533          if (!has_value(ua, i)) {
534             return 0;
535          }
536          rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
537          if (!rx->pool) {
538             ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
539             return 0;
540          }
541          if (!acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
542             rx->pool = NULL;
543             ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]);
544             return 0;
545          }
546          break;
547       case 7:                         /* all specified */
548          rx->all = true;
549          break;
550       /*
551        * All keywords 7 or greater are ignored or handled by a select prompt
552        */
553       default:
554          break;
555       }
556    }
557
558    if (!done) {
559       ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n"
560                   "to be restored. You will be presented several methods\n"
561                   "of specifying the JobIds. Then you will be allowed to\n"
562                   "select which files from those JobIds are to be restored.\n\n"));
563    }
564
565    /* If choice not already made above, prompt */
566    for ( ; !done; ) {
567       char *fname;
568       int len;
569       bool gui_save;
570
571       start_prompt(ua, _("To select the JobIds, you have the following choices:\n"));
572       for (int i=0; list[i]; i++) {
573          add_prompt(ua, list[i]);
574       }
575       done = true;
576       switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
577       case -1:                        /* error or cancel */
578          return 0;
579       case 0:                         /* list last 20 Jobs run */
580          if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
581             ua->error_msg(_("SQL query not authorized.\n"));
582             return 0;
583          }
584          gui_save = ua->jcr->gui;
585          ua->jcr->gui = true;
586          db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, 1, HORZ_LIST);
587          ua->jcr->gui = gui_save;
588          done = false;
589          break;
590       case 1:                         /* list where a file is saved */
591          if (!get_client_name(ua, rx)) {
592             return 0;
593          }
594          if (!get_cmd(ua, _("Enter Filename (no path):"))) {
595             return 0;
596          }
597          len = strlen(ua->cmd);
598          fname = (char *)malloc(len * 2 + 1);
599          db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len);
600          Mmsg(rx->query, uar_file[db_type], rx->ClientName, fname);
601          free(fname);
602          gui_save = ua->jcr->gui;
603          ua->jcr->gui = true;
604          db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, 1, HORZ_LIST);
605          ua->jcr->gui = gui_save;
606          done = false;
607          break;
608       case 2:                         /* enter a list of JobIds */
609          if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
610             return 0;
611          }
612          pm_strcpy(rx->JobIds, ua->cmd);
613          break;
614       case 3:                         /* Enter an SQL list command */
615          if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), 8)) {
616             ua->error_msg(_("SQL query not authorized.\n"));
617             return 0;
618          }
619          if (!get_cmd(ua, _("Enter SQL list command: "))) {
620             return 0;
621          }
622          gui_save = ua->jcr->gui;
623          ua->jcr->gui = true;
624          db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, 1, HORZ_LIST);
625          ua->jcr->gui = gui_save;
626          done = false;
627          break;
628       case 4:                         /* Select the most recent backups */
629          if (!have_date) {
630             bstrutime(date, sizeof(date), now);
631          }
632          if (!select_backups_before_date(ua, rx, date)) {
633             return 0;
634          }
635          break;
636       case 5:                         /* select backup at specified time */
637          if (!have_date) {
638             if (!get_date(ua, date, sizeof(date))) {
639                return 0;
640             }
641          }
642          if (!select_backups_before_date(ua, rx, date)) {
643             return 0;
644          }
645          break;
646       case 6:                         /* Enter files */
647          if (!have_date) {
648             bstrutime(date, sizeof(date), now);
649          }
650          if (!get_client_name(ua, rx)) {
651             return 0;
652          }
653          ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
654                         "containing a list of file names with paths, and terminate\n"
655                         "them with a blank line.\n"));
656          for ( ;; ) {
657             if (!get_cmd(ua, _("Enter full filename: "))) {
658                return 0;
659             }
660             len = strlen(ua->cmd);
661             if (len == 0) {
662                break;
663             }
664             insert_one_file_or_dir(ua, rx, date, false);
665          }
666          return 2;
667        case 7:                        /* enter files backed up before specified time */
668          if (!have_date) {
669             if (!get_date(ua, date, sizeof(date))) {
670                return 0;
671             }
672          }
673          if (!get_client_name(ua, rx)) {
674             return 0;
675          }
676          ua->send_msg(_("Enter file names with paths, or < to enter a filename\n"
677                         "containing a list of file names with paths, and terminate\n"
678                         "them with a blank line.\n"));
679          for ( ;; ) {
680             if (!get_cmd(ua, _("Enter full filename: "))) {
681                return 0;
682             }
683             len = strlen(ua->cmd);
684             if (len == 0) {
685                break;
686             }
687             insert_one_file_or_dir(ua, rx, date, false);
688          }
689          return 2;
690
691       case 8:                         /* Find JobIds for current backup */
692          if (!have_date) {
693             bstrutime(date, sizeof(date), now);
694          }
695          if (!select_backups_before_date(ua, rx, date)) {
696             return 0;
697          }
698          done = false;
699          break;
700
701       case 9:                         /* Find JobIds for give date */
702          if (!have_date) {
703             if (!get_date(ua, date, sizeof(date))) {
704                return 0;
705             }
706          }
707          if (!select_backups_before_date(ua, rx, date)) {
708             return 0;
709          }
710          done = false;
711          break;
712
713       case 10:                        /* Enter directories */
714          if (*rx->JobIds != 0) {
715             ua->send_msg(_("You have already selected the following JobIds: %s\n"),
716                rx->JobIds);
717          } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) {
718             if (*rx->JobIds != 0 && *ua->cmd) {
719                pm_strcat(rx->JobIds, ",");
720             }
721             pm_strcat(rx->JobIds, ua->cmd);
722          }
723          if (*rx->JobIds == 0 || *rx->JobIds == '.') {
724             return 0;                 /* nothing entered, return */
725          }
726          if (!have_date) {
727             bstrutime(date, sizeof(date), now);
728          }
729          if (!get_client_name(ua, rx)) {
730             return 0;
731          }
732          ua->send_msg(_("Enter full directory names or start the name\n"
733                         "with a < to indicate it is a filename containing a list\n"
734                         "of directories and terminate them with a blank line.\n"));
735          for ( ;; ) {
736             if (!get_cmd(ua, _("Enter directory name: "))) {
737                return 0;
738             }
739             len = strlen(ua->cmd);
740             if (len == 0) {
741                break;
742             }
743             /* Add trailing slash to end of directory names */
744             if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) {
745                strcat(ua->cmd, "/");
746             }
747             insert_one_file_or_dir(ua, rx, date, true);
748          }
749          return 2;
750
751       case 11:                        /* Choose a jobid and select jobs */
752          if (!get_cmd(ua, _("Enter JobId to restore: ")) ||
753              !is_an_integer(ua->cmd)) 
754          {
755             return 0;
756          }
757
758          memset(&jr, 0, sizeof(JOB_DBR));
759          jr.JobId = str_to_int64(ua->cmd);
760          if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
761             ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
762                           ua->cmd, db_strerror(ua->db));
763             return 0;
764          }
765          jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
766          if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, rx->JobIds)) {
767             return 0;
768          }
769          Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds);
770          break;
771       case 12:                        /* Cancel or quit */
772          return 0;
773       }
774    }
775
776    memset(&jr, 0, sizeof(JOB_DBR));
777    POOLMEM *JobIds = get_pool_memory(PM_FNAME);
778    *JobIds = 0;
779    rx->TotalFiles = 0;
780    /*        
781     * Find total number of files to be restored, and filter the JobId
782     *  list to contain only ones permitted by the ACL conditions.
783     */
784    for (p=rx->JobIds; ; ) {
785       char ed1[50];
786       int stat = get_next_jobid_from_list(&p, &JobId);
787       if (stat < 0) {
788          ua->error_msg(_("Invalid JobId in list.\n"));
789          free_pool_memory(JobIds);
790          return 0;
791       }
792       if (stat == 0) {
793          break;
794       }
795       if (jr.JobId == JobId) {
796          continue;                    /* duplicate of last JobId */
797       }
798       memset(&jr, 0, sizeof(JOB_DBR));
799       jr.JobId = JobId;
800       if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
801          ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
802             edit_int64(JobId, ed1), db_strerror(ua->db));
803          free_pool_memory(JobIds);
804          return 0;
805       }
806       if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
807          ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"),
808             edit_int64(JobId, ed1), jr.Name);
809          continue;
810       }
811       if (*JobIds != 0) {
812          pm_strcat(JobIds, ",");
813       }
814       pm_strcat(JobIds, edit_int64(JobId, ed1));
815       rx->TotalFiles += jr.JobFiles;
816    }
817    free_pool_memory(rx->JobIds);
818    rx->JobIds = JobIds;               /* Set ACL filtered list */
819    if (*rx->JobIds == 0) {
820       ua->warning_msg(_("No Jobs selected.\n"));
821       return 0;
822    }
823
824    if (strchr(rx->JobIds,',')) {
825       ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds);
826    } else {
827       ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds);
828    }
829    return 1;
830 }
831
832 /*
833  * Get date from user
834  */
835 static int get_date(UAContext *ua, char *date, int date_len)
836 {
837    ua->send_msg(_("The restored files will the most current backup\n"
838                   "BEFORE the date you specify below.\n\n"));
839    for ( ;; ) {
840       if (!get_cmd(ua, _("Enter date as YYYY-MM-DD HH:MM:SS :"))) {
841          return 0;
842       }
843       if (str_to_utime(ua->cmd) != 0) {
844          break;
845       }
846       ua->error_msg(_("Improper date format.\n"));
847    }
848    bstrncpy(date, ua->cmd, date_len);
849    return 1;
850 }
851
852 /*
853  * Insert a single file, or read a list of files from a file
854  */
855 static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, bool dir)
856 {
857    FILE *ffd;
858    char file[5000];
859    char *p = ua->cmd;
860    int line = 0;
861
862    switch (*p) {
863    case '<':
864       p++;
865       if ((ffd = fopen(p, "rb")) == NULL) {
866          berrno be;
867          ua->error_msg(_("Cannot open file %s: ERR=%s\n"),
868             p, be.bstrerror());
869          break;
870       }
871       while (fgets(file, sizeof(file), ffd)) {
872          line++;
873          if (dir) {
874             if (!insert_dir_into_findex_list(ua, rx, file, date)) {
875                ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
876             }
877          } else {
878             if (!insert_file_into_findex_list(ua, rx, file, date)) {
879                ua->error_msg(_("Error occurred on line %d of file \"%s\"\n"), line, p);
880             }
881          }
882       }
883       fclose(ffd);
884       break;
885    case '?':
886       p++;
887       insert_table_into_findex_list(ua, rx, p);
888       break;
889    default:
890       if (dir) {
891          insert_dir_into_findex_list(ua, rx, ua->cmd, date);
892       } else {
893          insert_file_into_findex_list(ua, rx, ua->cmd, date);
894       }
895       break;
896    }
897 }
898
899 /*
900  * For a given file (path+filename), split into path and file, then
901  *   lookup the most recent backup in the catalog to get the JobId
902  *   and FileIndex, then insert them into the findex list.
903  */
904 static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *file,
905                                         char *date)
906 {
907    strip_trailing_newline(file);
908    split_path_and_filename(ua, rx, file);
909    if (*rx->JobIds == 0) {
910       Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, 
911            rx->ClientName);
912    } else {
913       Mmsg(rx->query, uar_jobids_fileindex, rx->JobIds, date,
914            rx->path, rx->fname, rx->ClientName);
915    }
916    rx->found = false;
917    /* Find and insert jobid and File Index */
918    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
919       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
920          rx->query, db_strerror(ua->db));
921    }
922    if (!rx->found) {
923       ua->error_msg(_("No database record found for: %s\n"), file);
924 //    ua->error_msg("Query=%s\n", rx->query);
925       return true;
926    }
927    return true;
928 }
929
930 /*
931  * For a given path lookup the most recent backup in the catalog
932  * to get the JobId and FileIndexes of all files in that directory.
933  */
934 static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *dir,
935                                         char *date)
936 {  
937    strip_trailing_junk(dir);
938    if (*rx->JobIds == 0) {
939       ua->error_msg(_("No JobId specified cannot continue.\n"));
940       return false;
941    } else {
942       Mmsg(rx->query, uar_jobid_fileindex_from_dir[db_type], rx->JobIds, dir, rx->ClientName);
943    }
944    rx->found = false;
945    /* Find and insert jobid and File Index */
946    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
947       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
948          rx->query, db_strerror(ua->db));
949    }
950    if (!rx->found) {
951       ua->error_msg(_("No database record found for: %s\n"), dir);
952       return true;
953    }
954    return true;
955 }
956
957 /*
958  * Get the JobId and FileIndexes of all files in the specified table
959  */
960 static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
961 {
962    strip_trailing_junk(table);
963    Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
964
965    rx->found = false;
966    /* Find and insert jobid and File Index */
967    if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
968       ua->error_msg(_("Query failed: %s. ERR=%s\n"),
969          rx->query, db_strerror(ua->db));
970    }
971    if (!rx->found) {
972       ua->error_msg(_("No table found: %s\n"), table);
973       return true;
974    }
975    return true;
976 }
977
978 static void split_path_and_filename(UAContext *ua, RESTORE_CTX *rx, char *name)
979 {
980    char *p, *f;
981
982    /* Find path without the filename.
983     * I.e. everything after the last / is a "filename".
984     * OK, maybe it is a directory name, but we treat it like
985     * a filename. If we don't find a / then the whole name
986     * must be a path name (e.g. c:).
987     */
988    for (p=f=name; *p; p++) {
989       if (IsPathSeparator(*p)) {
990          f = p;                       /* set pos of last slash */
991       }
992    }
993    if (IsPathSeparator(*f)) {         /* did we find a slash? */
994       f++;                            /* yes, point to filename */
995    } else {                           /* no, whole thing must be path name */
996       f = p;
997    }
998
999    /* If filename doesn't exist (i.e. root directory), we
1000     * simply create a blank name consisting of a single
1001     * space. This makes handling zero length filenames
1002     * easier.
1003     */
1004    rx->fnl = p - f;
1005    if (rx->fnl > 0) {
1006       rx->fname = check_pool_memory_size(rx->fname, 2*(rx->fnl)+1);
1007       db_escape_string(ua->jcr, ua->db, rx->fname, f, rx->fnl);
1008    } else {
1009       rx->fname[0] = 0;
1010       rx->fnl = 0;
1011    }
1012
1013    rx->pnl = f - name;
1014    if (rx->pnl > 0) {
1015       rx->path = check_pool_memory_size(rx->path, 2*(rx->pnl)+1);
1016       db_escape_string(ua->jcr, ua->db, rx->path, name, rx->pnl);
1017    } else {
1018       rx->path[0] = 0;
1019       rx->pnl = 0;
1020    }
1021
1022    Dmsg2(100, "split path=%s file=%s\n", rx->path, rx->fname);
1023 }
1024
1025 static bool ask_for_fileregex(UAContext *ua, RESTORE_CTX *rx)
1026 {
1027    if (find_arg(ua, NT_("all")) >= 0) {  /* if user enters all on command line */
1028       return true;                       /* select everything */
1029    }
1030    ua->send_msg(_("\n\nFor one or more of the JobIds selected, no files were found,\n"
1031                  "so file selection is not possible.\n"
1032                  "Most likely your retention policy pruned the files.\n"));
1033    if (get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
1034       if (ua->pint32_val == 1)
1035          return true;
1036       while (get_cmd(ua, _("\nRegexp matching files to restore? (empty to abort): "))) {
1037          if (ua->cmd[0] == '\0') {
1038             break;
1039          } else {
1040             regex_t *fileregex_re = NULL;
1041             int rc;
1042             char errmsg[500] = "";
1043
1044             fileregex_re = (regex_t *)bmalloc(sizeof(regex_t));
1045             rc = regcomp(fileregex_re, ua->cmd, REG_EXTENDED|REG_NOSUB);
1046             if (rc != 0) {
1047                regerror(rc, fileregex_re, errmsg, sizeof(errmsg));
1048             }
1049             regfree(fileregex_re);
1050             free(fileregex_re);
1051             if (*errmsg) {
1052                ua->send_msg(_("Regex compile error: %s\n"), errmsg);
1053             } else {
1054                rx->bsr->fileregex = bstrdup(ua->cmd);
1055                return true;
1056             }
1057          }
1058       }
1059    }
1060    return false;
1061 }
1062
1063 static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
1064 {
1065    TREE_CTX tree;
1066    JobId_t JobId, last_JobId;
1067    char *p;
1068    bool OK = true;
1069    char ed1[50];
1070
1071    memset(&tree, 0, sizeof(TREE_CTX));
1072    /*
1073     * Build the directory tree containing JobIds user selected
1074     */
1075    tree.root = new_tree(rx->TotalFiles);
1076    tree.ua = ua;
1077    tree.all = rx->all;
1078    last_JobId = 0;
1079    /*
1080     * For display purposes, the same JobId, with different volumes may
1081     * appear more than once, however, we only insert it once.
1082     */
1083    p = rx->JobIds;
1084    tree.FileEstimate = 0;
1085    if (get_next_jobid_from_list(&p, &JobId) > 0) {
1086       /* Use first JobId as estimate of the number of files to restore */
1087       Mmsg(rx->query, uar_count_files, edit_int64(JobId, ed1));
1088       if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1089          ua->error_msg("%s\n", db_strerror(ua->db));
1090       }
1091       if (rx->found) {
1092          /* Add about 25% more than this job for over estimate */
1093          tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
1094          tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
1095       }
1096    }
1097
1098    ua->info_msg(_("\nBuilding directory tree for JobId(s) %s ...  "),
1099                 rx->JobIds);
1100
1101 #define new_get_file_list
1102 #ifdef new_get_file_list
1103    if (!db_get_file_list(ua->jcr, ua->db, rx->JobIds, insert_tree_handler, (void *)&tree)) {
1104       ua->error_msg("%s", db_strerror(ua->db));
1105    }
1106    if (*rx->BaseJobIds) {
1107       pm_strcat(rx->JobIds, ",");
1108       pm_strcat(rx->JobIds, rx->BaseJobIds);
1109    }
1110 #else
1111    for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1112       char ed1[50];
1113
1114       if (JobId == last_JobId) {
1115          continue;                    /* eliminate duplicate JobIds */
1116       }
1117       last_JobId = JobId;
1118       /*
1119        * Find files for this JobId and insert them in the tree
1120        */
1121       Mmsg(rx->query, uar_sel_files, edit_int64(JobId, ed1));
1122       if (!db_sql_query(ua->db, rx->query, insert_tree_handler, (void *)&tree)) {
1123          ua->error_msg("%s", db_strerror(ua->db));
1124       }
1125    }
1126 #endif
1127    /*
1128     * Look at the first JobId on the list (presumably the oldest) and
1129     *  if it is marked purged, don't do the manual selection because
1130     *  the Job was pruned, so the tree is incomplete.
1131     */
1132    if (tree.FileCount != 0) {
1133       /* Find out if any Job is purged */
1134       Mmsg(rx->query, "SELECT SUM(PurgedFiles) FROM Job WHERE JobId IN (%s)", rx->JobIds);
1135       if (!db_sql_query(ua->db, rx->query, restore_count_handler, (void *)rx)) {
1136          ua->error_msg("%s\n", db_strerror(ua->db));
1137       }
1138       /* rx->JobId is the PurgedFiles flag */
1139       if (rx->found && rx->JobId > 0) {
1140          tree.FileCount = 0;           /* set count to zero, no tree selection */
1141       }
1142    }
1143    if (tree.FileCount == 0) {
1144       OK = ask_for_fileregex(ua, rx);
1145       if (OK) {
1146          last_JobId = 0;
1147          for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
1148              if (JobId == last_JobId) {
1149                 continue;                    /* eliminate duplicate JobIds */
1150              }
1151              add_findex_all(rx->bsr, JobId);
1152          }
1153       }
1154    } else {
1155       char ec1[50];
1156       if (tree.all) {
1157          ua->info_msg(_("\n%s files inserted into the tree and marked for extraction.\n"),
1158                       edit_uint64_with_commas(tree.FileCount, ec1));
1159       } else {
1160          ua->info_msg(_("\n%s files inserted into the tree.\n"),
1161                       edit_uint64_with_commas(tree.FileCount, ec1));
1162       }
1163
1164       if (find_arg(ua, NT_("done")) < 0) {
1165          /* Let the user interact in selecting which files to restore */
1166          OK = user_select_files_from_tree(&tree);
1167       }
1168
1169       /*
1170        * Walk down through the tree finding all files marked to be
1171        *  extracted making a bootstrap file.
1172        */
1173       if (OK) {
1174          for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) {
1175             Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node);
1176             if (node->extract || node->extract_dir) {
1177                Dmsg3(400, "JobId=%lld type=%d FI=%d\n", (uint64_t)node->JobId, node->type, node->FileIndex);
1178                add_findex(rx->bsr, node->JobId, node->FileIndex);
1179                if (node->extract && node->type != TN_NEWDIR) {
1180                   rx->selected_files++;  /* count only saved files */
1181                }
1182             }
1183          }
1184       }
1185    }
1186
1187    free_tree(tree.root);              /* free the directory tree */
1188    return OK;
1189 }
1190
1191
1192 /*
1193  * This routine is used to get the current backup or a backup
1194  *   before the specified date.
1195  */
1196 static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date)
1197 {
1198    bool ok = false;
1199    FILESET_DBR fsr;
1200    CLIENT_DBR cr;
1201    char fileset_name[MAX_NAME_LENGTH];
1202    char ed1[50], ed2[50];
1203    char pool_select[MAX_NAME_LENGTH];
1204    int i;
1205
1206    /* Create temp tables */
1207    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1208    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1209    if (!db_sql_query(ua->db, uar_create_temp[db_type], NULL, NULL)) {
1210       ua->error_msg("%s\n", db_strerror(ua->db));
1211    }
1212    if (!db_sql_query(ua->db, uar_create_temp1[db_type], NULL, NULL)) {
1213       ua->error_msg("%s\n", db_strerror(ua->db));
1214    }
1215    /*
1216     * Select Client from the Catalog
1217     */
1218    memset(&cr, 0, sizeof(cr));
1219    if (!get_client_dbr(ua, &cr)) {
1220       goto bail_out;
1221    }
1222    bstrncpy(rx->ClientName, cr.Name, sizeof(rx->ClientName));
1223
1224    /*
1225     * Get FileSet
1226     */
1227    memset(&fsr, 0, sizeof(fsr));
1228    i = find_arg_with_value(ua, "FileSet");
1229    if (i >= 0) {
1230       bstrncpy(fsr.FileSet, ua->argv[i], sizeof(fsr.FileSet));
1231       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1232          ua->error_msg(_("Error getting FileSet \"%s\": ERR=%s\n"), fsr.FileSet,
1233             db_strerror(ua->db));
1234          i = -1;
1235       }
1236    }
1237    if (i < 0) {                       /* fileset not found */
1238       edit_int64(cr.ClientId, ed1);
1239       Mmsg(rx->query, uar_sel_fileset, ed1, ed1);
1240       start_prompt(ua, _("The defined FileSet resources are:\n"));
1241       if (!db_sql_query(ua->db, rx->query, fileset_handler, (void *)ua)) {
1242          ua->error_msg("%s\n", db_strerror(ua->db));
1243       }
1244       if (do_prompt(ua, _("FileSet"), _("Select FileSet resource"),
1245                  fileset_name, sizeof(fileset_name)) < 0) {
1246          ua->error_msg(_("No FileSet found for client \"%s\".\n"), cr.Name);
1247          goto bail_out;
1248       }
1249
1250       bstrncpy(fsr.FileSet, fileset_name, sizeof(fsr.FileSet));
1251       if (!db_get_fileset_record(ua->jcr, ua->db, &fsr)) {
1252          ua->warning_msg(_("Error getting FileSet record: %s\n"), db_strerror(ua->db));
1253          ua->send_msg(_("This probably means you modified the FileSet.\n"
1254                      "Continuing anyway.\n"));
1255       }
1256    }
1257
1258    /* If Pool specified, add PoolId specification */
1259    pool_select[0] = 0;
1260    if (rx->pool) {
1261       POOL_DBR pr;
1262       memset(&pr, 0, sizeof(pr));
1263       bstrncpy(pr.Name, rx->pool->name(), sizeof(pr.Name));
1264       if (db_get_pool_record(ua->jcr, ua->db, &pr)) {
1265          bsnprintf(pool_select, sizeof(pool_select), "AND Media.PoolId=%s ", 
1266             edit_int64(pr.PoolId, ed1));
1267       } else {
1268          ua->warning_msg(_("Pool \"%s\" not found, using any pool.\n"), pr.Name);
1269       }
1270    }
1271
1272    /* Find JobId of last Full backup for this client, fileset */
1273    edit_int64(cr.ClientId, ed1);
1274    Mmsg(rx->query, uar_last_full, ed1, ed1, date, fsr.FileSet,
1275          pool_select);
1276    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1277       ua->error_msg("%s\n", db_strerror(ua->db));
1278       goto bail_out;
1279    }
1280
1281    /* Find all Volumes used by that JobId */
1282    if (!db_sql_query(ua->db, uar_full, NULL, NULL)) {
1283       ua->error_msg("%s\n", db_strerror(ua->db));
1284       goto bail_out;
1285    }
1286
1287    /* Note, this is needed because I don't seem to get the callback
1288     * from the call just above.
1289     */
1290    rx->JobTDate = 0;
1291    if (!db_sql_query(ua->db, uar_sel_all_temp1, last_full_handler, (void *)rx)) {
1292       ua->warning_msg("%s\n", db_strerror(ua->db));
1293    }
1294    if (rx->JobTDate == 0) {
1295       ua->error_msg(_("No Full backup before %s found.\n"), date);
1296       goto bail_out;
1297    }
1298
1299    /* Now find most recent Differental Job after Full save, if any */
1300    Mmsg(rx->query, uar_dif, edit_uint64(rx->JobTDate, ed1), date,
1301         edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1302    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1303       ua->warning_msg("%s\n", db_strerror(ua->db));
1304    }
1305    /* Now update JobTDate to look into Differental, if any */
1306    rx->JobTDate = 0;
1307    if (!db_sql_query(ua->db, uar_sel_all_temp, last_full_handler, (void *)rx)) {
1308       ua->warning_msg("%s\n", db_strerror(ua->db));
1309    }
1310    if (rx->JobTDate == 0) {
1311       ua->error_msg(_("No Full backup before %s found.\n"), date);
1312       goto bail_out;
1313    }
1314
1315    /* Now find all Incremental Jobs after Full/dif save */
1316    Mmsg(rx->query, uar_inc, edit_uint64(rx->JobTDate, ed1), date,
1317         edit_int64(cr.ClientId, ed2), fsr.FileSet, pool_select);
1318    if (!db_sql_query(ua->db, rx->query, NULL, NULL)) {
1319       ua->warning_msg("%s\n", db_strerror(ua->db));
1320    }
1321
1322    /* Get the JobIds from that list */
1323    rx->last_jobid[0] = rx->JobIds[0] = 0;
1324
1325    if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)rx)) {
1326       ua->warning_msg("%s\n", db_strerror(ua->db));
1327    }
1328
1329    if (rx->JobIds[0] != 0) {
1330       if (find_arg(ua, NT_("copies")) > 0) {
1331          /* Display a list of all copies */
1332          db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, 
1333                                 prtit, ua, HORZ_LIST);
1334       }
1335       /* Display a list of Jobs selected for this restore */
1336       db_list_sql_query(ua->jcr, ua->db, uar_list_temp, prtit, ua, 1,HORZ_LIST);
1337       ok = true;
1338
1339    } else {
1340       ua->warning_msg(_("No jobs found.\n"));
1341    }
1342
1343 bail_out:
1344    db_sql_query(ua->db, uar_del_temp, NULL, NULL);
1345    db_sql_query(ua->db, uar_del_temp1, NULL, NULL);
1346    return ok;
1347 }
1348
1349
1350 /* 
1351  * Return next JobId from comma separated list   
1352  *
1353  * Returns:
1354  *   1 if next JobId returned
1355  *   0 if no more JobIds are in list
1356  *  -1 there is an error
1357  */
1358 int get_next_jobid_from_list(char **p, JobId_t *JobId)
1359 {
1360    const int maxlen = 30;
1361    char jobid[maxlen+1];
1362    char *q = *p;
1363
1364    jobid[0] = 0;
1365    for (int i=0; i<maxlen; i++) {
1366       if (*q == 0) {
1367          break;
1368       } else if (*q == ',') {
1369          q++;
1370          break;
1371       }
1372       jobid[i] = *q++;
1373       jobid[i+1] = 0;
1374    }
1375    if (jobid[0] == 0) {
1376       return 0;
1377    } else if (!is_a_number(jobid)) {
1378       return -1;                      /* error */
1379    }
1380    *p = q;
1381    *JobId = str_to_int64(jobid);
1382    return 1;
1383 }
1384
1385 static int restore_count_handler(void *ctx, int num_fields, char **row)
1386 {
1387    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1388    rx->JobId = str_to_int64(row[0]);
1389    rx->found = true;
1390    return 0;
1391 }
1392
1393 /*
1394  * Callback handler to get JobId and FileIndex for files
1395  *   can insert more than one depending on the caller.
1396  */
1397 static int jobid_fileindex_handler(void *ctx, int num_fields, char **row)
1398 {
1399    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1400
1401    Dmsg2(200, "JobId=%s FileIndex=%s\n", row[0], row[1]);
1402    rx->JobId = str_to_int64(row[0]);
1403    add_findex(rx->bsr, rx->JobId, str_to_int64(row[1]));
1404    rx->found = true;
1405    rx->selected_files++;
1406    return 0;
1407 }
1408
1409 /*
1410  * Callback handler make list of JobIds
1411  */
1412 static int jobid_handler(void *ctx, int num_fields, char **row)
1413 {
1414    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1415
1416    if (strcmp(rx->last_jobid, row[0]) == 0) {
1417       return 0;                       /* duplicate id */
1418    }
1419    bstrncpy(rx->last_jobid, row[0], sizeof(rx->last_jobid));
1420    if (rx->JobIds[0] != 0) {
1421       pm_strcat(rx->JobIds, ",");
1422    }
1423    pm_strcat(rx->JobIds, row[0]);
1424    return 0;
1425 }
1426
1427
1428 /*
1429  * Callback handler to pickup last Full backup JobTDate
1430  */
1431 static int last_full_handler(void *ctx, int num_fields, char **row)
1432 {
1433    RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
1434
1435    rx->JobTDate = str_to_int64(row[1]);
1436    return 0;
1437 }
1438
1439 /*
1440  * Callback handler build FileSet name prompt list
1441  */
1442 static int fileset_handler(void *ctx, int num_fields, char **row)
1443 {
1444    /* row[0] = FileSet (name) */
1445    if (row[0]) {
1446       add_prompt((UAContext *)ctx, row[0]);
1447    }
1448    return 0;
1449 }
1450
1451 /*
1452  * Free names in the list
1453  */
1454 static void free_name_list(NAME_LIST *name_list)
1455 {
1456    for (int i=0; i < name_list->num_ids; i++) {
1457       free(name_list->name[i]);
1458    }
1459    bfree_and_null(name_list->name);
1460    name_list->max_ids = 0;
1461    name_list->num_ids = 0;
1462 }
1463
1464 void find_storage_resource(UAContext *ua, RESTORE_CTX &rx, char *Storage, char *MediaType) 
1465 {
1466    STORE *store;
1467
1468    if (rx.store) {
1469       Dmsg1(200, "Already have store=%s\n", rx.store->name());
1470       return;
1471    }
1472    /*
1473     * Try looking up Storage by name
1474     */
1475    LockRes();
1476    foreach_res(store, R_STORAGE) {
1477       if (strcmp(Storage, store->name()) == 0) {
1478          if (acl_access_ok(ua, Storage_ACL, store->name())) {
1479             rx.store = store;
1480          }
1481          break;
1482       }
1483    }
1484    UnlockRes();
1485
1486    if (rx.store) {
1487       /* Check if an explicit storage resource is given */
1488       store = NULL;
1489       int i = find_arg_with_value(ua, "storage");
1490       if (i > 0) {
1491          store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1492          if (store && !acl_access_ok(ua, Storage_ACL, store->name())) {
1493             store = NULL;
1494          }
1495       }
1496       if (store && (store != rx.store)) {
1497          ua->info_msg(_("Warning default storage overridden by \"%s\" on command line.\n"),
1498             store->name());
1499          rx.store = store;
1500          Dmsg1(200, "Set store=%s\n", rx.store->name());
1501       }
1502       return;
1503    }
1504
1505    /* If no storage resource, try to find one from MediaType */
1506    if (!rx.store) {
1507       LockRes();
1508       foreach_res(store, R_STORAGE) {
1509          if (strcmp(MediaType, store->media_type) == 0) {
1510             if (acl_access_ok(ua, Storage_ACL, store->name())) {
1511                rx.store = store;
1512                Dmsg1(200, "Set store=%s\n", rx.store->name());
1513                ua->warning_msg(_("Storage \"%s\" not found, using Storage \"%s\" from MediaType \"%s\".\n"),
1514                   Storage, store->name(), MediaType);
1515             }
1516             UnlockRes();
1517             return;
1518          }
1519       }
1520       UnlockRes();
1521       ua->warning_msg(_("\nUnable to find Storage resource for\n"
1522          "MediaType \"%s\", needed by the Jobs you selected.\n"), MediaType);
1523    }
1524
1525    /* Take command line arg, or ask user if none */
1526    rx.store = get_storage_resource(ua, false /* don't use default */);
1527    if (rx.store) {
1528       Dmsg1(200, "Set store=%s\n", rx.store->name());
1529    }
1530
1531 }