aboutsummaryrefslogtreecommitdiff
path: root/page.c
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-03-16 01:49:18 +0100
committerXavier Del Campo Romero <xavi.dcr@tutanota.com>2023-03-16 01:49:18 +0100
commit27b8a6971861cb4ead0eb07965bb4873f591ad24 (patch)
tree1144acabc28d22f1e76f08322e5393c68dfccda5 /page.c
parent7e2e37d40ac130079570a4f8111d4d5ca5fc9344 (diff)
downloadslcl-27b8a6971861cb4ead0eb07965bb4873f591ad24.tar.gz
page.c: Set Content-Disposition when serving files
So far, slcl used the default browser behaviour (i.e., Content-Disposition: inline), which means files were typically shown on the web browser itself. However, this caused two issues: - Users would have to right-click -> "Save Link As..." to download a file, which might be inconvenient for some users. - The original file name would not be retrieved for publicly shared files. Now, file download is always requested to the browser, and the original file path is retrieved via readlink(2).
Diffstat (limited to 'page.c')
-rw-r--r--page.c101
1 files changed, 96 insertions, 5 deletions
diff --git a/page.c b/page.c
index 555b5a3..17adb8a 100644
--- a/page.c
+++ b/page.c
@@ -3,6 +3,8 @@
#include "html.h"
#include <dynstr.h>
#include <dirent.h>
+#include <fcntl.h>
+#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -902,12 +904,33 @@ end:
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");
+ struct dynstr b, d;
+ char *bn;
+
+ dynstr_init(&b);
+ dynstr_init(&d);
if (!f)
{
fprintf(stderr, "%s: fopen(3): %s\n", __func__, strerror(errno));
- return -1;
+ goto end;
+ }
+ else if (dynstr_append(&b, "%s", res))
+ {
+ fprintf(stderr, "%s: dynstr_append res failed\n", __func__);
+ goto end;
+ }
+ else if (!(bn = basename(b.str)))
+ {
+ fprintf(stderr, "%s: basename(3) failed\n", __func__);
+ goto end;
+ }
+ else if (dynstr_append(&d, "attachment; filename=\"%s\"", bn))
+ {
+ fprintf(stderr, "%s: dynstr_append attachment failed\n", __func__);
+ goto end;
}
*r = (const struct http_response)
@@ -917,7 +940,22 @@ static int serve_file(struct http_response *const r,
.n = sb->st_size
};
- return 0;
+ if (http_response_add_header(r, "Content-Disposition", d.str))
+ {
+ fprintf(stderr, "%s: http_response_add_header failed\n", __func__);
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+ dynstr_free(&b);
+ dynstr_free(&d);
+
+ if (ret && f && fclose(f))
+ fprintf(stderr, "%s: fclose(3): %s\n", __func__, strerror(errno));
+
+ return ret;
}
static int page_not_found(struct http_response *const r)
@@ -975,9 +1013,48 @@ int page_resource(struct http_response *const r, const char *const dir,
return -1;
}
+static char *resolve_link(const char *const res)
+{
+ size_t len = 1;
+ char *p = NULL;
+
+ for (;;)
+ {
+ char *const pp = realloc(p, len);
+
+ if (!pp)
+ {
+ fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno));
+ break;
+ }
+
+ p = pp;
+
+ const ssize_t n = readlink(res, p, len);
+
+ if (n < 0)
+ {
+ fprintf(stderr, "%s: readlink(2): %s\n", __func__, strerror(errno));
+ break;
+ }
+ else if (n < len)
+ {
+ p[len - 1] = '\0';
+ return p;
+ }
+
+ len++;
+ }
+
+ free(p);
+ return NULL;
+}
+
int page_public(struct http_response *const r, const char *const res)
{
+ int ret = -1;
struct stat sb;
+ char *path = NULL;
if (stat(res, &sb))
{
@@ -987,7 +1064,7 @@ int page_public(struct http_response *const r, const char *const res)
if (errno == ENOENT)
return page_not_found(r);
else
- return -1;
+ goto end;
}
const mode_t m = sb.st_mode;
@@ -995,10 +1072,24 @@ int page_public(struct http_response *const r, const char *const res)
if (!S_ISREG(m))
{
fprintf(stderr, "%s: only regular files are supported\n", __func__);
- return -1;
+ goto end;
+ }
+ else if (!(path = resolve_link(res)))
+ {
+ fprintf(stderr, "%s: resolve_link failed\n", __func__);
+ goto end;
+ }
+ else if (serve_file(r, &sb, path))
+ {
+ fprintf(stderr, "%s: serve_file failed\n", __func__);
+ goto end;
}
- return serve_file(r, &sb, res);
+ ret = 0;
+
+end:
+ free(path);
+ return ret;
}
int page_failed_login(struct http_response *const r)