summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar grunfink2025-05-04 11:05:47 +0200
committerGravatar grunfink2025-05-04 11:05:47 +0200
commit64d99af19ce864ffefec50fcf0d19d2d65411c35 (patch)
treed4690836faf6bfe1e11b7e15c5629fe3a5f3491d
parentPreprocess the webmention q_item. (diff)
downloadpenes-snac2-64d99af19ce864ffefec50fcf0d19d2d65411c35.tar.gz
penes-snac2-64d99af19ce864ffefec50fcf0d19d2d65411c35.tar.xz
penes-snac2-64d99af19ce864ffefec50fcf0d19d2d65411c35.zip
xs_webmention.h new file.
-rw-r--r--Makefile6
-rw-r--r--Makefile.NetBSD5
-rw-r--r--snac.c1
-rw-r--r--xs_curl.h4
-rw-r--r--xs_version.h2
-rw-r--r--xs_webmention.h118
6 files changed, 128 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index d6389b5..46fdb09 100644
--- a/Makefile
+++ b/Makefile
@@ -56,7 +56,7 @@ format.o: format.c xs.h xs_regex.h xs_mime.h xs_html.h xs_json.h \
56 xs_time.h xs_match.h snac.h http_codes.h 56 xs_time.h xs_match.h snac.h http_codes.h
57html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 57html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \
58 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \ 58 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
59 snac.h http_codes.h 59 xs_random.h snac.h http_codes.h
60http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ 60http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \
61 snac.h http_codes.h 61 snac.h http_codes.h
62httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ 62httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \
@@ -71,10 +71,10 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h
71snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ 71snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \
72 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \ 72 xs_json.h xs_curl.h xs_openssl.h xs_socket.h xs_unix_socket.h xs_url.h \
73 xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \ 73 xs_httpd.h xs_mime.h xs_regex.h xs_set.h xs_time.h xs_glob.h xs_random.h \
74 xs_match.h xs_fcgi.h xs_html.h xs_po.h snac.h http_codes.h 74 xs_match.h xs_fcgi.h xs_html.h xs_po.h xs_webmention.h snac.h \
75 http_codes.h
75upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h 76upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h
76utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ 77utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \
77 xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h 78 xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h
78webfinger.o: webfinger.c xs.h xs_json.h xs_curl.h xs_mime.h snac.h \ 79webfinger.o: webfinger.c xs.h xs_json.h xs_curl.h xs_mime.h snac.h \
79 http_codes.h 80 http_codes.h
80tests/smtp.o: tests/smtp.c xs.h xs_curl.h
diff --git a/Makefile.NetBSD b/Makefile.NetBSD
index 51c8181..ecf8205 100644
--- a/Makefile.NetBSD
+++ b/Makefile.NetBSD
@@ -45,7 +45,7 @@ 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
46html.o: html.c xs.h xs_io.h xs_json.h xs_regex.h xs_set.h xs_openssl.h \ 46html.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 xs_url.h \ 47 xs_time.h xs_mime.h xs_match.h xs_html.h xs_curl.h xs_unicode.h xs_url.h \
48 snac.h http_codes.h 48 xs_random.h snac.h http_codes.h
49http.o: http.c xs.h xs_io.h xs_openssl.h xs_curl.h xs_time.h xs_json.h \ 49http.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
51httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \ 51httpd.o: httpd.c xs.h xs_io.h xs_json.h xs_socket.h xs_unix_socket.h \
@@ -60,7 +60,8 @@ sandbox.o: sandbox.c xs.h snac.h http_codes.h
60snac.o: snac.c xs.h xs_hex.h xs_io.h xs_unicode_tbl.h xs_unicode.h \ 60snac.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 xs_po.h snac.h http_codes.h 63 xs_match.h xs_fcgi.h xs_html.h xs_po.h xs_webmention.h snac.h \
64 http_codes.h
64upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h 65upgrade.o: upgrade.c xs.h xs_io.h xs_json.h xs_glob.h snac.h http_codes.h
65utils.o: utils.c xs.h xs_io.h xs_json.h xs_time.h xs_openssl.h \ 66utils.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 67 xs_random.h xs_glob.h xs_curl.h xs_regex.h snac.h http_codes.h
diff --git a/snac.c b/snac.c
index 0a1c9cf..a35cd06 100644
--- a/snac.c
+++ b/snac.c
@@ -25,6 +25,7 @@
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#include "xs_po.h"
28#include "xs_webmention.h"
28 29
29#include "snac.h" 30#include "snac.h"
30 31
diff --git a/xs_curl.h b/xs_curl.h
index ac543b1..2301661 100644
--- a/xs_curl.h
+++ b/xs_curl.h
@@ -225,7 +225,7 @@ int xs_smtp_request(const char *url, const char *user, const char *pass,
225 225
226 if (use_ssl) 226 if (use_ssl)
227 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL); 227 curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
228 228
229 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from); 229 curl_easy_setopt(curl, CURLOPT_MAIL_FROM, from);
230 230
231 rcpt = curl_slist_append(rcpt, to); 231 rcpt = curl_slist_append(rcpt, to);
@@ -236,7 +236,7 @@ int xs_smtp_request(const char *url, const char *user, const char *pass,
236 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); 236 curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
237 237
238 res = curl_easy_perform(curl); 238 res = curl_easy_perform(curl);
239 239
240 curl_easy_cleanup(curl); 240 curl_easy_cleanup(curl);
241 curl_slist_free_all(rcpt); 241 curl_slist_free_all(rcpt);
242 242
diff --git a/xs_version.h b/xs_version.h
index f899dcb..c7789a7 100644
--- a/xs_version.h
+++ b/xs_version.h
@@ -1 +1 @@
/* d467dc71e518603250a55c8a67e26cf40e1710e9 2025-02-14T10:21:15+01:00 */ /* 871d420cef893b6efe32869407294baf084ce3ab 2025-05-04T11:01:01+02:00 */
diff --git a/xs_webmention.h b/xs_webmention.h
new file mode 100644
index 0000000..4ef2308
--- /dev/null
+++ b/xs_webmention.h
@@ -0,0 +1,118 @@
1/* copyright (c) 2025 grunfink et al. / MIT license */
2
3#ifndef _XS_WEBMENTION_H
4
5#define _XS_WEBMENTION_H
6
7int xs_webmention_send(const char *source, const char *target, const char *user_agent);
8
9
10#ifdef XS_IMPLEMENTATION
11
12int xs_webmention_send(const char *source, const char *target, const char *user_agent)
13/* sends a Webmention to target.
14 Returns: < 0, error; 0, no Webmention endpoint; > 0, Webmention sent */
15{
16 int status = 0;
17 xs *endpoint = NULL;
18
19 xs *ua = xs_fmt("%s (Webmention)", user_agent ? user_agent : "xs_webmention");
20 xs *headers = xs_dict_new();
21 headers = xs_dict_set(headers, "accept", "text/html");
22 headers = xs_dict_set(headers, "user-agent", ua);
23
24 xs *h_req = NULL;
25 int p_size = 0;
26
27 /* try first a HEAD, to see if there is a Webmention Link header */
28 h_req = xs_http_request("HEAD", target, headers, NULL, 0, &status, NULL, &p_size, 0);
29
30 /* return immediate failures */
31 if (status < 200 || status > 299)
32 return -1;
33
34 const char *link = xs_dict_get(h_req, "link");
35
36 if (xs_is_string(link) && xs_regex_match(link, "rel *= *(\"|')?webmention")) {
37 /* endpoint is between < and > */
38 xs *r = xs_regex_select_n(link, "<[^>]+>", 1);
39
40 if (xs_list_len(r) == 1) {
41 endpoint = xs_dup(xs_list_get(r, 0));
42 endpoint = xs_strip_chars_i(endpoint, "<>");
43 }
44 }
45
46 if (endpoint == NULL) {
47 /* no Link header; get the content */
48 xs *g_req = NULL;
49 xs *payload = NULL;
50
51 g_req = xs_http_request("GET", target, headers, NULL, 0, &status, &payload, &p_size, 0);
52
53 if (status < 200 || status > 299)
54 return -1;
55
56 const char *ctype = xs_dict_get(g_req, "content-type");
57
58 /* not HTML? no point in looking inside */
59 if (!xs_is_string(ctype) || xs_str_in(ctype, "text/html") == -1)
60 return -2;
61
62 if (!xs_is_string(payload))
63 return -3;
64
65 xs *links = xs_regex_select(payload, "<(a +|link +)[^>]+>");
66 const char *link;
67
68 xs_list_foreach(links, link) {
69 if (xs_regex_match(link, "rel *= *(\"|')?webmention")) {
70 /* found; extract the href */
71 xs *r = xs_regex_select_n(link, "href *= *(\"|')?[^\"]+(\"|')", 1);
72
73 if (xs_list_len(r) == 1) {
74 xs *l = xs_split_n(xs_list_get(r, 0), "=", 1);
75
76 if (xs_list_len(l) == 2) {
77 endpoint = xs_dup(xs_list_get(l, 1));
78 endpoint = xs_strip_chars_i(endpoint, " \"'");
79
80 break;
81 }
82 }
83 }
84 }
85 }
86
87 /* is it a relative endpoint? */
88 if (xs_is_string(endpoint)) {
89 if (!xs_startswith(endpoint, "https://") && !xs_startswith(endpoint, "http://")) {
90 xs *l = xs_split(target, "/");
91
92 if (xs_list_len(l) < 3)
93 endpoint = xs_free(endpoint);
94 else {
95 xs *s = xs_fmt("%s/" "/%s", xs_list_get(l, 0), xs_list_get(l, 2));
96 endpoint = xs_str_wrap_i(s, endpoint, NULL);
97 }
98 }
99 }
100
101 if (xs_is_string(endpoint)) {
102 /* got it! */
103 headers = xs_dict_set(headers, "content-type", "application/x-www-form-urlencoded");
104
105 xs *body = xs_fmt("source=%s&target=%s", source, target);
106
107 xs *rsp = xs_http_request("POST", endpoint, headers, body, strlen(body), &status, NULL, 0, 0);
108 }
109 else
110 status = 0;
111
112 return status >= 200 && status <= 299;
113}
114
115
116#endif /* XS_IMPLEMENTATION */
117
118#endif /* _XS_WEBMENTION_H */