diff options
| author | 2025-06-24 14:06:41 +0200 | |
|---|---|---|
| committer | 2025-06-24 14:06:41 +0200 | |
| commit | f88bdd796045dd206b38e07adb13b5af4489b4c5 (patch) | |
| tree | c7796a9d7a2ff7b7cfa40e3b9df3f09f999f57fa | |
| parent | Some JSON code tweaks. (diff) | |
| download | snac2-f88bdd796045dd206b38e07adb13b5af4489b4c5.tar.gz snac2-f88bdd796045dd206b38e07adb13b5af4489b4c5.tar.xz snac2-f88bdd796045dd206b38e07adb13b5af4489b4c5.zip | |
Added a webmention hook.
| -rw-r--r-- | html.c | 20 | ||||
| -rw-r--r-- | httpd.c | 62 | ||||
| -rw-r--r-- | xs_webmention.h | 42 |
3 files changed, 119 insertions, 5 deletions
| @@ -868,6 +868,14 @@ xs_html *html_user_head(snac *user, const char *desc, const char *url) | |||
| 868 | xs_html_attr("type", "application/activity+json"), | 868 | xs_html_attr("type", "application/activity+json"), |
| 869 | xs_html_attr("href", url ? url : user->actor))); | 869 | xs_html_attr("href", url ? url : user->actor))); |
| 870 | 870 | ||
| 871 | /* webmention hook */ | ||
| 872 | xs *wbh = xs_fmt("%s/webmention-hook", srv_baseurl); | ||
| 873 | |||
| 874 | xs_html_add(head, | ||
| 875 | xs_html_sctag("link", | ||
| 876 | xs_html_attr("rel", "webmention"), | ||
| 877 | xs_html_attr("href", wbh))); | ||
| 878 | |||
| 871 | return head; | 879 | return head; |
| 872 | } | 880 | } |
| 873 | 881 | ||
| @@ -3407,13 +3415,15 @@ xs_str *html_notifications(snac *user, int skip, int show) | |||
| 3407 | 3415 | ||
| 3408 | const char *actor_id = xs_dict_get(noti, "actor"); | 3416 | const char *actor_id = xs_dict_get(noti, "actor"); |
| 3409 | xs *actor = NULL; | 3417 | xs *actor = NULL; |
| 3418 | xs *a_name = NULL; | ||
| 3410 | 3419 | ||
| 3411 | if (!valid_status(actor_get(actor_id, &actor))) | 3420 | if (valid_status(actor_get(actor_id, &actor))) |
| 3412 | continue; | 3421 | a_name = actor_name(actor, proxy); |
| 3422 | else | ||
| 3423 | a_name = xs_dup(actor_id); | ||
| 3413 | 3424 | ||
| 3414 | xs *a_name = actor_name(actor, proxy); | 3425 | xs *label_sanitized = sanitize(type); |
| 3415 | xs *label_sanatized = sanitize(type); | 3426 | const char *label = label_sanitized; |
| 3416 | const char *label = label_sanatized; | ||
| 3417 | 3427 | ||
| 3418 | if (strcmp(type, "Create") == 0) | 3428 | if (strcmp(type, "Create") == 0) |
| 3419 | label = L("Mention"); | 3429 | label = L("Mention"); |
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "xs_openssl.h" | 12 | #include "xs_openssl.h" |
| 13 | #include "xs_fcgi.h" | 13 | #include "xs_fcgi.h" |
| 14 | #include "xs_html.h" | 14 | #include "xs_html.h" |
| 15 | #include "xs_webmention.h" | ||
| 15 | 16 | ||
| 16 | #include "snac.h" | 17 | #include "snac.h" |
| 17 | 18 | ||
| @@ -373,6 +374,63 @@ int server_get_handler(xs_dict *req, const char *q_path, | |||
| 373 | } | 374 | } |
| 374 | 375 | ||
| 375 | 376 | ||
| 377 | int server_post_handler(const xs_dict *req, const char *q_path, | ||
| 378 | char *payload, int p_size, | ||
| 379 | char **body, int *b_size, char **ctype) | ||
| 380 | { | ||
| 381 | int status = 0; | ||
| 382 | |||
| 383 | if (strcmp(q_path, "/webmention-hook") == 0) { | ||
| 384 | status = HTTP_STATUS_BAD_REQUEST; | ||
| 385 | |||
| 386 | const xs_dict *p_vars = xs_dict_get(req, "p_vars"); | ||
| 387 | |||
| 388 | if (!xs_is_dict(p_vars)) | ||
| 389 | return status; | ||
| 390 | |||
| 391 | const char *source = xs_dict_get(p_vars, "source"); | ||
| 392 | const char *target = xs_dict_get(p_vars, "target"); | ||
| 393 | |||
| 394 | if (!xs_is_string(source) || !xs_is_string(target)) { | ||
| 395 | srv_debug(1, xs_fmt("webmention-hook bad source or target")); | ||
| 396 | return status; | ||
| 397 | } | ||
| 398 | |||
| 399 | if (!xs_startswith(target, srv_baseurl)) { | ||
| 400 | srv_debug(1, xs_fmt("webmention-hook unknown target %s", target)); | ||
| 401 | return status; | ||
| 402 | } | ||
| 403 | |||
| 404 | if (!object_here(target)) { | ||
| 405 | srv_debug(0, xs_fmt("webmention-hook target %s not / no longer here", target)); | ||
| 406 | return status; | ||
| 407 | } | ||
| 408 | |||
| 409 | /* get the user */ | ||
| 410 | xs *s1 = xs_replace(target, srv_baseurl, ""); | ||
| 411 | |||
| 412 | xs *l1 = xs_split(s1, "/"); | ||
| 413 | const char *uid = xs_list_get(l1, 1); | ||
| 414 | snac user; | ||
| 415 | |||
| 416 | if (!xs_is_string(uid) || !user_open(&user, uid)) | ||
| 417 | return status; | ||
| 418 | |||
| 419 | int r = xs_webmention_hook(source, target, USER_AGENT); | ||
| 420 | |||
| 421 | if (r > 0) | ||
| 422 | notify_add(&user, "Webmention", NULL, source, target, xs_stock(XSTYPE_DICT)); | ||
| 423 | |||
| 424 | srv_log(xs_fmt("webmention-hook source=%s target=%s %d", source, target, r)); | ||
| 425 | |||
| 426 | user_free(&user); | ||
| 427 | status = HTTP_STATUS_OK; | ||
| 428 | } | ||
| 429 | |||
| 430 | return status; | ||
| 431 | } | ||
| 432 | |||
| 433 | |||
| 376 | void httpd_connection(FILE *f) | 434 | void httpd_connection(FILE *f) |
| 377 | /* the connection processor */ | 435 | /* the connection processor */ |
| 378 | { | 436 | { |
| @@ -444,6 +502,10 @@ void httpd_connection(FILE *f) | |||
| 444 | else | 502 | else |
| 445 | if (strcmp(method, "POST") == 0) { | 503 | if (strcmp(method, "POST") == 0) { |
| 446 | 504 | ||
| 505 | if (status == 0) | ||
| 506 | status = server_post_handler(req, q_path, | ||
| 507 | payload, p_size, &body, &b_size, &ctype); | ||
| 508 | |||
| 447 | #ifndef NO_MASTODON_API | 509 | #ifndef NO_MASTODON_API |
| 448 | if (status == 0) | 510 | if (status == 0) |
| 449 | status = oauth_post_handler(req, q_path, | 511 | status = oauth_post_handler(req, q_path, |
diff --git a/xs_webmention.h b/xs_webmention.h index 8415629..e177573 100644 --- a/xs_webmention.h +++ b/xs_webmention.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #define _XS_WEBMENTION_H | 5 | #define _XS_WEBMENTION_H |
| 6 | 6 | ||
| 7 | int xs_webmention_send(const char *source, const char *target, const char *user_agent); | 7 | int xs_webmention_send(const char *source, const char *target, const char *user_agent); |
| 8 | int xs_webmention_hook(const char *source, const char *target, const char *user_agent); | ||
| 8 | 9 | ||
| 9 | 10 | ||
| 10 | #ifdef XS_IMPLEMENTATION | 11 | #ifdef XS_IMPLEMENTATION |
| @@ -118,6 +119,47 @@ int xs_webmention_send(const char *source, const char *target, const char *user_ | |||
| 118 | } | 119 | } |
| 119 | 120 | ||
| 120 | 121 | ||
| 122 | int xs_webmention_hook(const char *source, const char *target, const char *user_agent) | ||
| 123 | /* a Webmention has been received for a target that is ours; check if the source | ||
| 124 | really contains a link to our target */ | ||
| 125 | { | ||
| 126 | int status = 0; | ||
| 127 | |||
| 128 | xs *ua = xs_fmt("%s (Webmention)", user_agent ? user_agent : "xs_webmention"); | ||
| 129 | xs *headers = xs_dict_new(); | ||
| 130 | headers = xs_dict_set(headers, "accept", "text/html"); | ||
| 131 | headers = xs_dict_set(headers, "user-agent", ua); | ||
| 132 | |||
| 133 | xs *g_req = NULL; | ||
| 134 | xs *payload = NULL; | ||
| 135 | int p_size = 0; | ||
| 136 | |||
| 137 | g_req = xs_http_request("GET", source, headers, NULL, 0, &status, &payload, &p_size, 0); | ||
| 138 | |||
| 139 | if (status < 200 || status > 299) | ||
| 140 | return -1; | ||
| 141 | |||
| 142 | if (!xs_is_string(payload)) | ||
| 143 | return -2; | ||
| 144 | |||
| 145 | /* note: a "rogue" webmention can include a link to our target in commented-out HTML code */ | ||
| 146 | |||
| 147 | xs *links = xs_regex_select(payload, "<(a +|link +)[^>]+>"); | ||
| 148 | const char *link; | ||
| 149 | |||
| 150 | status = 0; | ||
| 151 | xs_list_foreach(links, link) { | ||
| 152 | /* if the link contains our target, it's valid */ | ||
| 153 | if (xs_str_in(link, target) != -1) { | ||
| 154 | status = 1; | ||
| 155 | break; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | return status; | ||
| 160 | } | ||
| 161 | |||
| 162 | |||
| 121 | #endif /* XS_IMPLEMENTATION */ | 163 | #endif /* XS_IMPLEMENTATION */ |
| 122 | 164 | ||
| 123 | #endif /* _XS_WEBMENTION_H */ | 165 | #endif /* _XS_WEBMENTION_H */ |