snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

commit 309e46917a00d220dea4aff0b19ce38660459fff
parent 3d9256337ad035a7ba1395530dbee3adb0fe9bdc
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date:   Wed, 26 Feb 2025 15:14:00 +0200

Reduce heap allocations

Diffstat:
Mactivitypub.c | 20++++++++++++--------
Mdata.c | 267+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mhtml.c | 17++++++++---------
Mmain.c | 2+-
Mmastoapi.c | 43+++++++++++++++++++++++--------------------
Msnac.c | 17++++++++++++-----
Msnac.h | 6++++++
Mutils.c | 2+-
Mxs.h | 13++++++++++---
Mxs_hex.h | 17+++++++++++------
Mxs_openssl.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mxs_regex.h | 15++++++++-------
Mxs_set.h | 12++++++------
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));