diff options
| -rw-r--r-- | Makefile | 21 | ||||
| -rw-r--r-- | Makefile.NetBSD | 14 | ||||
| -rw-r--r-- | activitypub.c | 14 | ||||
| -rw-r--r-- | data.c | 45 | ||||
| -rw-r--r-- | doc/snac.8 | 17 | ||||
| -rw-r--r-- | format.c | 2 | ||||
| -rw-r--r-- | html.c | 224 | ||||
| -rw-r--r-- | httpd.c | 5 | ||||
| -rw-r--r-- | po/en.po | 687 | ||||
| -rw-r--r-- | snac.c | 3 | ||||
| -rw-r--r-- | snac.h | 12 | ||||
| -rw-r--r-- | xs_curl.h | 11 | ||||
| -rw-r--r-- | xs_json.h | 40 | ||||
| -rw-r--r-- | xs_po.h | 86 | ||||
| -rw-r--r-- | xs_regex.h | 6 | ||||
| -rw-r--r-- | xs_version.h | 2 |
16 files changed, 1062 insertions, 127 deletions
| @@ -38,24 +38,31 @@ uninstall: | |||
| 38 | rm $(PREFIX_MAN)/man5/snac.5 | 38 | rm $(PREFIX_MAN)/man5/snac.5 |
| 39 | rm $(PREFIX_MAN)/man8/snac.8 | 39 | rm $(PREFIX_MAN)/man8/snac.8 |
| 40 | 40 | ||
| 41 | update-po: | ||
| 42 | mkdir -p po | ||
| 43 | [ -f "po/en.po" ] || xgettext -o po/en.po --language=C --keyword=L --from-code=utf-8 *.c | ||
| 44 | for a in po/*.po ; do \ | ||
| 45 | xgettext --omit-header -j -o $$a --language=C --keyword=L --from-code=utf-8 *.c ; \ | ||
| 46 | done | ||
| 47 | |||
| 41 | activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ | 48 | activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ |
| 42 | xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ | 49 | xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ |
| 43 | snac.h http_codes.h | 50 | snac.h http_codes.h |
| 44 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ | 51 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ |
| 45 | xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \ | 52 | xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ |
| 46 | http_codes.h | 53 | xs_po.h snac.h http_codes.h |
| 47 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ | 54 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ |
| 48 | xs_time.h xs_match.h snac.h http_codes.h | 55 | xs_time.h xs_match.h snac.h http_codes.h |
| 49 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ | 56 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ |
| 50 | xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ | 57 | xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ |
| 51 | http_codes.h | 58 | snac.h http_codes.h |
| 52 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ | 59 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ |
| 53 | snac.h http_codes.h | 60 | snac.h http_codes.h |
| 54 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ | 61 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ |
| 55 | xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ | 62 | xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ |
| 56 | http_codes.h | 63 | http_codes.h |
| 57 | main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h snac.h \ | 64 | main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \ |
| 58 | http_codes.h | 65 | snac.h http_codes.h |
| 59 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ | 66 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ |
| 60 | xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ | 67 | xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ |
| 61 | snac.h http_codes.h | 68 | snac.h http_codes.h |
| @@ -63,7 +70,7 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h | |||
| 63 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ | 70 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ |
| 64 | xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ | 71 | xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ |
| 65 | xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ | 72 | xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ |
| 66 | xs_match.h xs_fcgi.h xs_html.h snac.h http_codes.h | 73 | xs_match.h xs_fcgi.h xs_html.h xs_po.h snac.h http_codes.h |
| 67 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h | 74 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h |
| 68 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ | 75 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ |
| 69 | xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h | 76 | xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h |
diff --git a/Makefile.NetBSD b/Makefile.NetBSD index 93222b2..51c8181 100644 --- a/Makefile.NetBSD +++ b/Makefile.NetBSD | |||
| @@ -39,20 +39,20 @@ activitypub.o: activitypub.c xs.h xs_json.h xs_curl.h xs_mime.h \ | |||
| 39 | xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ | 39 | xs_openssl.h xs_regex.h xs_time.h xs_set.h xs_match.h xs_unicode.h \ |
| 40 | snac.h http_codes.h | 40 | snac.h http_codes.h |
| 41 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ | 41 | data.o: data.c xs.h xs_hex.h xs_io.h xs_json.h xs_openssl.h xs_glob.h \ |
| 42 | xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h snac.h \ | 42 | xs_set.h xs_time.h xs_regex.h xs_match.h xs_unicode.h xs_random.h \ |
| 43 | http_codes.h | 43 | xs_po.h snac.h http_codes.h |
| 44 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ | 44 | format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \ |
| 45 | xs_time.h xs_match.h snac.h http_codes.h | 45 | xs_time.h xs_match.h snac.h http_codes.h |
| 46 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ | 46 | html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ |
| 47 | xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h snac.h \ | 47 | xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ |
| 48 | http_codes.h | 48 | snac.h http_codes.h |
| 49 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ | 49 | http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ |
| 50 | snac.h http_codes.h | 50 | snac.h http_codes.h |
| 51 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ | 51 | httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ |
| 52 | xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ | 52 | xs_httpd.h xs_mime.h xs_time.h xs_openssl.h xs_fcgi.h xs_html.h snac.h \ |
| 53 | http_codes.h | 53 | http_codes.h |
| 54 | main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h snac.h \ | 54 | main.o: main.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h xs_match.h \ |
| 55 | http_codes.h | 55 | snac.h http_codes.h |
| 56 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ | 56 | mastoapi.o: mastoapi.c xs.h xs_hex.h xs_openssl.h xs_json.h xs_io.h \ |
| 57 | xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ | 57 | xs_time.h xs_glob.h xs_set.h xs_random.h xs_url.h xs_mime.h xs_match.h \ |
| 58 | snac.h http_codes.h | 58 | snac.h http_codes.h |
| @@ -60,7 +60,7 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h | |||
| 60 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ | 60 | snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ |
| 61 | xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ | 61 | xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ |
| 62 | xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ | 62 | xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ |
| 63 | xs_match.h xs_fcgi.h xs_html.h snac.h http_codes.h | 63 | xs_match.h xs_fcgi.h xs_html.h xs_po.h snac.h http_codes.h |
| 64 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h | 64 | upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h |
| 65 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ | 65 | utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ |
| 66 | xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h | 66 | xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h |
diff --git a/activitypub.c b/activitypub.c index 643baba..e2519e6 100644 --- a/activitypub.c +++ b/activitypub.c | |||
| @@ -2715,6 +2715,12 @@ int process_user_queue(snac *snac) | |||
| 2715 | } | 2715 | } |
| 2716 | 2716 | ||
| 2717 | 2717 | ||
| 2718 | xs_str *str_status(int status) | ||
| 2719 | { | ||
| 2720 | return xs_fmt("%d %s", status, status < 0 ? xs_curl_strerr(status) : http_status_text(status)); | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | |||
| 2718 | void process_queue_item(xs_dict *q_item) | 2724 | void process_queue_item(xs_dict *q_item) |
| 2719 | /* processes an item from the global queue */ | 2725 | /* processes an item from the global queue */ |
| 2720 | { | 2726 | { |
| @@ -2771,7 +2777,9 @@ void process_queue_item(xs_dict *q_item) | |||
| 2771 | else | 2777 | else |
| 2772 | payload = xs_str_new(NULL); | 2778 | payload = xs_str_new(NULL); |
| 2773 | 2779 | ||
| 2774 | srv_log(xs_fmt("output message: sent to inbox %s %d%s", inbox, status, payload)); | 2780 | xs *s_status = str_status(status); |
| 2781 | |||
| 2782 | srv_log(xs_fmt("output message: sent to inbox %s (%s)%s", inbox, s_status, payload)); | ||
| 2775 | 2783 | ||
| 2776 | if (!valid_status(status)) { | 2784 | if (!valid_status(status)) { |
| 2777 | retries++; | 2785 | retries++; |
| @@ -2789,10 +2797,10 @@ void process_queue_item(xs_dict *q_item) | |||
| 2789 | || status == HTTP_STATUS_UNPROCESSABLE_CONTENT | 2797 | || status == HTTP_STATUS_UNPROCESSABLE_CONTENT |
| 2790 | || status < 0) | 2798 | || status < 0) |
| 2791 | /* explicit error: discard */ | 2799 | /* explicit error: discard */ |
| 2792 | srv_log(xs_fmt("output message: error %s %d", inbox, status)); | 2800 | srv_log(xs_fmt("output message: error %s (%s)", inbox, s_status)); |
| 2793 | else | 2801 | else |
| 2794 | if (retries > queue_retry_max) | 2802 | if (retries > queue_retry_max) |
| 2795 | srv_log(xs_fmt("output message: giving up %s %d", inbox, status)); | 2803 | srv_log(xs_fmt("output message: giving up %s (%s)", inbox, s_status)); |
| 2796 | else { | 2804 | else { |
| 2797 | /* requeue */ | 2805 | /* requeue */ |
| 2798 | enqueue_output_raw(keyid, seckey, msg, inbox, retries, status); | 2806 | enqueue_output_raw(keyid, seckey, msg, inbox, retries, status); |
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "xs_match.h" | 13 | #include "xs_match.h" |
| 14 | #include "xs_unicode.h" | 14 | #include "xs_unicode.h" |
| 15 | #include "xs_random.h" | 15 | #include "xs_random.h" |
| 16 | #include "xs_po.h" | ||
| 16 | 17 | ||
| 17 | #include "snac.h" | 18 | #include "snac.h" |
| 18 | 19 | ||
| @@ -98,6 +99,9 @@ int srv_open(const char *basedir, int auto_upgrade) | |||
| 98 | if (error != NULL) | 99 | if (error != NULL) |
| 99 | srv_log(error); | 100 | srv_log(error); |
| 100 | 101 | ||
| 102 | if (!ret) | ||
| 103 | return ret; | ||
| 104 | |||
| 101 | /* create the queue/ subdir, just in case */ | 105 | /* create the queue/ subdir, just in case */ |
| 102 | xs *qdir = xs_fmt("%s/queue", srv_basedir); | 106 | xs *qdir = xs_fmt("%s/queue", srv_basedir); |
| 103 | mkdirx(qdir); | 107 | mkdirx(qdir); |
| @@ -148,6 +152,29 @@ int srv_open(const char *basedir, int auto_upgrade) | |||
| 148 | mkdirx(expdir); | 152 | mkdirx(expdir); |
| 149 | } | 153 | } |
| 150 | 154 | ||
| 155 | /* languages */ | ||
| 156 | srv_langs = xs_dict_new(); | ||
| 157 | srv_langs = xs_dict_set(srv_langs, "en", xs_stock(XSTYPE_NULL)); | ||
| 158 | |||
| 159 | xs *l_dir = xs_fmt("%s/lang/", srv_basedir); | ||
| 160 | mkdirx(l_dir); | ||
| 161 | |||
| 162 | l_dir = xs_str_cat(l_dir, "*.po"); | ||
| 163 | xs *pos = xs_glob(l_dir, 0, 0); | ||
| 164 | const char *po; | ||
| 165 | |||
| 166 | xs_list_foreach(pos, po) { | ||
| 167 | xs *d = xs_po_to_dict(po); | ||
| 168 | |||
| 169 | if (xs_is_dict(d)) { | ||
| 170 | xs *l = xs_split(po, "/"); | ||
| 171 | xs *id = xs_dup(xs_list_get(l, -1)); | ||
| 172 | id = xs_replace_i(id, ".po", ""); | ||
| 173 | |||
| 174 | srv_langs = xs_dict_set(srv_langs, id, d); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 151 | return ret; | 178 | return ret; |
| 152 | } | 179 | } |
| 153 | 180 | ||
| @@ -4064,3 +4091,21 @@ void badlogin_inc(const char *user, const char *addr) | |||
| 4064 | pthread_mutex_unlock(&data_mutex); | 4091 | pthread_mutex_unlock(&data_mutex); |
| 4065 | } | 4092 | } |
| 4066 | } | 4093 | } |
| 4094 | |||
| 4095 | |||
| 4096 | /** language strings **/ | ||
| 4097 | |||
| 4098 | const char *lang_str(const char *str, const snac *user) | ||
| 4099 | /* returns a translated string */ | ||
| 4100 | { | ||
| 4101 | const char *n_str = str; | ||
| 4102 | |||
| 4103 | if (user && xs_is_dict(user->lang) && xs_is_string(str)) { | ||
| 4104 | n_str = xs_dict_get(user->lang, str); | ||
| 4105 | |||
| 4106 | if (xs_is_null(n_str) || *n_str == '\0') | ||
| 4107 | n_str = str; | ||
| 4108 | } | ||
| 4109 | |||
| 4110 | return n_str; | ||
| 4111 | } | ||
| @@ -23,8 +23,12 @@ Ultrix machine in your grandfather basement, probably MacOS) support hard | |||
| 23 | links on their native filesystems. Don't do fancy things like moving the | 23 | links on their native filesystems. Don't do fancy things like moving the |
| 24 | subdirectories to different filesystems. Also, if you move your | 24 | subdirectories to different filesystems. Also, if you move your |
| 25 | .Nm | 25 | .Nm |
| 26 | installation to another server, do it with a tool that respect hard | 26 | installation to another server, do it with a tool that keeps hard |
| 27 | link counts. Remember: | 27 | links, like |
| 28 | .Xr tar 1 | ||
| 29 | or | ||
| 30 | .Xr rsync 1 | ||
| 31 | with the -H switch. Remember: | ||
| 28 | .Nm | 32 | .Nm |
| 29 | is a very UNIXy program that loves hard links. | 33 | is a very UNIXy program that loves hard links. |
| 30 | .Ss Building and Installation | 34 | .Ss Building and Installation |
| @@ -194,9 +198,7 @@ By setting this to true, no inbox collection is done. Inbox collection helps | |||
| 194 | being discovered from remote instances, but also increases network traffic. | 198 | being discovered from remote instances, but also increases network traffic. |
| 195 | .It Ic http_headers | 199 | .It Ic http_headers |
| 196 | If you need to add more HTTP response headers for whatever reason, you can | 200 | If you need to add more HTTP response headers for whatever reason, you can |
| 197 | fill this object with the required header/value pairs. For example, for enhanced | 201 | fill this object with the required header/value pairs. |
| 198 | XSS security, you can set the "Content-Security-Policy" header to "script-src ;" | ||
| 199 | to be totally sure that no JavaScript is executed. | ||
| 200 | .It Ic show_instance_timeline | 202 | .It Ic show_instance_timeline |
| 201 | If this is set to true, the instance base URL will show a timeline with the latest | 203 | If this is set to true, the instance base URL will show a timeline with the latest |
| 202 | user posts instead of the default greeting static page. If other information | 204 | user posts instead of the default greeting static page. If other information |
| @@ -327,8 +329,9 @@ These weapons of mass destruction can be written into the | |||
| 327 | file in the server base directory, one per line; if this file exists, | 329 | file in the server base directory, one per line; if this file exists, |
| 328 | all posts' content will be matched (after being stripped of HTML tags) | 330 | all posts' content will be matched (after being stripped of HTML tags) |
| 329 | against these regexes, one by one, and any match will make the post to | 331 | against these regexes, one by one, and any match will make the post to |
| 330 | be rejected. If you don't know about regular expressions, don't use this | 332 | be rejected. Use lower case, the regex will be case insensitive by default. |
| 331 | option (or learn about them in some tutorial, there are gazillions of | 333 | If you don't know about regular expressions, don't use this |
| 334 | option (or learn about them inw some tutorial, there are gazillions of | ||
| 332 | them out there), as you and your users may start missing posts. Also, | 335 | them out there), as you and your users may start missing posts. Also, |
| 333 | given that every regular expression implementation supports a different | 336 | given that every regular expression implementation supports a different |
| 334 | set of features, consider reading the documentation about the one | 337 | set of features, consider reading the documentation about the one |
| @@ -458,7 +458,7 @@ xs_str *sanitize(const char *content) | |||
| 458 | 458 | ||
| 459 | if (valid_tags[i]) { | 459 | if (valid_tags[i]) { |
| 460 | /* accepted tag: rebuild it with only the accepted elements */ | 460 | /* accepted tag: rebuild it with only the accepted elements */ |
| 461 | xs *el = xs_regex_select(v, "(src|href|rel|class|target)=\"[^\"]*\""); | 461 | xs *el = xs_regex_select(v, "(src|href|rel|class|target)=(\"[^\"]*\"|'[^']*')"); |
| 462 | xs *s3 = xs_join(el, " "); | 462 | xs *s3 = xs_join(el, " "); |
| 463 | 463 | ||
| 464 | s2 = xs_fmt("<%s%s%s%s>", | 464 | s2 = xs_fmt("<%s%s%s%s>", |
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | #include "snac.h" | 18 | #include "snac.h" |
| 19 | 19 | ||
| 20 | int login(snac *snac, const xs_dict *headers) | 20 | int login(snac *user, const xs_dict *headers) |
| 21 | /* tries a login */ | 21 | /* tries a login */ |
| 22 | { | 22 | { |
| 23 | int logged_in = 0; | 23 | int logged_in = 0; |
| @@ -31,23 +31,23 @@ int login(snac *snac, const xs_dict *headers) | |||
| 31 | xs *l1 = xs_split_n(s2, ":", 1); | 31 | xs *l1 = xs_split_n(s2, ":", 1); |
| 32 | 32 | ||
| 33 | if (xs_list_len(l1) == 2) { | 33 | if (xs_list_len(l1) == 2) { |
| 34 | const char *user = xs_list_get(l1, 0); | 34 | const char *uid = xs_list_get(l1, 0); |
| 35 | const char *pwd = xs_list_get(l1, 1); | 35 | const char *pwd = xs_list_get(l1, 1); |
| 36 | const char *addr = xs_or(xs_dict_get(headers, "remote-addr"), | 36 | const char *addr = xs_or(xs_dict_get(headers, "remote-addr"), |
| 37 | xs_dict_get(headers, "x-forwarded-for")); | 37 | xs_dict_get(headers, "x-forwarded-for")); |
| 38 | 38 | ||
| 39 | if (badlogin_check(user, addr)) { | 39 | if (badlogin_check(uid, addr)) { |
| 40 | logged_in = check_password(user, pwd, | 40 | logged_in = check_password(uid, pwd, |
| 41 | xs_dict_get(snac->config, "passwd")); | 41 | xs_dict_get(user->config, "passwd")); |
| 42 | 42 | ||
| 43 | if (!logged_in) | 43 | if (!logged_in) |
| 44 | badlogin_inc(user, addr); | 44 | badlogin_inc(uid, addr); |
| 45 | } | 45 | } |
| 46 | } | 46 | } |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | if (logged_in) | 49 | if (logged_in) |
| 50 | lastlog_write(snac, "web"); | 50 | lastlog_write(user, "web"); |
| 51 | 51 | ||
| 52 | return logged_in; | 52 | return logged_in; |
| 53 | } | 53 | } |
| @@ -69,7 +69,7 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 69 | 69 | ||
| 70 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); | 70 | xs *style = xs_fmt("height: %dem; width: %dem; vertical-align: middle;", ems, ems); |
| 71 | 71 | ||
| 72 | const char *v; | 72 | const xs_dict *v; |
| 73 | int c = 0; | 73 | int c = 0; |
| 74 | 74 | ||
| 75 | while (xs_list_next(tag_list, &v, &c)) { | 75 | while (xs_list_next(tag_list, &v, &c)) { |
| @@ -77,19 +77,25 @@ xs_str *replace_shortnames(xs_str *s, const xs_list *tag, int ems, const char *p | |||
| 77 | 77 | ||
| 78 | if (t && strcmp(t, "Emoji") == 0) { | 78 | if (t && strcmp(t, "Emoji") == 0) { |
| 79 | const char *n = xs_dict_get(v, "name"); | 79 | const char *n = xs_dict_get(v, "name"); |
| 80 | const char *i = xs_dict_get(v, "icon"); | 80 | const xs_dict *i = xs_dict_get(v, "icon"); |
| 81 | 81 | ||
| 82 | if (n && i) { | 82 | if (xs_is_string(n) && xs_is_dict(i)) { |
| 83 | const char *u = xs_dict_get(i, "url"); | 83 | const char *u = xs_dict_get(i, "url"); |
| 84 | xs *url = make_url(u, proxy, 0); | 84 | const char *mt = xs_dict_get(i, "mediaType"); |
| 85 | |||
| 86 | if (xs_is_string(u) && xs_is_string(mt) && strcmp(mt, "image/svg+xml")) { | ||
| 87 | xs *url = make_url(u, proxy, 0); | ||
| 85 | 88 | ||
| 86 | xs_html *img = xs_html_sctag("img", | 89 | xs_html *img = xs_html_sctag("img", |
| 87 | xs_html_attr("loading", "lazy"), | 90 | xs_html_attr("loading", "lazy"), |
| 88 | xs_html_attr("src", url), | 91 | xs_html_attr("src", url), |
| 89 | xs_html_attr("style", style)); | 92 | xs_html_attr("style", style)); |
| 90 | 93 | ||
| 91 | xs *s1 = xs_html_render(img); | 94 | xs *s1 = xs_html_render(img); |
| 92 | s = xs_replace_i(s, n, s1); | 95 | s = xs_replace_i(s, n, s1); |
| 96 | } | ||
| 97 | else | ||
| 98 | s = xs_replace_i(s, n, ""); | ||
| 93 | } | 99 | } |
| 94 | } | 100 | } |
| 95 | } | 101 | } |
| @@ -621,6 +627,9 @@ static xs_html *html_instance_body(void) | |||
| 621 | const char *email = xs_dict_get(srv_config, "admin_email"); | 627 | const char *email = xs_dict_get(srv_config, "admin_email"); |
| 622 | const char *acct = xs_dict_get(srv_config, "admin_account"); | 628 | const char *acct = xs_dict_get(srv_config, "admin_account"); |
| 623 | 629 | ||
| 630 | /* for L() */ | ||
| 631 | const snac *user = NULL; | ||
| 632 | |||
| 624 | xs *blurb = xs_replace(snac_blurb, "%host%", host); | 633 | xs *blurb = xs_replace(snac_blurb, "%host%", host); |
| 625 | 634 | ||
| 626 | xs_html *dl; | 635 | xs_html *dl; |
| @@ -735,9 +744,11 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) | |||
| 735 | xs *fwers = follower_list(user); | 744 | xs *fwers = follower_list(user); |
| 736 | xs *fwing = following_list(user); | 745 | xs *fwing = following_list(user); |
| 737 | 746 | ||
| 738 | xs *s1 = xs_fmt(L("%d following, %d followers · "), | 747 | xs *s1 = xs_fmt(L("%d following, %d followers"), |
| 739 | xs_list_len(fwing), xs_list_len(fwers)); | 748 | xs_list_len(fwing), xs_list_len(fwers)); |
| 740 | 749 | ||
| 750 | s1 = xs_str_cat(s1, " · "); | ||
| 751 | |||
| 741 | s_desc = xs_str_prepend_i(s_desc, s1); | 752 | s_desc = xs_str_prepend_i(s_desc, s1); |
| 742 | } | 753 | } |
| 743 | 754 | ||
| @@ -1052,8 +1063,8 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1052 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); | 1063 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); |
| 1053 | 1064 | ||
| 1054 | if (*latitude && *longitude) { | 1065 | if (*latitude && *longitude) { |
| 1055 | xs *label = xs_fmt(L("%s,%s"), latitude, longitude); | 1066 | xs *label = xs_fmt("%s,%s", latitude, longitude); |
| 1056 | xs *url = xs_fmt(L("https://openstreetmap.org/search?query=%s,%s"), | 1067 | xs *url = xs_fmt("https://openstreetmap.org/search?query=%s,%s", |
| 1057 | latitude, longitude); | 1068 | latitude, longitude); |
| 1058 | 1069 | ||
| 1059 | xs_html_add(top_user, | 1070 | xs_html_add(top_user, |
| @@ -1069,7 +1080,7 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1069 | xs *fwers = follower_list(user); | 1080 | xs *fwers = follower_list(user); |
| 1070 | xs *fwing = following_list(user); | 1081 | xs *fwing = following_list(user); |
| 1071 | 1082 | ||
| 1072 | xs *s1 = xs_fmt(L("%d following %d followers"), | 1083 | xs *s1 = xs_fmt(L("%d following, %d followers"), |
| 1073 | xs_list_len(fwing), xs_list_len(fwers)); | 1084 | xs_list_len(fwing), xs_list_len(fwers)); |
| 1074 | 1085 | ||
| 1075 | xs_html_add(top_user, | 1086 | xs_html_add(top_user, |
| @@ -1085,16 +1096,16 @@ static xs_html *html_user_body(snac *user, int read_only) | |||
| 1085 | } | 1096 | } |
| 1086 | 1097 | ||
| 1087 | 1098 | ||
| 1088 | xs_html *html_top_controls(snac *snac) | 1099 | xs_html *html_top_controls(snac *user) |
| 1089 | /* generates the top controls */ | 1100 | /* generates the top controls */ |
| 1090 | { | 1101 | { |
| 1091 | xs *ops_action = xs_fmt("%s/admin/action", snac->actor); | 1102 | xs *ops_action = xs_fmt("%s/admin/action", user->actor); |
| 1092 | 1103 | ||
| 1093 | xs_html *top_controls = xs_html_tag("div", | 1104 | xs_html *top_controls = xs_html_tag("div", |
| 1094 | xs_html_attr("class", "snac-top-controls"), | 1105 | xs_html_attr("class", "snac-top-controls"), |
| 1095 | 1106 | ||
| 1096 | /** new post **/ | 1107 | /** new post **/ |
| 1097 | html_note(snac, L("New Post..."), | 1108 | html_note(user, L("New Post..."), |
| 1098 | "new_post_div", "new_post_form", | 1109 | "new_post_div", "new_post_form", |
| 1099 | L("What's on your mind?"), "", | 1110 | L("What's on your mind?"), "", |
| 1100 | NULL, NULL, | 1111 | NULL, NULL, |
| @@ -1164,53 +1175,53 @@ xs_html *html_top_controls(snac *snac) | |||
| 1164 | const char *email = "[disabled by admin]"; | 1175 | const char *email = "[disabled by admin]"; |
| 1165 | 1176 | ||
| 1166 | if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { | 1177 | if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) { |
| 1167 | email = xs_dict_get(snac->config_o, "email"); | 1178 | email = xs_dict_get(user->config_o, "email"); |
| 1168 | if (xs_is_null(email)) { | 1179 | if (xs_is_null(email)) { |
| 1169 | email = xs_dict_get(snac->config, "email"); | 1180 | email = xs_dict_get(user->config, "email"); |
| 1170 | 1181 | ||
| 1171 | if (xs_is_null(email)) | 1182 | if (xs_is_null(email)) |
| 1172 | email = ""; | 1183 | email = ""; |
| 1173 | } | 1184 | } |
| 1174 | } | 1185 | } |
| 1175 | 1186 | ||
| 1176 | const char *cw = xs_dict_get(snac->config, "cw"); | 1187 | const char *cw = xs_dict_get(user->config, "cw"); |
| 1177 | if (xs_is_null(cw)) | 1188 | if (xs_is_null(cw)) |
| 1178 | cw = ""; | 1189 | cw = ""; |
| 1179 | 1190 | ||
| 1180 | const char *telegram_bot = xs_dict_get(snac->config, "telegram_bot"); | 1191 | const char *telegram_bot = xs_dict_get(user->config, "telegram_bot"); |
| 1181 | if (xs_is_null(telegram_bot)) | 1192 | if (xs_is_null(telegram_bot)) |
| 1182 | telegram_bot = ""; | 1193 | telegram_bot = ""; |
| 1183 | 1194 | ||
| 1184 | const char *telegram_chat_id = xs_dict_get(snac->config, "telegram_chat_id"); | 1195 | const char *telegram_chat_id = xs_dict_get(user->config, "telegram_chat_id"); |
| 1185 | if (xs_is_null(telegram_chat_id)) | 1196 | if (xs_is_null(telegram_chat_id)) |
| 1186 | telegram_chat_id = ""; | 1197 | telegram_chat_id = ""; |
| 1187 | 1198 | ||
| 1188 | const char *ntfy_server = xs_dict_get(snac->config, "ntfy_server"); | 1199 | const char *ntfy_server = xs_dict_get(user->config, "ntfy_server"); |
| 1189 | if (xs_is_null(ntfy_server)) | 1200 | if (xs_is_null(ntfy_server)) |
| 1190 | ntfy_server = ""; | 1201 | ntfy_server = ""; |
| 1191 | 1202 | ||
| 1192 | const char *ntfy_token = xs_dict_get(snac->config, "ntfy_token"); | 1203 | const char *ntfy_token = xs_dict_get(user->config, "ntfy_token"); |
| 1193 | if (xs_is_null(ntfy_token)) | 1204 | if (xs_is_null(ntfy_token)) |
| 1194 | ntfy_token = ""; | 1205 | ntfy_token = ""; |
| 1195 | 1206 | ||
| 1196 | const char *purge_days = xs_dict_get(snac->config, "purge_days"); | 1207 | const char *purge_days = xs_dict_get(user->config, "purge_days"); |
| 1197 | if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) | 1208 | if (!xs_is_null(purge_days) && xs_type(purge_days) == XSTYPE_NUMBER) |
| 1198 | purge_days = (char *)xs_number_str(purge_days); | 1209 | purge_days = (char *)xs_number_str(purge_days); |
| 1199 | else | 1210 | else |
| 1200 | purge_days = "0"; | 1211 | purge_days = "0"; |
| 1201 | 1212 | ||
| 1202 | const xs_val *d_dm_f_u = xs_dict_get(snac->config, "drop_dm_from_unknown"); | 1213 | const xs_val *d_dm_f_u = xs_dict_get(user->config, "drop_dm_from_unknown"); |
| 1203 | const xs_val *bot = xs_dict_get(snac->config, "bot"); | 1214 | const xs_val *bot = xs_dict_get(user->config, "bot"); |
| 1204 | const xs_val *a_private = xs_dict_get(snac->config, "private"); | 1215 | const xs_val *a_private = xs_dict_get(user->config, "private"); |
| 1205 | const xs_val *auto_boost = xs_dict_get(snac->config, "auto_boost"); | 1216 | const xs_val *auto_boost = xs_dict_get(user->config, "auto_boost"); |
| 1206 | const xs_val *coll_thrds = xs_dict_get(snac->config, "collapse_threads"); | 1217 | const xs_val *coll_thrds = xs_dict_get(user->config, "collapse_threads"); |
| 1207 | const xs_val *pending = xs_dict_get(snac->config, "approve_followers"); | 1218 | const xs_val *pending = xs_dict_get(user->config, "approve_followers"); |
| 1208 | const xs_val *show_foll = xs_dict_get(snac->config, "show_contact_metrics"); | 1219 | const xs_val *show_foll = xs_dict_get(user->config, "show_contact_metrics"); |
| 1209 | const char *latitude = xs_dict_get_def(snac->config, "latitude", ""); | 1220 | const char *latitude = xs_dict_get_def(user->config, "latitude", ""); |
| 1210 | const char *longitude = xs_dict_get_def(snac->config, "longitude", ""); | 1221 | const char *longitude = xs_dict_get_def(user->config, "longitude", ""); |
| 1211 | 1222 | ||
| 1212 | xs *metadata = NULL; | 1223 | xs *metadata = NULL; |
| 1213 | const xs_dict *md = xs_dict_get(snac->config, "metadata"); | 1224 | const xs_dict *md = xs_dict_get(user->config, "metadata"); |
| 1214 | 1225 | ||
| 1215 | if (xs_type(md) == XSTYPE_DICT) { | 1226 | if (xs_type(md) == XSTYPE_DICT) { |
| 1216 | const xs_str *k; | 1227 | const xs_str *k; |
| @@ -1232,7 +1243,29 @@ xs_html *html_top_controls(snac *snac) | |||
| 1232 | else | 1243 | else |
| 1233 | metadata = xs_str_new(NULL); | 1244 | metadata = xs_str_new(NULL); |
| 1234 | 1245 | ||
| 1235 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", snac->actor); | 1246 | /* ui language */ |
| 1247 | xs_html *lang_select = xs_html_tag("select", | ||
| 1248 | xs_html_attr("name", "web_ui_lang")); | ||
| 1249 | |||
| 1250 | const char *u_lang = xs_dict_get_def(user->config, "lang", "en"); | ||
| 1251 | const char *lang; | ||
| 1252 | const xs_dict *langs; | ||
| 1253 | |||
| 1254 | xs_dict_foreach(srv_langs, lang, langs) { | ||
| 1255 | if (strcmp(u_lang, lang) == 0) | ||
| 1256 | xs_html_add(lang_select, | ||
| 1257 | xs_html_tag("option", | ||
| 1258 | xs_html_text(lang), | ||
| 1259 | xs_html_attr("value", lang), | ||
| 1260 | xs_html_attr("selected", "selected"))); | ||
| 1261 | else | ||
| 1262 | xs_html_add(lang_select, | ||
| 1263 | xs_html_tag("option", | ||
| 1264 | xs_html_text(lang), | ||
| 1265 | xs_html_attr("value", lang))); | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | xs *user_setup_action = xs_fmt("%s/admin/user-setup", user->actor); | ||
| 1236 | 1269 | ||
| 1237 | xs_html_add(top_controls, | 1270 | xs_html_add(top_controls, |
| 1238 | xs_html_tag("details", | 1271 | xs_html_tag("details", |
| @@ -1251,7 +1284,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1251 | xs_html_sctag("input", | 1284 | xs_html_sctag("input", |
| 1252 | xs_html_attr("type", "text"), | 1285 | xs_html_attr("type", "text"), |
| 1253 | xs_html_attr("name", "name"), | 1286 | xs_html_attr("name", "name"), |
| 1254 | xs_html_attr("value", xs_dict_get(snac->config, "name")), | 1287 | xs_html_attr("value", xs_dict_get(user->config, "name")), |
| 1255 | xs_html_attr("placeholder", L("Your name")))), | 1288 | xs_html_attr("placeholder", L("Your name")))), |
| 1256 | xs_html_tag("p", | 1289 | xs_html_tag("p", |
| 1257 | xs_html_text(L("Avatar: ")), | 1290 | xs_html_text(L("Avatar: ")), |
| @@ -1281,7 +1314,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1281 | xs_html_attr("cols", "40"), | 1314 | xs_html_attr("cols", "40"), |
| 1282 | xs_html_attr("rows", "4"), | 1315 | xs_html_attr("rows", "4"), |
| 1283 | xs_html_attr("placeholder", L("Write about yourself here...")), | 1316 | xs_html_attr("placeholder", L("Write about yourself here...")), |
| 1284 | xs_html_text(xs_dict_get(snac->config, "bio")))), | 1317 | xs_html_text(xs_dict_get(user->config, "bio")))), |
| 1285 | xs_html_sctag("input", | 1318 | xs_html_sctag("input", |
| 1286 | xs_html_attr("type", "checkbox"), | 1319 | xs_html_attr("type", "checkbox"), |
| 1287 | xs_html_attr("name", "cw"), | 1320 | xs_html_attr("name", "cw"), |
| @@ -1423,6 +1456,11 @@ xs_html *html_top_controls(snac *snac) | |||
| 1423 | xs_html_text(metadata))), | 1456 | xs_html_text(metadata))), |
| 1424 | 1457 | ||
| 1425 | xs_html_tag("p", | 1458 | xs_html_tag("p", |
| 1459 | xs_html_text(L("Web interface language:")), | ||
| 1460 | xs_html_sctag("br", NULL), | ||
| 1461 | lang_select), | ||
| 1462 | |||
| 1463 | xs_html_tag("p", | ||
| 1426 | xs_html_text(L("New password:")), | 1464 | xs_html_text(L("New password:")), |
| 1427 | xs_html_sctag("br", NULL), | 1465 | xs_html_sctag("br", NULL), |
| 1428 | xs_html_sctag("input", | 1466 | xs_html_sctag("input", |
| @@ -1444,8 +1482,8 @@ xs_html *html_top_controls(snac *snac) | |||
| 1444 | 1482 | ||
| 1445 | xs_html_tag("p", NULL))))); | 1483 | xs_html_tag("p", NULL))))); |
| 1446 | 1484 | ||
| 1447 | xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", snac->actor); | 1485 | xs *followed_hashtags_action = xs_fmt("%s/admin/followed-hashtags", user->actor); |
| 1448 | xs *followed_hashtags = xs_join(xs_dict_get_def(snac->config, | 1486 | xs *followed_hashtags = xs_join(xs_dict_get_def(user->config, |
| 1449 | "followed_hashtags", xs_stock(XSTYPE_LIST)), "\n"); | 1487 | "followed_hashtags", xs_stock(XSTYPE_LIST)), "\n"); |
| 1450 | 1488 | ||
| 1451 | xs_html_add(top_controls, | 1489 | xs_html_add(top_controls, |
| @@ -1480,7 +1518,7 @@ xs_html *html_top_controls(snac *snac) | |||
| 1480 | } | 1518 | } |
| 1481 | 1519 | ||
| 1482 | 1520 | ||
| 1483 | static xs_html *html_button(char *clss, char *label, char *hint) | 1521 | static xs_html *html_button(const char *clss, const char *label, const char *hint) |
| 1484 | { | 1522 | { |
| 1485 | xs *c = xs_fmt("snac-btn-%s", clss); | 1523 | xs *c = xs_fmt("snac-btn-%s", clss); |
| 1486 | 1524 | ||
| @@ -1496,7 +1534,7 @@ static xs_html *html_button(char *clss, char *label, char *hint) | |||
| 1496 | } | 1534 | } |
| 1497 | 1535 | ||
| 1498 | 1536 | ||
| 1499 | xs_str *build_mentions(snac *snac, const xs_dict *msg) | 1537 | xs_str *build_mentions(snac *user, const xs_dict *msg) |
| 1500 | /* returns a string with the mentions in msg */ | 1538 | /* returns a string with the mentions in msg */ |
| 1501 | { | 1539 | { |
| 1502 | xs_str *s = xs_str_new(NULL); | 1540 | xs_str *s = xs_str_new(NULL); |
| @@ -1510,7 +1548,7 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) | |||
| 1510 | const char *name = xs_dict_get(v, "name"); | 1548 | const char *name = xs_dict_get(v, "name"); |
| 1511 | 1549 | ||
| 1512 | if (type && strcmp(type, "Mention") == 0 && | 1550 | if (type && strcmp(type, "Mention") == 0 && |
| 1513 | href && strcmp(href, snac->actor) != 0 && name) { | 1551 | href && strcmp(href, user->actor) != 0 && name) { |
| 1514 | xs *s1 = NULL; | 1552 | xs *s1 = NULL; |
| 1515 | 1553 | ||
| 1516 | if (name[0] != '@') { | 1554 | if (name[0] != '@') { |
| @@ -1551,7 +1589,7 @@ xs_str *build_mentions(snac *snac, const xs_dict *msg) | |||
| 1551 | } | 1589 | } |
| 1552 | 1590 | ||
| 1553 | 1591 | ||
| 1554 | xs_html *html_entry_controls(snac *snac, const char *actor, | 1592 | xs_html *html_entry_controls(snac *user, const char *actor, |
| 1555 | const xs_dict *msg, const char *md5) | 1593 | const xs_dict *msg, const char *md5) |
| 1556 | { | 1594 | { |
| 1557 | const char *id = xs_dict_get(msg, "id"); | 1595 | const char *id = xs_dict_get(msg, "id"); |
| @@ -1560,7 +1598,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1560 | xs *likes = object_likes(id); | 1598 | xs *likes = object_likes(id); |
| 1561 | xs *boosts = object_announces(id); | 1599 | xs *boosts = object_announces(id); |
| 1562 | 1600 | ||
| 1563 | xs *action = xs_fmt("%s/admin/action", snac->actor); | 1601 | xs *action = xs_fmt("%s/admin/action", user->actor); |
| 1564 | xs *redir = xs_fmt("%s_entry", md5); | 1602 | xs *redir = xs_fmt("%s_entry", md5); |
| 1565 | 1603 | ||
| 1566 | xs_html *form; | 1604 | xs_html *form; |
| @@ -1587,8 +1625,8 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1587 | xs_html_attr("name", "redir"), | 1625 | xs_html_attr("name", "redir"), |
| 1588 | xs_html_attr("value", redir)))); | 1626 | xs_html_attr("value", redir)))); |
| 1589 | 1627 | ||
| 1590 | if (!xs_startswith(id, snac->actor)) { | 1628 | if (!xs_startswith(id, user->actor)) { |
| 1591 | if (xs_list_in(likes, snac->md5) == -1) { | 1629 | if (xs_list_in(likes, user->md5) == -1) { |
| 1592 | /* not already liked; add button */ | 1630 | /* not already liked; add button */ |
| 1593 | xs_html_add(form, | 1631 | xs_html_add(form, |
| 1594 | html_button("like", L("Like"), L("Say you like this post"))); | 1632 | html_button("like", L("Like"), L("Say you like this post"))); |
| @@ -1600,7 +1638,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1600 | } | 1638 | } |
| 1601 | } | 1639 | } |
| 1602 | else { | 1640 | else { |
| 1603 | if (is_pinned(snac, id)) | 1641 | if (is_pinned(user, id)) |
| 1604 | xs_html_add(form, | 1642 | xs_html_add(form, |
| 1605 | html_button("unpin", L("Unpin"), L("Unpin this post from your timeline"))); | 1643 | html_button("unpin", L("Unpin"), L("Unpin this post from your timeline"))); |
| 1606 | else | 1644 | else |
| @@ -1609,7 +1647,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1609 | } | 1647 | } |
| 1610 | 1648 | ||
| 1611 | if (is_msg_public(msg)) { | 1649 | if (is_msg_public(msg)) { |
| 1612 | if (xs_list_in(boosts, snac->md5) == -1) { | 1650 | if (xs_list_in(boosts, user->md5) == -1) { |
| 1613 | /* not already boosted; add button */ | 1651 | /* not already boosted; add button */ |
| 1614 | xs_html_add(form, | 1652 | xs_html_add(form, |
| 1615 | html_button("boost", L("Boost"), L("Announce this post to your followers"))); | 1653 | html_button("boost", L("Boost"), L("Announce this post to your followers"))); |
| @@ -1621,16 +1659,16 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1621 | } | 1659 | } |
| 1622 | } | 1660 | } |
| 1623 | 1661 | ||
| 1624 | if (is_bookmarked(snac, id)) | 1662 | if (is_bookmarked(user, id)) |
| 1625 | xs_html_add(form, | 1663 | xs_html_add(form, |
| 1626 | html_button("unbookmark", L("Unbookmark"), L("Delete this post from your bookmarks"))); | 1664 | html_button("unbookmark", L("Unbookmark"), L("Delete this post from your bookmarks"))); |
| 1627 | else | 1665 | else |
| 1628 | xs_html_add(form, | 1666 | xs_html_add(form, |
| 1629 | html_button("bookmark", L("Bookmark"), L("Add this post to your bookmarks"))); | 1667 | html_button("bookmark", L("Bookmark"), L("Add this post to your bookmarks"))); |
| 1630 | 1668 | ||
| 1631 | if (strcmp(actor, snac->actor) != 0) { | 1669 | if (strcmp(actor, user->actor) != 0) { |
| 1632 | /* controls for other actors than this one */ | 1670 | /* controls for other actors than this one */ |
| 1633 | if (following_check(snac, actor)) { | 1671 | if (following_check(user, actor)) { |
| 1634 | xs_html_add(form, | 1672 | xs_html_add(form, |
| 1635 | html_button("unfollow", L("Unfollow"), L("Stop following this user's activity"))); | 1673 | html_button("unfollow", L("Unfollow"), L("Stop following this user's activity"))); |
| 1636 | } | 1674 | } |
| @@ -1640,7 +1678,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1640 | } | 1678 | } |
| 1641 | 1679 | ||
| 1642 | if (!xs_is_null(group)) { | 1680 | if (!xs_is_null(group)) { |
| 1643 | if (following_check(snac, group)) { | 1681 | if (following_check(user, group)) { |
| 1644 | xs_html_add(form, | 1682 | xs_html_add(form, |
| 1645 | html_button("unfollow", L("Unfollow Group"), | 1683 | html_button("unfollow", L("Unfollow Group"), |
| 1646 | L("Stop following this group or channel"))); | 1684 | L("Stop following this group or channel"))); |
| @@ -1666,7 +1704,7 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1666 | 1704 | ||
| 1667 | const char *prev_src = xs_dict_get(msg, "sourceContent"); | 1705 | const char *prev_src = xs_dict_get(msg, "sourceContent"); |
| 1668 | 1706 | ||
| 1669 | if (!xs_is_null(prev_src) && strcmp(actor, snac->actor) == 0) { /** edit **/ | 1707 | if (!xs_is_null(prev_src) && strcmp(actor, user->actor) == 0) { /** edit **/ |
| 1670 | /* post can be edited */ | 1708 | /* post can be edited */ |
| 1671 | xs *div_id = xs_fmt("%s_edit", md5); | 1709 | xs *div_id = xs_fmt("%s_edit", md5); |
| 1672 | xs *form_id = xs_fmt("%s_edit_form", md5); | 1710 | xs *form_id = xs_fmt("%s_edit_form", md5); |
| @@ -1693,26 +1731,26 @@ xs_html *html_entry_controls(snac *snac, const char *actor, | |||
| 1693 | 1731 | ||
| 1694 | xs_html_add(controls, xs_html_tag("div", | 1732 | xs_html_add(controls, xs_html_tag("div", |
| 1695 | xs_html_tag("p", NULL), | 1733 | xs_html_tag("p", NULL), |
| 1696 | html_note(snac, L("Edit..."), | 1734 | html_note(user, L("Edit..."), |
| 1697 | div_id, form_id, | 1735 | div_id, form_id, |
| 1698 | "", prev_src, | 1736 | "", prev_src, |
| 1699 | id, NULL, | 1737 | id, NULL, |
| 1700 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), | 1738 | xs_dict_get(msg, "sensitive"), xs_dict_get(msg, "summary"), |
| 1701 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, | 1739 | xs_stock(is_msg_public(msg) ? XSTYPE_FALSE : XSTYPE_TRUE), redir, |
| 1702 | NULL, 0, att_files, att_alt_texts, is_draft(snac, id))), | 1740 | NULL, 0, att_files, att_alt_texts, is_draft(user, id))), |
| 1703 | xs_html_tag("p", NULL)); | 1741 | xs_html_tag("p", NULL)); |
| 1704 | } | 1742 | } |
| 1705 | 1743 | ||
| 1706 | { /** reply **/ | 1744 | { /** reply **/ |
| 1707 | /* the post textarea */ | 1745 | /* the post textarea */ |
| 1708 | xs *ct = build_mentions(snac, msg); | 1746 | xs *ct = build_mentions(user, msg); |
| 1709 | xs *div_id = xs_fmt("%s_reply", md5); | 1747 | xs *div_id = xs_fmt("%s_reply", md5); |
| 1710 | xs *form_id = xs_fmt("%s_reply_form", md5); | 1748 | xs *form_id = xs_fmt("%s_reply_form", md5); |
| 1711 | xs *redir = xs_fmt("%s_entry", md5); | 1749 | xs *redir = xs_fmt("%s_entry", md5); |
| 1712 | 1750 | ||
| 1713 | xs_html_add(controls, xs_html_tag("div", | 1751 | xs_html_add(controls, xs_html_tag("div", |
| 1714 | xs_html_tag("p", NULL), | 1752 | xs_html_tag("p", NULL), |
| 1715 | html_note(snac, L("Reply..."), | 1753 | html_note(user, L("Reply..."), |
| 1716 | div_id, form_id, | 1754 | div_id, form_id, |
| 1717 | "", ct, | 1755 | "", ct, |
| 1718 | NULL, NULL, | 1756 | NULL, NULL, |
| @@ -1839,7 +1877,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 1839 | xs_html_raw(" 📌 "))); | 1877 | xs_html_raw(" 📌 "))); |
| 1840 | } | 1878 | } |
| 1841 | 1879 | ||
| 1842 | if (user && is_bookmarked(user, id)) { | 1880 | if (user && !read_only && is_bookmarked(user, id)) { |
| 1843 | /* add a bookmark emoji */ | 1881 | /* add a bookmark emoji */ |
| 1844 | xs_html_add(score, | 1882 | xs_html_add(score, |
| 1845 | xs_html_tag("span", | 1883 | xs_html_tag("span", |
| @@ -2242,6 +2280,11 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2242 | if (content && xs_str_in(content, o_href) != -1) | 2280 | if (content && xs_str_in(content, o_href) != -1) |
| 2243 | continue; | 2281 | continue; |
| 2244 | 2282 | ||
| 2283 | /* drop silently any attachment that may include JavaScript */ | ||
| 2284 | if (strcmp(type, "image/svg+xml") == 0 || | ||
| 2285 | strcmp(type, "text/html") == 0) | ||
| 2286 | continue; | ||
| 2287 | |||
| 2245 | /* do this attachment include an icon? */ | 2288 | /* do this attachment include an icon? */ |
| 2246 | const xs_dict *icon = xs_dict_get(a, "icon"); | 2289 | const xs_dict *icon = xs_dict_get(a, "icon"); |
| 2247 | if (xs_type(icon) == XSTYPE_DICT) { | 2290 | if (xs_type(icon) == XSTYPE_DICT) { |
| @@ -2571,7 +2614,7 @@ xs_html *html_entry(snac *user, xs_dict *msg, int read_only, | |||
| 2571 | } | 2614 | } |
| 2572 | 2615 | ||
| 2573 | 2616 | ||
| 2574 | xs_html *html_footer(void) | 2617 | xs_html *html_footer(const snac *user) |
| 2575 | { | 2618 | { |
| 2576 | return xs_html_tag("div", | 2619 | return xs_html_tag("div", |
| 2577 | xs_html_attr("class", "snac-footer"), | 2620 | xs_html_attr("class", "snac-footer"), |
| @@ -2879,13 +2922,13 @@ xs_str *html_timeline(snac *user, const xs_list *list, int read_only, | |||
| 2879 | } | 2922 | } |
| 2880 | 2923 | ||
| 2881 | xs_html_add(body, | 2924 | xs_html_add(body, |
| 2882 | html_footer()); | 2925 | html_footer(user)); |
| 2883 | 2926 | ||
| 2884 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 2927 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 2885 | } | 2928 | } |
| 2886 | 2929 | ||
| 2887 | 2930 | ||
| 2888 | xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, const char *proxy) | 2931 | xs_html *html_people_list(snac *user, xs_list *list, const char *header, const char *t, const char *proxy) |
| 2889 | { | 2932 | { |
| 2890 | xs_html *snac_posts; | 2933 | xs_html *snac_posts; |
| 2891 | xs_html *people = xs_html_tag("div", | 2934 | xs_html *people = xs_html_tag("div", |
| @@ -2910,7 +2953,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2910 | xs_html_attr("name", md5)), | 2953 | xs_html_attr("name", md5)), |
| 2911 | xs_html_tag("div", | 2954 | xs_html_tag("div", |
| 2912 | xs_html_attr("class", "snac-post-header"), | 2955 | xs_html_attr("class", "snac-post-header"), |
| 2913 | html_actor_icon(snac, actor, xs_dict_get(actor, "published"), | 2956 | html_actor_icon(user, actor, xs_dict_get(actor, "published"), |
| 2914 | NULL, NULL, 0, 1, proxy, NULL, NULL))); | 2957 | NULL, NULL, 0, 1, proxy, NULL, NULL))); |
| 2915 | 2958 | ||
| 2916 | /* content (user bio) */ | 2959 | /* content (user bio) */ |
| @@ -2934,7 +2977,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2934 | } | 2977 | } |
| 2935 | 2978 | ||
| 2936 | /* buttons */ | 2979 | /* buttons */ |
| 2937 | xs *btn_form_action = xs_fmt("%s/admin/action", snac->actor); | 2980 | xs *btn_form_action = xs_fmt("%s/admin/action", user->actor); |
| 2938 | 2981 | ||
| 2939 | xs_html *snac_controls = xs_html_tag("div", | 2982 | xs_html *snac_controls = xs_html_tag("div", |
| 2940 | xs_html_attr("class", "snac-controls")); | 2983 | xs_html_attr("class", "snac-controls")); |
| @@ -2954,12 +2997,12 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2954 | 2997 | ||
| 2955 | xs_html_add(snac_controls, form); | 2998 | xs_html_add(snac_controls, form); |
| 2956 | 2999 | ||
| 2957 | if (following_check(snac, actor_id)) { | 3000 | if (following_check(user, actor_id)) { |
| 2958 | xs_html_add(form, | 3001 | xs_html_add(form, |
| 2959 | html_button("unfollow", L("Unfollow"), | 3002 | html_button("unfollow", L("Unfollow"), |
| 2960 | L("Stop following this user's activity"))); | 3003 | L("Stop following this user's activity"))); |
| 2961 | 3004 | ||
| 2962 | if (is_limited(snac, actor_id)) | 3005 | if (is_limited(user, actor_id)) |
| 2963 | xs_html_add(form, | 3006 | xs_html_add(form, |
| 2964 | html_button("unlimit", L("Unlimit"), | 3007 | html_button("unlimit", L("Unlimit"), |
| 2965 | L("Allow announces (boosts) from this user"))); | 3008 | L("Allow announces (boosts) from this user"))); |
| @@ -2973,12 +3016,12 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2973 | html_button("follow", L("Follow"), | 3016 | html_button("follow", L("Follow"), |
| 2974 | L("Start following this user's activity"))); | 3017 | L("Start following this user's activity"))); |
| 2975 | 3018 | ||
| 2976 | if (follower_check(snac, actor_id)) | 3019 | if (follower_check(user, actor_id)) |
| 2977 | xs_html_add(form, | 3020 | xs_html_add(form, |
| 2978 | html_button("delete", L("Delete"), L("Delete this user"))); | 3021 | html_button("delete", L("Delete"), L("Delete this user"))); |
| 2979 | } | 3022 | } |
| 2980 | 3023 | ||
| 2981 | if (pending_check(snac, actor_id)) { | 3024 | if (pending_check(user, actor_id)) { |
| 2982 | xs_html_add(form, | 3025 | xs_html_add(form, |
| 2983 | html_button("approve", L("Approve"), | 3026 | html_button("approve", L("Approve"), |
| 2984 | L("Approve this follow request"))); | 3027 | L("Approve this follow request"))); |
| @@ -2987,7 +3030,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 2987 | html_button("discard", L("Discard"), L("Discard this follow request"))); | 3030 | html_button("discard", L("Discard"), L("Discard this follow request"))); |
| 2988 | } | 3031 | } |
| 2989 | 3032 | ||
| 2990 | if (is_muted(snac, actor_id)) | 3033 | if (is_muted(user, actor_id)) |
| 2991 | xs_html_add(form, | 3034 | xs_html_add(form, |
| 2992 | html_button("unmute", L("Unmute"), | 3035 | html_button("unmute", L("Unmute"), |
| 2993 | L("Stop blocking activities from this user"))); | 3036 | L("Stop blocking activities from this user"))); |
| @@ -3002,7 +3045,7 @@ xs_html *html_people_list(snac *snac, xs_list *list, char *header, char *t, cons | |||
| 3002 | 3045 | ||
| 3003 | xs_html_add(snac_controls, | 3046 | xs_html_add(snac_controls, |
| 3004 | xs_html_tag("p", NULL), | 3047 | xs_html_tag("p", NULL), |
| 3005 | html_note(snac, L("Direct Message..."), | 3048 | html_note(user, L("Direct Message..."), |
| 3006 | dm_div_id, dm_form_id, | 3049 | dm_div_id, dm_form_id, |
| 3007 | "", "", | 3050 | "", "", |
| 3008 | NULL, actor_id, | 3051 | NULL, actor_id, |
| @@ -3048,7 +3091,7 @@ xs_str *html_people(snac *user) | |||
| 3048 | html_user_head(user, NULL, NULL), | 3091 | html_user_head(user, NULL, NULL), |
| 3049 | xs_html_add(html_user_body(user, 0), | 3092 | xs_html_add(html_user_body(user, 0), |
| 3050 | lists, | 3093 | lists, |
| 3051 | html_footer())); | 3094 | html_footer(user))); |
| 3052 | 3095 | ||
| 3053 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); | 3096 | return xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3054 | } | 3097 | } |
| @@ -3298,7 +3341,7 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3298 | xs_set_free(&rep); | 3341 | xs_set_free(&rep); |
| 3299 | 3342 | ||
| 3300 | xs_html_add(body, | 3343 | xs_html_add(body, |
| 3301 | html_footer()); | 3344 | html_footer(user)); |
| 3302 | 3345 | ||
| 3303 | /* set the check time to now */ | 3346 | /* set the check time to now */ |
| 3304 | xs *dummy = notify_check_time(user, 1); | 3347 | xs *dummy = notify_check_time(user, 1); |
| @@ -3310,12 +3353,24 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3310 | } | 3353 | } |
| 3311 | 3354 | ||
| 3312 | 3355 | ||
| 3356 | void set_user_lang(snac *user) | ||
| 3357 | /* sets the language dict according to user configuration */ | ||
| 3358 | { | ||
| 3359 | user->lang = NULL; | ||
| 3360 | const char *lang = xs_dict_get(user->config, "lang"); | ||
| 3361 | |||
| 3362 | if (xs_is_string(lang)) | ||
| 3363 | user->lang = xs_dict_get(srv_langs, lang); | ||
| 3364 | } | ||
| 3365 | |||
| 3366 | |||
| 3313 | int html_get_handler(const xs_dict *req, const char *q_path, | 3367 | int html_get_handler(const xs_dict *req, const char *q_path, |
| 3314 | char **body, int *b_size, char **ctype, | 3368 | char **body, int *b_size, char **ctype, |
| 3315 | xs_str **etag, xs_str **last_modified) | 3369 | xs_str **etag, xs_str **last_modified) |
| 3316 | { | 3370 | { |
| 3317 | const char *accept = xs_dict_get(req, "accept"); | 3371 | const char *accept = xs_dict_get(req, "accept"); |
| 3318 | int status = HTTP_STATUS_NOT_FOUND; | 3372 | int status = HTTP_STATUS_NOT_FOUND; |
| 3373 | const snac *user = NULL; | ||
| 3319 | snac snac; | 3374 | snac snac; |
| 3320 | xs *uid = NULL; | 3375 | xs *uid = NULL; |
| 3321 | const char *p_path; | 3376 | const char *p_path; |
| @@ -3382,6 +3437,9 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3382 | return HTTP_STATUS_NOT_FOUND; | 3437 | return HTTP_STATUS_NOT_FOUND; |
| 3383 | } | 3438 | } |
| 3384 | 3439 | ||
| 3440 | user = &snac; /* for L() */ | ||
| 3441 | set_user_lang(&snac); | ||
| 3442 | |||
| 3385 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) | 3443 | if (xs_is_true(xs_dict_get(srv_config, "proxy_media"))) |
| 3386 | proxy = 1; | 3444 | proxy = 1; |
| 3387 | 3445 | ||
| @@ -3550,7 +3608,7 @@ int html_get_handler(const xs_dict *req, const char *q_path, | |||
| 3550 | html_user_head(&snac, NULL, NULL), | 3608 | html_user_head(&snac, NULL, NULL), |
| 3551 | xs_html_add(html_user_body(&snac, 0), | 3609 | xs_html_add(html_user_body(&snac, 0), |
| 3552 | page, | 3610 | page, |
| 3553 | html_footer())); | 3611 | html_footer(user))); |
| 3554 | 3612 | ||
| 3555 | *body = xs_html_render_s(html, "<!DOCTYPE html>\n"); | 3613 | *body = xs_html_render_s(html, "<!DOCTYPE html>\n"); |
| 3556 | *b_size = strlen(*body); | 3614 | *b_size = strlen(*body); |
| @@ -4005,6 +4063,7 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4005 | (void)ctype; | 4063 | (void)ctype; |
| 4006 | 4064 | ||
| 4007 | int status = 0; | 4065 | int status = 0; |
| 4066 | const snac *user = NULL; | ||
| 4008 | snac snac; | 4067 | snac snac; |
| 4009 | const char *uid; | 4068 | const char *uid; |
| 4010 | const char *p_path; | 4069 | const char *p_path; |
| @@ -4028,6 +4087,9 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4028 | return HTTP_STATUS_UNAUTHORIZED; | 4087 | return HTTP_STATUS_UNAUTHORIZED; |
| 4029 | } | 4088 | } |
| 4030 | 4089 | ||
| 4090 | user = &snac; /* for L() */ | ||
| 4091 | set_user_lang(&snac); | ||
| 4092 | |||
| 4031 | p_vars = xs_dict_get(req, "p_vars"); | 4093 | p_vars = xs_dict_get(req, "p_vars"); |
| 4032 | 4094 | ||
| 4033 | if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ | 4095 | if (p_path && strcmp(p_path, "admin/note") == 0) { /** **/ |
| @@ -4470,6 +4532,8 @@ int html_post_handler(const xs_dict *req, const char *q_path, | |||
| 4470 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); | 4532 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_TRUE)); |
| 4471 | else | 4533 | else |
| 4472 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); | 4534 | snac.config = xs_dict_set(snac.config, "show_contact_metrics", xs_stock(XSTYPE_FALSE)); |
| 4535 | if ((v = xs_dict_get(p_vars, "web_ui_lang")) != NULL) | ||
| 4536 | snac.config = xs_dict_set(snac.config, "lang", v); | ||
| 4473 | 4537 | ||
| 4474 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); | 4538 | snac.config = xs_dict_set(snac.config, "latitude", xs_dict_get_def(p_vars, "latitude", "")); |
| 4475 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); | 4539 | snac.config = xs_dict_set(snac.config, "longitude", xs_dict_get_def(p_vars, "longitude", "")); |
| @@ -211,6 +211,8 @@ int server_get_handler(xs_dict *req, const char *q_path, | |||
| 211 | { | 211 | { |
| 212 | int status = 0; | 212 | int status = 0; |
| 213 | 213 | ||
| 214 | const snac *user = NULL; | ||
| 215 | |||
| 214 | /* is it the server root? */ | 216 | /* is it the server root? */ |
| 215 | if (*q_path == '\0' || strcmp(q_path, "/") == 0) { | 217 | if (*q_path == '\0' || strcmp(q_path, "/") == 0) { |
| 216 | const xs_dict *q_vars = xs_dict_get(req, "q_vars"); | 218 | const xs_dict *q_vars = xs_dict_get(req, "q_vars"); |
| @@ -553,6 +555,9 @@ void httpd_connection(FILE *f) | |||
| 553 | headers = xs_dict_append(headers, "access-control-allow-origin", "*"); | 555 | headers = xs_dict_append(headers, "access-control-allow-origin", "*"); |
| 554 | headers = xs_dict_append(headers, "access-control-allow-headers", "*"); | 556 | headers = xs_dict_append(headers, "access-control-allow-headers", "*"); |
| 555 | 557 | ||
| 558 | /* disable any form of fucking JavaScript */ | ||
| 559 | headers = xs_dict_append(headers, "Content-Security-Policy", "script-src ;"); | ||
| 560 | |||
| 556 | if (p_state->use_fcgi) | 561 | if (p_state->use_fcgi) |
| 557 | xs_fcgi_response(f, status, headers, body, b_size, fcgi_id); | 562 | xs_fcgi_response(f, status, headers, body, b_size, fcgi_id); |
| 558 | else | 563 | else |
diff --git a/po/en.po b/po/en.po new file mode 100644 index 0000000..ca5e816 --- /dev/null +++ b/po/en.po | |||
| @@ -0,0 +1,687 @@ | |||
| 1 | # snac message translation file | ||
| 2 | # | ||
| 3 | #, fuzzy | ||
| 4 | msgid "" | ||
| 5 | msgstr "" | ||
| 6 | "Project-Id-Version: snac\n" | ||
| 7 | "Last-Translator: grunfink\n" | ||
| 8 | "Language: en\n" | ||
| 9 | "Content-Type: text/plain; charset=UTF-8\n" | ||
| 10 | |||
| 11 | #: html.c:367 | ||
| 12 | msgid "Sensitive content: " | ||
| 13 | msgstr "" | ||
| 14 | |||
| 15 | #: html.c:375 | ||
| 16 | msgid "Sensitive content description" | ||
| 17 | msgstr "" | ||
| 18 | |||
| 19 | #: html.c:388 | ||
| 20 | msgid "Only for mentioned people: " | ||
| 21 | msgstr "" | ||
| 22 | |||
| 23 | #: html.c:411 | ||
| 24 | msgid "Reply to (URL): " | ||
| 25 | msgstr "" | ||
| 26 | |||
| 27 | #: html.c:420 | ||
| 28 | msgid "Don't send, but store as a draft" | ||
| 29 | msgstr "" | ||
| 30 | |||
| 31 | #: html.c:421 | ||
| 32 | msgid "Draft:" | ||
| 33 | msgstr "" | ||
| 34 | |||
| 35 | #: html.c:441 | ||
| 36 | msgid "Attachments..." | ||
| 37 | msgstr "" | ||
| 38 | |||
| 39 | #: html.c:464 | ||
| 40 | msgid "File:" | ||
| 41 | msgstr "" | ||
| 42 | |||
| 43 | #: html.c:468 | ||
| 44 | msgid "Clear this field to delete the attachment" | ||
| 45 | msgstr "" | ||
| 46 | |||
| 47 | #: html.c:477 html.c:502 | ||
| 48 | msgid "Attachment description" | ||
| 49 | msgstr "" | ||
| 50 | |||
| 51 | #: html.c:513 | ||
| 52 | msgid "Poll..." | ||
| 53 | msgstr "" | ||
| 54 | |||
| 55 | #: html.c:515 | ||
| 56 | msgid "Poll options (one per line, up to 8):" | ||
| 57 | msgstr "" | ||
| 58 | |||
| 59 | #: html.c:527 | ||
| 60 | msgid "One choice" | ||
| 61 | msgstr "" | ||
| 62 | |||
| 63 | #: html.c:530 | ||
| 64 | msgid "Multiple choices" | ||
| 65 | msgstr "" | ||
| 66 | |||
| 67 | #: html.c:536 | ||
| 68 | msgid "End in 5 minutes" | ||
| 69 | msgstr "" | ||
| 70 | |||
| 71 | #: html.c:540 | ||
| 72 | msgid "End in 1 hour" | ||
| 73 | msgstr "" | ||
| 74 | |||
| 75 | #: html.c:543 | ||
| 76 | msgid "End in 1 day" | ||
| 77 | msgstr "" | ||
| 78 | |||
| 79 | #: html.c:551 | ||
| 80 | msgid "Post" | ||
| 81 | msgstr "" | ||
| 82 | |||
| 83 | #: html.c:648 html.c:655 | ||
| 84 | msgid "Site description" | ||
| 85 | msgstr "" | ||
| 86 | |||
| 87 | #: html.c:666 | ||
| 88 | msgid "Admin email" | ||
| 89 | msgstr "" | ||
| 90 | |||
| 91 | #: html.c:679 | ||
| 92 | msgid "Admin account" | ||
| 93 | msgstr "" | ||
| 94 | |||
| 95 | #: html.c:747 html.c:1083 | ||
| 96 | #, c-format | ||
| 97 | msgid "%d following, %d followers" | ||
| 98 | msgstr "" | ||
| 99 | |||
| 100 | #: html.c:837 | ||
| 101 | msgid "RSS" | ||
| 102 | msgstr "" | ||
| 103 | |||
| 104 | #: html.c:842 html.c:870 | ||
| 105 | msgid "private" | ||
| 106 | msgstr "" | ||
| 107 | |||
| 108 | #: html.c:866 | ||
| 109 | msgid "public" | ||
| 110 | msgstr "" | ||
| 111 | |||
| 112 | #: html.c:874 | ||
| 113 | msgid "notifications" | ||
| 114 | msgstr "" | ||
| 115 | |||
| 116 | #: html.c:879 | ||
| 117 | msgid "people" | ||
| 118 | msgstr "" | ||
| 119 | |||
| 120 | #: html.c:883 | ||
| 121 | msgid "instance" | ||
| 122 | msgstr "" | ||
| 123 | |||
| 124 | #: html.c:892 | ||
| 125 | msgid "" | ||
| 126 | "Search posts by URL or content (regular expression), @user@host accounts, or " | ||
| 127 | "#tag" | ||
| 128 | msgstr "" | ||
| 129 | |||
| 130 | #: html.c:893 | ||
| 131 | msgid "Content search" | ||
| 132 | msgstr "" | ||
| 133 | |||
| 134 | #: html.c:1015 | ||
| 135 | msgid "verified link" | ||
| 136 | msgstr "" | ||
| 137 | |||
| 138 | #: html.c:1072 html.c:2420 html.c:2433 html.c:2442 | ||
| 139 | msgid "Location: " | ||
| 140 | msgstr "" | ||
| 141 | |||
| 142 | #: html.c:1108 | ||
| 143 | msgid "New Post..." | ||
| 144 | msgstr "" | ||
| 145 | |||
| 146 | #: html.c:1110 | ||
| 147 | msgid "What's on your mind?" | ||
| 148 | msgstr "" | ||
| 149 | |||
| 150 | #: html.c:1119 | ||
| 151 | msgid "Operations..." | ||
| 152 | msgstr "" | ||
| 153 | |||
| 154 | #: html.c:1134 html.c:1677 html.c:3016 html.c:4333 | ||
| 155 | msgid "Follow" | ||
| 156 | msgstr "" | ||
| 157 | |||
| 158 | #: html.c:1136 | ||
| 159 | msgid "(by URL or user@host)" | ||
| 160 | msgstr "" | ||
| 161 | |||
| 162 | #: html.c:1151 html.c:1653 html.c:4285 | ||
| 163 | msgid "Boost" | ||
| 164 | msgstr "" | ||
| 165 | |||
| 166 | #: html.c:1153 html.c:1170 | ||
| 167 | msgid "(by URL)" | ||
| 168 | msgstr "" | ||
| 169 | |||
| 170 | #: html.c:1168 html.c:1632 html.c:4276 | ||
| 171 | msgid "Like" | ||
| 172 | msgstr "" | ||
| 173 | |||
| 174 | #: html.c:1273 | ||
| 175 | msgid "User Settings..." | ||
| 176 | msgstr "" | ||
| 177 | |||
| 178 | #: html.c:1282 | ||
| 179 | msgid "Display name:" | ||
| 180 | msgstr "" | ||
| 181 | |||
| 182 | #: html.c:1288 | ||
| 183 | msgid "Your name" | ||
| 184 | msgstr "" | ||
| 185 | |||
| 186 | #: html.c:1290 | ||
| 187 | msgid "Avatar: " | ||
| 188 | msgstr "" | ||
| 189 | |||
| 190 | #: html.c:1298 | ||
| 191 | msgid "Delete current avatar" | ||
| 192 | msgstr "" | ||
| 193 | |||
| 194 | #: html.c:1300 | ||
| 195 | msgid "Header image (banner): " | ||
| 196 | msgstr "" | ||
| 197 | |||
| 198 | #: html.c:1308 | ||
| 199 | msgid "Delete current header image" | ||
| 200 | msgstr "" | ||
| 201 | |||
| 202 | #: html.c:1310 | ||
| 203 | msgid "Bio:" | ||
| 204 | msgstr "" | ||
| 205 | |||
| 206 | #: html.c:1316 | ||
| 207 | msgid "Write about yourself here..." | ||
| 208 | msgstr "" | ||
| 209 | |||
| 210 | #: html.c:1325 | ||
| 211 | msgid "Always show sensitive content" | ||
| 212 | msgstr "" | ||
| 213 | |||
| 214 | #: html.c:1327 | ||
| 215 | msgid "Email address for notifications:" | ||
| 216 | msgstr "" | ||
| 217 | |||
| 218 | #: html.c:1335 | ||
| 219 | msgid "Telegram notifications (bot key and chat id):" | ||
| 220 | msgstr "" | ||
| 221 | |||
| 222 | #: html.c:1349 | ||
| 223 | msgid "ntfy notifications (ntfy server and token):" | ||
| 224 | msgstr "" | ||
| 225 | |||
| 226 | #: html.c:1363 | ||
| 227 | msgid "Maximum days to keep posts (0: server settings):" | ||
| 228 | msgstr "" | ||
| 229 | |||
| 230 | #: html.c:1377 | ||
| 231 | msgid "Drop direct messages from people you don't follow" | ||
| 232 | msgstr "" | ||
| 233 | |||
| 234 | #: html.c:1386 | ||
| 235 | msgid "This account is a bot" | ||
| 236 | msgstr "" | ||
| 237 | |||
| 238 | #: html.c:1395 | ||
| 239 | msgid "Auto-boost all mentions to this account" | ||
| 240 | msgstr "" | ||
| 241 | |||
| 242 | #: html.c:1404 | ||
| 243 | msgid "This account is private (posts are not shown through the web)" | ||
| 244 | msgstr "" | ||
| 245 | |||
| 246 | #: html.c:1414 | ||
| 247 | msgid "Collapse top threads by default" | ||
| 248 | msgstr "" | ||
| 249 | |||
| 250 | #: html.c:1423 | ||
| 251 | msgid "Follow requests must be approved" | ||
| 252 | msgstr "" | ||
| 253 | |||
| 254 | #: html.c:1432 | ||
| 255 | msgid "Publish follower and following metrics" | ||
| 256 | msgstr "" | ||
| 257 | |||
| 258 | #: html.c:1434 | ||
| 259 | msgid "Current location:" | ||
| 260 | msgstr "" | ||
| 261 | |||
| 262 | #: html.c:1448 | ||
| 263 | msgid "Profile metadata (key=value pairs in each line):" | ||
| 264 | msgstr "" | ||
| 265 | |||
| 266 | #: html.c:1459 | ||
| 267 | msgid "Web interface language:" | ||
| 268 | msgstr "" | ||
| 269 | |||
| 270 | #: html.c:1464 | ||
| 271 | msgid "New password:" | ||
| 272 | msgstr "" | ||
| 273 | |||
| 274 | #: html.c:1471 | ||
| 275 | msgid "Repeat new password:" | ||
| 276 | msgstr "" | ||
| 277 | |||
| 278 | #: html.c:1481 | ||
| 279 | msgid "Update user info" | ||
| 280 | msgstr "" | ||
| 281 | |||
| 282 | #: html.c:1492 | ||
| 283 | msgid "Followed hashtags..." | ||
| 284 | msgstr "" | ||
| 285 | |||
| 286 | #: html.c:1494 | ||
| 287 | msgid "One hashtag per line" | ||
| 288 | msgstr "" | ||
| 289 | |||
| 290 | #: html.c:1515 | ||
| 291 | msgid "Update hashtags" | ||
| 292 | msgstr "" | ||
| 293 | |||
| 294 | #: html.c:1632 | ||
| 295 | msgid "Say you like this post" | ||
| 296 | msgstr "" | ||
| 297 | |||
| 298 | #: html.c:1637 html.c:4294 | ||
| 299 | msgid "Unlike" | ||
| 300 | msgstr "" | ||
| 301 | |||
| 302 | #: html.c:1637 | ||
| 303 | msgid "Nah don't like it that much" | ||
| 304 | msgstr "" | ||
| 305 | |||
| 306 | #: html.c:1643 html.c:4426 | ||
| 307 | msgid "Unpin" | ||
| 308 | msgstr "" | ||
| 309 | |||
| 310 | #: html.c:1643 | ||
| 311 | msgid "Unpin this post from your timeline" | ||
| 312 | msgstr "" | ||
| 313 | |||
| 314 | #: html.c:1646 html.c:4421 | ||
| 315 | msgid "Pin" | ||
| 316 | msgstr "" | ||
| 317 | |||
| 318 | #: html.c:1646 | ||
| 319 | msgid "Pin this post to the top of your timeline" | ||
| 320 | msgstr "" | ||
| 321 | |||
| 322 | #: html.c:1653 | ||
| 323 | msgid "Announce this post to your followers" | ||
| 324 | msgstr "" | ||
| 325 | |||
| 326 | #: html.c:1658 html.c:4302 | ||
| 327 | msgid "Unboost" | ||
| 328 | msgstr "" | ||
| 329 | |||
| 330 | #: html.c:1658 | ||
| 331 | msgid "I regret I boosted this" | ||
| 332 | msgstr "" | ||
| 333 | |||
| 334 | #: html.c:1664 html.c:4436 | ||
| 335 | msgid "Unbookmark" | ||
| 336 | msgstr "" | ||
| 337 | |||
| 338 | #: html.c:1664 | ||
| 339 | msgid "Delete this post from your bookmarks" | ||
| 340 | msgstr "" | ||
| 341 | |||
| 342 | #: html.c:1667 html.c:4431 | ||
| 343 | msgid "Bookmark" | ||
| 344 | msgstr "" | ||
| 345 | |||
| 346 | #: html.c:1667 | ||
| 347 | msgid "Add this post to your bookmarks" | ||
| 348 | msgstr "" | ||
| 349 | |||
| 350 | #: html.c:1673 html.c:3002 html.c:3190 html.c:4346 | ||
| 351 | msgid "Unfollow" | ||
| 352 | msgstr "" | ||
| 353 | |||
| 354 | #: html.c:1673 html.c:3003 | ||
| 355 | msgid "Stop following this user's activity" | ||
| 356 | msgstr "" | ||
| 357 | |||
| 358 | #: html.c:1677 html.c:3017 | ||
| 359 | msgid "Start following this user's activity" | ||
| 360 | msgstr "" | ||
| 361 | |||
| 362 | #: html.c:1683 html.c:4376 | ||
| 363 | msgid "Unfollow Group" | ||
| 364 | msgstr "" | ||
| 365 | |||
| 366 | #: html.c:1684 | ||
| 367 | msgid "Stop following this group or channel" | ||
| 368 | msgstr "" | ||
| 369 | |||
| 370 | #: html.c:1688 html.c:4363 | ||
| 371 | msgid "Follow Group" | ||
| 372 | msgstr "" | ||
| 373 | |||
| 374 | #: html.c:1689 | ||
| 375 | msgid "Start following this group or channel" | ||
| 376 | msgstr "" | ||
| 377 | |||
| 378 | #: html.c:1694 html.c:3039 html.c:4310 | ||
| 379 | msgid "MUTE" | ||
| 380 | msgstr "" | ||
| 381 | |||
| 382 | #: html.c:1695 | ||
| 383 | msgid "Block any activity from this user forever" | ||
| 384 | msgstr "" | ||
| 385 | |||
| 386 | #: html.c:1700 html.c:3021 html.c:4393 | ||
| 387 | msgid "Delete" | ||
| 388 | msgstr "" | ||
| 389 | |||
| 390 | #: html.c:1700 | ||
| 391 | msgid "Delete this post" | ||
| 392 | msgstr "" | ||
| 393 | |||
| 394 | #: html.c:1703 html.c:4318 | ||
| 395 | msgid "Hide" | ||
| 396 | msgstr "" | ||
| 397 | |||
| 398 | #: html.c:1703 | ||
| 399 | msgid "Hide this post and its children" | ||
| 400 | msgstr "" | ||
| 401 | |||
| 402 | #: html.c:1734 | ||
| 403 | msgid "Edit..." | ||
| 404 | msgstr "" | ||
| 405 | |||
| 406 | #: html.c:1753 | ||
| 407 | msgid "Reply..." | ||
| 408 | msgstr "" | ||
| 409 | |||
| 410 | #: html.c:1804 | ||
| 411 | msgid "Truncated (too deep)" | ||
| 412 | msgstr "" | ||
| 413 | |||
| 414 | #: html.c:1813 | ||
| 415 | msgid "follows you" | ||
| 416 | msgstr "" | ||
| 417 | |||
| 418 | #: html.c:1876 | ||
| 419 | msgid "Pinned" | ||
| 420 | msgstr "" | ||
| 421 | |||
| 422 | #: html.c:1884 | ||
| 423 | msgid "Bookmarked" | ||
| 424 | msgstr "" | ||
| 425 | |||
| 426 | #: html.c:1892 | ||
| 427 | msgid "Poll" | ||
| 428 | msgstr "" | ||
| 429 | |||
| 430 | #: html.c:1899 | ||
| 431 | msgid "Voted" | ||
| 432 | msgstr "" | ||
| 433 | |||
| 434 | #: html.c:1908 | ||
| 435 | msgid "Event" | ||
| 436 | msgstr "" | ||
| 437 | |||
| 438 | #: html.c:1940 html.c:1969 | ||
| 439 | msgid "boosted" | ||
| 440 | msgstr "" | ||
| 441 | |||
| 442 | #: html.c:1985 | ||
| 443 | msgid "in reply to" | ||
| 444 | msgstr "" | ||
| 445 | |||
| 446 | #: html.c:2036 | ||
| 447 | msgid " [SENSITIVE CONTENT]" | ||
| 448 | msgstr "" | ||
| 449 | |||
| 450 | #: html.c:2213 | ||
| 451 | msgid "Vote" | ||
| 452 | msgstr "" | ||
| 453 | |||
| 454 | #: html.c:2223 | ||
| 455 | msgid "Closed" | ||
| 456 | msgstr "" | ||
| 457 | |||
| 458 | #: html.c:2248 | ||
| 459 | msgid "Closes in" | ||
| 460 | msgstr "" | ||
| 461 | |||
| 462 | #: html.c:2327 | ||
| 463 | msgid "Video" | ||
| 464 | msgstr "" | ||
| 465 | |||
| 466 | #: html.c:2342 | ||
| 467 | msgid "Audio" | ||
| 468 | msgstr "" | ||
| 469 | |||
| 470 | #: html.c:2364 | ||
| 471 | msgid "Attachment" | ||
| 472 | msgstr "" | ||
| 473 | |||
| 474 | #: html.c:2378 | ||
| 475 | msgid "Alt..." | ||
| 476 | msgstr "" | ||
| 477 | |||
| 478 | #: html.c:2391 | ||
| 479 | msgid "Source channel or community" | ||
| 480 | msgstr "" | ||
| 481 | |||
| 482 | #: html.c:2485 | ||
| 483 | msgid "Time: " | ||
| 484 | msgstr "" | ||
| 485 | |||
| 486 | #: html.c:2560 | ||
| 487 | msgid "Older..." | ||
| 488 | msgstr "" | ||
| 489 | |||
| 490 | #: html.c:2623 | ||
| 491 | msgid "about this site" | ||
| 492 | msgstr "" | ||
| 493 | |||
| 494 | #: html.c:2625 | ||
| 495 | msgid "powered by " | ||
| 496 | msgstr "" | ||
| 497 | |||
| 498 | #: html.c:2690 | ||
| 499 | msgid "Dismiss" | ||
| 500 | msgstr "" | ||
| 501 | |||
| 502 | #: html.c:2707 | ||
| 503 | #, c-format | ||
| 504 | msgid "Timeline for list '%s'" | ||
| 505 | msgstr "" | ||
| 506 | |||
| 507 | #: html.c:2726 html.c:3767 | ||
| 508 | msgid "Pinned posts" | ||
| 509 | msgstr "" | ||
| 510 | |||
| 511 | #: html.c:2738 html.c:3782 | ||
| 512 | msgid "Bookmarked posts" | ||
| 513 | msgstr "" | ||
| 514 | |||
| 515 | #: html.c:2750 html.c:3797 | ||
| 516 | msgid "Post drafts" | ||
| 517 | msgstr "" | ||
| 518 | |||
| 519 | #: html.c:2809 | ||
| 520 | msgid "No more unseen posts" | ||
| 521 | msgstr "" | ||
| 522 | |||
| 523 | #: html.c:2813 html.c:2913 | ||
| 524 | msgid "Back to top" | ||
| 525 | msgstr "" | ||
| 526 | |||
| 527 | #: html.c:2866 | ||
| 528 | msgid "History" | ||
| 529 | msgstr "" | ||
| 530 | |||
| 531 | #: html.c:2918 html.c:3338 | ||
| 532 | msgid "More..." | ||
| 533 | msgstr "" | ||
| 534 | |||
| 535 | #: html.c:3007 html.c:4329 | ||
| 536 | msgid "Unlimit" | ||
| 537 | msgstr "" | ||
| 538 | |||
| 539 | #: html.c:3008 | ||
| 540 | msgid "Allow announces (boosts) from this user" | ||
| 541 | msgstr "" | ||
| 542 | |||
| 543 | #: html.c:3011 html.c:4325 | ||
| 544 | msgid "Limit" | ||
| 545 | msgstr "" | ||
| 546 | |||
| 547 | #: html.c:3012 | ||
| 548 | msgid "Block announces (boosts) from this user" | ||
| 549 | msgstr "" | ||
| 550 | |||
| 551 | #: html.c:3021 | ||
| 552 | msgid "Delete this user" | ||
| 553 | msgstr "" | ||
| 554 | |||
| 555 | #: html.c:3026 html.c:4441 | ||
| 556 | msgid "Approve" | ||
| 557 | msgstr "" | ||
| 558 | |||
| 559 | #: html.c:3027 | ||
| 560 | msgid "Approve this follow request" | ||
| 561 | msgstr "" | ||
| 562 | |||
| 563 | #: html.c:3030 html.c:4465 | ||
| 564 | msgid "Discard" | ||
| 565 | msgstr "" | ||
| 566 | |||
| 567 | #: html.c:3030 | ||
| 568 | msgid "Discard this follow request" | ||
| 569 | msgstr "" | ||
| 570 | |||
| 571 | #: html.c:3035 html.c:4314 | ||
| 572 | msgid "Unmute" | ||
| 573 | msgstr "" | ||
| 574 | |||
| 575 | #: html.c:3036 | ||
| 576 | msgid "Stop blocking activities from this user" | ||
| 577 | msgstr "" | ||
| 578 | |||
| 579 | #: html.c:3040 | ||
| 580 | msgid "Block any activity from this user" | ||
| 581 | msgstr "" | ||
| 582 | |||
| 583 | #: html.c:3048 | ||
| 584 | msgid "Direct Message..." | ||
| 585 | msgstr "" | ||
| 586 | |||
| 587 | #: html.c:3083 | ||
| 588 | msgid "Pending follow confirmations" | ||
| 589 | msgstr "" | ||
| 590 | |||
| 591 | #: html.c:3087 | ||
| 592 | msgid "People you follow" | ||
| 593 | msgstr "" | ||
| 594 | |||
| 595 | #: html.c:3088 | ||
| 596 | msgid "People that follow you" | ||
| 597 | msgstr "" | ||
| 598 | |||
| 599 | #: html.c:3127 | ||
| 600 | msgid "Clear all" | ||
| 601 | msgstr "" | ||
| 602 | |||
| 603 | #: html.c:3184 | ||
| 604 | msgid "Mention" | ||
| 605 | msgstr "" | ||
| 606 | |||
| 607 | #: html.c:3187 | ||
| 608 | msgid "Finished poll" | ||
| 609 | msgstr "" | ||
| 610 | |||
| 611 | #: html.c:3202 | ||
| 612 | msgid "Follow Request" | ||
| 613 | msgstr "" | ||
| 614 | |||
| 615 | #: html.c:3285 | ||
| 616 | msgid "Context" | ||
| 617 | msgstr "" | ||
| 618 | |||
| 619 | #: html.c:3296 | ||
| 620 | msgid "New" | ||
| 621 | msgstr "" | ||
| 622 | |||
| 623 | #: html.c:3311 | ||
| 624 | msgid "Already seen" | ||
| 625 | msgstr "" | ||
| 626 | |||
| 627 | #: html.c:3326 | ||
| 628 | msgid "None" | ||
| 629 | msgstr "" | ||
| 630 | |||
| 631 | #: html.c:3592 | ||
| 632 | #, c-format | ||
| 633 | msgid "Search results for account %s" | ||
| 634 | msgstr "" | ||
| 635 | |||
| 636 | #: html.c:3599 | ||
| 637 | #, c-format | ||
| 638 | msgid "Account %s not found" | ||
| 639 | msgstr "" | ||
| 640 | |||
| 641 | #: html.c:3630 | ||
| 642 | #, c-format | ||
| 643 | msgid "Search results for tag %s" | ||
| 644 | msgstr "" | ||
| 645 | |||
| 646 | #: html.c:3630 | ||
| 647 | #, c-format | ||
| 648 | msgid "Nothing found for tag %s" | ||
| 649 | msgstr "" | ||
| 650 | |||
| 651 | #: html.c:3646 | ||
| 652 | #, c-format | ||
| 653 | msgid "Search results for '%s' (may be more)" | ||
| 654 | msgstr "" | ||
| 655 | |||
| 656 | #: html.c:3649 | ||
| 657 | #, c-format | ||
| 658 | msgid "Search results for '%s'" | ||
| 659 | msgstr "" | ||
| 660 | |||
| 661 | #: html.c:3652 | ||
| 662 | #, c-format | ||
| 663 | msgid "No more matches for '%s'" | ||
| 664 | msgstr "" | ||
| 665 | |||
| 666 | #: html.c:3654 | ||
| 667 | #, c-format | ||
| 668 | msgid "Nothing found for '%s'" | ||
| 669 | msgstr "" | ||
| 670 | |||
| 671 | #: html.c:3752 | ||
| 672 | msgid "Showing instance timeline" | ||
| 673 | msgstr "" | ||
| 674 | |||
| 675 | #: html.c:3820 | ||
| 676 | #, c-format | ||
| 677 | msgid "Showing timeline for list '%s'" | ||
| 678 | msgstr "" | ||
| 679 | |||
| 680 | #: httpd.c:250 | ||
| 681 | #, c-format | ||
| 682 | msgid "Search results for tag #%s" | ||
| 683 | msgstr "" | ||
| 684 | |||
| 685 | #: httpd.c:259 | ||
| 686 | msgid "Recent posts by users in this instance" | ||
| 687 | msgstr "" | ||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "xs_match.h" | 24 | #include "xs_match.h" |
| 25 | #include "xs_fcgi.h" | 25 | #include "xs_fcgi.h" |
| 26 | #include "xs_html.h" | 26 | #include "xs_html.h" |
| 27 | #include "xs_po.h" | ||
| 27 | 28 | ||
| 28 | #include "snac.h" | 29 | #include "snac.h" |
| 29 | 30 | ||
| @@ -34,6 +35,7 @@ xs_str *srv_basedir = NULL; | |||
| 34 | xs_dict *srv_config = NULL; | 35 | xs_dict *srv_config = NULL; |
| 35 | xs_str *srv_baseurl = NULL; | 36 | xs_str *srv_baseurl = NULL; |
| 36 | xs_str *srv_proxy_token_seed = NULL; | 37 | xs_str *srv_proxy_token_seed = NULL; |
| 38 | xs_dict *srv_langs = NULL; | ||
| 37 | 39 | ||
| 38 | int dbglevel = 0; | 40 | int dbglevel = 0; |
| 39 | 41 | ||
| @@ -179,6 +181,7 @@ const char *http_status_text(int status) | |||
| 179 | /* translate status codes to canonical status texts */ | 181 | /* translate status codes to canonical status texts */ |
| 180 | { | 182 | { |
| 181 | switch (status) { | 183 | switch (status) { |
| 184 | case 399: return "Timeout"; | ||
| 182 | #define HTTP_STATUS(code, name, text) case HTTP_STATUS_ ## name: return #text; | 185 | #define HTTP_STATUS(code, name, text) case HTTP_STATUS_ ## name: return #text; |
| 183 | #include "http_codes.h" | 186 | #include "http_codes.h" |
| 184 | #undef HTTP_STATUS | 187 | #undef HTTP_STATUS |
| @@ -1,7 +1,7 @@ | |||
| 1 | /* snac - A simple, minimalistic ActivityPub instance */ | 1 | /* snac - A simple, minimalistic ActivityPub instance */ |
| 2 | /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ | 2 | /* copyright (c) 2022 - 2025 grunfink et al. / MIT license */ |
| 3 | 3 | ||
| 4 | #define VERSION "2.72" | 4 | #define VERSION "2.73-dev" |
| 5 | 5 | ||
| 6 | #define USER_AGENT "snac/" VERSION | 6 | #define USER_AGENT "snac/" VERSION |
| 7 | 7 | ||
| @@ -16,6 +16,10 @@ | |||
| 16 | #define MAX_THREADS 256 | 16 | #define MAX_THREADS 256 |
| 17 | #endif | 17 | #endif |
| 18 | 18 | ||
| 19 | #ifndef MAX_JSON_DEPTH | ||
| 20 | #define MAX_JSON_DEPTH 8 | ||
| 21 | #endif | ||
| 22 | |||
| 19 | #ifndef MAX_CONVERSATION_LEVELS | 23 | #ifndef MAX_CONVERSATION_LEVELS |
| 20 | #define MAX_CONVERSATION_LEVELS 48 | 24 | #define MAX_CONVERSATION_LEVELS 48 |
| 21 | #endif | 25 | #endif |
| @@ -29,10 +33,11 @@ extern xs_str *srv_basedir; | |||
| 29 | extern xs_dict *srv_config; | 33 | extern xs_dict *srv_config; |
| 30 | extern xs_str *srv_baseurl; | 34 | extern xs_str *srv_baseurl; |
| 31 | extern xs_str *srv_proxy_token_seed; | 35 | extern xs_str *srv_proxy_token_seed; |
| 36 | extern xs_dict *srv_langs; | ||
| 32 | 37 | ||
| 33 | extern int dbglevel; | 38 | extern int dbglevel; |
| 34 | 39 | ||
| 35 | #define L(s) (s) | 40 | #define L(s) lang_str((s), user) |
| 36 | 41 | ||
| 37 | #define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video|Audio|Event" | 42 | #define POSTLIKE_OBJECT_TYPE "Note|Question|Page|Article|Video|Audio|Event" |
| 38 | 43 | ||
| @@ -55,6 +60,7 @@ typedef struct { | |||
| 55 | xs_dict *links; /* validated links */ | 60 | xs_dict *links; /* validated links */ |
| 56 | xs_str *actor; /* actor url */ | 61 | xs_str *actor; /* actor url */ |
| 57 | xs_str *md5; /* actor url md5 */ | 62 | xs_str *md5; /* actor url md5 */ |
| 63 | const xs_dict *lang;/* string translation dict */ | ||
| 58 | } snac; | 64 | } snac; |
| 59 | 65 | ||
| 60 | typedef struct { | 66 | typedef struct { |
| @@ -441,3 +447,5 @@ xs_str *make_url(const char *href, const char *proxy, int by_token); | |||
| 441 | 447 | ||
| 442 | int badlogin_check(const char *user, const char *addr); | 448 | int badlogin_check(const char *user, const char *addr); |
| 443 | void badlogin_inc(const char *user, const char *addr); | 449 | void badlogin_inc(const char *user, const char *addr); |
| 450 | |||
| 451 | const char *lang_str(const char *str, const snac *user); | ||
| @@ -13,6 +13,8 @@ int xs_smtp_request(const char *url, const char *user, const char *pass, | |||
| 13 | const char *from, const char *to, const xs_str *body, | 13 | const char *from, const char *to, const xs_str *body, |
| 14 | int use_ssl); | 14 | int use_ssl); |
| 15 | 15 | ||
| 16 | const char *xs_curl_strerr(int errnum); | ||
| 17 | |||
| 16 | #ifdef XS_IMPLEMENTATION | 18 | #ifdef XS_IMPLEMENTATION |
| 17 | 19 | ||
| 18 | #include <curl/curl.h> | 20 | #include <curl/curl.h> |
| @@ -240,6 +242,15 @@ int xs_smtp_request(const char *url, const char *user, const char *pass, | |||
| 240 | return (int)res; | 242 | return (int)res; |
| 241 | } | 243 | } |
| 242 | 244 | ||
| 245 | |||
| 246 | const char *xs_curl_strerr(int errnum) | ||
| 247 | { | ||
| 248 | CURLcode cc = errnum < 0 ? -errnum : errnum; | ||
| 249 | |||
| 250 | return curl_easy_strerror(cc); | ||
| 251 | } | ||
| 252 | |||
| 253 | |||
| 243 | #endif /* XS_IMPLEMENTATION */ | 254 | #endif /* XS_IMPLEMENTATION */ |
| 244 | 255 | ||
| 245 | #endif /* _XS_CURL_H */ | 256 | #endif /* _XS_CURL_H */ |
| @@ -4,17 +4,23 @@ | |||
| 4 | 4 | ||
| 5 | #define _XS_JSON_H | 5 | #define _XS_JSON_H |
| 6 | 6 | ||
| 7 | #ifndef MAX_JSON_DEPTH | ||
| 8 | #define MAX_JSON_DEPTH 32 | ||
| 9 | #endif | ||
| 10 | |||
| 7 | int xs_json_dump(const xs_val *data, int indent, FILE *f); | 11 | int xs_json_dump(const xs_val *data, int indent, FILE *f); |
| 8 | xs_str *xs_json_dumps(const xs_val *data, int indent); | 12 | xs_str *xs_json_dumps(const xs_val *data, int indent); |
| 9 | 13 | ||
| 10 | xs_val *xs_json_load(FILE *f); | 14 | xs_val *xs_json_load_full(FILE *f, int maxdepth); |
| 11 | xs_val *xs_json_loads(const xs_str *json); | 15 | xs_val *xs_json_loads_full(const xs_str *json, int maxdepth); |
| 16 | #define xs_json_load(f) xs_json_load_full(f, MAX_JSON_DEPTH) | ||
| 17 | #define xs_json_loads(s) xs_json_loads_full(s, MAX_JSON_DEPTH) | ||
| 12 | 18 | ||
| 13 | xstype xs_json_load_type(FILE *f); | 19 | xstype xs_json_load_type(FILE *f); |
| 14 | int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c); | 20 | int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c); |
| 15 | int xs_json_load_object_iter(FILE *f, xs_str **key, xs_val **value, xstype *pt, int *c); | 21 | int xs_json_load_object_iter(FILE *f, xs_str **key, xs_val **value, xstype *pt, int *c); |
| 16 | xs_list *xs_json_load_array(FILE *f); | 22 | xs_list *xs_json_load_array(FILE *f, int maxdepth); |
| 17 | xs_dict *xs_json_load_object(FILE *f); | 23 | xs_dict *xs_json_load_object(FILE *f, int maxdepth); |
| 18 | 24 | ||
| 19 | 25 | ||
| 20 | #ifdef XS_IMPLEMENTATION | 26 | #ifdef XS_IMPLEMENTATION |
| @@ -371,7 +377,7 @@ int xs_json_load_array_iter(FILE *f, xs_val **value, xstype *pt, int *c) | |||
| 371 | } | 377 | } |
| 372 | 378 | ||
| 373 | 379 | ||
| 374 | xs_list *xs_json_load_array(FILE *f) | 380 | xs_list *xs_json_load_array(FILE *f, int maxdepth) |
| 375 | /* loads a full JSON array (after the initial OBRACK) */ | 381 | /* loads a full JSON array (after the initial OBRACK) */ |
| 376 | { | 382 | { |
| 377 | xstype t; | 383 | xstype t; |
| @@ -387,12 +393,12 @@ xs_list *xs_json_load_array(FILE *f) | |||
| 387 | 393 | ||
| 388 | if (r == 1) { | 394 | if (r == 1) { |
| 389 | /* partial load? */ | 395 | /* partial load? */ |
| 390 | if (v == NULL) { | 396 | if (v == NULL && maxdepth != 0) { |
| 391 | if (t == XSTYPE_LIST) | 397 | if (t == XSTYPE_LIST) |
| 392 | v = xs_json_load_array(f); | 398 | v = xs_json_load_array(f, maxdepth - 1); |
| 393 | else | 399 | else |
| 394 | if (t == XSTYPE_DICT) | 400 | if (t == XSTYPE_DICT) |
| 395 | v = xs_json_load_object(f); | 401 | v = xs_json_load_object(f, maxdepth - 1); |
| 396 | } | 402 | } |
| 397 | 403 | ||
| 398 | /* still null? fail */ | 404 | /* still null? fail */ |
| @@ -459,7 +465,7 @@ int xs_json_load_object_iter(FILE *f, xs_str **key, xs_val **value, xstype *pt, | |||
| 459 | } | 465 | } |
| 460 | 466 | ||
| 461 | 467 | ||
| 462 | xs_dict *xs_json_load_object(FILE *f) | 468 | xs_dict *xs_json_load_object(FILE *f, int maxdepth) |
| 463 | /* loads a full JSON object (after the initial OCURLY) */ | 469 | /* loads a full JSON object (after the initial OCURLY) */ |
| 464 | { | 470 | { |
| 465 | xstype t; | 471 | xstype t; |
| @@ -476,12 +482,12 @@ xs_dict *xs_json_load_object(FILE *f) | |||
| 476 | 482 | ||
| 477 | if (r == 1) { | 483 | if (r == 1) { |
| 478 | /* partial load? */ | 484 | /* partial load? */ |
| 479 | if (v == NULL) { | 485 | if (v == NULL && maxdepth != 0) { |
| 480 | if (t == XSTYPE_LIST) | 486 | if (t == XSTYPE_LIST) |
| 481 | v = xs_json_load_array(f); | 487 | v = xs_json_load_array(f, maxdepth - 1); |
| 482 | else | 488 | else |
| 483 | if (t == XSTYPE_DICT) | 489 | if (t == XSTYPE_DICT) |
| 484 | v = xs_json_load_object(f); | 490 | v = xs_json_load_object(f, maxdepth - 1); |
| 485 | } | 491 | } |
| 486 | 492 | ||
| 487 | /* still null? fail */ | 493 | /* still null? fail */ |
| @@ -500,14 +506,14 @@ xs_dict *xs_json_load_object(FILE *f) | |||
| 500 | } | 506 | } |
| 501 | 507 | ||
| 502 | 508 | ||
| 503 | xs_val *xs_json_loads(const xs_str *json) | 509 | xs_val *xs_json_loads_full(const xs_str *json, int maxdepth) |
| 504 | /* loads a string in JSON format and converts to a multiple data */ | 510 | /* loads a string in JSON format and converts to a multiple data */ |
| 505 | { | 511 | { |
| 506 | FILE *f; | 512 | FILE *f; |
| 507 | xs_val *v = NULL; | 513 | xs_val *v = NULL; |
| 508 | 514 | ||
| 509 | if ((f = fmemopen((char *)json, strlen(json), "r")) != NULL) { | 515 | if ((f = fmemopen((char *)json, strlen(json), "r")) != NULL) { |
| 510 | v = xs_json_load(f); | 516 | v = xs_json_load_full(f, maxdepth); |
| 511 | fclose(f); | 517 | fclose(f); |
| 512 | } | 518 | } |
| 513 | 519 | ||
| @@ -533,17 +539,17 @@ xstype xs_json_load_type(FILE *f) | |||
| 533 | } | 539 | } |
| 534 | 540 | ||
| 535 | 541 | ||
| 536 | xs_val *xs_json_load(FILE *f) | 542 | xs_val *xs_json_load_full(FILE *f, int maxdepth) |
| 537 | /* loads a JSON file */ | 543 | /* loads a JSON file */ |
| 538 | { | 544 | { |
| 539 | xs_val *v = NULL; | 545 | xs_val *v = NULL; |
| 540 | xstype t = xs_json_load_type(f); | 546 | xstype t = xs_json_load_type(f); |
| 541 | 547 | ||
| 542 | if (t == XSTYPE_LIST) | 548 | if (t == XSTYPE_LIST) |
| 543 | v = xs_json_load_array(f); | 549 | v = xs_json_load_array(f, maxdepth); |
| 544 | else | 550 | else |
| 545 | if (t == XSTYPE_DICT) | 551 | if (t == XSTYPE_DICT) |
| 546 | v = xs_json_load_object(f); | 552 | v = xs_json_load_object(f, maxdepth); |
| 547 | 553 | ||
| 548 | return v; | 554 | return v; |
| 549 | } | 555 | } |
| @@ -0,0 +1,86 @@ | |||
| 1 | /* copyright (c) 2025 grunfink et al. / MIT license */ | ||
| 2 | |||
| 3 | #ifndef _XS_PO_H | ||
| 4 | |||
| 5 | #define _XS_PO_H | ||
| 6 | |||
| 7 | xs_dict *xs_po_to_dict(const char *fn); | ||
| 8 | |||
| 9 | #ifdef XS_IMPLEMENTATION | ||
| 10 | |||
| 11 | xs_dict *xs_po_to_dict(const char *fn) | ||
| 12 | /* converts a PO file to a dict */ | ||
| 13 | { | ||
| 14 | xs_dict *d = NULL; | ||
| 15 | FILE *f; | ||
| 16 | |||
| 17 | if ((f = fopen(fn, "r")) != NULL) { | ||
| 18 | d = xs_dict_new(); | ||
| 19 | |||
| 20 | xs *k = NULL; | ||
| 21 | xs *v = NULL; | ||
| 22 | enum { IN_NONE, IN_K, IN_V } mode = IN_NONE; | ||
| 23 | |||
| 24 | while (!feof(f)) { | ||
| 25 | xs *l = xs_strip_i(xs_readline(f)); | ||
| 26 | |||
| 27 | /* discard empty lines and comments */ | ||
| 28 | if (*l == '\0' || *l == '#') | ||
| 29 | continue; | ||
| 30 | |||
| 31 | if (xs_startswith(l, "msgid ")) { | ||
| 32 | if (mode == IN_V) { | ||
| 33 | /* flush */ | ||
| 34 | if (xs_is_string(k) && xs_is_string(v) && *v) | ||
| 35 | d = xs_dict_set(d, k, v); | ||
| 36 | |||
| 37 | k = xs_free(k); | ||
| 38 | v = xs_free(v); | ||
| 39 | } | ||
| 40 | |||
| 41 | l = xs_replace_i(l, "msgid ", ""); | ||
| 42 | mode = IN_K; | ||
| 43 | |||
| 44 | k = xs_str_new(NULL); | ||
| 45 | } | ||
| 46 | else | ||
| 47 | if (xs_startswith(l, "msgstr ")) { | ||
| 48 | if (mode != IN_K) | ||
| 49 | break; | ||
| 50 | |||
| 51 | l = xs_replace_i(l, "msgstr ", ""); | ||
| 52 | mode = IN_V; | ||
| 53 | |||
| 54 | v = xs_str_new(NULL); | ||
| 55 | } | ||
| 56 | |||
| 57 | l = xs_replace_i(l, "\\n", "\n"); | ||
| 58 | l = xs_strip_chars_i(l, "\""); | ||
| 59 | |||
| 60 | switch (mode) { | ||
| 61 | case IN_K: | ||
| 62 | k = xs_str_cat(k, l); | ||
| 63 | break; | ||
| 64 | |||
| 65 | case IN_V: | ||
| 66 | v = xs_str_cat(v, l); | ||
| 67 | break; | ||
| 68 | |||
| 69 | case IN_NONE: | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /* final flush */ | ||
| 75 | if (xs_is_string(k) && xs_is_string(v) && *v) | ||
| 76 | d = xs_dict_set(d, k, v); | ||
| 77 | |||
| 78 | fclose(f); | ||
| 79 | } | ||
| 80 | |||
| 81 | return d; | ||
| 82 | } | ||
| 83 | |||
| 84 | #endif /* XS_IMPLEMENTATION */ | ||
| 85 | |||
| 86 | #endif /* XS_PO_H */ | ||
| @@ -43,11 +43,13 @@ xs_list *xs_regex_split_n(const char *str, const char *rx, int count) | |||
| 43 | while (count > 0 && !regexec(&re, (p = str + offset), 1, &rm, offset > 0 ? REG_NOTBOL : 0)) { | 43 | while (count > 0 && !regexec(&re, (p = str + offset), 1, &rm, offset > 0 ? REG_NOTBOL : 0)) { |
| 44 | /* add first the leading part of the string */ | 44 | /* add first the leading part of the string */ |
| 45 | xs *s1 = xs_str_new_sz(p, rm.rm_so); | 45 | xs *s1 = xs_str_new_sz(p, rm.rm_so); |
| 46 | list = xs_list_append(list, s1); | 46 | |
| 47 | list = xs_list_append(list, xs_is_string(s1) ? s1 : ""); | ||
| 47 | 48 | ||
| 48 | /* add now the matched text as the separator */ | 49 | /* add now the matched text as the separator */ |
| 49 | xs *s2 = xs_str_new_sz(p + rm.rm_so, rm.rm_eo - rm.rm_so); | 50 | xs *s2 = xs_str_new_sz(p + rm.rm_so, rm.rm_eo - rm.rm_so); |
| 50 | list = xs_list_append(list, s2); | 51 | |
| 52 | list = xs_list_append(list, xs_is_string(s2) ? s2 : ""); | ||
| 51 | 53 | ||
| 52 | /* move forward */ | 54 | /* move forward */ |
| 53 | offset += rm.rm_eo; | 55 | offset += rm.rm_eo; |
diff --git a/xs_version.h b/xs_version.h index 7314133..f899dcb 100644 --- a/xs_version.h +++ b/xs_version.h | |||
| @@ -1 +1 @@ | |||
| /* 2f43b93e9d2b63360c802e09f4c68adfef74c673 2025-01-28T07:40:50+01:00 */ | /* d467dc71e518603250a55c8a67e26cf40e1710e9 2025-02-14T10:21:15+01:00 */ | ||