Compare commits
2 Commits
master
...
imagemagic
Author | SHA1 | Date |
---|---|---|
Xavier Del Campo Romero | fa97b1904c | |
Xavier Del Campo Romero | 30d31e8c9d |
|
@ -2,3 +2,4 @@ build/
|
|||
slcl
|
||||
*.o
|
||||
*.d
|
||||
/Makefile
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[submodule "dynstr"]
|
||||
path = dynstr
|
||||
url = https://gitea.privatedns.org/xavi92/dynstr
|
||||
[submodule "mkdir_r"]
|
||||
path = mkdir_r
|
||||
url = https://gitea.privatedns.org/xavi92/mkdir_r
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
project(slcl)
|
||||
option(THUMBNAILS "Enables thumbnail generation for images." OFF)
|
||||
add_executable(${PROJECT_NAME}
|
||||
auth.c
|
||||
base64.c
|
||||
|
@ -13,9 +14,26 @@ add_executable(${PROJECT_NAME}
|
|||
page.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_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
|
||||
add_subdirectory(dynstr)
|
||||
add_subdirectory(mkdir_r)
|
||||
find_package(cJSON 1.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.
|
||||
- A simple JSON file as the credentials database.
|
||||
- No JavaScript.
|
||||
- Optional, resizable thumbnails (made available by
|
||||
[GraphicsMagick](http://www.graphicsmagick.org/)).
|
||||
|
||||
### TLS
|
||||
|
||||
|
@ -42,9 +44,12 @@ to `slcl`. If required, encryption should be done before uploading e.g.: using
|
|||
- cJSON >= 1.7.15.
|
||||
- [`dynstr`](https://gitea.privatedns.org/xavi92/dynstr)
|
||||
(provided as a `git` submodule).
|
||||
- [`mkdir_r`](https://gitea.privatedns.org/xavi92/mkdir_r)
|
||||
(provided as a `git` submodule).
|
||||
- `xxd` (for [`usergen`](usergen) only).
|
||||
- `jq` (for [`usergen`](usergen) only).
|
||||
- CMake (optional).
|
||||
- [GraphicsMagick](http://www.graphicsmagick.org/) >= 1.4 (optional).
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
|
@ -57,7 +62,7 @@ sudo apt install build-essential libcjson-dev libssl-dev
|
|||
#### Optional packages
|
||||
|
||||
```sh
|
||||
sudo apt install cmake xxd jq
|
||||
sudo apt install cmake xxd jq libgraphicsmagick1-dev
|
||||
```
|
||||
|
||||
## 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
|
||||
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).
|
||||
|
||||
`slcl` can be built using the standard build process:
|
||||
|
@ -74,6 +80,7 @@ them:
|
|||
#### Make
|
||||
|
||||
```sh
|
||||
$ ./configure
|
||||
$ 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 "hex.h"
|
||||
#include "http.h"
|
||||
#include "mkdir_r.h"
|
||||
#include "page.h"
|
||||
#include "thumbnail.h"
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.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,
|
||||
struct http_response *const r, void *const user)
|
||||
{
|
||||
int ret = -1;
|
||||
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))
|
||||
{
|
||||
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
||||
return page_forbidden(r);
|
||||
ret = page_forbidden(r);
|
||||
goto end;
|
||||
}
|
||||
|
||||
const char *const username = p->cookie.field,
|
||||
*const resource = p->resource + strlen("/user/");
|
||||
|
||||
if (path_isrel(resource))
|
||||
else if (path_isrel(p->resource))
|
||||
{
|
||||
fprintf(stderr, "%s: illegal relative path %s\n", __func__, resource);
|
||||
return page_forbidden(r);
|
||||
fprintf(stderr, "%s: illegal relative path %s\n",
|
||||
__func__, p->resource);
|
||||
ret = page_forbidden(r);
|
||||
goto end;
|
||||
}
|
||||
|
||||
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)
|
||||
else if (!(adir = auth_dir(a)))
|
||||
{
|
||||
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (dynstr_append(&dir, "%s%s", p->resource, sep))
|
||||
{
|
||||
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))
|
||||
else if (dynstr_append(&d, "%s/user/%s/", adir, username))
|
||||
{
|
||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
|
||||
const struct page_resource res =
|
||||
{
|
||||
.root = adir,
|
||||
.username = username,
|
||||
.user_root = d.str,
|
||||
.res = p->resource + strlen("/user")
|
||||
};
|
||||
|
||||
bool available;
|
||||
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__);
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -707,12 +705,10 @@ static int getnode(const struct http_payload *const p,
|
|||
.max = max
|
||||
} : NULL;
|
||||
|
||||
ret = page_resource(r, dir.str, root.str, d.str, ppq);
|
||||
ret = page_resource(r, &res, ppq);
|
||||
|
||||
end:
|
||||
dynstr_free(&dir);
|
||||
dynstr_free(&d);
|
||||
dynstr_free(&root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -797,25 +793,84 @@ static int rename_or_move(const char *const old, const char *const new)
|
|||
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,
|
||||
const char *const user, const char *const root, const char *const dir)
|
||||
{
|
||||
int ret = -1;
|
||||
struct dynstr d;
|
||||
struct dynstr d, dird, th;
|
||||
|
||||
dynstr_init(&d);
|
||||
dynstr_init(&dird);
|
||||
dynstr_init(&th);
|
||||
|
||||
if (!root)
|
||||
{
|
||||
fprintf(stderr, "%s: auth_dir failed\n", __func__);
|
||||
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__);
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -825,6 +880,8 @@ static int upload_file(const struct http_post_file *const f,
|
|||
|
||||
end:
|
||||
dynstr_free(&d);
|
||||
dynstr_free(&dird);
|
||||
dynstr_free(&th);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1129,47 +1186,74 @@ static int parse_args(const int argc, char *const argv[],
|
|||
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)
|
||||
{
|
||||
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;
|
||||
fprintf(stderr, "%s: auth_cookie failed\n", __func__);
|
||||
ret = page_forbidden(r);
|
||||
goto end;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int ret = -1;
|
||||
struct dynstr user, public;
|
||||
struct dynstr user, public, thumbnails;
|
||||
struct sb;
|
||||
|
||||
dynstr_init(&user);
|
||||
dynstr_init(&public);
|
||||
dynstr_init(&thumbnails);
|
||||
|
||||
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__);
|
||||
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;
|
||||
|
||||
end:
|
||||
dynstr_free(&user);
|
||||
dynstr_free(&public);
|
||||
dynstr_free(&thumbnails);
|
||||
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, "/upload", HTTP_OP_POST, upload, 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))
|
||||
goto end;
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 822ff99e6f6a5c8ad7309a535d384d354b2d326e
|
254
page.c
254
page.c
|
@ -3,6 +3,7 @@
|
|||
#include "page.h"
|
||||
#include "http.h"
|
||||
#include "html.h"
|
||||
#include "thumbnail.h"
|
||||
#include <dynstr.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -45,7 +46,102 @@
|
|||
" </form>\n"
|
||||
#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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
int ret = -1;
|
||||
const char *const fdir = dir + strlen("/user");
|
||||
const char *const sep = dir_separator(dir);
|
||||
struct html_node *form, *file, *submit;
|
||||
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__);
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -243,18 +339,37 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int add_element(struct html_node *const n, const char *const dir,
|
||||
const char *const res, const char *const name)
|
||||
static int user_resource(const struct page_resource *const r,
|
||||
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;
|
||||
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 dynstr path;
|
||||
const char *const sep = res[strlen(res) - 1] != '/' ? "/" : "";
|
||||
struct dynstr path, rel;
|
||||
|
||||
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__);
|
||||
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++)
|
||||
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",
|
||||
__func__, i);
|
||||
|
@ -281,7 +398,13 @@ static int add_element(struct html_node *const n, const char *const dir,
|
|||
__func__, path.str, strerror(errno));
|
||||
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__);
|
||||
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__);
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -306,6 +429,7 @@ static int add_element(struct html_node *const n, const char *const dir,
|
|||
|
||||
end:
|
||||
dynstr_free(&path);
|
||||
dynstr_free(&rel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -743,7 +867,6 @@ end:
|
|||
static struct html_node *resource_layout(const char *const dir,
|
||||
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"),
|
||||
*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__);
|
||||
goto end;
|
||||
}
|
||||
else if (common_head(head, fdir))
|
||||
else if (common_head(head, dir))
|
||||
{
|
||||
fprintf(stderr, "%s: common_head failed\n", __func__);
|
||||
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__);
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -808,14 +931,21 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int add_elements(const char *const root, const char *const res,
|
||||
const char *const dir, struct html_node *const table)
|
||||
static int add_elements(const struct page_resource *const r,
|
||||
struct html_node *const table)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret = -1, n = 0;
|
||||
struct dynstr d;
|
||||
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));
|
||||
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;
|
||||
|
||||
if (!strcmp(name, ".")
|
||||
|| (!strcmp(name, "..") && !strcmp(root, res)))
|
||||
|| (!strcmp(name, "..") && !strcmp(r->user_root, d.str)))
|
||||
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__);
|
||||
goto end;
|
||||
|
@ -844,25 +974,25 @@ end:
|
|||
free(pde[i]);
|
||||
|
||||
free(pde);
|
||||
dynstr_free(&d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int list_dir(struct http_response *const r, const char *const dir,
|
||||
const char *const root, const char *const res,
|
||||
const struct page_quota *const q)
|
||||
static int list_dir(struct http_response *const r,
|
||||
const struct page_resource *const res, const struct page_quota *const q)
|
||||
{
|
||||
int ret = -1;
|
||||
struct dynstr out;
|
||||
struct html_node *table, *const html = resource_layout(dir, q, &table);
|
||||
struct html_node *table, *html = NULL;
|
||||
|
||||
dynstr_init(&out);
|
||||
|
||||
if (!html)
|
||||
if (!(html = resource_layout(res->res, q, &table)))
|
||||
{
|
||||
fprintf(stderr, "%s: resource_layout failed\n", __func__);
|
||||
goto end;
|
||||
}
|
||||
else if (add_elements(root, res, dir, table))
|
||||
else if (add_elements(res, table))
|
||||
{
|
||||
fprintf(stderr, "%s: read_elements failed\n", __func__);
|
||||
goto end;
|
||||
|
@ -907,14 +1037,14 @@ static int serve_file(struct http_response *const r,
|
|||
const struct stat *const sb, const char *const res)
|
||||
{
|
||||
int ret = -1;
|
||||
FILE *const f = fopen(res, "rb");
|
||||
FILE *f = NULL;
|
||||
struct dynstr b, d;
|
||||
char *bn;
|
||||
|
||||
dynstr_init(&b);
|
||||
dynstr_init(&d);
|
||||
|
||||
if (!f)
|
||||
if (!(f = fopen(res, "rb")))
|
||||
{
|
||||
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
|
||||
goto end;
|
||||
|
@ -987,32 +1117,43 @@ static int page_not_found(struct http_response *const r)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int page_resource(struct http_response *const r, const char *const dir,
|
||||
const char *const root, const char *const res,
|
||||
const struct page_quota *const q)
|
||||
int page_resource(struct http_response *const r,
|
||||
const struct page_resource *const res, const struct page_quota *const q)
|
||||
{
|
||||
int ret = -1;
|
||||
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",
|
||||
__func__, res, strerror(errno));
|
||||
__func__, res->res, strerror(errno));
|
||||
|
||||
if (errno == ENOENT)
|
||||
return page_not_found(r);
|
||||
else
|
||||
return -1;
|
||||
ret = page_not_found(r);
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
const mode_t m = sb.st_mode;
|
||||
|
||||
if (S_ISDIR(m))
|
||||
return list_dir(r, dir, root, res, q);
|
||||
ret = list_dir(r, res, q);
|
||||
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);
|
||||
return -1;
|
||||
end:
|
||||
dynstr_free(&d);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *resolve_link(const char *const res)
|
||||
|
@ -1403,3 +1544,30 @@ end:
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
struct page_resource
|
||||
{
|
||||
const char *root, *user_root, *username, *res;
|
||||
};
|
||||
|
||||
int page_login(struct http_response *r);
|
||||
int page_style(struct http_response *r);
|
||||
int page_failed_login(struct http_response *r);
|
||||
int page_forbidden(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,
|
||||
const char *res, const struct page_quota *q);
|
||||
int page_resource(struct http_response *r, const struct page_resource *res,
|
||||
const struct page_quota *q);
|
||||
int page_public(struct http_response *r, const char *res);
|
||||
int page_share(struct http_response *r, const char *path);
|
||||
int page_quota_exceeded(struct http_response *r, unsigned long long len,
|
||||
unsigned long long quota);
|
||||
int page_thumbnail(struct http_response *r, const char *res);
|
||||
|
||||
#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