WIP thumbnail stuff
This commit is contained in:
parent
30d31e8c9d
commit
fa97b1904c
|
@ -2,3 +2,4 @@ build/
|
||||||
slcl
|
slcl
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
|
/Makefile
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(slcl)
|
project(slcl)
|
||||||
|
option(THUMBNAILS "Enables thumbnail generation for images." OFF)
|
||||||
add_executable(${PROJECT_NAME}
|
add_executable(${PROJECT_NAME}
|
||||||
auth.c
|
auth.c
|
||||||
base64.c
|
base64.c
|
||||||
|
@ -13,9 +14,26 @@ add_executable(${PROJECT_NAME}
|
||||||
page.c
|
page.c
|
||||||
server.c
|
server.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(NOT DEFINED THUMBNAIL_HEIGHT)
|
||||||
|
set(THUMBNAIL_HEIGHT 96)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(THUMBNAILS)
|
||||||
|
find_package(ImageMagick REQUIRED)
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE thumbnail.c)
|
||||||
|
target_compile_definitions(${PROJECT_NAME}
|
||||||
|
PRIVATE THUMBNAIL_HEIGHT=${THUMBNAIL_HEIGHT})
|
||||||
|
elseif(THUMBNAIL_HEIGHT)
|
||||||
|
target_sources(${PROJECT_NAME} PRIVATE thumbnail_stub.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
|
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
|
target_compile_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
|
||||||
add_subdirectory(dynstr)
|
add_subdirectory(dynstr)
|
||||||
|
add_subdirectory(mkdir_r)
|
||||||
find_package(cJSON 1.0 REQUIRED)
|
find_package(cJSON 1.0 REQUIRED)
|
||||||
find_package(OpenSSL 3.0 REQUIRED)
|
find_package(OpenSSL 3.0 REQUIRED)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE dynstr cjson OpenSSL::SSL)
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
PRIVATE dynstr mkdir_r cjson OpenSSL::SSL ImageMagick)
|
||||||
|
#message(FATAL_ERROR "TODO")
|
||||||
|
|
36
Makefile
36
Makefile
|
@ -1,36 +0,0 @@
|
||||||
.POSIX:
|
|
||||||
.SUFFIXES: .c .o
|
|
||||||
|
|
||||||
PROJECT = slcl
|
|
||||||
O = -Og
|
|
||||||
CDEFS = -D_FILE_OFFSET_BITS=64 # Required for large file support on 32-bit.
|
|
||||||
CFLAGS = $(O) $(CDEFS) -g -Wall -Idynstr/include -MD -MF -
|
|
||||||
LIBS = -lcjson -lssl -lm -lcrypto
|
|
||||||
LDFLAGS = $(LIBS)
|
|
||||||
DEPS = $(OBJECTS:.o=.d)
|
|
||||||
OBJECTS = \
|
|
||||||
auth.o \
|
|
||||||
base64.o \
|
|
||||||
cftw.o \
|
|
||||||
handler.o \
|
|
||||||
hex.o \
|
|
||||||
html.o \
|
|
||||||
http.o \
|
|
||||||
jwt.o \
|
|
||||||
main.o \
|
|
||||||
page.o \
|
|
||||||
server.o \
|
|
||||||
dynstr/dynstr.o
|
|
||||||
|
|
||||||
all: $(PROJECT)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OBJECTS) $(DEPS)
|
|
||||||
|
|
||||||
$(PROJECT): $(OBJECTS)
|
|
||||||
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
|
|
||||||
|
|
||||||
.c.o:
|
|
||||||
$(CC) $(CFLAGS) -c $< -o $@ > $(@:.o=.d)
|
|
||||||
|
|
||||||
-include $(DEPS)
|
|
11
README.md
11
README.md
|
@ -16,6 +16,8 @@ simplicity and efficiency.
|
||||||
- Its own, tiny HTTP/1.0 and 1.1-compatible server.
|
- Its own, tiny HTTP/1.0 and 1.1-compatible server.
|
||||||
- A simple JSON file as the credentials database.
|
- A simple JSON file as the credentials database.
|
||||||
- No JavaScript.
|
- No JavaScript.
|
||||||
|
- Optional, resizable thumbnails (made available by
|
||||||
|
[GraphicsMagick](http://www.graphicsmagick.org/)).
|
||||||
|
|
||||||
### TLS
|
### TLS
|
||||||
|
|
||||||
|
@ -42,9 +44,12 @@ to `slcl`. If required, encryption should be done before uploading e.g.: using
|
||||||
- cJSON >= 1.7.15.
|
- cJSON >= 1.7.15.
|
||||||
- [`dynstr`](https://gitea.privatedns.org/xavi92/dynstr)
|
- [`dynstr`](https://gitea.privatedns.org/xavi92/dynstr)
|
||||||
(provided as a `git` submodule).
|
(provided as a `git` submodule).
|
||||||
|
- [`mkdir_r`](https://gitea.privatedns.org/xavi92/mkdir_r)
|
||||||
|
(provided as a `git` submodule).
|
||||||
- `xxd` (for [`usergen`](usergen) only).
|
- `xxd` (for [`usergen`](usergen) only).
|
||||||
- `jq` (for [`usergen`](usergen) only).
|
- `jq` (for [`usergen`](usergen) only).
|
||||||
- CMake (optional).
|
- CMake (optional).
|
||||||
|
- [GraphicsMagick](http://www.graphicsmagick.org/) >= 1.4 (optional).
|
||||||
|
|
||||||
### Ubuntu / Debian
|
### Ubuntu / Debian
|
||||||
|
|
||||||
|
@ -57,7 +62,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 libgraphicsmagick1-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
@ -66,7 +71,8 @@ sudo apt install cmake xxd jq
|
||||||
Two build environments are provided for `slcl` - feel free to choose any of
|
Two build environments are provided for `slcl` - feel free to choose any of
|
||||||
them:
|
them:
|
||||||
|
|
||||||
- A mostly POSIX-compliant [`Makefile`](Makefile).
|
- A [`configure`](configure) POSIX shell script that generates a mostly
|
||||||
|
POSIX-compliant `Makefile`.
|
||||||
- A [`CMakeLists.txt`](CMakeLists.txt).
|
- A [`CMakeLists.txt`](CMakeLists.txt).
|
||||||
|
|
||||||
`slcl` can be built using the standard build process:
|
`slcl` can be built using the standard build process:
|
||||||
|
@ -74,6 +80,7 @@ them:
|
||||||
#### Make
|
#### Make
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
$ ./configure
|
||||||
$ make
|
$ make
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DEFAULT_PREFIX=/usr/local
|
||||||
|
DEFAULT_THUMBNAIL_HEIGHT=96
|
||||||
|
|
||||||
|
usage()
|
||||||
|
{
|
||||||
|
printf "$0 [OPTION]...\n\n"
|
||||||
|
printf "Configuration:\n"
|
||||||
|
printf "%s\t\t\t" "--prefix=PREFIX"
|
||||||
|
printf "Sets installation prefix (default: $DEFAULT_PREFIX).\n"
|
||||||
|
printf "%s\t\t\t" "--thumbnails"
|
||||||
|
printf "Enables thumbnail generation for images.\n"
|
||||||
|
printf "%s\t" "--thumbnail-height=HEIGHT"
|
||||||
|
printf "Sets thumbnail height, in pixels "
|
||||||
|
printf "(default: $DEFAULT_THUMBNAIL_HEIGHT). "
|
||||||
|
printf "Requires --thumbnails.\n"
|
||||||
|
printf "%s\t\t\t" "-h|--help"
|
||||||
|
printf "Prints this help page.\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
CC=${CC:-cc}
|
||||||
|
CFLAGS=${CFLAGS:-}
|
||||||
|
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
case "$arg" in
|
||||||
|
--thumbnails)
|
||||||
|
THUMBNAILS=1
|
||||||
|
;;
|
||||||
|
--thumbnail-height=*)
|
||||||
|
THUMBNAIL_HEIGHT=${arg#*=}
|
||||||
|
;;
|
||||||
|
--prefix=*)
|
||||||
|
PREFIX=${arg#*=}
|
||||||
|
;;
|
||||||
|
-h | --help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid argument $arg" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
THUMBNAILS=${THUMBNAILS:-0}
|
||||||
|
THUMBNAIL_HEIGHT=${THUMBNAIL_HEIGHT:-$DEFAULT_THUMBNAIL_HEIGHT}
|
||||||
|
PREFIX=${PREFIX:-$DEFAULT_PREFIX}
|
||||||
|
|
||||||
|
OBJECTS="\
|
||||||
|
auth.o \
|
||||||
|
base64.o \
|
||||||
|
cftw.o \
|
||||||
|
handler.o \
|
||||||
|
hex.o \
|
||||||
|
html.o \
|
||||||
|
http.o \
|
||||||
|
jwt.o \
|
||||||
|
main.o \
|
||||||
|
page.o \
|
||||||
|
server.o \
|
||||||
|
dynstr/dynstr.o"
|
||||||
|
|
||||||
|
LIBS="\
|
||||||
|
-lcjson \
|
||||||
|
-lssl \
|
||||||
|
-lm \
|
||||||
|
-lcrypto"
|
||||||
|
|
||||||
|
CFLAGS=${CFLAGS:-'$(O) $(CDEFS) -g -Wall -Idynstr/include -Imkdir_r -MD -MF -'}
|
||||||
|
|
||||||
|
if [ $THUMBNAILS -ne 0 ]
|
||||||
|
then
|
||||||
|
LIBS="$LIBS $(GraphicsMagick-config --libs)"
|
||||||
|
CFLAGS="$CFLAGS $(GraphicsMagick-config --cppflags |
|
||||||
|
tr '\n' ' ')"
|
||||||
|
CFLAGS="$CFLAGS -DTHUMBNAIL_HEIGHT=$THUMBNAIL_HEIGHT"
|
||||||
|
CFLAGS="$CFLAGS -Imkdir_r/private_include"
|
||||||
|
LDFLAGS="$LDFLAGS $(GraphicsMagick-config --ldflags)"
|
||||||
|
OBJECTS="$OBJECTS \
|
||||||
|
thumbnail.o \
|
||||||
|
mkdir_r/mkdir_r.o \
|
||||||
|
mkdir_r/posix.o"
|
||||||
|
else
|
||||||
|
OBJECTS="$OBJECTS thumbnail_stub.o mkdir_r_stub.o"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > Makefile <<"EOF"
|
||||||
|
.POSIX:
|
||||||
|
.SUFFIXES: .c .o
|
||||||
|
|
||||||
|
PROJECT = slcl
|
||||||
|
O = -Og
|
||||||
|
CDEFS = -D_FILE_OFFSET_BITS=64 # Required for large file support on 32-bit.
|
||||||
|
DEPS = $(OBJECTS:.o=.d)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >> Makefile <<EOF
|
||||||
|
CFLAGS = $CFLAGS
|
||||||
|
LIBS = $LIBS
|
||||||
|
LDFLAGS = $LDFLAGS
|
||||||
|
OBJECTS = $OBJECTS
|
||||||
|
PREFIX = $PREFIX
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat >> Makefile <<"EOF"
|
||||||
|
all: $(PROJECT)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJECTS) $(DEPS)
|
||||||
|
|
||||||
|
install: $(PROJECT)
|
||||||
|
mkdir -p $(PREFIX) $(PREFIX)/bin
|
||||||
|
cp $(PROJECT) $(PREFIX)/bin
|
||||||
|
chmod 0755 $(PREFIX)/bin/$(PROJECT)
|
||||||
|
+cd doc && $(MAKE) install PREFIX=$(PREFIX)
|
||||||
|
|
||||||
|
$(PROJECT): $(OBJECTS)
|
||||||
|
$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ > $(@:.o=.d)
|
||||||
|
|
||||||
|
-include $(DEPS)
|
||||||
|
EOF
|
|
@ -0,0 +1,14 @@
|
||||||
|
.POSIX:
|
||||||
|
|
||||||
|
DOCS1 = \
|
||||||
|
man1/slcl.1 \
|
||||||
|
man1/usergen.1
|
||||||
|
|
||||||
|
DOCSPREFIX=$(PREFIX)/share/man
|
||||||
|
DOCS1PREFIX=$(DOCSPREFIX)/man1
|
||||||
|
|
||||||
|
install: $(DOCS1)
|
||||||
|
test $(PREFIX) || (echo Please define PREFIX. >&2 ; exit 1)
|
||||||
|
mkdir -p $(DOCS1PREFIX)
|
||||||
|
cp $(DOCS1) $(DOCS1PREFIX)
|
||||||
|
for d in $(DOCS1); do chmod 0644 $(DOCSPREFIX)/$$d; done
|
222
main.c
222
main.c
|
@ -5,7 +5,9 @@
|
||||||
#include "handler.h"
|
#include "handler.h"
|
||||||
#include "hex.h"
|
#include "hex.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
|
#include "mkdir_r.h"
|
||||||
#include "page.h"
|
#include "page.h"
|
||||||
|
#include "thumbnail.h"
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <dynstr.h>
|
#include <dynstr.h>
|
||||||
|
@ -643,58 +645,54 @@ static int check_length(const unsigned long long len,
|
||||||
static int getnode(const struct http_payload *const p,
|
static int getnode(const struct http_payload *const p,
|
||||||
struct http_response *const r, void *const user)
|
struct http_response *const r, void *const user)
|
||||||
{
|
{
|
||||||
|
int ret = -1;
|
||||||
struct auth *const a = user;
|
struct auth *const a = user;
|
||||||
|
const char *adir, *const username = p->cookie.field;
|
||||||
|
struct dynstr d;
|
||||||
|
|
||||||
|
dynstr_init(&d);
|
||||||
|
|
||||||
if (auth_cookie(a, &p->cookie))
|
if (auth_cookie(a, &p->cookie))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
||||||
return page_forbidden(r);
|
ret = page_forbidden(r);
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
|
else if (path_isrel(p->resource))
|
||||||
const char *const username = p->cookie.field,
|
|
||||||
*const resource = p->resource + strlen("/user/");
|
|
||||||
|
|
||||||
if (path_isrel(resource))
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: illegal relative path %s\n", __func__, resource);
|
fprintf(stderr, "%s: illegal relative path %s\n",
|
||||||
return page_forbidden(r);
|
__func__, p->resource);
|
||||||
|
ret = page_forbidden(r);
|
||||||
|
goto end;
|
||||||
}
|
}
|
||||||
|
else if (!(adir = auth_dir(a)))
|
||||||
int ret = -1;
|
|
||||||
struct dynstr dir, root, d;
|
|
||||||
const char *const adir = auth_dir(a),
|
|
||||||
*const sep = p->resource[strlen(p->resource) - 1] != '/' ? "/" : "";
|
|
||||||
|
|
||||||
dynstr_init(&dir);
|
|
||||||
dynstr_init(&d);
|
|
||||||
dynstr_init(&root);
|
|
||||||
|
|
||||||
if (!adir)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (dynstr_append(&dir, "%s%s", p->resource, sep))
|
else if (dynstr_append(&d, "%s/user/%s/", adir, username))
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: dynstr_append dird failed\n", __func__);
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
else if (dynstr_append(&root, "%s/user/%s/", adir, username)
|
|
||||||
|| dynstr_append(&d, "%s%s", root.str, resource))
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const struct page_resource res =
|
||||||
|
{
|
||||||
|
.root = adir,
|
||||||
|
.username = username,
|
||||||
|
.user_root = d.str,
|
||||||
|
.res = p->resource + strlen("/user")
|
||||||
|
};
|
||||||
|
|
||||||
bool available;
|
bool available;
|
||||||
unsigned long long cur, max;
|
unsigned long long cur, max;
|
||||||
|
|
||||||
if (auth_quota(a, username, &available, &max))
|
if (auth_quota(a, res.username, &available, &max))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: quota_available failed\n", __func__);
|
fprintf(stderr, "%s: quota_available failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (available && quota_current(a, username, &cur))
|
else if (available && quota_current(a, res.username, &cur))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: quota_current failed\n", __func__);
|
fprintf(stderr, "%s: quota_current failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -707,12 +705,10 @@ static int getnode(const struct http_payload *const p,
|
||||||
.max = max
|
.max = max
|
||||||
} : NULL;
|
} : NULL;
|
||||||
|
|
||||||
ret = page_resource(r, dir.str, root.str, d.str, ppq);
|
ret = page_resource(r, &res, ppq);
|
||||||
|
|
||||||
end:
|
end:
|
||||||
dynstr_free(&dir);
|
|
||||||
dynstr_free(&d);
|
dynstr_free(&d);
|
||||||
dynstr_free(&root);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -797,25 +793,84 @@ static int rename_or_move(const char *const old, const char *const new)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ensure_dir(const char *const dir)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (stat(dir, &sb))
|
||||||
|
{
|
||||||
|
switch (errno)
|
||||||
|
{
|
||||||
|
case ENOENT:
|
||||||
|
if (mkdir(dir, S_IRWXU))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: mkdir(2) %s: %s\n",
|
||||||
|
__func__, dir, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Created empty directory at %s\n", dir);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!S_ISDIR(sb.st_mode))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: %s not a directory\n", __func__, dir);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int upload_file(const struct http_post_file *const f,
|
static int upload_file(const struct http_post_file *const f,
|
||||||
const char *const user, const char *const root, const char *const dir)
|
const char *const user, const char *const root, const char *const dir)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
struct dynstr d;
|
struct dynstr d, dird, th;
|
||||||
|
|
||||||
dynstr_init(&d);
|
dynstr_init(&d);
|
||||||
|
dynstr_init(&dird);
|
||||||
|
dynstr_init(&th);
|
||||||
|
|
||||||
if (!root)
|
if (!root)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (dynstr_append(&d, "%s/user/%s/%s%s", root, user, dir, f->filename))
|
else if (dynstr_append(&d, "%s/user/%s%s%s", root, user, dir, f->filename))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (rename_or_move(f->tmpname, d.str))
|
else if (thumbnail_configured())
|
||||||
|
{
|
||||||
|
if (dynstr_append(&dird, "%s/thumbnails/%s%s", root, user, dir))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append dird failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&th, "%s%s", dird.str, f->filename))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append th failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (mkdir_r(dird.str, S_IRWXU))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: ensure_dir failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (thumbnail_create(f->tmpname, th.str) < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: thumbnail_create failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rename_or_move(f->tmpname, d.str))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: rename_or_move failed\n", __func__);
|
fprintf(stderr, "%s: rename_or_move failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -825,6 +880,8 @@ static int upload_file(const struct http_post_file *const f,
|
||||||
|
|
||||||
end:
|
end:
|
||||||
dynstr_free(&d);
|
dynstr_free(&d);
|
||||||
|
dynstr_free(&dird);
|
||||||
|
dynstr_free(&th);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1129,47 +1186,74 @@ static int parse_args(const int argc, char *const argv[],
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ensure_dir(const char *const dir)
|
static int getthumbnail(const struct http_payload *const p,
|
||||||
|
struct http_response *const r, void *const user)
|
||||||
{
|
{
|
||||||
struct stat sb;
|
int ret = -1;
|
||||||
|
struct auth *const a = user;
|
||||||
|
struct dynstr d, dir;
|
||||||
|
char *name;
|
||||||
|
|
||||||
if (stat(dir, &sb))
|
dynstr_init(&d);
|
||||||
|
dynstr_init(&dir);
|
||||||
|
|
||||||
|
if (auth_cookie(a, &p->cookie))
|
||||||
{
|
{
|
||||||
switch (errno)
|
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
||||||
{
|
ret = page_forbidden(r);
|
||||||
case ENOENT:
|
goto end;
|
||||||
if (mkdir(dir, S_IRWXU))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: mkdir(2) %s: %s\n",
|
|
||||||
__func__, dir, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Created empty directory at %s\n", dir);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!S_ISDIR(sb.st_mode))
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: %s not a directory\n", __func__, dir);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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 (!(name = dirname(dir.str)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dirname(3) failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (ensure_dir(name))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: ensure_dir 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 int init_dirs(const char *const dir)
|
static int init_dirs(const char *const dir)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
struct dynstr user, public;
|
struct dynstr user, public, thumbnails;
|
||||||
struct sb;
|
struct sb;
|
||||||
|
|
||||||
dynstr_init(&user);
|
dynstr_init(&user);
|
||||||
dynstr_init(&public);
|
dynstr_init(&public);
|
||||||
|
dynstr_init(&thumbnails);
|
||||||
|
|
||||||
if (dynstr_append(&user, "%s/user", dir))
|
if (dynstr_append(&user, "%s/user", dir))
|
||||||
{
|
{
|
||||||
|
@ -1196,12 +1280,26 @@ static int init_dirs(const char *const dir)
|
||||||
fprintf(stderr, "%s: ensure_dir public failed\n", __func__);
|
fprintf(stderr, "%s: ensure_dir public failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
if (thumbnail_configured())
|
||||||
|
{
|
||||||
|
if (dynstr_append(&thumbnails, "%s/thumbnails", dir))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append thumbnails failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (ensure_dir(thumbnails.str))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: ensure_dir thumbnails failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
dynstr_free(&user);
|
dynstr_free(&user);
|
||||||
dynstr_free(&public);
|
dynstr_free(&public);
|
||||||
|
dynstr_free(&thumbnails);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,6 +1335,8 @@ int main(const int argc, char *const argv[])
|
||||||
|| handler_add(h, "/share", HTTP_OP_POST, share, a)
|
|| handler_add(h, "/share", HTTP_OP_POST, share, a)
|
||||||
|| handler_add(h, "/upload", HTTP_OP_POST, upload, a)
|
|| handler_add(h, "/upload", HTTP_OP_POST, upload, a)
|
||||||
|| handler_add(h, "/mkdir", HTTP_OP_POST, createdir, a)
|
|| handler_add(h, "/mkdir", HTTP_OP_POST, createdir, a)
|
||||||
|
|| (thumbnail_configured()
|
||||||
|
&& handler_add(h, "/thumbnails/*", HTTP_OP_GET, getthumbnail, a))
|
||||||
|| handler_listen(h, port))
|
|| handler_listen(h, port))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
|
254
page.c
254
page.c
|
@ -3,6 +3,7 @@
|
||||||
#include "page.h"
|
#include "page.h"
|
||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "html.h"
|
#include "html.h"
|
||||||
|
#include "thumbnail.h"
|
||||||
#include <dynstr.h>
|
#include <dynstr.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -45,7 +46,102 @@
|
||||||
" </form>\n"
|
" </form>\n"
|
||||||
#define MAXSIZEFMT "18446744073709551615.0 XiB"
|
#define MAXSIZEFMT "18446744073709551615.0 XiB"
|
||||||
|
|
||||||
static int prepare_name(struct html_node *const n, struct stat *const sb,
|
static const char *dir_separator(const char *const dir)
|
||||||
|
{
|
||||||
|
return dir[strlen(dir) - 1] != '/' ? "/" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int prepare_thumbnail(struct html_node *const n,
|
||||||
|
const struct stat *const sb,
|
||||||
|
const struct page_resource *const r, const char *const name)
|
||||||
|
{
|
||||||
|
int ret = -1, res;
|
||||||
|
const char *const sep = dir_separator(r->res);
|
||||||
|
struct html_node *img;
|
||||||
|
struct dynstr abs, rel;
|
||||||
|
struct stat tsb;
|
||||||
|
struct thumbnail_dim dim;
|
||||||
|
char w[sizeof "18446744073709551615"], h[sizeof w];
|
||||||
|
|
||||||
|
dynstr_init(&abs);
|
||||||
|
dynstr_init(&rel);
|
||||||
|
|
||||||
|
if (!S_ISREG(sb->st_mode))
|
||||||
|
{
|
||||||
|
ret = 0;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&rel, "/thumbnails/%s%s%s%s",
|
||||||
|
r->username, r->res, sep, name))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append rel failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&abs, "%s%s", r->root, 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 (thumbnail_dim(abs.str, &dim))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: thumbnail_dim failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = snprintf(w, sizeof w, "%lu", dim.w);
|
||||||
|
|
||||||
|
if (res < 0 || res >= sizeof w)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: snprintf(3) w failed with %d\n", __func__, res);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = snprintf(h, sizeof h, "%lu", dim.h);
|
||||||
|
|
||||||
|
if (res < 0 || res >= sizeof h)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: snprintf(3) h failed with %d\n", __func__, res);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
else if (html_node_add_attr(img, "width", w))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: html_node_add_attr w failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (html_node_add_attr(img, "height", h))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: html_node_add_attr h 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, const struct stat *const sb,
|
||||||
const char *const dir, const char *const name)
|
const char *const dir, const char *const name)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
@ -169,7 +265,7 @@ static int prepare_share(struct html_node *const n,
|
||||||
const struct stat *const sb, const char *const dir, const char *const name)
|
const struct stat *const sb, const char *const dir, const char *const name)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
const char *const fdir = dir + strlen("/user");
|
const char *const sep = dir_separator(dir);
|
||||||
struct html_node *form, *file, *submit;
|
struct html_node *form, *file, *submit;
|
||||||
struct dynstr d;
|
struct dynstr d;
|
||||||
|
|
||||||
|
@ -213,7 +309,7 @@ static int prepare_share(struct html_node *const n,
|
||||||
fprintf(stderr, "%s: html_node_add_attr file name failed\n", __func__);
|
fprintf(stderr, "%s: html_node_add_attr file name failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (dynstr_append(&d, "%s%s", fdir, name))
|
else if (dynstr_append(&d, "%s%s%s", dir, sep, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -243,18 +339,37 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_element(struct html_node *const n, const char *const dir,
|
static int user_resource(const struct page_resource *const r,
|
||||||
const char *const res, const char *const name)
|
struct dynstr *const d)
|
||||||
|
{
|
||||||
|
if (dynstr_append(d, "%s/user/%s%s", r->root, r->username, r->res))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_element(const struct page_resource *const r,
|
||||||
|
struct html_node *const n, const char *const absres,
|
||||||
|
const char *const name)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
enum {NAME, SIZE, DATE, SHARE, COLUMNS};
|
enum {THUMBNAIL, NAME, SIZE, DATE, SHARE, COLUMNS};
|
||||||
|
const char *const sep = dir_separator(r->res);
|
||||||
struct html_node *tr, *td[COLUMNS];
|
struct html_node *tr, *td[COLUMNS];
|
||||||
struct dynstr path;
|
struct dynstr path, rel;
|
||||||
const char *const sep = res[strlen(res) - 1] != '/' ? "/" : "";
|
|
||||||
|
|
||||||
dynstr_init(&path);
|
dynstr_init(&path);
|
||||||
|
dynstr_init(&rel);
|
||||||
|
|
||||||
if (dynstr_append(&path, "%s%s%s", res, sep, name))
|
if (dynstr_append(&path, "%s%s%s", absres, sep, name))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dynstr_append(&rel, "/user%s%s", r->res, sep))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -266,7 +381,9 @@ static int add_element(struct html_node *const n, const char *const dir,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof td / sizeof *td; i++)
|
for (size_t i = 0; i < sizeof td / sizeof *td; i++)
|
||||||
if (!(td[i] = html_node_add_child(tr, "td")))
|
if (i == THUMBNAIL && !thumbnail_configured())
|
||||||
|
continue;
|
||||||
|
else if (!(td[i] = html_node_add_child(tr, "td")))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: html_node_add_child td[%zu] failed\n",
|
fprintf(stderr, "%s: html_node_add_child td[%zu] failed\n",
|
||||||
__func__, i);
|
__func__, i);
|
||||||
|
@ -281,7 +398,13 @@ static int add_element(struct html_node *const n, const char *const dir,
|
||||||
__func__, path.str, strerror(errno));
|
__func__, path.str, strerror(errno));
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_name(td[NAME], &sb, dir, name))
|
else if (thumbnail_configured()
|
||||||
|
&& prepare_thumbnail(td[THUMBNAIL], &sb, r, name))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: prepare_thumbnail failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (prepare_name(td[NAME], &sb, rel.str, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_name failed\n", __func__);
|
fprintf(stderr, "%s: prepare_name failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -296,7 +419,7 @@ 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, rel.str, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
fprintf(stderr, "%s: prepare_date failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -306,6 +429,7 @@ static int add_element(struct html_node *const n, const char *const dir,
|
||||||
|
|
||||||
end:
|
end:
|
||||||
dynstr_free(&path);
|
dynstr_free(&path);
|
||||||
|
dynstr_free(&rel);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -743,7 +867,6 @@ end:
|
||||||
static struct html_node *resource_layout(const char *const dir,
|
static struct html_node *resource_layout(const char *const dir,
|
||||||
const struct page_quota *const q, struct html_node **const table)
|
const struct page_quota *const q, struct html_node **const table)
|
||||||
{
|
{
|
||||||
const char *const fdir = dir + strlen("/user");
|
|
||||||
struct html_node *const html = html_node_alloc("html"),
|
struct html_node *const html = html_node_alloc("html"),
|
||||||
*ret = NULL, *head, *body;
|
*ret = NULL, *head, *body;
|
||||||
|
|
||||||
|
@ -767,17 +890,17 @@ static struct html_node *resource_layout(const char *const dir,
|
||||||
fprintf(stderr, "%s: html_node_add_child table failed\n", __func__);
|
fprintf(stderr, "%s: html_node_add_child table failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (common_head(head, fdir))
|
else if (common_head(head, dir))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: common_head failed\n", __func__);
|
fprintf(stderr, "%s: common_head failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_upload_form(body, fdir))
|
else if (prepare_upload_form(body, dir))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (prepare_mkdir_form(body, fdir))
|
else if (prepare_mkdir_form(body, dir))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
fprintf(stderr, "%s: prepare_upload_form failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -808,14 +931,21 @@ 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 r,
|
||||||
const char *const dir, struct html_node *const table)
|
struct html_node *const table)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1, n = 0;
|
||||||
|
struct dynstr d;
|
||||||
struct dirent **pde = NULL;
|
struct dirent **pde = NULL;
|
||||||
const int n = scandir(res, &pde, NULL, alphasort);
|
|
||||||
|
|
||||||
if (n < 0)
|
dynstr_init(&d);
|
||||||
|
|
||||||
|
if (user_resource(r, &d))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: user_resource failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if ((n = scandir(d.str, &pde, NULL, alphasort)) < 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: scandir(3): %s\n", __func__, strerror(errno));
|
fprintf(stderr, "%s: scandir(3): %s\n", __func__, strerror(errno));
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -827,9 +957,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(r->user_root, d.str)))
|
||||||
continue;
|
continue;
|
||||||
else if (add_element(table, dir, res, name))
|
else if (add_element(r, table, d.str, name))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: add_element failed\n", __func__);
|
fprintf(stderr, "%s: add_element failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -844,25 +974,25 @@ end:
|
||||||
free(pde[i]);
|
free(pde[i]);
|
||||||
|
|
||||||
free(pde);
|
free(pde);
|
||||||
|
dynstr_free(&d);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int list_dir(struct http_response *const r, const char *const dir,
|
static int list_dir(struct http_response *const r,
|
||||||
const char *const root, const char *const res,
|
const struct page_resource *const res, const struct page_quota *const q)
|
||||||
const struct page_quota *const q)
|
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
struct dynstr out;
|
struct dynstr out;
|
||||||
struct html_node *table, *const html = resource_layout(dir, q, &table);
|
struct html_node *table, *html = NULL;
|
||||||
|
|
||||||
dynstr_init(&out);
|
dynstr_init(&out);
|
||||||
|
|
||||||
if (!html)
|
if (!(html = resource_layout(res->res, q, &table)))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (add_elements(root, res, dir, table))
|
else if (add_elements(res, table))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -907,14 +1037,14 @@ static int serve_file(struct http_response *const r,
|
||||||
const struct stat *const sb, const char *const res)
|
const struct stat *const sb, const char *const res)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
FILE *const f = fopen(res, "rb");
|
FILE *f = NULL;
|
||||||
struct dynstr b, d;
|
struct dynstr b, d;
|
||||||
char *bn;
|
char *bn;
|
||||||
|
|
||||||
dynstr_init(&b);
|
dynstr_init(&b);
|
||||||
dynstr_init(&d);
|
dynstr_init(&d);
|
||||||
|
|
||||||
if (!f)
|
if (!(f = fopen(res, "rb")))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
|
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -987,32 +1117,43 @@ static int page_not_found(struct http_response *const r)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int page_resource(struct http_response *const r, const char *const dir,
|
int page_resource(struct http_response *const r,
|
||||||
const char *const root, const char *const res,
|
const struct page_resource *const res, const struct page_quota *const q)
|
||||||
const struct page_quota *const q)
|
|
||||||
{
|
{
|
||||||
|
int ret = -1;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
struct dynstr d;
|
||||||
|
|
||||||
if (stat(res, &sb))
|
dynstr_init(&d);
|
||||||
|
|
||||||
|
if (user_resource(res, &d))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: user_resource failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (stat(d.str, &sb))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
||||||
__func__, res, strerror(errno));
|
__func__, res->res, strerror(errno));
|
||||||
|
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
return page_not_found(r);
|
ret = page_not_found(r);
|
||||||
else
|
|
||||||
return -1;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mode_t m = sb.st_mode;
|
const mode_t m = sb.st_mode;
|
||||||
|
|
||||||
if (S_ISDIR(m))
|
if (S_ISDIR(m))
|
||||||
return list_dir(r, dir, root, res, q);
|
ret = list_dir(r, res, q);
|
||||||
else if (S_ISREG(m))
|
else if (S_ISREG(m))
|
||||||
return serve_file(r, &sb, res);
|
ret = serve_file(r, &sb, d.str);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: unexpected st_mode %jd\n", __func__, (intmax_t)m);
|
||||||
|
|
||||||
fprintf(stderr, "%s: unexpected st_mode %jd\n", __func__, (intmax_t)m);
|
end:
|
||||||
return -1;
|
dynstr_free(&d);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *resolve_link(const char *const res)
|
static char *resolve_link(const char *const res)
|
||||||
|
@ -1403,3 +1544,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);
|
||||||
|
}
|
||||||
|
|
10
page.h
10
page.h
|
@ -8,16 +8,22 @@ struct page_quota
|
||||||
unsigned long long cur, max;
|
unsigned long long cur, max;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct page_resource
|
||||||
|
{
|
||||||
|
const char *root, *user_root, *username, *res;
|
||||||
|
};
|
||||||
|
|
||||||
int page_login(struct http_response *r);
|
int page_login(struct http_response *r);
|
||||||
int page_style(struct http_response *r);
|
int page_style(struct http_response *r);
|
||||||
int page_failed_login(struct http_response *r);
|
int page_failed_login(struct http_response *r);
|
||||||
int page_forbidden(struct http_response *r);
|
int page_forbidden(struct http_response *r);
|
||||||
int page_bad_request(struct http_response *r);
|
int page_bad_request(struct http_response *r);
|
||||||
int page_resource(struct http_response *r, const char *dir, const char *root,
|
int page_resource(struct http_response *r, const struct page_resource *res,
|
||||||
const char *res, const struct page_quota *q);
|
const struct page_quota *q);
|
||||||
int page_public(struct http_response *r, const char *res);
|
int page_public(struct http_response *r, const char *res);
|
||||||
int page_share(struct http_response *r, const char *path);
|
int page_share(struct http_response *r, const char *path);
|
||||||
int page_quota_exceeded(struct http_response *r, unsigned long long len,
|
int page_quota_exceeded(struct http_response *r, unsigned long long len,
|
||||||
unsigned long long quota);
|
unsigned long long quota);
|
||||||
|
int page_thumbnail(struct http_response *r, const char *res);
|
||||||
|
|
||||||
#endif /* PAGE_H */
|
#endif /* PAGE_H */
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
#include "thumbnail.h"
|
||||||
|
#include <magick/api.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* http://www.graphicsmagick.org/api/api.html */
|
||||||
|
|
||||||
|
int thumbnail_create(const char *const src, const char *const dst)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
const size_t slen = strlen(src), dlen = strlen(dst);
|
||||||
|
ImageInfo *info = NULL;
|
||||||
|
Image *i = NULL, *t = NULL;
|
||||||
|
ExceptionInfo exc;
|
||||||
|
|
||||||
|
InitializeMagick(NULL);
|
||||||
|
GetExceptionInfo(&exc);
|
||||||
|
|
||||||
|
if (slen >= sizeof i->filename)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: src maximum length exceeded (%zu, max %zu)\n",
|
||||||
|
__func__, slen, sizeof i->filename - 1);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (dlen >= sizeof t->filename)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dst maximum length exceeded (%zu, max %zu)\n",
|
||||||
|
__func__, slen, sizeof t->filename - 1);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (!(info = CloneImageInfo(NULL)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: CloneImageInfo failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(info->filename, src);
|
||||||
|
info->adjoin = MagickTrue;
|
||||||
|
|
||||||
|
if (!(i = ReadImage(info, &exc)))
|
||||||
|
{
|
||||||
|
if (exc.severity == MissingDelegateError)
|
||||||
|
/* Non-image file format. */
|
||||||
|
ret = 1;
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: ReadImage failed: "
|
||||||
|
"reason: %s, description: %s\n",
|
||||||
|
__func__, exc.reason, exc.description);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned long y = i->rows > THUMBNAIL_HEIGHT ?
|
||||||
|
THUMBNAIL_HEIGHT : i->rows,
|
||||||
|
x = y * i->columns / i->rows;
|
||||||
|
|
||||||
|
if (!(t = ResizeImage(i, x, y, PointFilter, 1, &exc)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: ResizeImage failed, "
|
||||||
|
"reason: %s, description: %s\n",
|
||||||
|
__func__, exc.reason, exc.description);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (WriteImages(info, t, dst, &exc) != MagickPass)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: WriteImages failed, "
|
||||||
|
"reason: %s, description: %s\n",
|
||||||
|
__func__, exc.reason, exc.description);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
DestroyImageInfo(info);
|
||||||
|
DestroyImage(i);
|
||||||
|
DestroyImage(t);
|
||||||
|
DestroyExceptionInfo(&exc);
|
||||||
|
DestroyMagick();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thumbnail_configured(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int thumbnail_dim(const char *const path, struct thumbnail_dim *const d)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
const size_t slen = strlen(path);
|
||||||
|
ImageInfo *info = NULL;
|
||||||
|
Image *i = NULL;
|
||||||
|
ExceptionInfo exc;
|
||||||
|
|
||||||
|
InitializeMagick(NULL);
|
||||||
|
GetExceptionInfo(&exc);
|
||||||
|
|
||||||
|
if (slen >= sizeof i->filename)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: src maximum length exceeded (%zu, max %zu)\n",
|
||||||
|
__func__, slen, sizeof i->filename - 1);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
else if (!(info = CloneImageInfo(NULL)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: CloneImageInfo failed\n", __func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(info->filename, path);
|
||||||
|
info->adjoin = MagickTrue;
|
||||||
|
|
||||||
|
if (!(i = ReadImage(info, &exc)))
|
||||||
|
{
|
||||||
|
if (exc.severity == MissingDelegateError)
|
||||||
|
/* Non-image file format. */
|
||||||
|
ret = 1;
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: ReadImage failed: "
|
||||||
|
"reason: %s, description: %s\n",
|
||||||
|
__func__, exc.reason, exc.description);
|
||||||
|
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
*d = (const struct thumbnail_dim)
|
||||||
|
{
|
||||||
|
.w = i->columns,
|
||||||
|
.h = i->rows
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
DestroyImageInfo(info);
|
||||||
|
DestroyImage(i);
|
||||||
|
DestroyExceptionInfo(&exc);
|
||||||
|
DestroyMagick();
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef THUMBNAIL_H
|
||||||
|
#define THUMBNAIL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct thumbnail_dim
|
||||||
|
{
|
||||||
|
unsigned long w, h;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool thumbnail_configured(void);
|
||||||
|
int thumbnail_create(const char *src, const char *dst);
|
||||||
|
int thumbnail_dim(const char *path, struct thumbnail_dim *d);
|
||||||
|
|
||||||
|
#endif /* THUMBNAIL_H */
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "thumbnail.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
int thumbnail_create(const char *const src, const char *const dst)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thumbnail_configured(void)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int thumbnail_dim(const char *const path, struct thumbnail_dim *const d)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
Loading…
Reference in New Issue