Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
Xavier Del Campo Romero | 05a76f2899 | |
Xavier Del Campo Romero | aae26c510d |
|
@ -45,6 +45,8 @@ to `slcl`. If required, encryption should be done before uploading e.g.: using
|
||||||
- `xxd` (for [`usergen`](usergen) only).
|
- `xxd` (for [`usergen`](usergen) only).
|
||||||
- `jq` (for [`usergen`](usergen) only).
|
- `jq` (for [`usergen`](usergen) only).
|
||||||
- CMake (optional).
|
- CMake (optional).
|
||||||
|
- `entr` (for [`watchdir`](watchdir) only).
|
||||||
|
- ImageMagick (for [`tngen`](tngen) only).
|
||||||
|
|
||||||
### Ubuntu / Debian
|
### Ubuntu / Debian
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@ sudo apt install build-essential libcjson-dev libssl-dev
|
||||||
#### Optional packages
|
#### Optional packages
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install cmake xxd jq
|
sudo apt install cmake xxd jq inotify-tools imagemagick
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
57
main.c
57
main.c
|
@ -693,10 +693,14 @@ static int search(const struct http_payload *const p,
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
const struct auth *const a = user;
|
const struct auth *const a = user;
|
||||||
const char *const username = p->cookie.field, *const root = auth_dir(a);
|
const char *const username = p->cookie.field, *const root = auth_dir(a);
|
||||||
struct page_search s = {0};
|
|
||||||
int (*f)(struct http_response *);
|
int (*f)(struct http_response *);
|
||||||
char *dir = NULL;
|
char *dir = NULL;
|
||||||
struct dynstr userd, d, res;
|
struct dynstr userd, d, res;
|
||||||
|
struct page_search s =
|
||||||
|
{
|
||||||
|
.username = username,
|
||||||
|
.adir = root
|
||||||
|
};
|
||||||
|
|
||||||
dynstr_init(&userd);
|
dynstr_init(&userd);
|
||||||
dynstr_init(&d);
|
dynstr_init(&d);
|
||||||
|
@ -971,6 +975,8 @@ static int getnode(const struct http_payload *const p,
|
||||||
.r = r,
|
.r = r,
|
||||||
.args = p->args,
|
.args = p->args,
|
||||||
.n_args = p->n_args,
|
.n_args = p->n_args,
|
||||||
|
.username = username,
|
||||||
|
.adir = adir,
|
||||||
.dir = dir.str,
|
.dir = dir.str,
|
||||||
.root = root.str,
|
.root = root.str,
|
||||||
.res = d.str,
|
.res = d.str,
|
||||||
|
@ -1700,6 +1706,54 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int getthumbnail(const struct http_payload *const p,
|
||||||
|
struct http_response *const r, void *const user)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
struct auth *const a = user;
|
||||||
|
struct dynstr d, dir;
|
||||||
|
|
||||||
|
dynstr_init(&d);
|
||||||
|
dynstr_init(&dir);
|
||||||
|
|
||||||
|
if (auth_cookie(a, &p->cookie))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
||||||
|
ret = page_forbidden(r);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *const adir = auth_dir(a);
|
||||||
|
|
||||||
|
if (!adir)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&d, "%s%s", adir, p->resource))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_dup(&dir, &d))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_dup failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (page_thumbnail(r, d.str))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: page_public failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
dynstr_free(&d);
|
||||||
|
dynstr_free(&dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void usage(char *const argv[])
|
static void usage(char *const argv[])
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s [-t tmpdir] [-p port] dir\n", *argv);
|
fprintf(stderr, "%s [-t tmpdir] [-p port] dir\n", *argv);
|
||||||
|
@ -1941,6 +1995,7 @@ int main(int argc, char *argv[])
|
||||||
|| handler_add(h, "/mkdir", HTTP_OP_POST, createdir, a)
|
|| handler_add(h, "/mkdir", HTTP_OP_POST, createdir, a)
|
||||||
|| handler_add(h, "/confirm/rm", HTTP_OP_POST, confirm_rm, a)
|
|| handler_add(h, "/confirm/rm", HTTP_OP_POST, confirm_rm, a)
|
||||||
|| handler_add(h, "/rm", HTTP_OP_POST, rm, a)
|
|| handler_add(h, "/rm", HTTP_OP_POST, rm, a)
|
||||||
|
|| handler_add(h, "/thumbnails/*", HTTP_OP_GET, getthumbnail, a)
|
||||||
|| handler_listen(h, port))
|
|| handler_listen(h, port))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
|
131
page.c
131
page.c
|
@ -95,6 +95,67 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int prepare_thumbnail(struct html_node *const n,
|
||||||
|
const struct stat *const sb, const struct page_resource *const pr,
|
||||||
|
const char *const name, const char *const sep)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
struct html_node *img;
|
||||||
|
struct dynstr abs, rel;
|
||||||
|
struct stat tsb;
|
||||||
|
|
||||||
|
dynstr_init(&abs);
|
||||||
|
dynstr_init(&rel);
|
||||||
|
|
||||||
|
if (!S_ISREG(sb->st_mode))
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (!pr->adir || !pr->username)
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&rel, "/thumbnails/%s%s%s%s",
|
||||||
|
pr->username, pr->dir + strlen("/user"), sep, name))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append rel failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&abs, "%s%s", pr->adir, rel.str))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append rel failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (stat(abs.str, &tsb))
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
ret = 0;
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (!(img = html_node_add_child(n, "img")))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: html_node_add_child failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (html_node_add_attr(img, "src", rel.str))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: html_node_add_attr src failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
dynstr_free(&abs);
|
||||||
|
dynstr_free(&rel);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int prepare_name(struct html_node *const n, struct stat *const sb,
|
static int prepare_name(struct html_node *const n, struct stat *const sb,
|
||||||
const char *const dir, const char *const name)
|
const char *const dir, const char *const name)
|
||||||
{
|
{
|
||||||
|
@ -332,18 +393,19 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_element(struct html_node *const n, const char *const dir,
|
static int add_element(struct html_node *const n,
|
||||||
const char *const res, const char *const name, const bool chbx)
|
const struct page_resource *const pr, const char *const name,
|
||||||
|
const bool chbx)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
enum {RM_CHECKBOX, NAME, SIZE, DATE, SHARE, PREVIEW, COLUMNS};
|
enum {RM_CHECKBOX, THUMBNAIL, NAME, SIZE, DATE, SHARE, PREVIEW, COLUMNS};
|
||||||
struct html_node *tr, *td[COLUMNS];
|
struct html_node *tr, *td[COLUMNS];
|
||||||
struct dynstr path;
|
struct dynstr path;
|
||||||
const char *const sep = res[strlen(res) - 1] != '/' ? "/" : "";
|
const char *const sep = pr->res[strlen(pr->res) - 1] != '/' ? "/" : "";
|
||||||
|
|
||||||
dynstr_init(&path);
|
dynstr_init(&path);
|
||||||
|
|
||||||
if (dynstr_append(&path, "%s%s%s", res, sep, name))
|
if (dynstr_append(&path, "%s%s%s", pr->res, sep, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -376,7 +438,12 @@ static int add_element(struct html_node *const n, const char *const dir,
|
||||||
fprintf(stderr, "%s: prepare_rm_checkbox failed\n", __func__);
|
fprintf(stderr, "%s: prepare_rm_checkbox failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_name(td[NAME], &sb, dir, name))
|
else if (prepare_thumbnail(td[THUMBNAIL], &sb, pr, name, sep))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: prepare_thumbnail failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (prepare_name(td[NAME], &sb, pr->dir, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_name failed\n", __func__);
|
fprintf(stderr, "%s: prepare_name failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -391,12 +458,12 @@ static int add_element(struct html_node *const n, const char *const dir,
|
||||||
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_share(td[SHARE], &sb, dir, name))
|
else if (prepare_share(td[SHARE], &sb, pr->dir, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_preview(td[PREVIEW], &sb, dir, name))
|
else if (prepare_preview(td[PREVIEW], &sb, pr->dir, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -1117,12 +1184,12 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_elements(const char *const root, const char *const res,
|
static int add_elements(const struct page_resource *const pr,
|
||||||
const char *const dir, struct html_node *const table)
|
struct html_node *const table)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
struct dirent **pde = NULL;
|
struct dirent **pde = NULL;
|
||||||
const int n = scandir(res, &pde, NULL, alphasort);
|
const int n = scandir(pr->res, &pde, NULL, alphasort);
|
||||||
|
|
||||||
if (n < 0)
|
if (n < 0)
|
||||||
{
|
{
|
||||||
|
@ -1136,9 +1203,9 @@ static int add_elements(const char *const root, const char *const res,
|
||||||
const char *const name = de->d_name;
|
const char *const name = de->d_name;
|
||||||
|
|
||||||
if (!strcmp(name, ".")
|
if (!strcmp(name, ".")
|
||||||
|| (!strcmp(name, "..") && !strcmp(root, res)))
|
|| (!strcmp(name, "..") && !strcmp(pr->root, pr->res)))
|
||||||
continue;
|
continue;
|
||||||
else if (add_element(table, dir, res, name, true))
|
else if (add_element(table, pr, name, true))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: add_element failed\n", __func__);
|
fprintf(stderr, "%s: add_element failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -1170,7 +1237,7 @@ static int list_dir(const struct page_resource *const pr)
|
||||||
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (add_elements(pr->root, pr->res, pr->dir, table))
|
else if (add_elements(pr, table))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -1755,8 +1822,15 @@ static int add_search_results(struct html_node *const n,
|
||||||
for (size_t i = 0; i < s->n; i++)
|
for (size_t i = 0; i < s->n; i++)
|
||||||
{
|
{
|
||||||
const struct page_search_result *const r = &s->results[i];
|
const struct page_search_result *const r = &s->results[i];
|
||||||
|
const struct page_resource pr =
|
||||||
|
{
|
||||||
|
.dir = "/user/",
|
||||||
|
.adir = s->adir,
|
||||||
|
.username = s->username,
|
||||||
|
.res = s->root
|
||||||
|
};
|
||||||
|
|
||||||
if (add_element(table, "/user/", s->root, r->name, false))
|
if (add_element(table, &pr, r->name, false))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: add_element failed\n", __func__);
|
fprintf(stderr, "%s: add_element failed\n", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2139,3 +2213,30 @@ end:
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int page_thumbnail(struct http_response *const r, const char *const res)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (stat(res, &sb))
|
||||||
|
{
|
||||||
|
if (errno == ENOENT)
|
||||||
|
return page_not_found(r);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
||||||
|
__func__, res, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mode_t m = sb.st_mode;
|
||||||
|
|
||||||
|
if (!S_ISREG(m))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: only regular files are supported\n", __func__);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return serve_file(r, &sb, res, true);
|
||||||
|
}
|
||||||
|
|
5
page.h
5
page.h
|
@ -13,7 +13,7 @@ struct page_quota
|
||||||
struct page_resource
|
struct page_resource
|
||||||
{
|
{
|
||||||
struct http_response *r;
|
struct http_response *r;
|
||||||
const char *dir, *root, *res;
|
const char *adir, *username, *dir, *root, *res;
|
||||||
const struct page_quota *q;
|
const struct page_quota *q;
|
||||||
const struct http_arg *args;
|
const struct http_arg *args;
|
||||||
size_t n_args;
|
size_t n_args;
|
||||||
|
@ -26,7 +26,7 @@ struct page_search
|
||||||
char *name;
|
char *name;
|
||||||
} *results;
|
} *results;
|
||||||
|
|
||||||
const char *root;
|
const char *root, *adir, *username;
|
||||||
size_t n;
|
size_t n;
|
||||||
bool limit_exceeded;
|
bool limit_exceeded;
|
||||||
};
|
};
|
||||||
|
@ -49,5 +49,6 @@ int page_quota_exceeded(struct http_response *r, unsigned long long len,
|
||||||
unsigned long long quota);
|
unsigned long long quota);
|
||||||
int page_search(struct http_response *r, const struct page_search *s);
|
int page_search(struct http_response *r, const struct page_search *s);
|
||||||
int page_rm(struct http_response *r, const struct page_rm *rm);
|
int page_rm(struct http_response *r, const struct page_rm *rm);
|
||||||
|
int page_thumbnail(struct http_response *r, const char *res);
|
||||||
|
|
||||||
#endif /* PAGE_H */
|
#endif /* PAGE_H */
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
echo "$0 [-s <size>] [-h] -d <dir> <path>"
|
||||||
|
}
|
||||||
|
|
||||||
|
SIZE=96
|
||||||
|
|
||||||
|
while getopts s:d:h arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
d) DIR="$OPTARG"
|
||||||
|
;;
|
||||||
|
s) SIZE="$OPTARG"
|
||||||
|
;;
|
||||||
|
h) usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
?) usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$DIR" ]
|
||||||
|
then
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
shift $(($OPTIND - 1))
|
||||||
|
|
||||||
|
if [ $# != 1 ]; then
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
F="$1"
|
||||||
|
|
||||||
|
gen()
|
||||||
|
{
|
||||||
|
IN="$1"
|
||||||
|
OUT="$2"
|
||||||
|
|
||||||
|
if [ -z "$OUT" ]
|
||||||
|
then
|
||||||
|
echo Expected output filename >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$OUT")"
|
||||||
|
|
||||||
|
if convert -thumbnail x$SIZE "$IN" "$OUT"
|
||||||
|
then
|
||||||
|
echo Created $OUT
|
||||||
|
else
|
||||||
|
echo Failed to create $OUT >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while read f; do
|
||||||
|
if [ "$f" != "" ]
|
||||||
|
then
|
||||||
|
THUMBNAIL=$(echo "$f" | sed "s,$DIR/user/,$DIR/thumbnails/,")
|
||||||
|
gen "$f" "$THUMBNAIL"
|
||||||
|
fi
|
||||||
|
done <<-EOF
|
||||||
|
$(find "$F" -type f \
|
||||||
|
-a -iname '*.jpeg' \
|
||||||
|
-o -iname '*.jpg' \
|
||||||
|
-o -iname '*.png' \
|
||||||
|
-o -iname '*.jpeg')
|
||||||
|
EOF
|
|
@ -0,0 +1,52 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
echo "$0 [-s <size>] [-r] [-h] [-d <subdir>] <dir>"
|
||||||
|
}
|
||||||
|
|
||||||
|
REGEN=0
|
||||||
|
SIZE=96
|
||||||
|
|
||||||
|
while getopts rs:d:h arg
|
||||||
|
do
|
||||||
|
case $arg in
|
||||||
|
r) REGEN=1
|
||||||
|
;;
|
||||||
|
s) SIZE="$OPTARG"
|
||||||
|
;;
|
||||||
|
h) usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
d) SUBDIR="$OPTARG"
|
||||||
|
;;
|
||||||
|
?) usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
shift $(($OPTIND - 1))
|
||||||
|
|
||||||
|
if [ $# != 1 ]; then
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
DIR="$1"
|
||||||
|
|
||||||
|
[ "$REGEN" -eq 1 ] && "$(dirname $0)/tngen" \
|
||||||
|
${SIZE:+-s$SIZE} \
|
||||||
|
-d "$DIR" \
|
||||||
|
"$DIR/user"
|
||||||
|
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
F="$(inotifywait -e modify,move,create,delete \
|
||||||
|
--format "%w%f" -qr "$DIR/user/")"
|
||||||
|
sleep 1 # TODO: revisit this
|
||||||
|
"$(dirname $0)/tngen" \
|
||||||
|
${SIZE:+-s$SIZE} \
|
||||||
|
-d "$DIR" \
|
||||||
|
"$F"
|
||||||
|
done
|
Loading…
Reference in New Issue