Compare commits
2 Commits
master
...
imagemagic
Author | SHA1 | Date |
---|---|---|
Xavier Del Campo Romero | fa97b1904c | |
Xavier Del Campo Romero | 30d31e8c9d |
|
@ -2,4 +2,4 @@ build/
|
||||||
slcl
|
slcl
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
./Makefile
|
/Makefile
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
[submodule "libweb"]
|
[submodule "dynstr"]
|
||||||
path = libweb
|
path = dynstr
|
||||||
url = https://gitea.privatedns.org/xavi/libweb
|
url = https://gitea.privatedns.org/xavi92/dynstr
|
||||||
|
[submodule "mkdir_r"]
|
||||||
|
path = mkdir_r
|
||||||
|
url = https://gitea.privatedns.org/xavi92/mkdir_r
|
||||||
|
|
|
@ -1,36 +1,39 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(slcl LANGUAGES C VERSION 0.2.0)
|
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
|
||||||
cftw.c
|
cftw.c
|
||||||
|
handler.c
|
||||||
hex.c
|
hex.c
|
||||||
|
html.c
|
||||||
|
http.c
|
||||||
jwt.c
|
jwt.c
|
||||||
main.c
|
main.c
|
||||||
page.c
|
page.c
|
||||||
style.c
|
server.c
|
||||||
)
|
)
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE _FILE_OFFSET_BITS=64)
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR}/cmake)
|
|
||||||
find_package(web 0.3.0)
|
|
||||||
|
|
||||||
if(WEB_FOUND)
|
if(NOT DEFINED THUMBNAIL_HEIGHT)
|
||||||
find_package(dynstr 0.1.0)
|
set(THUMBNAIL_HEIGHT 96)
|
||||||
else()
|
|
||||||
message(STATUS "Using in-tree libweb")
|
|
||||||
add_subdirectory(libweb)
|
|
||||||
#dynstr is already provided by libweb.
|
|
||||||
endif()
|
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(cJSON 1.0 REQUIRED)
|
||||||
find_package(OpenSSL 2.0 REQUIRED)
|
find_package(OpenSSL 3.0 REQUIRED)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE web dynstr cjson OpenSSL::SSL)
|
target_link_libraries(${PROJECT_NAME}
|
||||||
install(TARGETS ${PROJECT_NAME})
|
PRIVATE dynstr mkdir_r cjson OpenSSL::SSL ImageMagick)
|
||||||
install(FILES usergen
|
#message(FATAL_ERROR "TODO")
|
||||||
TYPE BIN
|
|
||||||
PERMISSIONS
|
|
||||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
|
||||||
GROUP_READ GROUP_EXECUTE
|
|
||||||
WORLD_READ WORLD_EXECUTE)
|
|
||||||
add_subdirectory(doc)
|
|
||||||
|
|
67
README.md
67
README.md
|
@ -1,28 +1,23 @@
|
||||||
# slcl, a simple and lightweight cloud
|
# slcl, a suckless cloud
|
||||||
|
|
||||||
`slcl` is a simple and lightweight implementation of a web file server,
|
`slcl` is a simple and fast implementation of a web file server, commonly
|
||||||
commonly known as "cloud storage" or simply "cloud", written in C99 plus
|
known as "cloud storage" or simply "cloud", written in C99.
|
||||||
POSIX.1-2008 extensions.
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
|
|
||||||
![Screenshot of slcl's login page](doc/login.png)
|
|
||||||
|
|
||||||
![Screenshot of an example user directory](doc/user.png)
|
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
Intentionally, `slcl` does not share some of the philosophical views from the
|
While `slcl` might not share some of the philosophical views from the
|
||||||
[suckless project](https://suckless.org). However, it still strives towards
|
[suckless project](https://suckless.org), it still strives towards minimalism,
|
||||||
portability, minimalism, simplicity and efficiency.
|
simplicity and efficiency.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Private access directory with file uploading, with configurable quota.
|
- Private access directory with file uploading, with configurable quota.
|
||||||
- Read-only public file sharing.
|
- Read-only public file sharing.
|
||||||
- Uses [`libweb`](https://gitea.privatedns.org/xavi/libweb), a tiny web framework.
|
- 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
|
||||||
|
|
||||||
|
@ -45,27 +40,29 @@ to `slcl`. If required, encryption should be done before uploading e.g.: using
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- A POSIX environment.
|
- A POSIX environment.
|
||||||
- OpenSSL >= 2.0.
|
- OpenSSL >= 3.0.
|
||||||
- cJSON >= 1.7.15.
|
- cJSON >= 1.7.15.
|
||||||
- [`dynstr`](https://gitea.privatedns.org/xavi/dynstr)
|
- [`dynstr`](https://gitea.privatedns.org/xavi92/dynstr)
|
||||||
(provided as a `git` submodule by `libweb`).
|
|
||||||
- [`libweb`](https://gitea.privatedns.org/xavi/libweb)
|
|
||||||
(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).
|
||||||
- `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
|
||||||
|
|
||||||
#### Mandatory packages
|
#### Mandatory packages
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install build-essential libcjson-dev libssl-dev m4 jq
|
sudo apt install build-essential libcjson-dev libssl-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Optional packages
|
#### Optional packages
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install cmake
|
sudo apt install cmake xxd jq libgraphicsmagick1-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
@ -74,8 +71,8 @@ sudo apt install cmake
|
||||||
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 [`configure`](configure) POSIX shell and mostly POSIX-compliant
|
- A [`configure`](configure) POSIX shell script that generates a mostly
|
||||||
[`Makefile`](Makefile).
|
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:
|
||||||
|
@ -90,8 +87,9 @@ $ make
|
||||||
#### CMake
|
#### CMake
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ cmake -B build
|
$ mkdir build/
|
||||||
$ cmake --build build/
|
$ cmake ..
|
||||||
|
$ cmake --build .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setting up
|
### Setting up
|
||||||
|
@ -179,25 +177,6 @@ command line option. For example:
|
||||||
slcl -p 7822 ~/my-db/
|
slcl -p 7822 ~/my-db/
|
||||||
```
|
```
|
||||||
|
|
||||||
`slcl` requires a temporary directory where files uploaded by users are
|
|
||||||
temporarily stored until moved to the user directory. By default, `slcl`
|
|
||||||
attempts to retrieve the path to the temporary directory by inspecting the
|
|
||||||
`TMPDIR` environment variable, and falls back to `/tmp` if undefined.
|
|
||||||
|
|
||||||
If a custom temporary directory is required, it can be defined via command
|
|
||||||
line option `-t`. For example:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
slcl -t ~/my-tmp -p 7822 ~/my-db
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note:** system-level temporary directories such as `/tmp` might reside
|
|
||||||
on a filesytem different than the one where the database resides. This
|
|
||||||
would force `slcl` to copy the contents from uploaded files from the
|
|
||||||
temporary directory to the database, which might be an expensive operation.
|
|
||||||
Therefore, in order to avoid expensive copies, define a custom temporary
|
|
||||||
directory that resides on the same filesystem.
|
|
||||||
|
|
||||||
## Why this project?
|
## Why this project?
|
||||||
|
|
||||||
Previously, I had been recommended Nextcloud as an alternative to proprietary
|
Previously, I had been recommended Nextcloud as an alternative to proprietary
|
||||||
|
@ -228,7 +207,7 @@ be even available e.g.: phones.
|
||||||
## License
|
## License
|
||||||
|
|
||||||
```
|
```
|
||||||
slcl, a simple and lightweight cloud.
|
slcl, a suckless cloud.
|
||||||
Copyright (C) 2023 Xavier Del Campo Romero
|
Copyright (C) 2023 Xavier Del Campo Romero
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
|
9
auth.c
9
auth.c
|
@ -1,7 +1,7 @@
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
#include "hex.h"
|
#include "hex.h"
|
||||||
|
#include "http.h"
|
||||||
#include "jwt.h"
|
#include "jwt.h"
|
||||||
#include <libweb/http.h>
|
|
||||||
#include <cjson/cJSON.h>
|
#include <cjson/cJSON.h>
|
||||||
#include <dynstr.h>
|
#include <dynstr.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
|
@ -9,7 +9,6 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -35,7 +34,7 @@ static char *dump_db(const char *const path)
|
||||||
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
|
fprintf(stderr, "%s: stat(2): %s\n", __func__, strerror(errno));
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
else if (sb.st_size > SIZE_MAX - 1)
|
else if (sb.st_size > SIZE_MAX)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: %s too big (%llu bytes, %zu max)\n",
|
fprintf(stderr, "%s: %s too big (%llu bytes, %zu max)\n",
|
||||||
__func__, path, (unsigned long long)sb.st_size, (size_t)SIZE_MAX);
|
__func__, path, (unsigned long long)sb.st_size, (size_t)SIZE_MAX);
|
||||||
|
@ -422,7 +421,7 @@ int auth_quota(const struct auth *const a, const char *const user,
|
||||||
*available = true;
|
*available = true;
|
||||||
*quota = strtoull(qs, &end, 10);
|
*quota = strtoull(qs, &end, 10);
|
||||||
|
|
||||||
const unsigned long long mul = 1024ul * 1024ul;
|
const unsigned long long mul = 1024 * 1024;
|
||||||
|
|
||||||
if (errno || *end != '\0')
|
if (errno || *end != '\0')
|
||||||
{
|
{
|
||||||
|
@ -436,7 +435,7 @@ int auth_quota(const struct auth *const a, const char *const user,
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
*quota *= mul;
|
*quota *= 1024 * 1024;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
auth.h
2
auth.h
|
@ -1,7 +1,7 @@
|
||||||
#ifndef AUTH_H
|
#ifndef AUTH_H
|
||||||
#define AUTH_H
|
#define AUTH_H
|
||||||
|
|
||||||
#include <libweb/http.h>
|
#include "http.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
struct auth *auth_alloc(const char *dir);
|
struct auth *auth_alloc(const char *dir);
|
||||||
|
|
42
cftw.c
42
cftw.c
|
@ -1,5 +1,3 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
|
|
||||||
#include "cftw.h"
|
#include "cftw.h"
|
||||||
#include <dynstr.h>
|
#include <dynstr.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
@ -9,11 +7,12 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static int do_cftw(const char *const dirpath, int (*const fn)(const char *,
|
int cftw(const char *const dirpath, int (*const fn)(const char *,
|
||||||
const struct stat *, bool *, void *), bool *const done, void *const user)
|
const struct stat *, void *), void *const user)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
DIR *const d = opendir(dirpath);
|
DIR *const d = opendir(dirpath);
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
if (!d)
|
if (!d)
|
||||||
{
|
{
|
||||||
|
@ -21,31 +20,19 @@ static int do_cftw(const char *const dirpath, int (*const fn)(const char *,
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (;;)
|
while ((de = readdir(d)))
|
||||||
{
|
{
|
||||||
errno = 0;
|
|
||||||
struct dirent *const de = readdir(d);
|
|
||||||
|
|
||||||
if (errno)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "%s: readdir(3): %s\n", __func__, strerror(errno));
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
else if (!de)
|
|
||||||
break;
|
|
||||||
|
|
||||||
const char *const path = de->d_name;
|
const char *const path = de->d_name;
|
||||||
|
|
||||||
if (!strcmp(path, ".") || !strcmp(path, ".."))
|
if (!strcmp(path, ".") || !strcmp(path, ".."))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const char *const sep = dirpath[strlen(dirpath) - 1] == '/' ? "" : "/";
|
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
struct dynstr d;
|
struct dynstr d;
|
||||||
|
|
||||||
dynstr_init(&d);
|
dynstr_init(&d);
|
||||||
|
|
||||||
if (dynstr_append(&d, "%s%s%s", dirpath, sep, path))
|
if (dynstr_append(&d, "%s/%s", dirpath, path))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -57,21 +44,16 @@ static int do_cftw(const char *const dirpath, int (*const fn)(const char *,
|
||||||
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
fprintf(stderr, "%s: stat(2) %s: %s\n",
|
||||||
__func__, path, strerror(errno));
|
__func__, path, strerror(errno));
|
||||||
else if (S_ISDIR(sb.st_mode))
|
else if (S_ISDIR(sb.st_mode))
|
||||||
{
|
ret = cftw(d.str, fn, user);
|
||||||
if ((ret = do_cftw(d.str, fn, done, user)))
|
|
||||||
;
|
|
||||||
else if ((ret = fn(d.str, &sb, done, user)))
|
|
||||||
;
|
|
||||||
}
|
|
||||||
else if (S_ISREG(sb.st_mode))
|
else if (S_ISREG(sb.st_mode))
|
||||||
ret = fn(d.str, &sb, done, user);
|
ret = fn(d.str, &sb, user);
|
||||||
else
|
else
|
||||||
fprintf(stderr, "%s: unexpected st_mode %ju\n",
|
fprintf(stderr, "%s: unexpected st_mode %ju\n",
|
||||||
__func__, (uintmax_t)sb.st_mode);
|
__func__, (uintmax_t)sb.st_mode);
|
||||||
|
|
||||||
dynstr_free(&d);
|
dynstr_free(&d);
|
||||||
|
|
||||||
if (ret || *done)
|
if (ret)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +69,3 @@ end:
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cftw(const char *const dirpath, int (*const fn)(const char *,
|
|
||||||
const struct stat *, bool *, void *), void *const user)
|
|
||||||
{
|
|
||||||
bool done = false;
|
|
||||||
|
|
||||||
return do_cftw(dirpath, fn, &done, user);
|
|
||||||
}
|
|
||||||
|
|
3
cftw.h
3
cftw.h
|
@ -1,12 +1,11 @@
|
||||||
#ifndef CFTW_H
|
#ifndef CFTW_H
|
||||||
#define CFTW_H
|
#define CFTW_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
/* Thread-safe variant of ftw(3) and nftw(3) that allows passing an
|
/* Thread-safe variant of ftw(3) and nftw(3) that allows passing an
|
||||||
* opaque pointer and removes some unneeded parameters. */
|
* opaque pointer and removes some unneeded parameters. */
|
||||||
int cftw(const char *dirpath, int (*fn)(const char *fpath,
|
int cftw(const char *dirpath, int (*fn)(const char *fpath,
|
||||||
const struct stat *sb, bool *done, void *user), void *user);
|
const struct stat *sb, void *user), void *user);
|
||||||
|
|
||||||
#endif /* CFTW_H */
|
#endif /* CFTW_H */
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
mark_as_advanced(WEB_LIBRARY WEB_INCLUDE_DIR)
|
|
||||||
find_library(WEB_LIBRARY NAMES libweb web)
|
|
||||||
|
|
||||||
find_path(WEB_INCLUDE_DIR
|
|
||||||
NAMES
|
|
||||||
handler.h
|
|
||||||
html.h
|
|
||||||
http.h
|
|
||||||
server.h
|
|
||||||
wildcard_cmp.h
|
|
||||||
PATH_SUFFIXES libweb include/libweb)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(web
|
|
||||||
DEFAULT_MSG WEB_LIBRARY WEB_INCLUDE_DIR)
|
|
||||||
|
|
||||||
if(WEB_FOUND)
|
|
||||||
if(NOT TARGET web)
|
|
||||||
add_library(web UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(web PROPERTIES
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${WEB_INCLUDE_DIR}"
|
|
||||||
IMPORTED_LOCATION "${WEB_LIBRARY}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
|
@ -2,167 +2,130 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
default_prefix=/usr/local
|
DEFAULT_PREFIX=/usr/local
|
||||||
prefix=$default_prefix
|
DEFAULT_THUMBNAIL_HEIGHT=96
|
||||||
default_CC='c99'
|
|
||||||
# FILE_OFFSET_BITS=64 is required for large file support on 32-bit platforms.
|
|
||||||
default_CFLAGS='-O1 -g -D_FILE_OFFSET_BITS=64 -Wall -MD'
|
|
||||||
default_LDFLAGS="-lcjson -lssl -lm -lcrypto"
|
|
||||||
|
|
||||||
CC=${CC:-$default_CC}
|
usage()
|
||||||
CFLAGS=${CFLAGS:-"$default_CFLAGS $default_NPCFLAGS"}
|
|
||||||
LDFLAGS=${LDFLAGS:-$default_LDFLAGS}
|
|
||||||
|
|
||||||
help()
|
|
||||||
{
|
{
|
||||||
cat <<-EOF
|
printf "$0 [OPTION]...\n\n"
|
||||||
$0 [OPTION ...]
|
printf "Configuration:\n"
|
||||||
|
printf "%s\t\t\t" "--prefix=PREFIX"
|
||||||
--prefix Set installation directory [$default_prefix]
|
printf "Sets installation prefix (default: $DEFAULT_PREFIX).\n"
|
||||||
|
printf "%s\t\t\t" "--thumbnails"
|
||||||
Some influential environment variables:
|
printf "Enables thumbnail generation for images.\n"
|
||||||
CC C compiler [$default_CC]
|
printf "%s\t" "--thumbnail-height=HEIGHT"
|
||||||
CFLAGS C compiler flags [$default_CFLAGS]
|
printf "Sets thumbnail height, in pixels "
|
||||||
LDFLAGS Link-time flags [$default_LDFLAGS]
|
printf "(default: $DEFAULT_THUMBNAIL_HEIGHT). "
|
||||||
EOF
|
printf "Requires --thumbnails.\n"
|
||||||
|
printf "%s\t\t\t" "-h|--help"
|
||||||
|
printf "Prints this help page.\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
while true; do
|
CC=${CC:-cc}
|
||||||
split_arg=0
|
CFLAGS=${CFLAGS:-}
|
||||||
|
|
||||||
if printf "%s" "$1" | grep -e '=' > /dev/null
|
for arg
|
||||||
then
|
do
|
||||||
key="$(printf "%s" "$1" | cut -d '=' -f1)"
|
case "$arg" in
|
||||||
value="$(printf "%s" "$1" | cut -d '=' -f2)"
|
--thumbnails)
|
||||||
split_arg=1
|
THUMBNAILS=1
|
||||||
else
|
;;
|
||||||
key="$1"
|
--thumbnail-height=*)
|
||||||
value="$2"
|
THUMBNAIL_HEIGHT=${arg#*=}
|
||||||
fi
|
;;
|
||||||
|
--prefix=*)
|
||||||
case "$key" in
|
PREFIX=${arg#*=}
|
||||||
--prefix ) prefix="$value"; shift; test $split_arg -eq 0 && shift ;;
|
;;
|
||||||
-h | --help ) help; exit 0 ;;
|
-h | --help)
|
||||||
* ) test "$1" != "" && help && exit 1 || break ;;
|
usage
|
||||||
esac
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Invalid argument $arg" >&2
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if pkg-config dynstr
|
THUMBNAILS=${THUMBNAILS:-0}
|
||||||
then
|
THUMBNAIL_HEIGHT=${THUMBNAIL_HEIGHT:-$DEFAULT_THUMBNAIL_HEIGHT}
|
||||||
in_tree_dynstr=0
|
PREFIX=${PREFIX:-$DEFAULT_PREFIX}
|
||||||
CFLAGS="$CFLAGS $(pkg-config --cflags dynstr)"
|
|
||||||
LDFLAGS="$LDFLAGS $(pkg-config --libs dynstr)"
|
|
||||||
else
|
|
||||||
echo "Info: dynstr not found. Using in-tree copy" >&2
|
|
||||||
in_tree_dynstr=1
|
|
||||||
CFLAGS="$CFLAGS -Ilibweb/dynstr/include"
|
|
||||||
LDFLAGS="$LDFLAGS -Llibweb/dynstr -ldynstr"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if pkg-config libweb
|
OBJECTS="\
|
||||||
then
|
|
||||||
in_tree_libweb=0
|
|
||||||
CFLAGS="$CFLAGS $(pkg-config --cflags libweb)"
|
|
||||||
LDFLAGS="$LDFLAGS $(pkg-config --libs libweb)"
|
|
||||||
else
|
|
||||||
echo "Info: libweb not found. Using in-tree copy" >&2
|
|
||||||
in_tree_libweb=1
|
|
||||||
CFLAGS="$CFLAGS -Ilibweb/include"
|
|
||||||
LDFLAGS="$LDFLAGS -Llibweb -lweb"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
{
|
|
||||||
rm -f $F
|
|
||||||
}
|
|
||||||
|
|
||||||
F=/tmp/Makefile.slcl
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
cat <<EOF > $F
|
|
||||||
.POSIX:
|
|
||||||
|
|
||||||
CC = $CC
|
|
||||||
PREFIX = $prefix
|
|
||||||
DST = $prefix/bin
|
|
||||||
CFLAGS = $CFLAGS
|
|
||||||
LDFLAGS = $LDFLAGS
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat <<"EOF" >> $F
|
|
||||||
PROJECT = slcl
|
|
||||||
DEPS = $(OBJECTS:.o=.d)
|
|
||||||
OBJECTS = \
|
|
||||||
auth.o \
|
auth.o \
|
||||||
base64.o \
|
base64.o \
|
||||||
cftw.o \
|
cftw.o \
|
||||||
|
handler.o \
|
||||||
hex.o \
|
hex.o \
|
||||||
|
html.o \
|
||||||
|
http.o \
|
||||||
jwt.o \
|
jwt.o \
|
||||||
main.o \
|
main.o \
|
||||||
page.o \
|
page.o \
|
||||||
style.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)
|
all: $(PROJECT)
|
||||||
|
|
||||||
install: all usergen
|
|
||||||
mkdir -p $(DST)
|
|
||||||
cp slcl usergen $(DST)
|
|
||||||
chmod 0755 $(DST)/slcl
|
|
||||||
chmod 0755 $(DST)/usergen
|
|
||||||
+cd doc && $(MAKE) PREFIX=$(PREFIX) install
|
|
||||||
|
|
||||||
FORCE:
|
|
||||||
|
|
||||||
$(PROJECT): $(OBJECTS)
|
|
||||||
$(CC) $(OBJECTS) $(LDFLAGS) -o $@
|
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ $in_tree_dynstr -ne 0 ]
|
|
||||||
then
|
|
||||||
cat <<"EOF" >> $F
|
|
||||||
DYNSTR = libweb/dynstr/libdynstr.a
|
|
||||||
$(PROJECT): $(DYNSTR)
|
|
||||||
$(DYNSTR): FORCE
|
|
||||||
+cd libweb/dynstr && $(MAKE) CC=$(CC)
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $in_tree_libweb -ne 0 ]
|
|
||||||
then
|
|
||||||
cat <<"EOF" >> $F
|
|
||||||
LIBWEB = libweb/libweb.a
|
|
||||||
$(PROJECT): $(LIBWEB)
|
|
||||||
$(LIBWEB): FORCE
|
|
||||||
+cd libweb && $(MAKE) CC=$(CC)
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<"EOF" >> $F
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(OBJECTS) $(DEPS)
|
rm -f $(OBJECTS) $(DEPS)
|
||||||
EOF
|
|
||||||
|
|
||||||
if [ $in_tree_dynstr -ne 0 ]
|
install: $(PROJECT)
|
||||||
then
|
mkdir -p $(PREFIX) $(PREFIX)/bin
|
||||||
cat <<"EOF" >> $F
|
cp $(PROJECT) $(PREFIX)/bin
|
||||||
+cd libweb/dynstr && $(MAKE) clean
|
chmod 0755 $(PREFIX)/bin/$(PROJECT)
|
||||||
EOF
|
+cd doc && $(MAKE) install PREFIX=$(PREFIX)
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $in_tree_libweb -ne 0 ]
|
$(PROJECT): $(OBJECTS)
|
||||||
then
|
$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -o $@
|
||||||
cat <<"EOF" >> $F
|
|
||||||
+cd libweb && $(MAKE) clean
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<"EOF" >> $F
|
.c.o:
|
||||||
distclean: clean
|
$(CC) $(CFLAGS) -c $< -o $@ > $(@:.o=.d)
|
||||||
rm Makefile
|
|
||||||
EOF
|
|
||||||
|
|
||||||
cat <<"EOF" >> $F
|
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
mv $F Makefile
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
install(DIRECTORY man1
|
|
||||||
TYPE MAN
|
|
||||||
FILES_MATCHING PATTERN "*.1"
|
|
||||||
)
|
|
14
doc/Makefile
14
doc/Makefile
|
@ -1,8 +1,14 @@
|
||||||
.POSIX:
|
.POSIX:
|
||||||
|
|
||||||
PREFIX = /usr/local
|
DOCS1 = \
|
||||||
|
man1/slcl.1 \
|
||||||
|
man1/usergen.1
|
||||||
|
|
||||||
all:
|
DOCSPREFIX=$(PREFIX)/share/man
|
||||||
|
DOCS1PREFIX=$(DOCSPREFIX)/man1
|
||||||
|
|
||||||
install: all
|
install: $(DOCS1)
|
||||||
+cd man1 && $(MAKE) PREFIX=$(PREFIX) install
|
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
|
||||||
|
|
BIN
doc/login.png
BIN
doc/login.png
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
|
@ -1,16 +0,0 @@
|
||||||
.POSIX:
|
|
||||||
|
|
||||||
PREFIX = /usr/local
|
|
||||||
DST = $(PREFIX)/share/man/man1
|
|
||||||
OBJECTS = \
|
|
||||||
$(DST)/slcl.1 \
|
|
||||||
$(DST)/usergen.1
|
|
||||||
|
|
||||||
all:
|
|
||||||
|
|
||||||
install: $(OBJECTS)
|
|
||||||
|
|
||||||
$(DST)/%.1: %.1
|
|
||||||
mkdir -p $(DST)
|
|
||||||
cp $< $@
|
|
||||||
chmod 0644 $@
|
|
|
@ -1,7 +1,7 @@
|
||||||
.TH SLCL 1 slcl
|
.TH SLCL 1 slcl
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
slcl \- a simple and lightweight cloud
|
slcl \- a suckless cloud
|
||||||
|
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
.B slcl
|
.B slcl
|
||||||
|
@ -114,13 +114,14 @@ storing one file each, as well as a publicly-shared file by
|
||||||
└── file2.txt
|
└── file2.txt
|
||||||
.EE
|
.EE
|
||||||
|
|
||||||
.SH COPYRIGHT
|
.SH LICENSE
|
||||||
Copyright (C) 2023 Xavier Del Campo Romero.
|
See the LICENSE file for further reference.
|
||||||
.P
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
.SH AUTHORS
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
Written by Xavier Del Campo Romero.
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
.SH TODO
|
||||||
|
Allow deleting files and directories from the web interface.
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
.B usergen(1)
|
.B usergen(1)
|
||||||
|
|
|
@ -81,13 +81,11 @@ should be updated to something similar to:
|
||||||
}
|
}
|
||||||
.EE
|
.EE
|
||||||
|
|
||||||
.SH COPYRIGHT
|
.SH LICENSE
|
||||||
Copyright (C) 2023 Xavier Del Campo Romero.
|
See the LICENSE file for further reference.
|
||||||
.P
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
.SH AUTHORS
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
Written by Xavier Del Campo Romero.
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
|
|
||||||
|
|
BIN
doc/user.png
BIN
doc/user.png
Binary file not shown.
Before Width: | Height: | Size: 54 KiB |
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 909d716a1e6d0c03b61b2a73ad3c2bd20fa55cf2
|
|
@ -0,0 +1,356 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "handler.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "server.h"
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct handler
|
||||||
|
{
|
||||||
|
struct handler_cfg cfg;
|
||||||
|
struct elem
|
||||||
|
{
|
||||||
|
char *url;
|
||||||
|
enum http_op op;
|
||||||
|
handler_fn f;
|
||||||
|
void *user;
|
||||||
|
} *elem;
|
||||||
|
|
||||||
|
struct server *server;
|
||||||
|
struct client
|
||||||
|
{
|
||||||
|
struct handler *h;
|
||||||
|
struct server_client *c;
|
||||||
|
struct http_ctx *http;
|
||||||
|
struct client *next;
|
||||||
|
} *clients;
|
||||||
|
|
||||||
|
size_t n_cfg;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int on_read(void *const buf, const size_t n, void *const user)
|
||||||
|
{
|
||||||
|
struct client *const c = user;
|
||||||
|
|
||||||
|
return server_read(buf, n, c->c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_write(const void *const buf, const size_t n, void *const user)
|
||||||
|
{
|
||||||
|
struct client *const c = user;
|
||||||
|
|
||||||
|
return server_write(buf, n, c->c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wildcard_cmp(const char *s, const char *p)
|
||||||
|
{
|
||||||
|
while (*p && *s)
|
||||||
|
{
|
||||||
|
const char *const wc = strchr(p, '*');
|
||||||
|
|
||||||
|
if (!wc)
|
||||||
|
return strcmp(s, p);
|
||||||
|
|
||||||
|
const size_t n = wc - p;
|
||||||
|
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
const int r = strncmp(s, p, n);
|
||||||
|
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
p += n;
|
||||||
|
s += n;
|
||||||
|
}
|
||||||
|
else if (*(wc + 1) == *s)
|
||||||
|
{
|
||||||
|
p = wc + 1;
|
||||||
|
s += n;
|
||||||
|
}
|
||||||
|
else if (*(wc + 1) == '*')
|
||||||
|
p++;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s++;
|
||||||
|
p += n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*p)
|
||||||
|
if (*p++ != '*')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_payload(const struct http_payload *const p,
|
||||||
|
struct http_response *const r, void *const user)
|
||||||
|
{
|
||||||
|
struct client *const c = user;
|
||||||
|
struct handler *const h = c->h;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < h->n_cfg; i++)
|
||||||
|
{
|
||||||
|
const struct elem *const e = &h->elem[i];
|
||||||
|
|
||||||
|
if (e->op == p->op && !wildcard_cmp(p->resource, e->url))
|
||||||
|
return e->f(p, r, e->user);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Not found: %s\n", p->resource);
|
||||||
|
|
||||||
|
*r = (const struct http_response)
|
||||||
|
{
|
||||||
|
.status = HTTP_STATUS_NOT_FOUND
|
||||||
|
};
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int on_length(const unsigned long long len,
|
||||||
|
const struct http_cookie *const c, struct http_response *const r,
|
||||||
|
void *const user)
|
||||||
|
{
|
||||||
|
struct client *const cl = user;
|
||||||
|
struct handler *const h = cl->h;
|
||||||
|
|
||||||
|
if (h->cfg.length)
|
||||||
|
return h->cfg.length(len, c, r, h->cfg.user);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct client *find_or_alloc_client(struct handler *const h,
|
||||||
|
struct server_client *const c)
|
||||||
|
{
|
||||||
|
for (struct client *cl = h->clients; cl; cl = cl->next)
|
||||||
|
{
|
||||||
|
if (cl->c == c)
|
||||||
|
return cl;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct client *const ret = malloc(sizeof *ret);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct http_cfg cfg =
|
||||||
|
{
|
||||||
|
.read = on_read,
|
||||||
|
.write = on_write,
|
||||||
|
.payload = on_payload,
|
||||||
|
.length = on_length,
|
||||||
|
.user = ret,
|
||||||
|
.tmpdir = h->cfg.tmpdir
|
||||||
|
};
|
||||||
|
|
||||||
|
*ret = (const struct client)
|
||||||
|
{
|
||||||
|
.c = c,
|
||||||
|
.h = h,
|
||||||
|
.http = http_alloc(&cfg)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!ret->http)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: http_alloc failed\n", __func__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!h->clients)
|
||||||
|
h->clients = ret;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (struct client *c = h->clients; c; c = c->next)
|
||||||
|
if (!c->next)
|
||||||
|
{
|
||||||
|
c->next = ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void client_free(struct client *const c)
|
||||||
|
{
|
||||||
|
if (c)
|
||||||
|
http_free(c->http);
|
||||||
|
|
||||||
|
free(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_client_from_list(struct handler *const h,
|
||||||
|
struct client *const c)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (server_client_close(h->server, c->c))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: server_client_close failed\n",
|
||||||
|
__func__);
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (struct client *cl = h->clients, *prev = NULL; cl;
|
||||||
|
prev = cl, cl = cl->next)
|
||||||
|
{
|
||||||
|
if (cl == c)
|
||||||
|
{
|
||||||
|
if (!prev)
|
||||||
|
h->clients = c->next;
|
||||||
|
else
|
||||||
|
prev->next = cl->next;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
end:
|
||||||
|
client_free(c);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handler_listen(struct handler *const h, const short port)
|
||||||
|
{
|
||||||
|
if (!(h->server = server_init(port)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: server_init failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
bool exit, io;
|
||||||
|
struct server_client *const c = server_select(h->server, &io, &exit);
|
||||||
|
|
||||||
|
if (exit)
|
||||||
|
{
|
||||||
|
printf("Exiting...\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!c)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: server_select failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct client *const cl = find_or_alloc_client(h, c);
|
||||||
|
|
||||||
|
if (!cl)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: find_or_alloc_client failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (io)
|
||||||
|
{
|
||||||
|
bool write, close;
|
||||||
|
const int res = http_update(cl->http, &write, &close);
|
||||||
|
|
||||||
|
if (res || close)
|
||||||
|
{
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: http_update failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (remove_client_from_list(h, cl))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: remove_client_from_list failed\n",
|
||||||
|
__func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
server_client_write_pending(cl->c, write);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_clients(struct handler *const h)
|
||||||
|
{
|
||||||
|
for (struct client *c = h->clients; c;)
|
||||||
|
{
|
||||||
|
struct client *const next = c->next;
|
||||||
|
|
||||||
|
server_client_close(h->server, c->c);
|
||||||
|
client_free(c);
|
||||||
|
c = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handler_free(struct handler *const h)
|
||||||
|
{
|
||||||
|
if (h)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < h->n_cfg; i++)
|
||||||
|
free(h->elem[i].url);
|
||||||
|
|
||||||
|
free(h->elem);
|
||||||
|
free_clients(h);
|
||||||
|
server_close(h->server);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct handler *handler_alloc(const struct handler_cfg *const cfg)
|
||||||
|
{
|
||||||
|
struct handler *const h = malloc(sizeof *h);
|
||||||
|
|
||||||
|
if (!h)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: malloc(3) handler: %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*h = (const struct handler){.cfg = *cfg};
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handler_add(struct handler *const h, const char *url,
|
||||||
|
const enum http_op op, const handler_fn f, void *const user)
|
||||||
|
{
|
||||||
|
const size_t n = h->n_cfg + 1;
|
||||||
|
struct elem *const elem = realloc(h->elem, n * sizeof *h->elem);
|
||||||
|
|
||||||
|
if (!elem)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct elem *const e = &elem[h->n_cfg];
|
||||||
|
|
||||||
|
*e = (const struct elem)
|
||||||
|
{
|
||||||
|
.url = strdup(url),
|
||||||
|
.op = op,
|
||||||
|
.f = f,
|
||||||
|
.user = user
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!e->url)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h->elem = elem;
|
||||||
|
h->n_cfg = n;
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef HANDLER_H
|
||||||
|
#define HANDLER_H
|
||||||
|
|
||||||
|
#include "http.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef int (*handler_fn)(const struct http_payload *p,
|
||||||
|
struct http_response *r, void *user);
|
||||||
|
|
||||||
|
struct handler_cfg
|
||||||
|
{
|
||||||
|
const char *tmpdir;
|
||||||
|
int (*length)(unsigned long long len, const struct http_cookie *c,
|
||||||
|
struct http_response *r, void *user);
|
||||||
|
void *user;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct handler *handler_alloc(const struct handler_cfg *cfg);
|
||||||
|
void handler_free(struct handler *h);
|
||||||
|
int handler_add(struct handler *h, const char *url, enum http_op op,
|
||||||
|
handler_fn f, void *user);
|
||||||
|
int handler_listen(struct handler *h, short port);
|
||||||
|
|
||||||
|
#endif /* HANDLER_H */
|
|
@ -0,0 +1,301 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "html.h"
|
||||||
|
#include <dynstr.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct html_node
|
||||||
|
{
|
||||||
|
struct html_attribute
|
||||||
|
{
|
||||||
|
char *attr, *value;
|
||||||
|
} *attrs;
|
||||||
|
|
||||||
|
char *element, *value;
|
||||||
|
size_t n;
|
||||||
|
struct html_node *child, *sibling;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *html_encode(const char *s)
|
||||||
|
{
|
||||||
|
struct dynstr d;
|
||||||
|
|
||||||
|
dynstr_init(&d);
|
||||||
|
|
||||||
|
if (!*s && dynstr_append(&d, ""))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append empty failed\n", __func__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*s)
|
||||||
|
{
|
||||||
|
static const struct esc
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
const char *str;
|
||||||
|
} esc[] =
|
||||||
|
{
|
||||||
|
{.c = '<', .str = ">"},
|
||||||
|
{.c = '>', .str = "<"},
|
||||||
|
{.c = '&', .str = "&"},
|
||||||
|
{.c = '\"', .str = """},
|
||||||
|
{.c = '\'', .str = "'"}
|
||||||
|
};
|
||||||
|
|
||||||
|
char buf[sizeof "a"] = {0};
|
||||||
|
const char *str = NULL;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof esc / sizeof *esc; i++)
|
||||||
|
{
|
||||||
|
const struct esc *const e = &esc[i];
|
||||||
|
|
||||||
|
if (*s == e->c)
|
||||||
|
{
|
||||||
|
str = e->str;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
*buf = *s;
|
||||||
|
str = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynstr_append(&d, "%s", str))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: dynstr_append failed\n", __func__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.str;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
dynstr_free(&d);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int html_node_set_value(struct html_node *const n, const char *const val)
|
||||||
|
{
|
||||||
|
if (!(n->value = html_encode(val)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: html_encode failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int html_node_set_value_unescaped(struct html_node *const n,
|
||||||
|
const char *const val)
|
||||||
|
{
|
||||||
|
if (!(n->value = strdup(val)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int html_node_add_attr(struct html_node *const n, const char *const attr,
|
||||||
|
const char *const val)
|
||||||
|
{
|
||||||
|
const size_t el = n->n + 1;
|
||||||
|
struct html_attribute *const attrs = realloc(n->attrs,
|
||||||
|
el * sizeof *n->attrs), *a = NULL;
|
||||||
|
|
||||||
|
if (!attrs)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = &attrs[n->n];
|
||||||
|
*a = (const struct html_attribute){0};
|
||||||
|
|
||||||
|
if (!(a->attr = strdup(attr))
|
||||||
|
|| (val && !(a->value = strdup(val))))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno));
|
||||||
|
free(a->attr);
|
||||||
|
free(a->value);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->attrs = attrs;
|
||||||
|
n->n = el;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void html_node_add_sibling(struct html_node *const n,
|
||||||
|
struct html_node *const sibling)
|
||||||
|
{
|
||||||
|
for (struct html_node *c = n; c; c = c->sibling)
|
||||||
|
if (!c->sibling)
|
||||||
|
{
|
||||||
|
c->sibling = sibling;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct html_node *html_node_add_child(struct html_node *const n,
|
||||||
|
const char *const element)
|
||||||
|
{
|
||||||
|
struct html_node *const child = html_node_alloc(element);
|
||||||
|
|
||||||
|
if (!child)
|
||||||
|
return NULL;
|
||||||
|
else if (n->child)
|
||||||
|
html_node_add_sibling(n->child, child);
|
||||||
|
else
|
||||||
|
n->child = child;
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
int serialize_node(struct dynstr *const d, const struct html_node *const n,
|
||||||
|
const unsigned level)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < level; i++)
|
||||||
|
dynstr_append(d, "\t");
|
||||||
|
|
||||||
|
dynstr_append_or_ret_nonzero(d, "<%s", n->element);
|
||||||
|
|
||||||
|
if (n->n)
|
||||||
|
dynstr_append_or_ret_nonzero(d, " ");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n->n; i++)
|
||||||
|
{
|
||||||
|
const struct html_attribute *const a = &n->attrs[i];
|
||||||
|
|
||||||
|
if (a->value)
|
||||||
|
dynstr_append_or_ret_nonzero(d, "%s=\"%s\"", a->attr, a->value);
|
||||||
|
else
|
||||||
|
dynstr_append_or_ret_nonzero(d, "%s", a->attr);
|
||||||
|
|
||||||
|
if (i + 1 < n->n)
|
||||||
|
dynstr_append_or_ret_nonzero(d, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!n->value && !n->child)
|
||||||
|
dynstr_append_or_ret_nonzero(d, "/>");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dynstr_append_or_ret_nonzero(d, ">");
|
||||||
|
|
||||||
|
if (n->value)
|
||||||
|
dynstr_append_or_ret_nonzero(d, "%s", n->value);
|
||||||
|
|
||||||
|
if (n->child)
|
||||||
|
{
|
||||||
|
dynstr_append_or_ret_nonzero(d, "\n");
|
||||||
|
|
||||||
|
if (serialize_node(d, n->child, level + 1))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: serialize_node failed\n", __func__);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < level; i++)
|
||||||
|
dynstr_append(d, "\t");
|
||||||
|
}
|
||||||
|
|
||||||
|
dynstr_append_or_ret_nonzero(d, "</%s>", n->element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: print siblings */
|
||||||
|
|
||||||
|
dynstr_append_or_ret_nonzero(d, "\n");
|
||||||
|
|
||||||
|
if (n->sibling)
|
||||||
|
return serialize_node(d, n->sibling, level);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int html_serialize(const struct html_node *const n, struct dynstr *const d)
|
||||||
|
{
|
||||||
|
return serialize_node(d, n, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void html_attribute_free(struct html_attribute *const a)
|
||||||
|
{
|
||||||
|
if (a)
|
||||||
|
{
|
||||||
|
free(a->attr);
|
||||||
|
free(a->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void html_node_free(struct html_node *const n)
|
||||||
|
{
|
||||||
|
if (n)
|
||||||
|
{
|
||||||
|
struct html_node *s = n->sibling;
|
||||||
|
|
||||||
|
html_node_free(n->child);
|
||||||
|
|
||||||
|
while (s)
|
||||||
|
{
|
||||||
|
struct html_node *const next = s->sibling;
|
||||||
|
|
||||||
|
html_node_free(s->child);
|
||||||
|
free(s->element);
|
||||||
|
free(s->value);
|
||||||
|
|
||||||
|
for (size_t i = 0 ; i < s->n; i++)
|
||||||
|
html_attribute_free(&s->attrs[i]);
|
||||||
|
|
||||||
|
free(s->attrs);
|
||||||
|
free(s);
|
||||||
|
s = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(n->element);
|
||||||
|
free(n->value);
|
||||||
|
|
||||||
|
for (size_t i = 0 ; i < n->n; i++)
|
||||||
|
html_attribute_free(&n->attrs[i]);
|
||||||
|
|
||||||
|
free(n->attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct html_node *html_node_alloc(const char *const element)
|
||||||
|
{
|
||||||
|
struct html_node *const n = malloc(sizeof *n);
|
||||||
|
|
||||||
|
if (!n)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
*n = (const struct html_node)
|
||||||
|
{
|
||||||
|
.element = strdup(element)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!n->element)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
html_node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef HTML_H
|
||||||
|
#define HTML_H
|
||||||
|
|
||||||
|
#include <dynstr.h>
|
||||||
|
|
||||||
|
struct html_node *html_node_alloc(const char *element);
|
||||||
|
void html_node_free(struct html_node *n);
|
||||||
|
int html_node_set_value(struct html_node *n, const char *val);
|
||||||
|
int html_node_set_value_unescaped(struct html_node *n, const char *val);
|
||||||
|
int html_node_add_attr(struct html_node *n, const char *attr, const char *val);
|
||||||
|
struct html_node *html_node_add_child(struct html_node *n, const char *elem);
|
||||||
|
void html_node_add_sibling(struct html_node *n, struct html_node *sibling);
|
||||||
|
int html_serialize(const struct html_node *n, struct dynstr *d);
|
||||||
|
|
||||||
|
#endif /* HTML_H */
|
|
@ -0,0 +1,99 @@
|
||||||
|
#ifndef HTTP_H
|
||||||
|
#define HTTP_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct http_payload
|
||||||
|
{
|
||||||
|
enum http_op
|
||||||
|
{
|
||||||
|
HTTP_OP_GET,
|
||||||
|
HTTP_OP_POST
|
||||||
|
} op;
|
||||||
|
|
||||||
|
const char *resource;
|
||||||
|
|
||||||
|
struct http_cookie
|
||||||
|
{
|
||||||
|
const char *field, *value;
|
||||||
|
} cookie;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct http_post
|
||||||
|
{
|
||||||
|
bool expect_continue;
|
||||||
|
const void *data;
|
||||||
|
size_t n;
|
||||||
|
const char *dir;
|
||||||
|
|
||||||
|
const struct http_post_file
|
||||||
|
{
|
||||||
|
const char *tmpname, *filename;
|
||||||
|
} *files;
|
||||||
|
} post;
|
||||||
|
} u;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HTTP_STATUSES \
|
||||||
|
X(CONTINUE, "Continue", 100) \
|
||||||
|
X(OK, "OK", 200) \
|
||||||
|
X(SEE_OTHER, "See other", 303) \
|
||||||
|
X(BAD_REQUEST, "Bad Request", 400) \
|
||||||
|
X(UNAUTHORIZED, "Unauthorized", 401) \
|
||||||
|
X(FORBIDDEN, "Forbidden", 403) \
|
||||||
|
X(NOT_FOUND, "Not found", 404) \
|
||||||
|
X(PAYLOAD_TOO_LARGE, "Payload too large", 413) \
|
||||||
|
X(INTERNAL_ERROR, "Internal Server Error", 500)
|
||||||
|
|
||||||
|
struct http_response
|
||||||
|
{
|
||||||
|
enum http_status
|
||||||
|
{
|
||||||
|
#define X(x, y, z) HTTP_STATUS_##x,
|
||||||
|
HTTP_STATUSES
|
||||||
|
#undef X
|
||||||
|
} status;
|
||||||
|
|
||||||
|
struct http_header
|
||||||
|
{
|
||||||
|
char *header, *value;
|
||||||
|
} *headers;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
const void *ro;
|
||||||
|
void *rw;
|
||||||
|
} buf;
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
|
unsigned long long n;
|
||||||
|
size_t n_headers;
|
||||||
|
void (*free)(void *);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct http_cfg
|
||||||
|
{
|
||||||
|
int (*read)(void *buf , size_t n, void *user);
|
||||||
|
int (*write)(const void *buf, size_t n, void *user);
|
||||||
|
int (*payload)(const struct http_payload *p, struct http_response *r,
|
||||||
|
void *user);
|
||||||
|
int (*length)(unsigned long long len, const struct http_cookie *c,
|
||||||
|
struct http_response *r, void *user);
|
||||||
|
const char *tmpdir;
|
||||||
|
void *user;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct http_ctx *http_alloc(const struct http_cfg *cfg);
|
||||||
|
void http_free(struct http_ctx *h);
|
||||||
|
/* Positive return value: user input error, negative: fatal error. */
|
||||||
|
int http_update(struct http_ctx *h, bool *write, bool *close);
|
||||||
|
int http_response_add_header(struct http_response *r, const char *header,
|
||||||
|
const char *value);
|
||||||
|
char *http_cookie_create(const char *key, const char *value);
|
||||||
|
char *http_encode_url(const char *url);
|
||||||
|
char *http_decode_url(const char *url);
|
||||||
|
|
||||||
|
#endif /* HTTP_H */
|
1
libweb
1
libweb
|
@ -1 +0,0 @@
|
||||||
Subproject commit b4930f72bb9026c5a0871f4fa4cabe20cb0e6a9f
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 822ff99e6f6a5c8ad7309a535d384d354b2d326e
|
37
page.h
37
page.h
|
@ -1,9 +1,7 @@
|
||||||
#ifndef PAGE_H
|
#ifndef PAGE_H
|
||||||
#define PAGE_H
|
#define PAGE_H
|
||||||
|
|
||||||
#include <libweb/http.h>
|
#include "http.h"
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
struct page_quota
|
struct page_quota
|
||||||
{
|
{
|
||||||
|
@ -12,43 +10,20 @@ struct page_quota
|
||||||
|
|
||||||
struct page_resource
|
struct page_resource
|
||||||
{
|
{
|
||||||
struct http_response *r;
|
const char *root, *user_root, *username, *res;
|
||||||
const char *dir, *root, *res;
|
|
||||||
const struct page_quota *q;
|
|
||||||
const struct http_arg *args;
|
|
||||||
size_t n_args;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct page_search
|
|
||||||
{
|
|
||||||
struct page_search_result
|
|
||||||
{
|
|
||||||
char *name;
|
|
||||||
} *results;
|
|
||||||
|
|
||||||
const char *root;
|
|
||||||
size_t n;
|
|
||||||
bool limit_exceeded;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct page_rm
|
|
||||||
{
|
|
||||||
const char *dir, **items;
|
|
||||||
size_t n;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int page_login(struct http_response *r);
|
int page_login(struct http_response *r);
|
||||||
int page_style(struct http_response *r, const char *path);
|
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(const struct page_resource *r);
|
int page_resource(struct http_response *r, const struct page_resource *res,
|
||||||
int page_head_resource(struct http_response *r, const char *res);
|
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_search(struct http_response *r, const struct page_search *s);
|
int page_thumbnail(struct http_response *r, const char *res);
|
||||||
int page_rm(struct http_response *r, const struct page_rm *rm);
|
|
||||||
|
|
||||||
#endif /* PAGE_H */
|
#endif /* PAGE_H */
|
||||||
|
|
|
@ -0,0 +1,318 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include "server.h"
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct server
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
struct server_client
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool write;
|
||||||
|
} *c;
|
||||||
|
|
||||||
|
size_t n;
|
||||||
|
};
|
||||||
|
|
||||||
|
int server_close(struct server *const s)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
return 0;
|
||||||
|
else if (s->fd >= 0)
|
||||||
|
ret = close(s->fd);
|
||||||
|
|
||||||
|
free(s);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int server_client_close(struct server *const s, struct server_client *const c)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->n; i++)
|
||||||
|
{
|
||||||
|
struct server_client *const ref = &s->c[i];
|
||||||
|
|
||||||
|
if (c == ref)
|
||||||
|
{
|
||||||
|
const size_t n = s->n - 1;
|
||||||
|
|
||||||
|
if ((ret = close(c->fd)))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: close(2): %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (n)
|
||||||
|
{
|
||||||
|
memcpy(ref, ref + 1, (s->n - i - 1) * sizeof *ref);
|
||||||
|
|
||||||
|
struct server_client *const c = realloc(s->c, n * sizeof *s->c);
|
||||||
|
|
||||||
|
if (!c)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: realloc(3): %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->c = c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(s->c);
|
||||||
|
s->c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->n = n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int server_read(void *const buf, const size_t n, struct server_client *const c)
|
||||||
|
{
|
||||||
|
const ssize_t r = read(c->fd, buf, n);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
fprintf(stderr, "%s: read(2): %s\n", __func__, strerror(errno));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int server_write(const void *const buf, const size_t n,
|
||||||
|
struct server_client *const c)
|
||||||
|
{
|
||||||
|
const ssize_t w = write(c->fd, buf, n);
|
||||||
|
|
||||||
|
if (w < 0)
|
||||||
|
fprintf(stderr, "%s: write(2): %s\n", __func__, strerror(errno));
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct server_client *alloc_client(struct server *const s)
|
||||||
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
|
socklen_t sz = sizeof addr;
|
||||||
|
const int fd = accept(s->fd, (struct sockaddr *)&addr, &sz);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: accept(2): %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int flags = fcntl(fd, F_GETFL);
|
||||||
|
|
||||||
|
if (flags < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: fcntl(2) F_GETFL: %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: fcntl(2) F_SETFL: %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t n = s->n + 1;
|
||||||
|
struct server_client *const clients = realloc(s->c, n * sizeof *s->c);
|
||||||
|
|
||||||
|
if (!clients)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct server_client *const c = &clients[s->n];
|
||||||
|
|
||||||
|
*c = (const struct server_client)
|
||||||
|
{
|
||||||
|
.fd = fd
|
||||||
|
};
|
||||||
|
|
||||||
|
s->c = clients;
|
||||||
|
s->n = n;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void server_client_write_pending(struct server_client *const c,
|
||||||
|
const bool write)
|
||||||
|
{
|
||||||
|
c->write = write;
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile sig_atomic_t do_exit;
|
||||||
|
|
||||||
|
static void handle_signal(const int signum)
|
||||||
|
{
|
||||||
|
do_exit = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct server_client *server_select(struct server *const s, bool *const io,
|
||||||
|
bool *const exit)
|
||||||
|
{
|
||||||
|
int nfds = -1;
|
||||||
|
fd_set rfds, wfds;
|
||||||
|
|
||||||
|
*io = *exit = false;
|
||||||
|
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_ZERO(&wfds);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->n; i++)
|
||||||
|
{
|
||||||
|
const struct server_client *const c = &s->c[i];
|
||||||
|
const int fd = c->fd;
|
||||||
|
|
||||||
|
FD_SET(fd, &rfds);
|
||||||
|
|
||||||
|
if (c->write)
|
||||||
|
FD_SET(fd, &wfds);
|
||||||
|
|
||||||
|
if (fd > nfds)
|
||||||
|
nfds = fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_SET(s->fd, &rfds);
|
||||||
|
|
||||||
|
if (s->fd > nfds)
|
||||||
|
nfds = s->fd;
|
||||||
|
|
||||||
|
const int res = select(nfds + 1, &rfds, &wfds, NULL, NULL);
|
||||||
|
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
if (do_exit)
|
||||||
|
*exit = true;
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s: select(2): %s\n", __func__, strerror(errno));
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (FD_ISSET(s->fd, &rfds))
|
||||||
|
return alloc_client(s);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < s->n; i++)
|
||||||
|
{
|
||||||
|
struct server_client *const c = &s->c[i];
|
||||||
|
|
||||||
|
if (FD_ISSET(c->fd, &rfds) || FD_ISSET(c->fd, &wfds))
|
||||||
|
{
|
||||||
|
*io = true;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "%s: unlisted fd\n", __func__);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int init_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction sa =
|
||||||
|
{
|
||||||
|
.sa_handler = handle_signal,
|
||||||
|
.sa_flags = SA_RESTART
|
||||||
|
};
|
||||||
|
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
|
||||||
|
if (sigaction(SIGINT, &sa, NULL))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: sigaction(2) SIGINT: %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if (sigaction(SIGTERM, &sa, NULL))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: sigaction(2) SIGINT: %s\n",
|
||||||
|
__func__, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct server *server_init(const unsigned short port)
|
||||||
|
{
|
||||||
|
struct server *const s = malloc(sizeof *s);
|
||||||
|
|
||||||
|
if (!s)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
*s = (const struct server)
|
||||||
|
{
|
||||||
|
.fd = socket(AF_INET, SOCK_STREAM, 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if (s->fd < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: socket(2): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
else if (init_signals())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: init_signals failed\n", __func__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct sockaddr_in addr =
|
||||||
|
{
|
||||||
|
.sin_family = AF_INET,
|
||||||
|
.sin_port = htons(port)
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {QUEUE_LEN = 10};
|
||||||
|
|
||||||
|
if (bind(s->fd, (const struct sockaddr *)&addr, sizeof addr))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: bind(2): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
else if (listen(s->fd, QUEUE_LEN))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: listen(2): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in in;
|
||||||
|
socklen_t sz = sizeof in;
|
||||||
|
|
||||||
|
if (getsockname(s->fd, (struct sockaddr *)&in, &sz))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s: getsockname(2): %s\n", __func__, strerror(errno));
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Listening on port %hu\n", ntohs(in.sin_port));
|
||||||
|
return s;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
server_close(s);
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct server *server_init(unsigned short port);
|
||||||
|
struct server_client *server_select(struct server *s, bool *io, bool *exit);
|
||||||
|
int server_read(void *buf, size_t n, struct server_client *c);
|
||||||
|
int server_write(const void *buf, size_t n, struct server_client *c);
|
||||||
|
int server_close(struct server *s);
|
||||||
|
int server_client_close(struct server *s, struct server_client *c);
|
||||||
|
void server_client_write_pending(struct server_client *c, bool write);
|
||||||
|
|
||||||
|
#endif /* SERVER_H */
|
55
style.c
55
style.c
|
@ -1,55 +0,0 @@
|
||||||
#include "style.h"
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
const char style_default[] =
|
|
||||||
"body\n"
|
|
||||||
"{\n"
|
|
||||||
" font-family: 'Courier New', Courier, monospace;\n"
|
|
||||||
"}\n"
|
|
||||||
"td\n"
|
|
||||||
"{\n"
|
|
||||||
" font-size: 14px;\n"
|
|
||||||
"}\n"
|
|
||||||
"a\n"
|
|
||||||
"{\n"
|
|
||||||
" text-decoration: none;\n"
|
|
||||||
"}\n"
|
|
||||||
".userform\n"
|
|
||||||
"{\n"
|
|
||||||
" padding: 4px;\n"
|
|
||||||
"}\n"
|
|
||||||
".loginform\n"
|
|
||||||
"{\n"
|
|
||||||
" display: grid;\n"
|
|
||||||
"}\n"
|
|
||||||
"form, label, table\n"
|
|
||||||
"{\n"
|
|
||||||
" margin: auto;\n"
|
|
||||||
"}\n"
|
|
||||||
"div\n"
|
|
||||||
"{\n"
|
|
||||||
" align-items: center;\n"
|
|
||||||
" display: grid;\n"
|
|
||||||
"}\n"
|
|
||||||
"input, .abutton\n"
|
|
||||||
"{\n"
|
|
||||||
" margin: auto;\n"
|
|
||||||
" border: 1px solid;\n"
|
|
||||||
" border-radius: 8px;\n"
|
|
||||||
"}\n"
|
|
||||||
"header, footer\n"
|
|
||||||
"{\n"
|
|
||||||
" display: flex;\n"
|
|
||||||
" justify-content: center;\n"
|
|
||||||
" text-decoration: auto;\n"
|
|
||||||
"}\n"
|
|
||||||
"table\n"
|
|
||||||
"{\n"
|
|
||||||
" max-width: 50%;\n"
|
|
||||||
"}\n"
|
|
||||||
"tr:nth-child(even)\n"
|
|
||||||
"{\n"
|
|
||||||
" background-color: lightgray;\n"
|
|
||||||
"}\n";
|
|
||||||
|
|
||||||
const size_t style_default_len = sizeof style_default - 1;
|
|
9
style.h
9
style.h
|
@ -1,9 +0,0 @@
|
||||||
#ifndef STYLE_H
|
|
||||||
#define STYLE_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
extern const char style_default[];
|
|
||||||
extern const size_t style_default_len;
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -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;
|
||||||
|
}
|
52
usergen
52
usergen
|
@ -7,23 +7,6 @@ usage()
|
||||||
echo "$0 <dir>"
|
echo "$0 <dir>"
|
||||||
}
|
}
|
||||||
|
|
||||||
to_hex()
|
|
||||||
{
|
|
||||||
od -An -t x1 | tr -d ' ' | tr -d '\n'
|
|
||||||
}
|
|
||||||
|
|
||||||
to_bin()
|
|
||||||
{
|
|
||||||
sed -e 's,\([0-9a-f]\{2\}\),\\\\\\x\1,g' | xargs printf
|
|
||||||
}
|
|
||||||
|
|
||||||
mktemp_posix()
|
|
||||||
{
|
|
||||||
m4 <<EOF
|
|
||||||
mkstemp(${TMPDIR:-/tmp}/tmp.XXXXXX)
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ $# != 1 ]; then
|
if [ $# != 1 ]; then
|
||||||
usage >&2
|
usage >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -32,13 +15,7 @@ fi
|
||||||
DIR=$1
|
DIR=$1
|
||||||
|
|
||||||
echo Username: >&2
|
echo Username: >&2
|
||||||
IFS= read -r USER
|
read -r USER
|
||||||
|
|
||||||
if printf '%s' "$USER" | grep -qe '[[:space:]]'
|
|
||||||
then
|
|
||||||
echo Username cannot contain whitespaces >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
DB="$DIR/db.json"
|
DB="$DIR/db.json"
|
||||||
|
|
||||||
|
@ -48,34 +25,26 @@ then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
TTYCFG=$(stty -g)
|
|
||||||
trap "stty $TTYCFG" INT QUIT TERM EXIT
|
|
||||||
stty -echo
|
|
||||||
echo Password: >&2
|
echo Password: >&2
|
||||||
IFS= read -r PWD
|
read -r PWD
|
||||||
stty echo
|
|
||||||
# Force newline
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "Quota, in MiB (leave empty for unlimited quota):" >&2
|
echo "Quota, in MiB (leave empty for unlimited quota):" >&2
|
||||||
read -r QUOTA
|
read -r QUOTA
|
||||||
QUOTA="$(printf '%d' "$QUOTA")"
|
|
||||||
|
|
||||||
PWD=$(printf '%s' "$PWD" | to_hex)
|
PWD=$(printf '%s' $PWD | xxd -p | tr -d '\n')
|
||||||
SALT=$(openssl rand -hex 32)
|
SALT=$(openssl rand 32 | xxd -p | tr -d '\n')
|
||||||
KEY=$(openssl rand -hex 32)
|
KEY=$(openssl rand 32 | xxd -p | tr -d '\n')
|
||||||
PWD=$(printf '%s%s' "$SALT" "$PWD")
|
PWD=$(printf '%s%s' $SALT $PWD)
|
||||||
|
|
||||||
ROUNDS=1000
|
ROUNDS=1000
|
||||||
|
|
||||||
for i in $(seq $ROUNDS)
|
for i in $(seq $ROUNDS)
|
||||||
do
|
do
|
||||||
printf "\r%d/$ROUNDS" $i >&2
|
printf "\r%d/$ROUNDS" $i >&2
|
||||||
PWD=$(printf '%s' "$PWD" | to_bin | openssl sha256 -r | cut -d' ' -f1)
|
PWD=$(printf '%s' $PWD | xxd -p -r | sha256sum | cut -d' ' -f1)
|
||||||
done
|
done
|
||||||
|
|
||||||
echo >&2
|
echo >&2
|
||||||
TMP=$(mktemp_posix)
|
TMP=$(mktemp)
|
||||||
|
|
||||||
cleanup()
|
cleanup()
|
||||||
{
|
{
|
||||||
|
@ -87,11 +56,12 @@ trap cleanup EXIT
|
||||||
jq ".users += [
|
jq ".users += [
|
||||||
{
|
{
|
||||||
\"name\": \"$USER\",
|
\"name\": \"$USER\",
|
||||||
\"password\": \""$PWD"\",
|
\"password\": \"$PWD\",
|
||||||
\"salt\": \"$SALT\",
|
\"salt\": \"$SALT\",
|
||||||
\"key\": \"$KEY\",
|
\"key\": \"$KEY\",
|
||||||
\"quota\": \"$QUOTA\"
|
\"quota\": \"$QUOTA\"
|
||||||
}]" "$DB" > $TMP
|
}]" "$DB" > $TMP
|
||||||
|
|
||||||
mkdir -p "$DIR/user/$USER"
|
|
||||||
mv $TMP "$DB"
|
mv $TMP "$DB"
|
||||||
|
mkdir "$DIR/user/$USER"
|
||||||
|
test -d "$DIR/thumbnails" && mkdir -p "$DIR/thumbnails/$USER"
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
#include "wildcard_cmp.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
|
|
||||||
int wildcard_cmp(const char *s, const char *p, const bool casecmp)
|
|
||||||
{
|
|
||||||
const char *last = NULL;
|
|
||||||
int (*const cmp)(const char *, const char *) =
|
|
||||||
casecmp ? strcmp : strcasecmp;
|
|
||||||
int (*const ncmp)(const char *, const char *, size_t) =
|
|
||||||
casecmp ? strncmp : strncasecmp;
|
|
||||||
|
|
||||||
while (*p && *s)
|
|
||||||
{
|
|
||||||
const char *const wc = strchr(p, '*');
|
|
||||||
|
|
||||||
if (!wc)
|
|
||||||
{
|
|
||||||
const int r = cmp(s, p);
|
|
||||||
|
|
||||||
if (r && last)
|
|
||||||
{
|
|
||||||
p = last;
|
|
||||||
s += strlen(p);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t auxn = wc - p, rem = strlen(s),
|
|
||||||
n = auxn > rem ? rem : auxn;
|
|
||||||
|
|
||||||
if (n)
|
|
||||||
{
|
|
||||||
const int r = ncmp(s, p, n);
|
|
||||||
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
if (last)
|
|
||||||
p = last;
|
|
||||||
else
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
p += n;
|
|
||||||
|
|
||||||
s += n;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char next = *(wc + 1), wca[2] = {next}, sa[sizeof wca] = {*s};
|
|
||||||
|
|
||||||
if (!cmp(wca, sa))
|
|
||||||
{
|
|
||||||
last = p;
|
|
||||||
p = wc + 1;
|
|
||||||
}
|
|
||||||
else if (next == '*')
|
|
||||||
p++;
|
|
||||||
else
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*p)
|
|
||||||
if (*p++ != '*')
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#ifndef WILDCARD_CMP_H
|
|
||||||
#define WILDCARD_CMP_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
int wildcard_cmp(const char *s, const char *p, bool casecmp);
|
|
||||||
|
|
||||||
#endif /* WILDCARD_CMP_H */
|
|
Loading…
Reference in New Issue