+/* If the wildcards failed, the remote shell might give us a file matching the literal
+ * wildcards. Since "*" & "?" already match themselves, this just needs to deal with
+ * failed "[foo]" idioms.
+ */
+static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len)
+{
+ filter_rule *rule;
+ const char *arg = based_on->pattern, *cp;
+ char *p;
+ int cnt = 0;
+
+ if (arg_len < 0)
+ arg_len = strlen(arg);
+
+ for (cp = arg; *cp; cp++) {
+ if (*cp == '\\' && cp[1]) {
+ cp++;
+ } else if (*cp == '[')
+ cnt++;
+ }
+ if (!cnt)
+ return;
+
+ rule = new0(filter_rule);
+ rule->rflags = based_on->rflags;
+ rule->u.slash_cnt = based_on->u.slash_cnt;
+ p = rule->pattern = new_array(char, arg_len + cnt + 1);
+ for (cp = arg; *cp; ) {
+ if (*cp == '\\' && cp[1]) {
+ *p++ = *cp++;
+ } else if (*cp == '[')
+ *p++ = '\\';
+ *p++ = *cp++;
+ }
+ *p++ = '\0';
+
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,
+ rule->rflags & FILTRULE_DIRECTORY ? "/" : "");
+ }
+}
+
+static char *partial_string_buf = NULL;
+static int partial_string_len = 0;
+void implied_include_partial_string(const char *s_start, const char *s_end)
+{
+ partial_string_len = s_end - s_start;
+ if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */
+ partial_string_len = 0;
+ return;
+ }
+ if (!partial_string_buf)
+ partial_string_buf = new_array(char, MAXPATHLEN);
+ memcpy(partial_string_buf, s_start, partial_string_len);
+}
+
+void free_implied_include_partial_string()
+{
+ if (partial_string_buf) {
+ if (partial_string_len)
+ add_implied_include("", 0);
+ free(partial_string_buf);
+ partial_string_buf = NULL;
+ }
+ partial_string_len = 0; /* paranoia */
+}
+
+/* Each arg the client sends to the remote sender turns into an implied include
+ * that the receiver uses to validate the file list from the sender. */
+void add_implied_include(const char *arg, int skip_daemon_module)
+{
+ int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
+ int slash_cnt = 0;
+ const char *cp;
+ char *p;
+ if (trust_sender_args)
+ return;
+ if (partial_string_len) {
+ arg_len = strlen(arg);
+ if (partial_string_len + arg_len >= MAXPATHLEN) {
+ partial_string_len = 0;
+ return; /* Should be impossible... */
+ }
+ memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);
+ partial_string_len = 0;
+ arg = partial_string_buf;
+ }
+ if (skip_daemon_module) {
+ if ((cp = strchr(arg, '/')) != NULL)
+ arg = cp + 1;
+ else
+ arg = "";
+ }
+ if (relative_paths) {
+ if ((cp = strstr(arg, "/./")) != NULL)
+ arg = cp + 3;
+ } else if ((cp = strrchr(arg, '/')) != NULL) {
+ arg = cp + 1;
+ }
+ if (*arg == '.' && arg[1] == '\0')
+ arg++;
+ arg_len = strlen(arg);
+ if (arg_len) {
+ char *new_pat;
+ if (strpbrk(arg, "*[?")) {
+ /* We need to add room to escape backslashes if wildcard chars are present. */
+ for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
+ arg_len++;
+ saw_wild = 1;
+ }
+ arg_len++; /* Leave room for the prefixed slash */
+ p = new_pat = new_array(char, arg_len + 1);
+ *p++ = '/';
+ slash_cnt++;
+ for (cp = arg; *cp; ) {
+ switch (*cp) {
+ case '\\':
+ if (cp[1] == ']') {
+ if (!saw_wild)
+ cp++; /* A \] in a non-wild filter causes a problem, so drop the \ . */
+ } else if (!strchr("*[?", cp[1])) {
+ backslash_cnt++;
+ if (saw_wild)
+ *p++ = '\\';
+ }
+ *p++ = *cp++;
+ break;
+ case '/':
+ if (p[-1] == '/') { /* This is safe because of the initial slash. */
+ if (*++cp == '\0') {
+ slash_cnt--;
+ p--;
+ }
+ } else if (cp[1] == '\0') {
+ cp++;
+ } else {
+ slash_cnt++;
+ *p++ = *cp++;
+ }
+ break;
+ case '.':
+ if (p[-1] == '/') {
+ if (cp[1] == '/') {
+ cp += 2;
+ if (!*cp) {
+ slash_cnt--;
+ p--;
+ }
+ } else if (cp[1] == '\0') {
+ cp++;
+ slash_cnt--;
+ p--;
+ break;
+ }
+ } else
+ *p++ = *cp++;
+ break;
+ case '[':
+ saw_live_open_brkt = 1;
+ *p++ = *cp++;
+ break;
+ default:
+ *p++ = *cp++;
+ break;
+ }
+ }
+ *p = '\0';
+ arg_len = p - new_pat;
+ if (!arg_len)
+ free(new_pat);
+ else {
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
+ rule->u.slash_cnt = slash_cnt;
+ arg = rule->pattern = new_pat;
+ if (!implied_filter_list.head)
+ implied_filter_list.head = implied_filter_list.tail = rule;
+ else {
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ }
+ if (DEBUG_GTE(FILTER, 3))
+ rprintf(FINFO, "[%s] add_IMPlied_include(%s)\n", who_am_i(), arg);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, arg_len);
+ if (relative_paths && slash_cnt) {
+ filter_rule const *ent;
+ int found = 0;
+ slash_cnt = 1;
+ for (p = new_pat + 1; (p = strchr(p, '/')) != NULL; p++) {
+ *p = '\0';
+ for (ent = implied_filter_list.head; ent; ent = ent->next) {
+ if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ filter_rule *R_rule = new0(filter_rule);
+ R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
+ /* Check if our sub-path has wildcards or escaped backslashes */
+ if (saw_wild && strpbrk(rule->pattern, "*[?\\"))
+ R_rule->rflags |= FILTRULE_WILD;
+ R_rule->pattern = strdup(new_pat);
+ R_rule->u.slash_cnt = slash_cnt;
+ R_rule->next = implied_filter_list.head;
+ implied_filter_list.head = R_rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
+ who_am_i(), R_rule->pattern);
+ }
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(R_rule, -1);
+ }
+ *p = '/';
+ slash_cnt++;
+ }
+ }
+ }
+ }
+
+ if (recurse || xfer_dirs) {
+ /* Now create a rule with an added "/" & "**" or "*" at the end */
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
+ if (recurse)
+ rule->rflags |= FILTRULE_WILD2;
+ /* We must leave enough room for / * * \0. */
+ if (!saw_wild && backslash_cnt) {
+ /* We are appending a wildcard, so now the backslashes need to be escaped. */
+ p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
+ for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
+ if (*cp == '\\')
+ *p++ = '\\';
+ *p++ = *cp++;
+ }
+ } else {
+ p = rule->pattern = new_array(char, arg_len + 3 + 1);
+ if (arg_len) {
+ memcpy(p, arg, arg_len);
+ p += arg_len;
+ }
+ }
+ if (p[-1] != '/') {
+ *p++ = '/';
+ slash_cnt++;
+ }
+ *p++ = '*';
+ if (recurse)
+ *p++ = '*';
+ *p = '\0';
+ rule->u.slash_cnt = slash_cnt;
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ if (DEBUG_GTE(FILTER, 3))
+ rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, p - rule->pattern);
+ }
+}
+