aboutsummaryrefslogtreecommitdiff
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
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).
-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)