commit 309e46917a00d220dea4aff0b19ce38660459fff
parent 3d9256337ad035a7ba1395530dbee3adb0fe9bdc
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date: Wed, 26 Feb 2025 15:14:00 +0200
Reduce heap allocations
Diffstat:
13 files changed, 337 insertions(+), 145 deletions(-)
diff --git a/activitypub.c b/activitypub.c
@@ -1218,7 +1218,7 @@ xs_dict *msg_admiration(snac *snac, const char *object, const char *type)
if (valid_status(object_get(object, &a_msg))) {
xs *rcpts = xs_list_new();
- xs *o_md5 = xs_md5_hex(object, strlen(object));
+ const char *o_md5 = xs_md5(object);
xs *id = xs_fmt("%s/%s/%s", snac->actor, *type == 'L' ? "l" : "a", o_md5);
msg = msg_base(snac, type, id, snac->actor, "@now", object);
@@ -1793,23 +1793,27 @@ xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
xs *o = xs_list_new();
xs_list *p = (xs_list *)opts;
const xs_str *v;
- xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}");
+ xs_val *replies = NULL;
xs_set_init(&seen);
while (max && xs_list_iter(&p, &v)) {
if (*v) {
- xs *v2 = xs_dup(v);
- xs *d = xs_dict_new();
+ if (replies == NULL)
+ replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}");
+ const xs_val *vv = v;
+ xs *v2 = NULL;
- if (strlen(v2) > 60) {
- v2[60] = '\0';
- v2 = xs_str_cat(v2, "...");
+ if (strlen(v) > 62) {
+ vv = v2 = xs_realloc(NULL, 64);
+ memcpy(v2, v, 60);
+ strcpy(v2 + 60, "...");
}
if (xs_set_add(&seen, v2) == 1) {
+ xs *d = xs_dict_new();
d = xs_dict_append(d, "type", "Note");
- d = xs_dict_append(d, "name", v2);
+ d = xs_dict_append(d, "name", vv);
d = xs_dict_append(d, "replies", replies);
o = xs_list_append(o, d);
diff --git a/data.c b/data.c
@@ -23,6 +23,7 @@
#include <sys/time.h>
#include <fcntl.h>
#include <pthread.h>
+#include <dirent.h>
double disk_layout = 2.7;
@@ -31,6 +32,14 @@ pthread_mutex_t data_mutex = {0};
int snac_upgrade(xs_str **error);
+#define md5_fn(input, suffix) _md5_fn((char[MD5_HEX_SIZE + sizeof(suffix) - 1]){ 0 }, input, "" suffix)
+
+const char *_md5_fn(char *buffer, const char *input, const char *suffix)
+{
+ _xs_md5_buf(buffer, input, NULL);
+ strcpy(buffer + MD5_HEX_SIZE - 1, suffix);
+ return buffer;
+}
int srv_open(const char *basedir, int auto_upgrade)
/* opens a server */
@@ -200,8 +209,69 @@ void user_free(snac *snac)
xs_free(snac->links);
xs_free(snac->actor);
xs_free(snac->md5);
+ close(snac->basedfd);
}
+FILE *user_open_file(snac *user, const char *file, int wr)
+{
+ int fd = openat(user->basedfd, file, wr ? O_RDWR | O_CREAT : O_RDONLY, 0660);
+ if (fd < 0)
+ return NULL;
+ FILE *rv = fdopen(fd, wr ? "r+" : "r");
+ if (rv)
+ return rv;
+
+ close(fd);
+ return NULL;
+}
+
+int user_link_subfile(snac *user, const char *sub, const char *file, int todfd, const char *to)
+{
+ int subfd;
+ int ret;
+
+ if (mkdiratx(user->basedfd, sub) || (subfd = openat(user->basedfd, sub, O_RDONLY | O_DIRECTORY)) < 0)
+ return - 1;
+
+ ret = linkat(todfd, to, subfd, file, 0);
+ close(subfd);
+
+ return ret;
+}
+
+int user_unlink_subfile(snac *user, const char *sub, const char *file)
+{
+ int subfd;
+ int ret;
+
+ if ((subfd = openat(user->basedfd, sub, O_RDONLY | O_DIRECTORY)) < 0)
+ return -1;
+
+ ret = unlinkat(subfd, file, 0);
+ close(subfd);
+
+ return ret;
+}
+
+FILE *user_open_subfile(snac *user, const char *sub, const char *file, int wr)
+{
+ int subfd;
+ int fd;
+
+ if (mkdiratx(user->basedfd, sub) || (subfd = openat(user->basedfd, sub, O_RDONLY | O_DIRECTORY)) < 0)
+ return NULL;
+
+ fd = openat(subfd, file, wr ? O_RDWR | O_CREAT : O_RDONLY, 0660);
+ close(subfd);
+ if (fd < 0)
+ return NULL;
+ FILE *rv = fdopen(fd, wr ? "r+" : "r");
+ if (rv)
+ return rv;
+
+ close(fd);
+ return NULL;
+}
int user_open(snac *user, const char *uid)
/* opens a user */
@@ -211,7 +281,6 @@ int user_open(snac *user, const char *uid)
*user = (snac){0};
if (validate_uid(uid)) {
- xs *cfg_file = NULL;
FILE *f;
xs *t = xs_fmt("%s/user/%s", srv_basedir, uid);
@@ -239,18 +308,15 @@ int user_open(snac *user, const char *uid)
return ret;
user->basedir = xs_fmt("%s/user/%s", srv_basedir, user->uid);
+ user->basedfd = open(user->basedir, O_DIRECTORY);
- cfg_file = xs_fmt("%s/user.json", user->basedir);
-
- if ((f = fopen(cfg_file, "r")) != NULL) {
+ if ((f = user_open_file(user, "user.json", 0)) != NULL) {
/* read full config file */
user->config = xs_json_load(f);
fclose(f);
if (user->config != NULL) {
- xs *key_file = xs_fmt("%s/key.json", user->basedir);
-
- if ((f = fopen(key_file, "r")) != NULL) {
+ if ((f = user_open_file(user, "key.json", 0)) != NULL) {
user->key = xs_json_load(f);
fclose(f);
@@ -262,36 +328,33 @@ int user_open(snac *user, const char *uid)
ret = 1;
/* does it have a configuration override? */
- xs *cfg_file_o = xs_fmt("%s/user_o.json", user->basedir);
- if ((f = fopen(cfg_file_o, "r")) != NULL) {
+ if ((f = user_open_file(user, "user_o.json", 0)) != NULL) {
user->config_o = xs_json_load(f);
fclose(f);
if (user->config_o == NULL)
- srv_log(xs_fmt("error parsing '%s'", cfg_file_o));
+ srv_log(xs_fmt("error parsing '%s/user_o.json'", user->basedir));
}
if (user->config_o == NULL)
user->config_o = xs_dict_new();
}
else
- srv_log(xs_fmt("error parsing '%s'", key_file));
+ srv_log(xs_fmt("error parsing '%s/key.json'", user->basedir));
}
else
- srv_log(xs_fmt("error opening '%s' %d", key_file, errno));
+ srv_log(xs_fmt("error opening '%s/key.json' %d", user->basedir, errno));
}
else
- srv_log(xs_fmt("error parsing '%s'", cfg_file));
+ srv_log(xs_fmt("error parsing '%s/user.json'", user->basedir));
user->tz = xs_dict_get_def(user->config, "tz", "UTC");
}
else
- srv_debug(2, xs_fmt("error opening '%s' %d", cfg_file, errno));
+ srv_debug(2, xs_fmt("error opening '%s/user.json' %d", user->basedir, errno));
/* verified links */
- xs *links_file = xs_fmt("%s/links.json", user->basedir);
-
- if ((f = fopen(links_file, "r")) != NULL) {
+ if ((f = user_open_file(user, "links.json", 0)) != NULL) {
user->links = xs_json_load(f);
fclose(f);
}
@@ -305,6 +368,34 @@ int user_open(snac *user, const char *uid)
return ret;
}
+struct user_iter {
+ DIR *d;
+ struct dirent de;
+};
+
+void user_iter(struct user_iter *i)
+{
+ xs *d = xs_fmt("%s/user", srv_basedir);
+ i->d = opendir(d);
+}
+
+const char *user_iter_next(struct user_iter *i)
+{
+ struct dirent *d;
+ do {
+ if (readdir_r(i->d, &i->de, &d) != 0)
+ return NULL;
+ } while (d && (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")));
+
+ if (d)
+ return d->d_name;
+ return NULL;
+}
+
+void user_iter_close(struct user_iter *i)
+{
+ closedir(i->d);
+}
xs_list *user_list(void)
/* returns the list of user ids */
@@ -313,15 +404,13 @@ xs_list *user_list(void)
return xs_glob(spec, 1, 0);
}
-
int user_open_by_md5(snac *snac, const char *md5)
/* iterates all users searching by md5 */
{
- xs *ulist = user_list();
- xs_list *p = ulist;
- const xs_str *v;
-
- while (xs_list_iter(&p, &v)) {
+ __attribute__ ((__cleanup__(user_iter_close))) struct user_iter i = { NULL };
+ const char *v;
+ user_iter(&i);
+ while ((v = user_iter_next(&i))) {
user_open(snac, v);
if (strcmp(snac->md5, md5) == 0)
@@ -336,13 +425,11 @@ int user_open_by_md5(snac *snac, const char *md5)
int user_persist(snac *snac, int publish)
/* store user */
{
- xs *fn = xs_fmt("%s/user.json", snac->basedir);
- xs *bfn = xs_fmt("%s.bak", fn);
FILE *f;
if (publish) {
/* check if any of the relevant fields have really changed */
- if ((f = fopen(fn, "r")) != NULL) {
+ if ((f = user_open_file(snac, "user.json", 0)) != NULL) {
xs *old = xs_json_load(f);
fclose(f);
@@ -374,14 +461,14 @@ int user_persist(snac *snac, int publish)
}
}
- rename(fn, bfn);
+ renameat(snac->basedfd, "user.json", snac->basedfd, "user.json.bak");
- if ((f = fopen(fn, "w")) != NULL) {
+ if ((f = user_open_file(snac, "user.json", 1)) != NULL) {
xs_json_dump(snac->config, 4, f);
fclose(f);
}
else
- rename(bfn, fn);
+ renameat(snac->basedfd, "user.json.bak", snac->basedfd, "user.json");
history_del(snac, "timeline.html_");
timeline_touch(snac);
@@ -399,6 +486,24 @@ int user_persist(snac *snac, int publish)
}
+double mtime_f_nl(FILE *f, int *n_link)
+/* returns the mtime and number of links of a file or directory, or 0.0 */
+{
+ struct stat st;
+ double r = 0.0;
+ int n = 0;
+
+ if (f && fstat(fileno(f), &st) != -1) {
+ r = (double) st.st_mtim.tv_sec;
+ n = st.st_nlink;
+ }
+
+ if (n_link)
+ *n_link = n;
+
+ return r;
+}
+
double mtime_nl(const char *fn, int *n_link)
/* returns the mtime and number of links of a file or directory, or 0.0 */
{
@@ -481,7 +586,7 @@ int index_add_md5(const char *fn, const char *md5)
int index_add(const char *fn, const char *id)
/* adds an id to an index */
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return index_add_md5(fn, md5);
}
@@ -526,7 +631,7 @@ int index_del_md5(const char *fn, const char *md5)
int index_del(const char *fn, const char *id)
/* deletes an id from an index */
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return index_del_md5(fn, md5);
}
@@ -602,7 +707,7 @@ int index_in_md5(const char *fn, const char *md5)
int index_in(const char *fn, const char *id)
/* checks if the object id is already in the index */
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return index_in_md5(fn, md5);
}
@@ -795,7 +900,7 @@ static xs_str *_object_fn_by_md5(const char *md5, const char *func)
static xs_str *_object_fn(const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return _object_fn_by_md5(md5, "_object_fn");
}
@@ -840,7 +945,7 @@ int object_get_by_md5(const char *md5, xs_dict **obj)
int object_get(const char *id, xs_dict **obj)
/* returns a stored object, optionally of the requested type */
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return object_get_by_md5(md5, obj);
}
@@ -950,7 +1055,7 @@ int object_del_by_md5(const char *md5)
int object_del(const char *id)
/* deletes an object */
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return object_del_by_md5(md5);
}
@@ -978,7 +1083,7 @@ double object_ctime_by_md5(const char *md5)
double object_ctime(const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return object_ctime_by_md5(md5);
}
@@ -992,14 +1097,14 @@ double object_mtime_by_md5(const char *md5)
double object_mtime(const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return object_mtime_by_md5(md5);
}
void object_touch(const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
xs *fn = _object_fn_by_md5(md5, "object_touch");
if (mtime(fn))
@@ -1109,7 +1214,7 @@ xs_str *object_user_cache_fn_by_md5(snac *user, const char *md5, const char *cac
xs_str *object_user_cache_fn(snac *user, const char *id, const char *cachedir)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return object_user_cache_fn_by_md5(user, md5, cachedir);
}
@@ -1123,6 +1228,21 @@ xs_str *object_user_cache_index_fn(snac *user, const char *cachedir)
int _object_user_cache(snac *user, const char *id, const char *cachedir, int del)
/* adds or deletes from a user cache */
{
+ int ret;
+ xs *idx = object_user_cache_index_fn(user, cachedir);
+
+ if (del) {
+ ret = user_unlink_subfile(user, cachedir, md5_fn(id, ".json"));
+ index_del(idx, id);
+ }
+ else {
+ xs *ofn = _object_fn(id);
+ ret = user_link_subfile(user, cachedir, md5_fn(id, ".json"), AT_FDCWD, ofn);
+ index_add(idx, id);
+ }
+
+ return ret;
+#if 0
xs *ofn = _object_fn(id);
xs *cfn = object_user_cache_fn(user, id, cachedir);
xs *idx = object_user_cache_index_fn(user, cachedir);
@@ -1142,6 +1262,7 @@ int _object_user_cache(snac *user, const char *id, const char *cachedir, int del
}
return ret;
+#endif
}
@@ -1260,7 +1381,7 @@ int pending_add(snac *user, const char *actor, const xs_dict *msg)
/* stores the follow message for later confirmation */
{
xs *dir = xs_fmt("%s/pending", user->basedir);
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
xs *fn = xs_fmt("%s/%s.json", dir, md5);
FILE *f;
@@ -1279,7 +1400,7 @@ int pending_add(snac *user, const char *actor, const xs_dict *msg)
int pending_check(snac *user, const char *actor)
/* checks if there is a pending follow confirmation for the actor */
{
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
xs *fn = xs_fmt("%s/pending/%s.json", user->basedir, md5);
return mtime(fn) != 0;
@@ -1289,7 +1410,7 @@ int pending_check(snac *user, const char *actor)
xs_dict *pending_get(snac *user, const char *actor)
/* returns the pending follow confirmation for the actor */
{
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
xs *fn = xs_fmt("%s/pending/%s.json", user->basedir, md5);
xs_dict *msg = NULL;
FILE *f;
@@ -1306,7 +1427,7 @@ xs_dict *pending_get(snac *user, const char *actor)
void pending_del(snac *user, const char *actor)
/* deletes a pending follow confirmation for the actor */
{
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
xs *fn = xs_fmt("%s/pending/%s.json", user->basedir, md5);
unlink(fn);
@@ -1403,7 +1524,7 @@ int timeline_here_by_md5(snac *snac, const char *md5)
int timeline_here(snac *user, const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return timeline_here_by_md5(user, md5);
}
@@ -1637,7 +1758,7 @@ xs_list *timeline_instance_list(int skip, int show)
xs_str *_following_fn(snac *snac, const char *actor)
{
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
return xs_fmt("%s/following/%s.json", snac->basedir, md5);
}
@@ -1787,7 +1908,7 @@ xs_list *following_list(snac *snac)
xs_str *_muted_fn(snac *snac, const char *actor)
{
- xs *md5 = xs_md5_hex(actor, strlen(actor));
+ const char *md5 = xs_md5(actor);
return xs_fmt("%s/muted/%s", snac->basedir, md5);
}
@@ -2039,7 +2160,7 @@ void scheduled_process(snac *user)
xs_str *_hidden_fn(snac *snac, const char *id)
{
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return xs_fmt("%s/hidden/%s", snac->basedir, md5);
}
@@ -2167,7 +2288,7 @@ int limited(snac *user, const char *id, int cmd)
{
int ret = 0;
xs *dir = xs_fmt("%s/limited", user->basedir);
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
xs *fn = xs_fmt("%s/%s", dir, md5);
switch (cmd) {
@@ -2210,7 +2331,7 @@ void tag_index(const char *id, const xs_dict *obj)
/* update the tag indexes for this object */
{
const xs_list *tags = xs_dict_get(obj, "tag");
- xs *md5_id = xs_md5_hex(id, strlen(id));
+ const char *md5_id = xs_md5(id);
if (is_msg_public(obj) && xs_type(tags) == XSTYPE_LIST && xs_list_len(tags) > 0) {
xs *g_tag_dir = xs_fmt("%s/tag", srv_basedir);
@@ -2232,7 +2353,7 @@ void tag_index(const char *id, const xs_dict *obj)
name = xs_utf8_to_lower((xs_str *)name);
- xs *md5_tag = xs_md5_hex(name, strlen(name));
+ const char *md5_tag = xs_md5(name);
xs *tag_dir = xs_fmt("%s/%c%c", g_tag_dir, md5_tag[0], md5_tag[1]);
mkdirx(tag_dir);
@@ -2261,7 +2382,7 @@ xs_str *tag_fn(const char *tag)
tag++;
xs *lw_tag = xs_utf8_to_lower(tag);
- xs *md5 = xs_md5_hex(lw_tag, strlen(lw_tag));
+ const char *md5 = xs_md5(lw_tag);
return xs_fmt("%s/tag/%c%c/%s.idx", srv_basedir, md5[0], md5[1], md5);
}
@@ -2477,8 +2598,8 @@ void list_distribute(snac *user, const char *who, const xs_dict *post)
who = get_atto(post);
if (xs_type(who) == XSTYPE_STRING && xs_type(id) == XSTYPE_STRING) {
- xs *a_md5 = xs_md5_hex(who, strlen(who));
- xs *i_md5 = xs_md5_hex(id, strlen(id));
+ const char *a_md5 = xs_md5(who);
+ const char *i_md5 = xs_md5(id);
xs *spec = xs_fmt("%s/list/" "*.lst", user->basedir);
xs *ls = xs_glob(spec, 0, 0);
int c = 0;
@@ -2500,14 +2621,14 @@ void list_distribute(snac *user, const char *who, const xs_dict *post)
/** static data **/
-static int _load_raw_file(const char *fn, xs_val **data, int *size,
+static int _load_raw_file(FILE *f, xs_val **data, int *size,
const char *inm, xs_str **etag)
/* loads a cached file */
{
int status = HTTP_STATUS_NOT_FOUND;
- if (fn) {
- double tm = mtime(fn);
+ if (f) {
+ double tm = mtime_f(f);
if (tm > 0.0) {
/* file exists; build the etag */
@@ -2520,22 +2641,16 @@ static int _load_raw_file(const char *fn, xs_val **data, int *size,
}
else {
/* newer or never downloaded; read the full file */
- FILE *f;
-
- if ((f = fopen(fn, "rb")) != NULL) {
- *size = XS_ALL;
- *data = xs_read(f, size);
- fclose(f);
+ *size = XS_ALL;
+ *data = xs_read(f, size);
+ fclose(f);
- status = HTTP_STATUS_OK;
- }
+ status = HTTP_STATUS_OK;
}
/* if caller wants the etag, return it */
if (etag != NULL)
*etag = xs_dup(e);
-
- srv_debug(1, xs_fmt("_load_raw_file(): %s %d", fn, status));
}
}
@@ -2619,9 +2734,7 @@ int static_get(snac *snac, const char *id, xs_val **data, int *size,
const char *inm, xs_str **etag)
/* returns static content */
{
- xs *fn = _static_fn(snac, id);
-
- return _load_raw_file(fn, data, size, inm, etag);
+ return _load_raw_file(user_open_subfile(snac, "static", id, 0), data, size, inm, etag);
}
int static_get_partial(snac *snac, const char *id, xs_val **data, int *size,
@@ -2732,9 +2845,7 @@ void history_add(snac *snac, const char *id, const char *content, int size,
int history_get(snac *snac, const char *id, xs_str **content, int *size,
const char *inm, xs_str **etag)
{
- xs *fn = _history_fn(snac, id);
-
- return _load_raw_file(fn, content, size, inm, etag);
+ return _load_raw_file(user_open_subfile(snac, "history", id, 0), content, size, inm, etag);
}
@@ -2779,7 +2890,7 @@ void inbox_add(const char *inbox)
if (xs_startswith(inbox, srv_baseurl))
return;
- xs *md5 = xs_md5_hex(inbox, strlen(inbox));
+ const char *md5 = xs_md5(inbox);
xs *fn = xs_fmt("%s/inbox/%s", srv_basedir, md5);
FILE *f;
@@ -2841,7 +2952,7 @@ xs_str *_instance_block_fn(const char *instance)
xs *s1 = xs_replace(s, "https:/" "/", "");
xs *l = xs_split(s1, "/");
const char *p = xs_list_get(l, 0);
- xs *md5 = xs_md5_hex(p, strlen(p));
+ const char *md5 = xs_md5(p);
return xs_fmt("%s/block/%s", srv_basedir, md5);
}
@@ -3028,8 +3139,7 @@ xs_list *content_search(snac *user, const char *regex,
/* recalculate the md5 id to be sure it's not repeated
(it may have been searched by the "url" field instead of "id") */
- xs *new_md5 = xs_md5_hex(id, strlen(id));
- md5 = new_md5;
+ md5 = xs_md5(id);
/* test for the post URL */
if (strcmp(id, regex) == 0) {
@@ -4207,8 +4317,7 @@ xs_str *make_url(const char *href, const char *proxy, int by_token)
xs *p = NULL;
if (by_token) {
- xs *tks = xs_fmt("%s:%s", srv_proxy_token_seed, proxy);
- xs *tk = xs_md5_hex(tks, strlen(tks));
+ const char *tk = xs_md5(srv_proxy_token_seed, ":", proxy);
p = xs_fmt("%s/y/%s/", proxy, tk);
}
@@ -4228,7 +4337,7 @@ xs_str *make_url(const char *href, const char *proxy, int by_token)
xs_str *_badlogin_fn(const char *addr)
{
- xs *md5 = xs_md5_hex(addr, strlen(addr));
+ const char *md5 = xs_md5(addr);
xs *dir = xs_fmt("%s/badlogin", srv_basedir);
mkdirx(dir);
diff --git a/html.c b/html.c
@@ -203,7 +203,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
/* if this actor is a follower or being followed, create an
anchored link to the people page instead of the actor url */
if (fwer || fwing) {
- xs *md5 = xs_md5_hex(actor_id, strlen(actor_id));
+ const char *md5 = xs_md5(actor_id);
href = xs_fmt("%s/people#%s", user->actor, md5);
}
}
@@ -223,7 +223,7 @@ xs_html *html_actor_icon(snac *user, xs_dict *actor, const char *date,
xs_html_raw(name))); /* name is already html-escaped */
if (!xs_is_null(url)) {
- xs *md5 = xs_md5_hex(url, strlen(url));
+ const char *md5 = xs_md5(url);
xs_html_add(actor_icon,
xs_html_text(" "),
@@ -3170,7 +3170,7 @@ xs_html *html_people_list(snac *user, xs_list *list, const char *header, const c
const char *actor_id;
xs_list_foreach(list, actor_id) {
- xs *md5 = xs_md5_hex(actor_id, strlen(actor_id));
+ const char *md5 = xs_md5(actor_id);
xs *actor = NULL;
if (valid_status(actor_get(actor_id, &actor))) {
@@ -3506,7 +3506,7 @@ xs_str *html_notifications(snac *user, int skip, int show)
}
else
if (obj != NULL) {
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
xs *ctxt = xs_fmt("%s/admin/p/%s#%s_entry", user->actor, md5, md5);
xs_html *h = html_entry(user, obj, 0, 0, md5, 1);
@@ -4091,7 +4091,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
xs *msg = NULL;
if (valid_status(object_get(id, &msg))) {
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
xs *list = xs_list_new();
list = xs_list_append(list, md5);
@@ -4196,8 +4196,7 @@ int html_get_handler(const xs_dict *req, const char *q_path,
}
else {
/* proxy usage authorized by proxy_token */
- xs *tks = xs_fmt("%s:%s", srv_proxy_token_seed, snac.actor);
- xs *tk = xs_md5_hex(tks, strlen(tks));
+ const char *tk = xs_md5(srv_proxy_token_seed, ":", snac.actor);
xs *p = xs_fmt("y/%s/", tk);
if (xs_startswith(p_path, p))
@@ -4412,7 +4411,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
xs_rnd_buf(rnd, sizeof(rnd));
const char *ext = strrchr(fn, '.');
- xs *hash = xs_md5_hex(rnd, sizeof(rnd));
+ const char *hash = xs_md5_arr(rnd);
xs *id = xs_fmt("post-%s%s", hash, ext ? ext : "");
xs *url = xs_fmt("%s/s/%s", snac.actor, id);
int fo = xs_number_get(xs_list_get(attach_file, 1));
@@ -4892,7 +4891,7 @@ int html_post_handler(const xs_dict *req, const char *q_path,
if (xs_startswith(mimetype, "image/")) {
const char *ext = strrchr(fn, '.');
- xs *hash = xs_md5_hex(fn, strlen(fn));
+ const char *hash = xs_md5(fn);
xs *id = xs_fmt("%s-%s%s", uploads[n], hash, ext ? ext : "");
xs *url = xs_fmt("%s/s/%s", snac.actor, id);
int fo = xs_number_get(xs_list_get(uploaded_file, 1));
diff --git a/main.c b/main.c
@@ -764,7 +764,7 @@ int main(int argc, char *argv[])
fclose(f);
char *ext = strrchr(fn, '.');
- xs *hash = xs_md5_hex(fn, strlen(fn));
+ const char *hash = xs_md5(fn);
xs *id = xs_fmt("%s%s", hash, ext);
xs *url = xs_fmt("%s/s/%s", snac.actor, id);
diff --git a/mastoapi.c b/mastoapi.c
@@ -21,13 +21,16 @@
#include <sys/time.h>
-static xs_str *random_str(void)
+#define RAND_SIZE 4
+#define random_str() _random_str((char[RAND_SIZE * sizeof(unsigned int) * 2 + 1]){ 0 })
+#define reshuffle(buf) _random_str(buf)
+static const char *_random_str(char *buf)
/* just what is says in the tin */
{
- unsigned int data[4] = {0};
+ unsigned int data[RAND_SIZE] = {0};
xs_rnd_buf(data, sizeof(data));
- return xs_hex_enc((char *)data, sizeof(data));
+ return xs_hex_enc_buf((char *)data, sizeof(data), buf);
}
@@ -301,7 +304,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
/* check the login + password */
if (check_password(login, passwd, xs_dict_get(snac.config, "passwd"))) {
/* success! redirect to the desired uri */
- xs *code = random_str();
+ const char *code = random_str();
xs_free(*body);
@@ -355,7 +358,8 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
if (strcmp(cmd, "/token") == 0) { /** **/
xs *wrk = NULL;
const char *gtype = xs_dict_get(args, "grant_type");
- const char *code = xs_dict_get(args, "code");
+ const xs_val *code = xs_dict_get(args, "code");
+ xs *cc = NULL;
const char *cid = xs_dict_get(args, "client_id");
const char *csec = xs_dict_get(args, "client_secret");
const char *ruri = xs_dict_get(args, "redirect_uri");
@@ -386,7 +390,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
I'm not sure of the impacts of this right now, but Subway Tooter does not
provide a code so one must be generated */
if (xs_is_null(code)){
- code = random_str();
+ code = cc = xs_dup(random_str());
}
if (gtype && code && cid && csec && ruri) {
xs *app = app_get(cid);
@@ -403,7 +407,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
else {
xs *rsp = xs_dict_new();
xs *cat = xs_number_new(time(NULL));
- xs *tokid = random_str();
+ const char *tokid = random_str();
rsp = xs_dict_append(rsp, "access_token", tokid);
rsp = xs_dict_append(rsp, "token_type", "Bearer");
@@ -489,7 +493,7 @@ int oauth_post_handler(const xs_dict *req, const char *q_path,
/* check the login + password */
if (check_password(login, passwd, xs_dict_get(user.config, "passwd"))) {
/* success! create a new token */
- xs *tokid = random_str();
+ const char *tokid = random_str();
srv_debug(1, xs_fmt("x-snac-new-token: "
"successful login for %s, new token %s", login, tokid));
@@ -524,7 +528,7 @@ xs_str *mastoapi_id(const xs_dict *msg)
/* returns a somewhat Mastodon-compatible status id */
{
const char *id = xs_dict_get(msg, "id");
- xs *md5 = xs_md5_hex(id, strlen(id));
+ const char *md5 = xs_md5(id);
return xs_fmt("%10.0f%s", object_ctime_by_md5(md5), md5);
}
@@ -552,7 +556,7 @@ xs_dict *mastoapi_account(snac *logged, const xs_dict *actor)
display_name = prefu;
xs_dict *acct = xs_dict_new();
- xs *acct_md5 = xs_md5_hex(id, strlen(id));
+ const char *acct_md5 = xs_md5(id);
acct = xs_dict_append(acct, "id", acct_md5);
acct = xs_dict_append(acct, "username", prefu);
acct = xs_dict_append(acct, "display_name", display_name);
@@ -1070,7 +1074,7 @@ xs_dict *mastoapi_status(snac *snac, const xs_dict *msg)
const char *at = NULL;
if (!xs_is_null(at = get_atto(irto))) {
- xs *at_md5 = xs_md5_hex(at, strlen(at));
+ const char *at_md5 = xs_md5(at);
st = xs_dict_set(st, "in_reply_to_account_id", at_md5);
}
}
@@ -2672,19 +2676,18 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
if (name && ruri) {
xs *app = xs_dict_new();
xs *id = xs_replace_i(tid(0), ".", "");
- xs *csec = random_str();
- xs *vkey = random_str();
- xs *cid = NULL;
+ const char *csec = random_str();
+ const char *vkey = random_str();
+ char *cid = (char *)random_str();
/* pick a non-existent random cid */
for (;;) {
- cid = random_str();
xs *p_app = app_get(cid);
if (p_app == NULL)
break;
- xs_free(cid);
+ reshuffle(cid);
}
app = xs_dict_append(app, "name", name);
@@ -2952,7 +2955,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
v = xs_dict_get(v, "endpoint");
wpush = xs_dict_append(wpush, "endpoint", v);
- xs *server_key = random_str();
+ const char *server_key = random_str();
wpush = xs_dict_append(wpush, "server_key", server_key);
*body = xs_json_dumps(wpush, 4);
@@ -2980,7 +2983,7 @@ int mastoapi_post_handler(const xs_dict *req, const char *q_path,
char *ext = strrchr(fn, '.');
char rnd[32];
xs_rnd_buf(rnd, sizeof(rnd));
- xs *hash = xs_md5_hex(rnd, sizeof(rnd));
+ const char *hash = xs_md5_arr(rnd);
xs *id = xs_fmt("post-%s%s", hash, ext ? ext : "");
xs *url = xs_fmt("%s/s/%s", snac.actor, id);
int fo = xs_number_get(xs_list_get(file, 1));
@@ -3528,8 +3531,8 @@ void persist_image(const char *key, const xs_val *data, const char *payload, sna
/* Make sure we have a unique file name, otherwise updated images will not be
* re-loaded by clients. */
- xs *rnd = random_str();
- xs *hash = xs_md5_hex(rnd, strlen(rnd));
+ const char *rnd = random_str();
+ const char *hash = xs_md5(rnd);
xs *id = xs_fmt("%s%s", hash, ext);
xs *url = xs_fmt("%s/s/%s", snac->actor, id);
int fo = xs_number_get(xs_list_get(data, 1));
diff --git a/snac.c b/snac.c
@@ -31,6 +31,7 @@
#include <sys/time.h>
#include <sys/stat.h>
+#include <fcntl.h>
xs_str *srv_basedir = NULL;
xs_dict *srv_config = NULL;
@@ -40,23 +41,29 @@ xs_dict *srv_langs = NULL;
int dbglevel = 0;
-
-int mkdirx(const char *pathname)
-/* creates a directory with special permissions */
+int mkdiratx(int base, const char *pathname)
{
int ret;
- if ((ret = mkdir(pathname, DIR_PERM)) != -1) {
+ if ((ret = mkdirat(base, pathname, DIR_PERM)) != -1) {
/* try to the set the setgid bit, to allow system users
to create files in these directories using the
command-line tool. This may fail in some restricted
environments, but it's of no use there anyway */
- chmod(pathname, DIR_PERM_ADD);
+ fchmodat(base, pathname, DIR_PERM_ADD, 0);
}
+ if (errno == EEXIST)
+ return 0;
return ret;
}
+int mkdirx(const char *pathname)
+/* creates a directory with special permissions */
+{
+ return mkdiratx(AT_FDCWD, pathname);
+}
+
int valid_status(int status)
/* is this HTTP status valid? */
diff --git a/snac.h b/snac.h
@@ -42,6 +42,7 @@ extern int dbglevel;
#define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video|Audio|Event"
int mkdirx(const char *pathname);
+int mkdiratx(int base, const char *pathname);
int valid_status(int status);
xs_str *tid(int offset);
@@ -62,6 +63,7 @@ typedef struct {
xs_str *md5; /* actor url md5 */
const xs_dict *lang;/* string translation dict */
const char *tz; /* configured timezone */
+ int basedfd;
} snac;
typedef struct {
@@ -91,6 +93,8 @@ void user_free(snac *snac);
xs_list *user_list(void);
int user_open_by_md5(snac *snac, const char *md5);
int user_persist(snac *snac, int publish);
+FILE *user_open_file(snac *snac, const char *file, int wr);
+FILE *user_open_subfile(snac *snac, const char *sub, const char *file, int wr);
int validate_uid(const char *uid);
@@ -106,7 +110,9 @@ void srv_archive_error(const char *prefix, const xs_str *err,
void srv_archive_qitem(const char *prefix, xs_dict *q_item);
double mtime_nl(const char *fn, int *n_link);
+double mtime_f_nl(FILE *f, int *n_link);
#define mtime(fn) mtime_nl(fn, NULL)
+#define mtime_f(f) mtime_f_nl(f, NULL)
double f_ctime(const char *fn);
int index_add_md5(const char *fn, const char *md5);
diff --git a/utils.c b/utils.c
@@ -849,7 +849,7 @@ void import_list_csv(snac *user, const char *ifn)
xs *uid = NULL;
if (valid_status(webfinger_request(acct, &url, &uid))) {
- xs *actor_md5 = xs_md5_hex(url, strlen(url));
+ const char *actor_md5 = xs_md5(url);
list_content(user, list_id, actor_md5, 1);
snac_log(user, xs_fmt("Added %s to list %s", url, lname));
diff --git a/xs.h b/xs.h
@@ -408,7 +408,6 @@ xs_val *xs_expand(xs_val *data, int offset, int size)
{
xstype type = xs_type(data);
int sz = xs_size(data) + size;
- int n;
/* open room */
data = xs_realloc(data, _xs_blk_size(sz));
@@ -798,6 +797,13 @@ xs_list *_xs_list_append(xs_list *list, const xs_val *vals[])
return list;
}
+xs_list *xs_list_append_nstr(xs_list *list, const char *str, int n)
+{
+ list = xs_list_append_m(list, str, n + 1);
+ list[xs_size(list) - 2] = '\0';
+ return list;
+}
+
int xs_list_iter(xs_list **list, const xs_val **value)
/* iterates a list value */
@@ -1233,10 +1239,11 @@ xs_dict *_xs_dict_ensure(xs_dict *dict, const xs_str *key, int vsz, int *offset)
/* not enough room: new value will live at the end of the dict */
/* (old value is leaked inside the dict) */
*i = end;
+ *offset = *i;
dict = xs_expand(dict, end, vsz);
- }
- *offset = *i;
+ } else
+ *offset = *i;
}
}
else {
diff --git a/xs_hex.h b/xs_hex.h
@@ -9,6 +9,7 @@
int xs_hex_dec_1(char **dst, const char **src);
char *_xs_hex_enc(char *dst, const char *src, int src_size);
char *_xs_hex_dec(char *dst, const char *src, int src_size);
+ xs_str *xs_hex_enc_buf(const xs_val *data, int size, xs_str *buf);
#ifdef _XS_H
xs_str *xs_hex_enc(const xs_val *data, int size);
@@ -101,18 +102,22 @@ char *_xs_hex_dec(char *dst, const char *src, int src_size)
#ifdef _XS_H
-xs_str *xs_hex_enc(const xs_val *data, int size)
-/* returns an hexdump of data */
+xs_str *xs_hex_enc_buf(const xs_val *data, int size, xs_str *buf)
{
- xs_str *s = xs_realloc(NULL, _xs_blk_size(size * 2 + 1));
-
- char *q = _xs_hex_enc(s, data, size);
+ char *q = _xs_hex_enc(buf, data, size);
*q = '\0';
- return s;
+ return buf;
}
+xs_str *xs_hex_enc(const xs_val *data, int size)
+/* returns an hexdump of data */
+{
+ xs_str *s = xs_realloc(NULL, _xs_blk_size(size * 2 + 1));
+
+ return xs_hex_enc_buf(data, size, s);
+}
xs_val *xs_hex_dec(const xs_str *hex, int *size)
/* decodes an hexdump into data */
diff --git a/xs_openssl.h b/xs_openssl.h
@@ -5,9 +5,13 @@
#define _XS_OPENSSL_H
xs_str *_xs_digest(const xs_val *input, int size, const char *digest, int as_hex);
+xs_str *_xs_md5_buf(xs_str *buffer, const char *input, ...);
+xs_str *_xs_md5_buf_bin(xs_str *buffer, const void *input, int size);
#ifndef _XS_MD5_H
#define xs_md5_hex(input, size) _xs_digest(input, size, "md5", 1)
+#define xs_md5(...) _xs_md5_buf((char[MD5_HEX_SIZE]){ 0 }, __VA_ARGS__, NULL)
+#define xs_md5_arr(arr) _xs_md5_buf_bin((char[MD5_HEX_SIZE]){ 0 }, arr, sizeof(arr))
#endif /* XS_MD5_H */
#ifndef _XS_BASE64_H
@@ -90,6 +94,53 @@ xs_val *xs_base64_dec(const xs_str *data, int *size)
#endif /* _XS_BASE64_H */
+xs_str *_xs_md5_buf_bin(xs_str *buffer, const void *input, int size)
+{
+ const EVP_MD *md;
+
+ if ((md = EVP_get_digestbyname("md5")) == NULL)
+ return NULL;
+
+ unsigned char output[EVP_MAX_MD_SIZE];
+ unsigned int out_size;
+ EVP_MD_CTX *mdctx;
+
+ mdctx = EVP_MD_CTX_new();
+ EVP_DigestInit_ex(mdctx, md, NULL);
+ EVP_DigestUpdate(mdctx, input, size);
+ EVP_DigestFinal_ex(mdctx, output, &out_size);
+ EVP_MD_CTX_free(mdctx);
+
+ return xs_hex_enc_buf((const char *)output, out_size, buffer);
+}
+
+xs_str *_xs_md5_buf(xs_str *buffer, const char *input, ...)
+/* generic function for generating and encoding digests */
+{
+ const EVP_MD *md;
+ va_list args;
+
+ if ((md = EVP_get_digestbyname("md5")) == NULL)
+ return NULL;
+
+ unsigned char output[EVP_MAX_MD_SIZE];
+ unsigned int out_size;
+ EVP_MD_CTX *mdctx;
+
+ mdctx = EVP_MD_CTX_new();
+ EVP_DigestInit_ex(mdctx, md, NULL);
+ va_start(args, input);
+ while (input) {
+ EVP_DigestUpdate(mdctx, input, strlen(input));
+ input = va_arg(args, const char *);
+ }
+ va_end(args);
+ EVP_DigestFinal_ex(mdctx, output, &out_size);
+ EVP_MD_CTX_free(mdctx);
+
+ return xs_hex_enc_buf((const char *)output, out_size, buffer);
+}
+
xs_str *_xs_digest(const xs_val *input, int size, const char *digest, int as_hex)
/* generic function for generating and encoding digests */
diff --git a/xs_regex.h b/xs_regex.h
@@ -42,14 +42,15 @@ xs_list *xs_regex_split_n(const char *str, const char *rx, int count)
while (count > 0 && !regexec(&re, (p = str + offset), 1, &rm, offset > 0 ? REG_NOTBOL : 0)) {
/* add first the leading part of the string */
- xs *s1 = xs_str_new_sz(p, rm.rm_so);
-
- list = xs_list_append(list, xs_is_string(s1) ? s1 : "");
-
- /* add now the matched text as the separator */
- xs *s2 = xs_str_new_sz(p + rm.rm_so, rm.rm_eo - rm.rm_so);
+ if (xs_is_string(p))
+ list = xs_list_append_nstr(list, p, rm.rm_so);
+ else
+ list = xs_list_append(list, "");
- list = xs_list_append(list, xs_is_string(s2) ? s2 : "");
+ if (xs_is_string(p + rm.rm_so))
+ list = xs_list_append_nstr(list, p + rm.rm_so, rm.rm_eo - rm.rm_so);
+ else
+ list = xs_list_append(list, "");
/* move forward */
offset += rm.rm_eo;
diff --git a/xs_set.h b/xs_set.h
@@ -24,12 +24,10 @@ void xs_set_init(xs_set *s)
/* initializes a set */
{
/* arbitrary default */
- s->elems = 256;
+ s->elems = 0;
s->used = 0;
- s->hash = xs_realloc(NULL, s->elems * sizeof(int));
- s->list = xs_list_new();
-
- memset(s->hash, '\0', s->elems * sizeof(int));
+ s->hash = NULL;
+ s->list = NULL;
}
@@ -88,9 +86,11 @@ int xs_set_add(xs_set *s, const xs_val *data)
const xs_val *v;
/* expand! */
- s->elems *= 2;
+ s->elems = s->elems ? s->elems * 2 : 256;
s->used = 0;
s->hash = xs_realloc(s->hash, s->elems * sizeof(int));
+ if (!s->list)
+ s->list = xs_list_new();
memset(s->hash, '\0', s->elems * sizeof(int));