Optionally sanitize the args in parse_arguments() using the new
[rsync/rsync.git] / options.c
index 55a77f0..d5303c8 100644 (file)
--- a/options.c
+++ b/options.c
@@ -21,6 +21,8 @@
 #include "rsync.h"
 #include "popt.h"
 
+extern int sanitize_paths;
+extern char curr_dir[MAXPATHLEN];
 extern struct exclude_struct **exclude_list;
 
 int make_backups = 0;
@@ -303,7 +305,8 @@ void usage(enum logcode F)
 enum {OPT_VERSION = 1000, OPT_SENDER, OPT_EXCLUDE, OPT_EXCLUDE_FROM,
       OPT_DELETE_AFTER, OPT_DELETE_EXCLUDED, OPT_LINK_DEST,
       OPT_INCLUDE, OPT_INCLUDE_FROM, OPT_MODIFY_WINDOW,
-      OPT_READ_BATCH, OPT_WRITE_BATCH};
+      OPT_READ_BATCH, OPT_WRITE_BATCH,
+      OPT_REFUSED_BASE = 9000};
 
 static struct poptOption long_options[] = {
   /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
@@ -358,7 +361,7 @@ static struct poptOption long_options[] = {
   {"timeout",          0,  POPT_ARG_INT,    &io_timeout, 0, 0, 0 },
   {"temp-dir",        'T', POPT_ARG_STRING, &tmpdir, 0, 0, 0 },
   {"compare-dest",     0,  POPT_ARG_STRING, &compare_dest, 0, 0, 0 },
-  {"link-dest",        0,  POPT_ARG_STRING, 0,              OPT_LINK_DEST, 0, 0 },
+  {"link-dest",        0,  POPT_ARG_STRING, &compare_dest,  OPT_LINK_DEST, 0, 0 },
   /* TODO: Should this take an optional int giving the compression level? */
   {"compress",        'z', POPT_ARG_NONE,   &do_compression, 0, 0, 0 },
   {"daemon",           0,  POPT_ARG_NONE,   &daemon_opt, 0, 0, 0 },
@@ -414,35 +417,34 @@ void option_error(void)
 
 
 /**
- * Check to see if we should refuse this option
+ * Tweak the option table to disable all options that the rsyncd.conf
+ * file has told us to refuse.
  **/
-static int check_refuse_options(char *ref, int opt)
+static void set_refuse_options(char *bp)
 {
-       int i, len;
-       char *p;
-       const char *name;
-
-       for (i = 0; long_options[i].longName; i++) {
-               if (long_options[i].val == opt)
-                       break;
-       }
-
-       if (!long_options[i].longName)
-               return 0;
-
-       name = long_options[i].longName;
-       len = strlen(name);
-
-       while ((p = strstr(ref,name))) {
-               if ((p==ref || p[-1]==' ') &&
-                   (p[len] == ' ' || p[len] == 0)) {
-                       snprintf(err_buf, sizeof err_buf,
-                                "The '%s' option is not supported by this server\n", name);
-                       return 1;
+       struct poptOption *op;
+       char *cp;
+
+       while (1) {
+               if ((cp = strchr(bp, ' ')) != NULL)
+                       *cp= '\0';
+               for (op = long_options; ; op++) {
+                       if (!op->longName) {
+                               rprintf(FLOG,
+                                   "Unknown option %s in \"refuse options\" setting\n",
+                                   bp);
+                               break;
+                       }
+                       if (strcmp(bp, op->longName) == 0) {
+                               op->val = (op - long_options)+OPT_REFUSED_BASE;
+                               break;
+                       }
                }
-               ref += len;
+               if (!cp)
+                       break;
+               *cp = ' ';
+               bp = cp + 1;
        }
-       return 0;
 }
 
 
@@ -469,8 +471,12 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
 {
        int opt;
        char *ref = lp_refuse_options(module_id);
+       const char *arg;
        poptContext pc;
 
+       if (ref && *ref)
+               set_refuse_options(ref);
+
        /* TODO: Call poptReadDefaultConfig; handle errors. */
 
        /* The context leaks in case of an error, but if there's a
@@ -478,9 +484,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        pc = poptGetContext(RSYNC_NAME, *argc, *argv, long_options, 0);
 
        while ((opt = poptGetNextOpt(pc)) != -1) {
-               if (ref && check_refuse_options(ref, opt))
-                       return 0;
-
                /* most options are handled automatically by popt;
                 * only special cases are returned and listed here. */
 
@@ -517,12 +520,18 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        break;
 
                case OPT_EXCLUDE_FROM:
-                       add_exclude_file(&exclude_list, poptGetOptArg(pc),
+                       arg = poptGetOptArg(pc);
+                       if (sanitize_paths)
+                               arg = alloc_sanitize_path(arg, curr_dir);
+                       add_exclude_file(&exclude_list, arg,
                                         MISSING_FATAL, ADD_EXCLUDE);
                        break;
 
                case OPT_INCLUDE_FROM:
-                       add_exclude_file(&exclude_list, poptGetOptArg(pc),
+                       arg = poptGetOptArg(pc);
+                       if (sanitize_paths)
+                               arg = alloc_sanitize_path(arg, curr_dir);
+                       add_exclude_file(&exclude_list, arg,
                                         MISSING_FATAL, ADD_INCLUDE);
                        break;
 
@@ -566,7 +575,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
 
                case OPT_LINK_DEST:
 #if HAVE_LINK
-                       compare_dest = (char *)poptGetOptArg(pc);
                        link_dest = 1;
                        break;
 #else
@@ -577,13 +585,26 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                        return 0;
 #endif
 
-
                default:
-                       snprintf(err_buf, sizeof err_buf,
-                                "%s%s: %s\n",
-                                am_server ? "on remote machine: " : "",
-                                poptBadOption(pc, POPT_BADOPTION_NOALIAS),
-                                poptStrerror(opt));
+                       /* A large opt value means that set_refuse_options()
+                        * turned this option off (opt-BASE is its index). */
+                       if (opt >= OPT_REFUSED_BASE) {
+                               struct poptOption *op =
+                                   &long_options[opt-OPT_REFUSED_BASE];
+                               int n = snprintf(err_buf, sizeof err_buf,
+                                   "This server does not support --%s\n",
+                                   op->longName) - 1;
+                               if (op->shortName) {
+                                       snprintf(err_buf+n, sizeof err_buf-n,
+                                           " (-%c)\n", op->shortName);
+                               }
+                       } else {
+                               snprintf(err_buf, sizeof err_buf,
+                                   "%s%s: %s\n",
+                                   am_server ? "on remote machine: " : "",
+                                   poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+                                   poptStrerror(opt));
+                       }
                        return 0;
                }
        }
@@ -647,6 +668,27 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (relative_paths < 0)
                relative_paths = files_from? 1 : 0;
 
+       *argv = poptGetArgs(pc);
+       if (*argv)
+               *argc = count_args(*argv);
+       else
+               *argc = 0;
+
+       if (sanitize_paths) {
+               int i;
+               for (i = *argc; i-- > 0; )
+                       (*argv)[i] = alloc_sanitize_path((*argv)[i], NULL);
+               if (tmpdir)
+                       tmpdir = alloc_sanitize_path(tmpdir, curr_dir);
+               if (compare_dest)
+                       compare_dest = alloc_sanitize_path(compare_dest, curr_dir);
+               fprintf(stderr, "compare_dest=`%s'\n", compare_dest);
+               if (backup_dir)
+                       backup_dir = alloc_sanitize_path(backup_dir, curr_dir);
+               if (files_from)
+                       files_from = alloc_sanitize_path(files_from, curr_dir);
+       }
+
        if (!backup_suffix)
                backup_suffix = backup_dir ? "" : BACKUP_SUFFIX;
        backup_suffix_len = strlen(backup_suffix);
@@ -677,12 +719,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
        if (do_progress && !verbose)
                verbose = 1;
 
-       *argv = poptGetArgs(pc);
-       if (*argv)
-               *argc = count_args(*argv);
-       else
-               *argc = 0;
-
        if (files_from) {
                char *colon;
                if (*argc != 2) {
@@ -705,11 +741,6 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
                                exit_cleanup(RERR_SYNTAX);
                        }
                } else {
-                       extern int sanitize_paths;
-                       if (sanitize_paths) {
-                               files_from = strdup(files_from);
-                               sanitize_path(files_from, NULL);
-                       }
                        filesfrom_fd = open(files_from, O_RDONLY|O_BINARY);
                        if (filesfrom_fd < 0) {
                                rsyserr(FERROR, errno,