- Document Pool keyword for restore.
                 
 For 1.33
+- Implement alist processing for ACLs from Console.
 - Finish code passing files=nnn to restore start.
 - Add Console usr permissions -- do by adding regex filters for
   jobs, clients, storage, ...
 
 /*
  * Authenticate Director
  */
-int authenticate_director(JCR *jcr, DIRRES *director, char *name)
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons)
 {
    BSOCK *dir = jcr->dir_bsock;
    int ssl_need = BNET_SSL_NONE;
    char bashed_name[MAX_NAME_LENGTH];
+   char *password;
 
    /* 
     * Send my name to the Director then do authentication
     */
-   bstrncpy(bashed_name, name, sizeof(bashed_name));
-   bash_spaces(bashed_name);
+   if (cons) {
+      bstrncpy(bashed_name, cons->hdr.name, sizeof(bashed_name));
+      bash_spaces(bashed_name);
+      password = cons->password;
+   } else {
+      bstrncpy(bashed_name, "*UserAgent*", sizeof(bashed_name));
+      password = director->password;
+   }
    bnet_fsend(dir, hello, bashed_name);
 
-   if (!cram_md5_get_auth(dir, director->password, ssl_need) || 
-       !cram_md5_auth(dir, director->password, ssl_need)) {
+   if (!cram_md5_get_auth(dir, password, ssl_need) || 
+       !cram_md5_auth(dir, password, ssl_need)) {
       sendit( _("Director authorization problem.\n"
             "Most likely the passwords do not agree.\n"));  
       return 0;
 
 #endif
 
 /* Imported functions */
-int authenticate_director(JCR *jcr, DIRRES *director, char *name);
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
 
 
 
    LockRes();
    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
    UnlockRes();
-   char *con_name;
-   if (cons) {
-      con_name = cons->hdr.name;
-   } else {
-      con_name = "*UserAgent*";
-   }
-   if (!authenticate_director(&jcr, dir, con_name)) {
+   if (!authenticate_director(&jcr, dir, cons)) {
       fprintf(stderr, "ERR=%s", UA_sock->msg);
       terminate_console(0);
       return 1;
 
    {"rcfile",      store_dir,      ITEM(res_cons.rc_file), 0, 0, 0},
    {"historyfile", store_dir,      ITEM(res_cons.hist_file), 0, 0, 0},
    {"requiressl",  store_yesno,    ITEM(res_cons.require_ssl), 1, ITEM_DEFAULT, 0},
+   {"password",    store_password, ITEM(res_cons.password), 0, ITEM_REQUIRED, 0},
    {NULL, NULL, NULL, 0, 0, 0} 
 };
 
    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
    {"dirport",     store_int,      ITEM(res_dir.DIRport),  0, ITEM_DEFAULT, 9101},
-   {"address",     store_str,      ITEM(res_dir.address),  0, ITEM_REQUIRED, 0},
+   {"address",     store_str,      ITEM(res_dir.address),  0, 0, 0},
    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
    {"enablessl",   store_yesno,    ITEM(res_dir.enable_ssl), 1, ITEM_DEFAULT, 0},
    {NULL, NULL, NULL, 0, 0, 0} 
 
    char *rc_file;                     /* startup file */
    char *hist_file;                   /* command history file */
    int require_ssl;                   /* Require SSL on all connections */
+   char *password;                    /* UA server password */
 };
 typedef struct s_res_con CONRES;
 
 
          jobq.c mountreq.c msgchan.c next_vol.c newvol.c \
          recycle.c restore.c run_conf.c \
          scheduler.c sql_cmds.c \
-         ua_cmds.c ua_dotcmds.c \
+         ua_acl.c ua_cmds.c ua_dotcmds.c \
          ua_query.c \
          ua_input.c ua_label.c ua_output.c ua_prune.c \
          ua_purge.c ua_restore.c ua_run.c \
          jobq.o mountreq.o msgchan.o next_vol.o newvol.o \
          recycle.o restore.o run_conf.o \
          scheduler.o sql_cmds.o \
-         ua_cmds.o ua_dotcmds.o \
+         ua_acl.o ua_cmds.o ua_dotcmds.o \
          ua_query.o \
          ua_input.o ua_label.o ua_output.o ua_prune.o \
          ua_purge.o ua_restore.o ua_run.o \
 
 #  and to the console
 Messages {
   Name = Standard
+#
+# NOTE! If you send to two email or more email addresses, you will need
+#  to replace the %r in the from field (-f part) with a single valid
+#  email address in both the mailcommand and the operatorcommand.
+#
   mailcommand = "@sbindir@/bsmtp -h @smtp_host@ -f \"\(Bacula\) %r\" -s \"Bacula: %t %e of %c %l\" %r"
   operatorcommand = "@sbindir@/bsmtp -h @smtp_host@ -f \"\(Bacula\) %r\" -s \"Bacula: Intervention needed for %j\" %r"
   mail = @job_email@ = all, !skipped            
 
 void store_jobtype(LEX *lc, struct res_items *item, int index, int pass);
 void store_level(LEX *lc, struct res_items *item, int index, int pass);
 void store_replace(LEX *lc, struct res_items *item, int index, int pass);
+void store_acl(LEX *lc, struct res_items *item, int index, int pass);
 
 
 /* We build the current resource here as we are
    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
    {"enablessl",   store_yesno,    ITEM(res_con.enable_ssl), 1, ITEM_DEFAULT, 0},
    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
+   {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
+   {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
+   {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
+   {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
+   {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
+   {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
+   {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
+   {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
+   {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
    {NULL, NULL, NULL, 0, 0, 0}
 };
 
    {NULL, NULL, NULL, 0, 0, 0} 
 };
 
-/* Group resource -- not implemented
- *
- *   name         handler     value                 code flags    default_value
- */
-static struct res_items group_items[] = {
-   {"name",        store_name, ITEM(res_group.hdr.name), 0, ITEM_REQUIRED, 0},
-   {"description", store_str,  ITEM(res_group.hdr.desc), 0, 0, 0},
-   {NULL, NULL, NULL, 0, 0, 0} 
-};
-
 /* Pool resource
  *
  *   name            handler     value                        code flags default_value
    {"catalog",       cat_items,   R_CATALOG,   NULL},
    {"schedule",      sch_items,   R_SCHEDULE,  NULL},
    {"fileset",       fs_items,    R_FILESET,   NULL},
-   {"group",         group_items, R_GROUP,     NULL},
    {"pool",          pool_items,  R_POOL,      NULL},
    {"messages",      msgs_items,  R_MSGS,      NULL},
    {"counter",       counter_items, R_COUNTER, NULL},
    static char level_no[30];
    char *str = level_no;
 
-   sprintf(level_no, "%d", level);    /* default if not found */
+   bsnprintf(level_no, sizeof(level_no), "%d", level);    /* default if not found */
    for (i=0; joblevels[i].level_name; i++) {
       if (level == joblevels[i].level) {
         str = joblevels[i].level_name;
       if (res->res_sch.run) {
         int i;
         RUN *run = res->res_sch.run;
-        char buf[1000], num[10];
+        char buf[1000], num[30];
          sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
         if (!run) {
            break;
          bstrncpy(buf, "      hour=", sizeof(buf));
         for (i=0; i<24; i++) {
            if (bit_is_set(i, run->hour)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          bstrncpy(buf, "      mday=", sizeof(buf));
         for (i=0; i<31; i++) {
            if (bit_is_set(i, run->mday)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          bstrncpy(buf, "      month=", sizeof(buf));
         for (i=0; i<12; i++) {
            if (bit_is_set(i, run->month)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          bstrncpy(buf, "      wday=", sizeof(buf));
         for (i=0; i<7; i++) {
            if (bit_is_set(i, run->wday)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          bstrncpy(buf, "      wom=", sizeof(buf));
         for (i=0; i<5; i++) {
            if (bit_is_set(i, run->wom)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          bstrncpy(buf, "      woy=", sizeof(buf));
         for (i=0; i<54; i++) {
            if (bit_is_set(i, run->woy)) {
-               sprintf(num, "%d ", i);
+               bsnprintf(num, sizeof(num), "%d ", i);
               bstrncat(buf, num, sizeof(buf));
            }
         }
          sendit(sock, "Schedule: name=%s\n", res->res_sch.hdr.name);
       }
       break;
-   case R_GROUP:
-      sendit(sock, "Group: name=%s\n", res->res_group.hdr.name);
-      break;
    case R_POOL:
       sendit(sock, "Pool: name=%s PoolType=%s\n", res->res_pool.hdr.name,
              res->res_pool.pool_type);
       if (res->res_con.password) {
         free(res->res_con.password);
       }
+      for (int i=0; i<Num_ACL; i++) {
+        if (res->res_con.ACL_lists[i]) {
+           delete res->res_con.ACL_lists[i];
+           res->res_con.ACL_lists[i] = NULL;
+        }
+      }
       break;
    case R_CLIENT:
       if (res->res_client.address) {
       free_msgs_res((MSGS *)res);  /* free message resource */
       res = NULL;
       break;
-   case R_GROUP:
-      break;
    default:
       printf("Unknown resource type %d in free_resource.\n", type);
    }
       case R_CONSOLE:
       case R_CATALOG:
       case R_STORAGE:
-      case R_GROUP:
       case R_POOL:
       case R_MSGS:
       case R_FILESET:
    case R_SCHEDULE:
       size = sizeof(SCHED);
       break;
-   case R_GROUP:
-      size = sizeof(GROUP);
-      break;
    case R_POOL:
       size = sizeof(POOL);
       break;
    set_bit(index, res_all.hdr.item_present);
 }
 
+/* 
+ * Store ACL (access control list)
+ *
+ */
+void store_acl(LEX *lc, struct res_items *item, int index, int pass)
+{
+   int token;
+
+   for (;;) {
+      token = lex_get_token(lc, T_NAME);
+      if (pass == 1) {
+        if (((alist **)item->value)[item->code] == NULL) {   
+           ((alist **)item->value)[item->code] = new alist(10, owned_by_alist);
+//          Dmsg1(400, "Defined new ACL alist at %d\n", item->code);
+        }
+        ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
+//       Dmsg2(400, "Appended to %d %s\n", item->code, lc->str);
+      }
+      token = lex_get_token(lc, T_ALL);
+      if (token == T_COMMA) {
+        continue;                    /* get another ACL */
+      }
+      break;
+   }
+   set_bit(index, res_all.hdr.item_present);
+}
+
+
 #ifdef old_deprecated_code
 /* 
  * Store backup/verify info for Job record 
 
 /*
  * Resource codes -- they must be sequential for indexing   
  */
-#define R_FIRST               1001
-
-#define R_DIRECTOR            1001
-#define R_CLIENT              1002
-#define R_JOB                 1003
-#define R_STORAGE             1004
-#define R_CATALOG             1005
-#define R_SCHEDULE            1006
-#define R_FILESET             1007
-#define R_GROUP               1008
-#define R_POOL                1009
-#define R_MSGS                1010
-#define R_COUNTER             1011
-#define R_CONSOLE             1012
-#define R_JOBDEFS             1013
-
-#define R_LAST                R_JOBDEFS    
+enum {
+   R_DIRECTOR = 1001,
+   R_CLIENT,
+   R_JOB,
+   R_STORAGE,
+   R_CATALOG,
+   R_SCHEDULE,
+   R_FILESET,
+   R_POOL,
+   R_MSGS,
+   R_COUNTER,
+   R_CONSOLE,
+   R_JOBDEFS
+};
+
+#define R_FIRST  R_DIRECTOR
+#define R_LAST   R_JOBDEFS    
 
 /*
  * Some resource attributes
    utime_t SDConnectTimeout;          /* timeout in seconds */
 };
 
+
+/*
+ * Console ACL positions
+ */
+enum {
+   Job_ACL = 0,
+   Client_ACL,
+   Storage_ACL,
+   Schedule_ACL,
+   Run_ACL,
+   Pool_ACL,
+   Command_ACL,
+   FileSet_ACL,
+   Catalog_ACL,
+   Num_ACL                            /* keep last */
+};
+
 /* 
  *    Console Resource
  */
    RES   hdr;
    char *password;                    /* UA server password */
    int enable_ssl;                    /* Use SSL */
+   alist *ACL_lists[Num_ACL];         /* pointers to ACLs */
 };
 
 
    RUN *run;
 };
 
-/*
- *   Group Resource (not used)
- *
- */
-struct GROUP {
-   RES   hdr;
-};
-
 /*
  *   Counter Resource
  */
    JOB        res_job;
    FILESET    res_fs;
    SCHED      res_sch;
-   GROUP      res_group;
    POOL       res_pool;
    MSGS       res_msgs;
    COUNTER    res_counter;
 
 
 /* fd_cmds.c */
 extern int connect_to_file_daemon(JCR *jcr, int retry_interval,
-                                  int max_retry_time, int verbose);
+                                 int max_retry_time, int verbose);
 extern int send_include_list(JCR *jcr);
 extern int send_exclude_list(JCR *jcr);
 extern int send_bootstrap_file(JCR *jcr);
 extern int get_attributes_and_put_in_catalog(JCR *jcr);
 extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId);
 extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname, 
-                          char *link, char *attr, int stream);
+                         char *link, char *attr, int stream);
 extern void get_level_since_time(JCR *jcr, char *since, int since_len);
 extern int send_run_before_and_after_commands(JCR *jcr);
 
 
 /* msgchan.c */
 extern int connect_to_storage_daemon(JCR *jcr, int retry_interval,    
-                              int max_retry_time, int verbose);
+                             int max_retry_time, int verbose);
 extern int start_storage_daemon_job(JCR *jcr);
 extern int start_storage_daemon_message_thread(JCR *jcr);
 extern int bget_dirmsg(BSOCK *bs);
 /* newvol.c */
 int newVolume(JCR *jcr, MEDIA_DBR *mr);
 
+/* ua_acl.c */
+bool acl_access_ok(UAContext *ua, int acl, char *item);
+bool acl_access_ok(UAContext *ua, int acl, char *item, int len);
+
 /* ua_cmds.c */
 int do_a_command(UAContext *ua, char *cmd);
 int do_a_dot_command(UAContext *ua, char *cmd);
 void free_ua_context(UAContext *ua);
 
 /* ua_select.c */
-STORE   *select_storage_resource(UAContext *ua);
-JOB     *select_job_resource(UAContext *ua);
-JOB     *select_restore_job_resource(UAContext *ua);
-CLIENT  *select_client_resource(UAContext *ua);
+STORE  *select_storage_resource(UAContext *ua);
+JOB    *select_job_resource(UAContext *ua);
+JOB    *select_restore_job_resource(UAContext *ua);
+CLIENT *select_client_resource(UAContext *ua);
 FILESET *select_fileset_resource(UAContext *ua);
-int     select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
-int     select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
-int     select_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int     select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
-
-void    start_prompt(UAContext *ua, char *msg);
-void    add_prompt(UAContext *ua, char *prompt);
-int     do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
-CAT    *get_catalog_resource(UAContext *ua);           
+int    select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
+int    select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
+int    select_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int    select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+
+void   start_prompt(UAContext *ua, char *msg);
+void   add_prompt(UAContext *ua, char *prompt);
+int    do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
+CAT    *get_catalog_resource(UAContext *ua);          
 STORE  *get_storage_resource(UAContext *ua, int use_default);
-int     get_media_type(UAContext *ua, char *MediaType, int max_media);
-int     get_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int     get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+int    get_media_type(UAContext *ua, char *MediaType, int max_media);
+int    get_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int    get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
 POOL   *get_pool_resource(UAContext *ua);
 POOL   *select_pool_resource(UAContext *ua);
 CLIENT *get_client_resource(UAContext *ua);
-int     get_job_dbr(UAContext *ua, JOB_DBR *jr);
+int    get_job_dbr(UAContext *ua, JOB_DBR *jr);
 
 int find_arg_keyword(UAContext *ua, char **list);
 int find_arg(UAContext *ua, char *keyword);
 
 #ifndef __UA_H_
 #define __UA_H_ 1
 
-
 struct UAContext {
    BSOCK *UA_sock;
    BSOCK *sd;
    UAContext *ua;
 };
 
-
 #endif
 
--- /dev/null
+/*
+ *
+ *   Bacula Director -- User Agent Access Control List (ACL) handling
+ *
+ *     Kern Sibbald, January MMIV  
+ *
+ *   Version  $Id$
+ */
+
+/*
+   Copyright (C) 2004 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA.
+
+ */
+
+#include "bacula.h"
+#include "dird.h"
+
+/*  
+ * Check if access is permitted to item in acl   
+ */
+bool acl_access_ok(UAContext *ua, int acl, char *item)
+{
+   return acl_access_ok(ua, acl, item, strlen(item));
+}
+
+
+bool acl_access_ok(UAContext *ua, int acl, char *item, int len)
+{
+   if (!ua->cons) {
+      Dmsg0(400, "Root cons access OK.\n");
+      return true;                   /* No cons resource -> root console OK for everything */
+   }
+   alist *list = ua->cons->ACL_lists[acl];
+   if (!list) {
+      return false;                  /* List empty, reject */
+   }
+   for (int i=0; i<list->size(); i++) {
+      if (strncasecmp(item, (char *)list->get(i), len) == 0) {
+         Dmsg3(400, "Found %s in %d %s\n", item, acl, (char *)list->get(i));
+        return true;
+      }
+   }
+   return false;
+}
 
 {
    unsigned int i;
    int len, stat;
-   int found;
+   bool found = false;
 
-   found = 0;
    stat = 1;
 
-   Dmsg1(120, "Command: %s\n", ua->UA_sock->msg);
+   Dmsg1(200, "Command: %s\n", ua->UA_sock->msg);
    if (ua->argc == 0) {
       return 1;
    }
    len = strlen(ua->argk[0]);
    for (i=0; i<comsize; i++) {    /* search for command */
       if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
+        if (!acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
+           break;
+        }
         stat = (*commands[i].func)(ua, cmd);   /* go execute command */
-        found = 1;
+        found = true;
         break;
       }
    }
    if (!found) {
-      pm_strcat(&ua->UA_sock->msg, _(": is an illegal command\n"));
-      ua->UA_sock->msglen = strlen(ua->UA_sock->msg);
-      bnet_send(ua->UA_sock);
+      bnet_fsend(ua->UA_sock, _("%s: is an illegal command.\n"), ua->argk[0]);
    }
    return stat;
 }
 
    {N_("catalogs"),   R_CATALOG},
    {N_("schedules"),  R_SCHEDULE},
    {N_("filesets"),   R_FILESET},
-   {N_("groups"),     R_GROUP},
    {N_("pools"),      R_POOL},
    {N_("messages"),   R_MSGS},
    {N_("all"),        -1},
 
    start_prompt(ua, _("The defined Storage resources are:\n"));
    LockRes();
    foreach_res(store, R_STORAGE) {
-      add_prompt(ua, store->hdr.name);
+      if (acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+        add_prompt(ua, store->hdr.name);
+      }
    }
    UnlockRes();
    do_prompt(ua, _("Storage"),  _("Select Storage resource"), name, sizeof(name));
    start_prompt(ua, _("The defined FileSet resources are:\n"));
    LockRes();
    foreach_res(fs, R_FILESET) {
-      add_prompt(ua, fs->hdr.name);
+      if (acl_access_ok(ua, FileSet_ACL, fs->hdr.name)) {
+        add_prompt(ua, fs->hdr.name);
+      }
    }
    UnlockRes();
    do_prompt(ua, _("FileSet"), _("Select FileSet resource"), name, sizeof(name));
 
    for (i=1; i<ua->argc; i++) {
       if (strcasecmp(ua->argk[i], _("catalog")) == 0 && ua->argv[i]) {
-        catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
-        break;
+        if (acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
+           catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
+           break;
+        }
       }
    }
    if (!catalog) {
       start_prompt(ua, _("The defined Catalog resources are:\n"));
       LockRes();
       foreach_res(catalog, R_CATALOG) {
-        add_prompt(ua, catalog->hdr.name);
+        if (acl_access_ok(ua, Catalog_ACL, catalog->hdr.name)) {
+           add_prompt(ua, catalog->hdr.name);
+        }
       }
       UnlockRes();
       do_prompt(ua, _("Catalog"),  _("Select Catalog resource"), name, sizeof(name));
    start_prompt(ua, _("The defined Job resources are:\n"));
    LockRes();
    foreach_res(job, R_JOB) {
-      add_prompt(ua, job->hdr.name);
+      if (acl_access_ok(ua, Job_ACL, job->hdr.name)) {
+        add_prompt(ua, job->hdr.name);
+      }
    }
    UnlockRes();
    do_prompt(ua, _("Job"), _("Select Job resource"), name, sizeof(name));
    start_prompt(ua, _("The defined Restore Job resources are:\n"));
    LockRes();
    foreach_res(job, R_JOB) {
-      if (job->JobType == JT_RESTORE) {
+      if (job->JobType == JT_RESTORE && acl_access_ok(ua, Job_ACL, job->hdr.name)) {
         add_prompt(ua, job->hdr.name);
       }
    }
    start_prompt(ua, _("The defined Client resources are:\n"));
    LockRes();
    foreach_res(client, R_CLIENT) {
-      add_prompt(ua, client->hdr.name);
+      if (acl_access_ok(ua, Client_ACL, client->hdr.name)) {
+        add_prompt(ua, client->hdr.name);
+      }
    }
    UnlockRes();
    do_prompt(ua, _("Client"),  _("Select Client (File daemon) resource"), name, sizeof(name));
    for (i=1; i<ua->argc; i++) {
       if ((strcasecmp(ua->argk[i], _("client")) == 0 ||
            strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
+        if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
+           break;
+        }
         client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
         if (client) {
            return client;
    for (i=1; i<ua->argc; i++) {
       if ((strcasecmp(ua->argk[i], _("client")) == 0 ||               
            strcasecmp(ua->argk[i], _("fd")) == 0) && ua->argv[i]) {
+        if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
+           break;
+        }
         bstrncpy(cr->Name, ua->argv[i], sizeof(cr->Name));
         if (!db_get_client_record(ua->jcr, ua->db, cr)) {
             bsendmsg(ua, _("Could not find Client \"%s\": ERR=%s"), ua->argv[i],
    start_prompt(ua, _("Defined Clients:\n"));
    for (i=0; i < num_clients; i++) {
       ocr.ClientId = ids[i];
-      if (!db_get_client_record(ua->jcr, ua->db, &ocr)) {
+      if (!db_get_client_record(ua->jcr, ua->db, &ocr) ||
+         !acl_access_ok(ua, Client_ACL, ocr.Name)) {
         continue;
       }
       add_prompt(ua, ocr.Name);
 int get_pool_dbr(UAContext *ua, POOL_DBR *pr)
 {
    if (pr->Name[0]) {                /* If name already supplied */
-      if (db_get_pool_record(ua->jcr, ua->db, pr)) {
+      if (db_get_pool_record(ua->jcr, ua->db, pr) &&
+         acl_access_ok(ua, Pool_ACL, pr->Name)) { 
         return pr->PoolId;
       }
       bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), pr->Name, db_strerror(ua->db));
    uint32_t *ids; 
 
    for (i=1; i<ua->argc; i++) {
-      if (strcasecmp(ua->argk[i], _("pool")) == 0 && ua->argv[i]) {
+      if (strcasecmp(ua->argk[i], _("pool")) == 0 && ua->argv[i] &&
+         acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
         bstrncpy(pr->Name, ua->argv[i], sizeof(pr->Name));
         if (!db_get_pool_record(ua->jcr, ua->db, pr)) {
             bsendmsg(ua, _("Could not find Pool \"%s\": ERR=%s"), ua->argv[i],
    start_prompt(ua, _("Defined Pools:\n"));
    for (i=0; i < num_pools; i++) {
       opr.PoolId = ids[i];
-      if (!db_get_pool_record(ua->jcr, ua->db, &opr)) {
+      if (!db_get_pool_record(ua->jcr, ua->db, &opr) ||
+         !acl_access_ok(ua, Pool_ACL, opr.Name)) {
         continue;
       }
       add_prompt(ua, opr.Name);
    start_prompt(ua, _("The defined Pool resources are:\n"));
    LockRes();
    foreach_res(pool, R_POOL) {
-      add_prompt(ua, pool->hdr.name);
+      if (acl_access_ok(ua, Pool_ACL, pool->hdr.name)) {
+        add_prompt(ua, pool->hdr.name);
+      }
    }
    UnlockRes();
    do_prompt(ua, _("Pool"), _("Select Pool resource"), name, sizeof(name));
    int i;
    
    i = find_arg_with_value(ua, "pool");
-   if (i >= 0) {
+   if (i >= 0 && acl_access_ok(ua, Pool_ACL, ua->argv[i])) {
       pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
       if (pool) {
         return pool;
         jr->JobId = 0;
         bstrncpy(jr->Job, ua->argv[i], sizeof(jr->Job));
       } else if (strcasecmp(ua->argk[i], _("jobid")) == 0 && ua->argv[i]) {
-        jr->JobId = atoi(ua->argv[i]);
+        jr->JobId = str_to_int64(ua->argv[i]);
       } else {
         continue;
       }
            break;
 
          } else if (strcasecmp(ua->argk[i], _("jobid")) == 0) {
-           jobid = atoi(ua->argv[i]);
+           jobid = str_to_int64(ua->argv[i]);
            if (jobid <= 0) {
                bsendmsg(ua, _("Expecting jobid=nn command, got: %s\n"), ua->argk[i]);
               return NULL;
        }
       }
    }
-
+   if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+      store = NULL;
+   }
+   
    if (!store && store_name) {
       store = (STORE *)GetResWithName(R_STORAGE, store_name);
       if (!store) {
          bsendmsg(ua, "Storage resource \"%s\": not found\n", store_name);
       }
    }
+   if (store && !acl_access_ok(ua, Storage_ACL, store->hdr.name)) {
+      store = NULL;
+   }
    /* No keywords found, so present a selection list */
    if (!store) {
       store = select_storage_resource(ua);
 
 /*
  * Authenticate Director
  */
-int authenticate_director(JCR *jcr, DIRRES *director, char *name)
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons)
 {
    BSOCK *dir = jcr->dir_bsock;
    int ssl_need = BNET_SSL_NONE;
    char bashed_name[MAX_NAME_LENGTH];
+   char *password;
 
    /* 
     * Send my name to the Director then do authentication
     */
-   bstrncpy(bashed_name, name, sizeof(bashed_name));
-   bash_spaces(bashed_name);
+   if (cons) {
+      bstrncpy(bashed_name, cons->hdr.name, sizeof(bashed_name));
+      bash_spaces(bashed_name);
+      password = cons->password;
+   } else {
+      bstrncpy(bashed_name, "*UserAgent*", sizeof(bashed_name));
+      password = director->password;
+   }
    bnet_fsend(dir, hello, bashed_name);
 
-   if (!cram_md5_get_auth(dir, director->password, ssl_need) || 
-       !cram_md5_auth(dir, director->password, ssl_need)) {
+   if (!cram_md5_get_auth(dir, password, ssl_need) || 
+       !cram_md5_auth(dir, password, ssl_need)) {
       printf(_("%s: Director authorization problem.\n"), my_name);
       set_text(_("Director authorization problem.\n"), -1);
       return 0;
 
 #include "support.h"
 
 /* Imported functions */
-int authenticate_director(JCR *jcr, DIRRES *director, char *name);
+int authenticate_director(JCR *jcr, DIRRES *director, CONRES *cons);
        
 /* Exported variables */
 GtkWidget *app1;            /* application window */
 
    LockRes();
    ndir = 0;
-   for (dir=NULL; (dir = (DIRRES *)GetNextRes(R_DIRECTOR, (RES *)dir)); ) {
+   foreach_res(dir, R_DIRECTOR) {
       ndir++;
    }
    UnlockRes();
 Without that I don't how to speak to the Director :-(\n"), configfile);
    }
 
-   if (test_config) {
-      terminate_console(0);
-      exit(0);
-   }
 
 
    app1 = create_app1();
  */
 
    LockRes();
-   for (con = NULL; (con = (CONRES *)GetNextRes(R_CONSOLE, (RES *)con)); ) {
+   foreach_res(con, R_CONSOLE) {
+       if (!con->fontface) {
+          Dmsg1(400, "No fontface for %s\n", con->hdr.name);
+         continue;
+       }
        text_font = gdk_font_load(con->fontface);
        if (text_font == NULL) {
-           Dmsg2(404, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
+           Dmsg2(400, "Load of requested ConsoleFont \"%s\" (%s) failed!\n",
                  con->hdr.name, con->fontface);
        } else {
-           Dmsg2(404, "ConsoleFont \"%s\" (%s) loaded.\n",
+           Dmsg2(400, "ConsoleFont \"%s\" (%s) loaded.\n",
                  con->hdr.name, con->fontface);
           break;
        }          
    UnlockRes();
 
    if (text_font == NULL) {
-       Dmsg1(100, "Attempting to load fallback font %s\n",
+       Dmsg1(400, "Attempting to load fallback font %s\n",
               "-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
        text_font = gdk_font_load("-misc-fixed-medium-r-normal-*-*-130-*-*-c-*-iso8859-1");
    }
    gtk_widget_modify_font (status1, font_desc);
    pango_font_description_free (font_desc);
 
+   if (test_config) {
+      terminate_console(0);
+      exit(0);
+   }
+
    initial = gtk_timeout_add(100, initial_connect_to_director, (gpointer)NULL);
 
    gtk_main();
    LockRes();
    CONRES *cons = (CONRES *)GetNextRes(R_CONSOLE, (RES *)NULL);
    UnlockRes();
-   char *con_name;
-   if (cons) {
-      con_name = cons->hdr.name;
-   } else {
-      con_name = "*UserAgent*";
-   }
-   if (!authenticate_director(&jcr, dir, con_name)) {
+   if (!authenticate_director(&jcr, dir, cons)) {
       set_text(UA_sock->msg, UA_sock->msglen);
       return 0;
    }
 
  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
  *
  *   2. The generic config  scanner in lib/parse_config.c and 
- *      lib/parse_config.h.
- *      These files contain the parser code, some utility
- *      routines, and the common store routines (name, int,
- *      string).
+ *     lib/parse_config.h.
+ *     These files contain the parser code, some utility
+ *     routines, and the common store routines (name, int,
+ *     string).
  *
  *   3. The daemon specific file, which contains the Resource
- *      definitions as well as any specific store routines
- *      for the resource records.
+ *     definitions as well as any specific store routines
+ *     for the resource records.
  *
  *     Kern Sibbald, January MM, September MM
  *
    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
    {"dirport",     store_int,      ITEM(res_dir.DIRport),  0, ITEM_DEFAULT, 9101},
    {"address",     store_str,      ITEM(res_dir.address),  0, ITEM_REQUIRED, 0},
-   {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
+   {"password",    store_password, ITEM(res_dir.password), 0, 0, 0},
    {"enablessl", store_yesno,      ITEM(res_dir.enable_ssl), 1, ITEM_DEFAULT, 0},
    {NULL, NULL, NULL, 0, 0, 0} 
 };
 static struct res_items con_items[] = {
    {"name",        store_name,     ITEM(con_dir.hdr.name), 0, ITEM_REQUIRED, 0},
    {"description", store_str,      ITEM(con_dir.hdr.desc), 0, 0, 0},
-   {"font",        store_str,      ITEM(con_dir.fontface), 0, ITEM_REQUIRED, 0},
+   {"font",        store_str,      ITEM(con_dir.fontface), 0, 0, 0},
+   {"password",    store_password, ITEM(con_dir.password), 0, ITEM_REQUIRED, 0},
    {"requiressl",  store_yesno,    ITEM(con_dir.require_ssl), 1, ITEM_DEFAULT, 0},
    {NULL, NULL, NULL, 0, 0, 0} 
 };
  */
 struct s_res resources[] = {
    {"director",      dir_items,   R_DIRECTOR,  NULL},
-   {"consolefont",   con_items,   R_CONSOLE,   NULL},
-   {NULL,            NULL,        0,           NULL}
+   {"console",       con_items,   R_CONSOLE,   NULL},
+   {NULL,           NULL,        0,           NULL}
 };
 
 
       printf("No record for %d %s\n", type, res_to_str(type));
       return;
    }
-   if (type < 0) {                    /* no recursion */
+   if (type < 0) {                   /* no recursion */
       type = - type;
       recurse = 0;
    }
    switch (type) {
    case R_DIRECTOR:
       printf("Director: name=%s address=%s DIRport=%d\n", reshdr->name, 
-              res->res_dir.address, res->res_dir.DIRport);
+             res->res_dir.address, res->res_dir.DIRport);
       break;
    case R_CONSOLE:
       printf("Console: name=%s font face=%s\n", 
-             reshdr->name, NPRT(res->con_dir.fontface));
+            reshdr->name, NPRT(res->con_dir.fontface));
       break;
    default:
       printf("Unknown resource type %d\n", type);
    switch (type) {
    case R_DIRECTOR:
       if (res->res_dir.address) {
-         free(res->res_dir.address);
+        free(res->res_dir.address);
       }
       break;
    case R_CONSOLE:
       if (res->con_dir.fontface) {
-         free(res->con_dir.fontface);
+        free(res->con_dir.fontface);
       }
       break;
    default:
     */
    for (i=0; items[i].name; i++) {
       if (items[i].flags & ITEM_REQUIRED) {
-            if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {  
+           if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {  
                Emsg2(M_ABORT, 0, "%s item is required in %s resource, but not found.\n",
-                 items[i].name, resources[rindex]);
-             }
+                items[i].name, resources[rindex]);
+            }
       }
    }
 
       switch (type) {
       /* Resources not containing a resource */
       case R_DIRECTOR:
-         break;
+        break;
 
       case R_CONSOLE:
-         break;
+        break;
 
       default:
          Emsg1(M_ERROR, 0, "Unknown resource type %d\n", type);
-         error = 1;
-         break;
+        error = 1;
+        break;
       }
       /* Note, the resoure name was already saved during pass 1,
        * so here, we can just release it.
        */
       if (res_all.res_dir.hdr.name) {
-         free(res_all.res_dir.hdr.name);
-         res_all.res_dir.hdr.name = NULL;
+        free(res_all.res_dir.hdr.name);
+        res_all.res_dir.hdr.name = NULL;
       }
       if (res_all.res_dir.hdr.desc) {
-         free(res_all.res_dir.hdr.desc);
-         res_all.res_dir.hdr.desc = NULL;
+        free(res_all.res_dir.hdr.desc);
+        res_all.res_dir.hdr.desc = NULL;
       }
       return;
    }
       res = (URES *)malloc(size);
       memcpy(res, &res_all, size);
       if (!resources[rindex].res_head) {
-         resources[rindex].res_head = (RES *)res; /* store first entry */
+        resources[rindex].res_head = (RES *)res; /* store first entry */
       } else {
-         RES *next;
-         /* Add new res to end of chain */
-         for (next=resources[rindex].res_head; next->next; next=next->next) {
-            if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
-               Emsg2(M_ERROR_TERM, 0,
+        RES *next;
+        /* Add new res to end of chain */
+        for (next=resources[rindex].res_head; next->next; next=next->next) {
+           if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
+              Emsg2(M_ERROR_TERM, 0,
                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
-                  resources[rindex].name, res->res_dir.hdr.name);
-            }
-         }
-         next->next = (RES *)res;
+                 resources[rindex].name, res->res_dir.hdr.name);
+           }
+        }
+        next->next = (RES *)res;
          Dmsg2(90, "Inserting %s res: %s\n", res_to_str(type),
-               res->res_dir.hdr.name);
+              res->res_dir.hdr.name);
       }
    }
 }
 
 /*
  * Resource codes -- they must be sequential for indexing   
  */
-#define R_FIRST                      1001
 
-#define R_DIRECTOR                   1001
-#define R_CONSOLE                    1002
+enum {
+   R_DIRECTOR = 1001,
+   R_CONSOLE,
+};
 
-#define R_LAST                       R_CONSOLE
+#define R_FIRST     R_DIRECTOR
+#define R_LAST     R_CONSOLE
 
 /*
  * Some resource attributes
 struct s_con_dir {
    RES  hdr;
    char *fontface;                   /* Console Font specification */
+   char *password;                   /* UA server password */
    int require_ssl;                  /* Require SSL on all connections */
 };
 typedef struct s_con_dir CONRES;
 
 
  */
 
+/* Second arg of init */
+enum {
+  owned_by_alist = true,
+  not_owned_by_alist = false
+};
+
+
 /* 
  * Array list -- much like a simplified STL vector
  *   array of pointers to inserted items
 
    va_end(arg_ptr);
      
    if (lc->line_no > lc->begin_line_no) {
-      sprintf(more, _("Problem probably begins at Line %d.\n"), lc->begin_line_no);
+      bsnprintf(more, sizeof(more), 
+                _("Problem probably begins at line %d.\n"), lc->begin_line_no);
    } else {
       more[0] = 0;
    }
    e_msg(file, line, M_ERROR_TERM, 0, _("Config error: %s\n\
-            : Line %d, col %d of file %s\n%s\n%s"),
+            : line %d, col %d of file %s\n%s\n%s"),
       buf, lc->line_no, lc->col_no, lc->fname, lc->line, more);
 }
 
 {
    if (lf->str_len >= MAXSTRING-3) {
       Emsg3(M_ERROR_TERM, 0, _(
-            "Token too long, file: %s, line %d, begins at line %d\n"), 
+           _("Config token too long, file: %s, line %d, begins at line %d\n")), 
             lf->fname, lf->line_no, lf->begin_line_no);
    }
    lf->str[lf->str_len++] = ch;
 
 static uint32_t scan_pint(LEX *lf, char *str)
 {
-   double dval = 0;
+   int64_t val = 0;
    if (!is_a_number(str)) {
       scan_err1(lf, "expected a positive integer number, got: %s", str);
       /* NOT REACHED */
    } else {
       errno = 0;
-      dval = strtod(str, NULL);
-      if (errno != 0 || dval < 0) {
+      val = str_to_int64(str);
+      if (errno != 0 || val < 0) {
          scan_err1(lf, "expected a postive integer number, got: %s", str);
         /* NOT REACHED */
       }
    }
-   return (uint32_t)dval;
+   return (uint32_t)val;
 }
 
 /*       
         break;
       }
       errno = 0;
-      lf->int32_val = (int32_t)strtod(lf->str, NULL);
+      lf->int32_val = (int32_t)str_to_int64(lf->str);
       if (errno != 0) {
          scan_err2(lf, "expected an integer number, got %s: %s",
               lex_tok_to_str(token), lf->str);
         break;
       }
       errno = 0;
-      lf->int64_val = (int64_t)strtod(lf->str, NULL);
+      lf->int64_val = str_to_int64(lf->str);
       if (errno != 0) {
          scan_err2(lf, "expected an integer number, got %s: %s",
               lex_tok_to_str(token), lf->str);
 
            token = lex_get_token(lc, T_NAME);   /* scan destination */
            dest = check_pool_memory_size(dest, dest_len + lc->str_len + 2);
            if (dest[0] != 0) {
-               strcat(dest, " ");  /* separate multiple destinations with space */
+               pm_strcat(&dest, " ");  /* separate multiple destinations with space */
               dest_len++;
            }
-           strcat(dest, lc->str);
+           pm_strcat(&dest, lc->str);
            dest_len += lc->str_len;
             Dmsg2(100, "store_msgs newdest=%s: dest=%s:\n", lc->str, NPRT(dest));
            token = lex_get_token(lc, T_ALL);