aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-06-06 03:49:36 +0200
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-06-06 03:52:16 +0200
commit5a6c92d69bbb307ca09ac2ecbad54df4f2394bea (patch)
tree4ae024ff5218b5483731467b2e8ddd14c37057df /main.c
parent6e9ce3a25b438a94ad870ba85dd8c9bf8a28e043 (diff)
downloadslcl-5a6c92d69bbb307ca09ac2ecbad54df4f2394bea.tar.gz
Implement search
This new feature adds a HTML form on each directory listing that allows to search files recursively, starting from the current user directory. Wildcard patterns are also allowed.
Diffstat (limited to 'main.c')
-rw-r--r--main.c230
1 files changed, 224 insertions, 6 deletions
diff --git a/main.c b/main.c
index 1ca7a3c..2313fec 100644
--- a/main.c
+++ b/main.c
@@ -465,21 +465,239 @@ end:
return ret;
}
-static int search(const struct http_payload *const p,
- struct http_response *const r, void *const user)
+static int check_search_input(const struct http_payload *const p,
+ int (**const f)(struct http_response *), const struct auth *const a,
+ char **const dir, struct dynstr *const res)
{
- struct auth *const a = user;
+ int ret = auth_cookie(a, &p->cookie);
+ struct form *forms = NULL;
+ size_t n = 0;
- if (auth_cookie(a, &p->cookie))
+ if (ret < 0)
{
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
- return page_forbidden(r);
+ goto end;
+ }
+ else if (ret)
+ {
+ *f = page_forbidden;
+ goto end;
+ }
+ else if ((ret = get_forms(p, &forms, &n)))
+ {
+ if (ret < 0)
+ fprintf(stderr, "%s: get_forms failed\n", __func__);
+ else
+ *f = page_bad_request;
+
+ goto end;
+ }
+
+ const char *tdir = NULL, *tres = NULL;
+
+ for (size_t i = 0; i < n; i++)
+ {
+ const struct form *const f = &forms[i];
+
+ if (!strcmp(f->key, "dir"))
+ tdir = f->value;
+ else if (!strcmp(f->key, "name"))
+ tres = f->value;
}
- fprintf(stderr, "%s: TODO\n", __func__);
+ if (!tdir)
+ {
+ fprintf(stderr, "%s: expected non-null directory\n", __func__);
+ ret = 1;
+ *f = page_bad_request;
+ goto end;
+ }
+ else if (!tres)
+ {
+ fprintf(stderr, "%s: expected non-null resource\n", __func__);
+ ret = 1;
+ *f = page_bad_request;
+ goto end;
+ }
+ else if (path_isrel(tdir) || *tdir != '/' || tdir[strlen(tdir) - 1] != '/')
+ {
+ fprintf(stderr, "%s: invalid directory %s\n", __func__, tdir);
+ ret = 1;
+ *f = page_bad_request;
+ goto end;
+ }
+ else if (strchr(tres, '/') || path_isrel(tres))
+ {
+ fprintf(stderr, "%s: invalid resource %s\n", __func__, tres);
+ ret = 1;
+ *f = page_bad_request;
+ goto end;
+ }
+ else if (!(*dir = strdup(tdir)))
+ {
+ fprintf(stderr, "%s: strdup(3) dir: %s\n", __func__, strerror(errno));
+ ret = -1;
+ goto end;
+ }
+ else if (dynstr_append(res, "*%s*", tres))
+ {
+ fprintf(stderr, "%s: dynstr_append failed\n", __func__);
+ ret = -1;
+ goto end;
+ }
+
+end:
+ forms_free(forms, n);
+ return ret;
+}
+
+static void search_result_free(struct page_search_result *const r)
+{
+ if (r)
+ free(r->name);
+}
+
+static void search_results_free(struct page_search *const s)
+{
+ if (s)
+ {
+ for (size_t i = 0; i < s->n; i++)
+ search_result_free(&s->results[i]);
+
+ free(s->results);
+ }
+}
+
+struct search_args
+{
+ const char *root, *res;
+ struct page_search *s;
+};
+
+static int search_fn(const char *const fpath, const struct stat *const sb,
+ void *const user)
+{
+ const struct search_args *const sa = user;
+ const char *rel = fpath + strlen(sa->root);
+ struct page_search *const res = sa->s;
+ struct page_search_result *results = NULL, *r = NULL;
+
+ rel += strspn(rel, "/");
+
+ if (wildcard_cmp(rel, sa->res, false))
+ return 0;
+ else if (!(results = realloc(res->results,
+ (res->n + 1) * sizeof *res->results)))
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ goto failure;
+ }
+
+ r = &results[res->n];
+ *r = (const struct page_search_result)
+ {
+ .name = strdup(rel)
+ };
+
+ if (!r->name)
+ {
+ fprintf(stderr, "%s: strdup(3): %s", __func__, strerror(errno));
+ goto failure;
+ }
+
+ res->results = results;
+ res->n++;
+ return 0;
+
+failure:
+ free(results);
+ search_result_free(r);
return -1;
}
+static int do_search(const char *const abs, const char *const root,
+ const char *const res, struct page_search *const s)
+{
+ struct search_args sa =
+ {
+ .root = root,
+ .res = res,
+ .s = s
+ };
+
+ s->root = root;
+
+ if (cftw(abs, search_fn, &sa))
+ {
+ fprintf(stderr, "%s: cftw failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int search(const struct http_payload *const p,
+ struct http_response *const r, void *const user)
+{
+ int ret = -1;
+ const struct auth *const a = user;
+ const char *const username = p->cookie.field, *const root = auth_dir(a);
+ struct page_search s = {0};
+ int (*f)(struct http_response *);
+ char *dir = NULL;
+ struct dynstr userd, d, res;
+
+ dynstr_init(&userd);
+ dynstr_init(&d);
+ dynstr_init(&res);
+
+ if (!root)
+ {
+ fprintf(stderr, "%s: auth_dir failed\n", __func__);
+ goto end;
+ }
+ else if ((ret = check_search_input(p, &f, a, &dir, &res)))
+ {
+ if (ret < 0)
+ fprintf(stderr, "%s: check_search_input failed\n", __func__);
+ else if ((ret = f(r)))
+ fprintf(stderr, "%s: check_search_input callback failed\n",
+ __func__);
+
+ goto end;
+ }
+ else if (dynstr_append(&userd, "%s/user/%s", root, username))
+ {
+ fprintf(stderr, "%s: dynstr_append userd failed\n", __func__);
+ goto end;
+ }
+ else if (dynstr_append(&d, "%s/%s", userd.str, dir + strspn(dir, "/")))
+ {
+ fprintf(stderr, "%s: dynstr_append d failed\n", __func__);
+ goto end;
+ }
+ else if ((ret = do_search(d.str, userd.str, res.str, &s)))
+ {
+ if (ret < 0)
+ fprintf(stderr, "%s: do_search failed\n", __func__);
+
+ goto end;
+ }
+ else if ((ret = page_search(r, &s)))
+ {
+ fprintf(stderr, "%s: page_search failed\n", __func__);
+ goto end;
+ }
+
+end:
+ free(dir);
+ dynstr_free(&userd);
+ dynstr_free(&d);
+ dynstr_free(&res);
+ search_results_free(&s);
+ return ret;
+}
+
static int share(const struct http_payload *const p,
struct http_response *const r, void *const user)
{