activitypub.c 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640
  1. /* snac - A simple, minimalistic ActivityPub instance */
  2. /* copyright (c) 2022 - 2024 grunfink et al. / MIT license */
  3. #include "xs.h"
  4. #include "xs_json.h"
  5. #include "xs_curl.h"
  6. #include "xs_mime.h"
  7. #include "xs_openssl.h"
  8. #include "xs_regex.h"
  9. #include "xs_time.h"
  10. #include "xs_set.h"
  11. #include "xs_match.h"
  12. #include "snac.h"
  13. #include <sys/wait.h>
  14. const char *public_address = "https:/" "/www.w3.org/ns/activitystreams#Public";
  15. /* susie.png */
  16. const char *susie =
  17. "iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQAAAAC"
  18. "CEkxzAAAAUUlEQVQoz43R0QkAMQwCUDdw/y3dwE"
  19. "vsvzlL4X1IoQkAisKmwfAFT3RgJHbQezpSRoXEq"
  20. "eqCL9BJBf7h3QbOCCxV5EVWMEMwG7K1/WODtlvx"
  21. "AYTtEsDU9F34AAAAAElFTkSuQmCC";
  22. const char *susie_cool =
  23. "iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQAAAAC"
  24. "CEkxzAAAAV0lEQVQoz43RwQ3AMAwCQDZg/y3ZgN"
  25. "qo3+JaedwDOUQBQFHYaTB8wTM6sGl2cMPu+DFzn"
  26. "+ZcgN7wF7ZVihXkfSlWIVzIA6dbQzaygllpNuTX"
  27. "ZmmFNlvxADX1+o0cUPMbAAAAAElFTkSuQmCC";
  28. const char *susie_muertos =
  29. "iVBORw0KGgoAAAANSUhEUgAAAEAAAABAAQAAAAC"
  30. "CEkxzAAAAV0lEQVQoz4XQsQ0AMQxCUW/A/lv+DT"
  31. "ic6zGRolekIMyMELNp8PiCEw6Q4w4NoAt53IH5m"
  32. "xXksrZYgZwJrIox+Z8vJAfe2lCxG6AK7eKkWcEb"
  33. "QHbF617xAQatAAD7jJHUAAAAAElFTkSuQmCC";
  34. const char *default_avatar_base64(void)
  35. /* returns the default avatar in base64 */
  36. {
  37. time_t t = time(NULL);
  38. struct tm tm;
  39. const char *p = susie;
  40. gmtime_r(&t, &tm);
  41. if (tm.tm_mon == 10 && tm.tm_mday == 2)
  42. p = susie_muertos;
  43. else
  44. if (tm.tm_wday == 0 || tm.tm_wday == 6)
  45. p = susie_cool;
  46. return p;
  47. }
  48. int activitypub_request(snac *user, const char *url, xs_dict **data)
  49. /* request an object */
  50. {
  51. int status = 0;
  52. xs *response = NULL;
  53. xs *payload = NULL;
  54. int p_size;
  55. char *ctype;
  56. *data = NULL;
  57. if (user != NULL) {
  58. /* get from the net */
  59. response = http_signed_request(user, "GET", url,
  60. NULL, NULL, 0, &status, &payload, &p_size, 0);
  61. }
  62. if (status == 0 || (status >= 500 && status <= 599)) {
  63. /* I found an instance running Misskey that returned
  64. 500 on signed messages but returned the object
  65. perfectly without signing (?), so why not try */
  66. xs_free(response);
  67. xs *hdrs = xs_dict_new();
  68. hdrs = xs_dict_append(hdrs, "accept", "application/activity+json");
  69. hdrs = xs_dict_append(hdrs, "user-agent", USER_AGENT);
  70. response = xs_http_request("GET", url, hdrs,
  71. NULL, 0, &status, &payload, &p_size, 0);
  72. }
  73. if (valid_status(status)) {
  74. /* ensure it's ActivityPub data */
  75. ctype = xs_dict_get(response, "content-type");
  76. if (xs_is_null(ctype))
  77. status = 400;
  78. else
  79. if (xs_str_in(ctype, "application/activity+json") != -1 ||
  80. xs_str_in(ctype, "application/ld+json") != -1) {
  81. /* if there is no payload, fail */
  82. if (xs_is_null(payload))
  83. status = 400;
  84. else
  85. *data = xs_json_loads(payload);
  86. }
  87. else
  88. status = 500;
  89. }
  90. return status;
  91. }
  92. int actor_request(snac *user, const char *actor, xs_dict **data)
  93. /* request an actor */
  94. {
  95. int status;
  96. xs *payload = NULL;
  97. if (data)
  98. *data = NULL;
  99. /* get from disk first */
  100. status = actor_get(actor, data);
  101. if (status != 200) {
  102. /* actor data non-existent or stale: get from the net */
  103. status = activitypub_request(user, actor, &payload);
  104. if (valid_status(status)) {
  105. /* renew data */
  106. status = actor_add(actor, payload);
  107. if (data != NULL) {
  108. *data = payload;
  109. payload = NULL;
  110. }
  111. }
  112. else
  113. srv_debug(1, xs_fmt("actor_request error %s %d", actor, status));
  114. }
  115. /* collect the (presumed) shared inbox in this actor */
  116. if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) {
  117. if (valid_status(status) && data && *data)
  118. inbox_add_by_actor(*data);
  119. }
  120. else
  121. srv_debug(2, xs_fmt("NOT collected"));
  122. return status;
  123. }
  124. char *get_atto(const xs_dict *msg)
  125. /* gets the attributedTo field (an actor) */
  126. {
  127. char *actor = xs_dict_get(msg, "attributedTo");
  128. /* if the actor is a list of objects (like on Peertube videos), pick the Person */
  129. if (xs_type(actor) == XSTYPE_LIST) {
  130. xs_list *p = actor;
  131. xs_dict *v;
  132. actor = NULL;
  133. while (actor == NULL && xs_list_iter(&p, &v)) {
  134. if (xs_type(v) == XSTYPE_DICT) {
  135. char *type = xs_dict_get(v, "type");
  136. if (xs_type(type) == XSTYPE_STRING && strcmp(type, "Person") == 0) {
  137. actor = xs_dict_get(v, "id");
  138. if (xs_type(actor) != XSTYPE_STRING)
  139. actor = NULL;
  140. }
  141. }
  142. }
  143. }
  144. return actor;
  145. }
  146. xs_list *get_attachments(const xs_dict *msg)
  147. /* unify the garbage fire that are the attachments */
  148. {
  149. xs_list *l = xs_list_new();
  150. xs_list *p;
  151. /* try first the attachments list */
  152. if (!xs_is_null(p = xs_dict_get(msg, "attachment"))) {
  153. xs *attach = NULL;
  154. xs_val *v;
  155. /* ensure it's a list */
  156. if (xs_type(p) == XSTYPE_DICT) {
  157. attach = xs_list_new();
  158. attach = xs_list_append(attach, v);
  159. }
  160. else
  161. attach = xs_dup(p);
  162. if (xs_type(attach) == XSTYPE_LIST) {
  163. /* does the message have an image? */
  164. if (xs_type(v = xs_dict_get(msg, "image")) == XSTYPE_DICT) {
  165. /* add it to the attachment list */
  166. attach = xs_list_append(attach, v);
  167. }
  168. }
  169. /* now iterate the list */
  170. p = attach;
  171. while (xs_list_iter(&p, &v)) {
  172. char *type = xs_dict_get(v, "mediaType");
  173. if (xs_is_null(type))
  174. type = xs_dict_get(v, "type");
  175. if (xs_is_null(type))
  176. continue;
  177. char *href = xs_dict_get(v, "url");
  178. if (xs_is_null(href))
  179. href = xs_dict_get(v, "href");
  180. if (xs_is_null(href))
  181. continue;
  182. /* infer MIME type from non-specific attachments */
  183. if (xs_list_len(attach) < 2 && xs_match(type, "Link|Document")) {
  184. char *mt = (char *)xs_mime_by_ext(href);
  185. if (xs_match(mt, "image/*|audio/*|video/*")) /* */
  186. type = mt;
  187. }
  188. char *name = xs_dict_get(v, "name");
  189. if (xs_is_null(name))
  190. name = xs_dict_get(msg, "name");
  191. if (xs_is_null(name))
  192. name = L("No description");
  193. xs *d = xs_dict_new();
  194. d = xs_dict_append(d, "type", type);
  195. d = xs_dict_append(d, "href", href);
  196. d = xs_dict_append(d, "name", name);
  197. l = xs_list_append(l, d);
  198. }
  199. }
  200. /** urls (attachments from Peertube) **/
  201. p = xs_dict_get(msg, "url");
  202. if (xs_type(p) == XSTYPE_LIST) {
  203. char *href = NULL;
  204. char *type = NULL;
  205. xs_val *v;
  206. while (href == NULL && xs_list_iter(&p, &v)) {
  207. if (xs_type(v) == XSTYPE_DICT) {
  208. char *mtype = xs_dict_get(v, "type");
  209. if (xs_type(mtype) == XSTYPE_STRING && strcmp(mtype, "Link") == 0) {
  210. mtype = xs_dict_get(v, "mediaType");
  211. xs_list *tag = xs_dict_get(v, "tag");
  212. if (xs_type(mtype) == XSTYPE_STRING &&
  213. strcmp(mtype, "application/x-mpegURL") == 0 &&
  214. xs_type(tag) == XSTYPE_LIST) {
  215. /* now iterate the tag list, looking for a video URL */
  216. xs_dict *d;
  217. while (href == NULL && xs_list_iter(&tag, &d)) {
  218. if (xs_type(d) == XSTYPE_DICT) {
  219. if (xs_type(mtype = xs_dict_get(d, "mediaType")) == XSTYPE_STRING &&
  220. xs_startswith(mtype, "video/")) {
  221. char *h = xs_dict_get(d, "href");
  222. /* this is probably it */
  223. if (xs_type(h) == XSTYPE_STRING) {
  224. href = h;
  225. type = mtype;
  226. }
  227. }
  228. }
  229. }
  230. }
  231. }
  232. }
  233. }
  234. if (href && type) {
  235. xs *d = xs_dict_new();
  236. d = xs_dict_append(d, "href", href);
  237. d = xs_dict_append(d, "type", type);
  238. d = xs_dict_append(d, "name", "---");
  239. l = xs_list_append(l, d);
  240. }
  241. }
  242. return l;
  243. }
  244. int timeline_request(snac *snac, char **id, xs_str **wrk, int level)
  245. /* ensures that an entry and its ancestors are in the timeline */
  246. {
  247. int status = 0;
  248. if (level < 256 && !xs_is_null(*id)) {
  249. xs *msg = NULL;
  250. /* is the object already there? */
  251. if (!valid_status(object_get(*id, &msg))) {
  252. /* no; download it */
  253. status = activitypub_request(snac, *id, &msg);
  254. if (valid_status(status)) {
  255. xs_dict *object = msg;
  256. const char *type = xs_dict_get(object, "type");
  257. /* get the id again from the object, as it may be different */
  258. const char *nid = xs_dict_get(object, "id");
  259. if (xs_type(nid) != XSTYPE_STRING)
  260. return 0;
  261. if (wrk && strcmp(nid, *id) != 0) {
  262. snac_debug(snac, 1,
  263. xs_fmt("timeline_request canonical id for %s is %s", *id, nid));
  264. *wrk = xs_dup(nid);
  265. *id = *wrk;
  266. }
  267. if (xs_is_null(type))
  268. type = "(null)";
  269. srv_debug(2, xs_fmt("timeline_request type %s '%s'", nid, type));
  270. if (strcmp(type, "Create") == 0) {
  271. /* some software like lemmy nest Announce + Create + Note */
  272. if (!xs_is_null(object = xs_dict_get(object, "object"))) {
  273. type = xs_dict_get(object, "type");
  274. nid = xs_dict_get(object, "id");
  275. }
  276. else
  277. type = "(null)";
  278. }
  279. if (xs_match(type, "Note|Page|Article|Video")) {
  280. const char *actor = get_atto(object);
  281. /* request (and drop) the actor for this entry */
  282. if (!xs_is_null(actor))
  283. actor_request(snac, actor, NULL);
  284. /* does it have an ancestor? */
  285. char *in_reply_to = xs_dict_get(object, "inReplyTo");
  286. /* store */
  287. timeline_add(snac, nid, object);
  288. /* recurse! */
  289. timeline_request(snac, &in_reply_to, NULL, level + 1);
  290. }
  291. }
  292. }
  293. enqueue_request_replies(snac, *id);
  294. }
  295. return status;
  296. }
  297. void timeline_request_replies(snac *user, const char *id)
  298. /* requests all replies of a message */
  299. /* FIXME: experimental -- needs more testing */
  300. {
  301. /* FIXME: TEMPORARILY DISABLED */
  302. /* Reason: I've found that many of the posts in the 'replies' Collection
  303. do not have an inReplyTo field (why??? aren't they 'replies'???).
  304. For this reason, these requested objects are not stored as children
  305. of the original post and they are shown as out-of-context, top level posts.
  306. This process is disabled until I find an elegant way of providing a parent
  307. for these 'stray' children. */
  308. return;
  309. xs *msg = NULL;
  310. if (!valid_status(object_get(id, &msg)))
  311. return;
  312. /* does it have a replies collection? */
  313. const xs_dict *replies = xs_dict_get(msg, "replies");
  314. if (!xs_is_null(replies)) {
  315. const char *type = xs_dict_get(replies, "type");
  316. const char *first = xs_dict_get(replies, "first");
  317. if (!xs_is_null(type) && !xs_is_null(first) && strcmp(type, "Collection") == 0) {
  318. const char *next = xs_dict_get(first, "next");
  319. if (!xs_is_null(next)) {
  320. xs *rpls = NULL;
  321. int status = activitypub_request(user, next, &rpls);
  322. /* request the Collection of replies */
  323. if (valid_status(status)) {
  324. xs_list *items = xs_dict_get(rpls, "items");
  325. if (xs_type(items) == XSTYPE_LIST) {
  326. xs_val *v;
  327. /* request them all */
  328. while (xs_list_iter(&items, &v)) {
  329. if (xs_type(v) == XSTYPE_DICT) {
  330. /* not an id, but the object itself (!) */
  331. const char *c_id = xs_dict_get(v, "id");
  332. if (!xs_is_null(id)) {
  333. snac_debug(user, 0, xs_fmt("embedded reply %s", c_id));
  334. object_add(c_id, v);
  335. /* get its own children */
  336. timeline_request_replies(user, v);
  337. }
  338. }
  339. else {
  340. snac_debug(user, 0, xs_fmt("request reply %s", v));
  341. timeline_request(user, &v, NULL, 0);
  342. }
  343. }
  344. }
  345. }
  346. else
  347. snac_debug(user, 0, xs_fmt("replies request error %s %d", next, status));
  348. }
  349. }
  350. }
  351. }
  352. int send_to_inbox_raw(const char *keyid, const char *seckey,
  353. const xs_str *inbox, const xs_dict *msg,
  354. xs_val **payload, int *p_size, int timeout)
  355. /* sends a message to an Inbox */
  356. {
  357. int status;
  358. xs_dict *response;
  359. xs *j_msg = xs_json_dumps((xs_dict *)msg, 4);
  360. response = http_signed_request_raw(keyid, seckey, "POST", inbox,
  361. NULL, j_msg, strlen(j_msg), &status, payload, p_size, timeout);
  362. xs_free(response);
  363. return status;
  364. }
  365. int send_to_inbox(snac *snac, const xs_str *inbox, const xs_dict *msg,
  366. xs_val **payload, int *p_size, int timeout)
  367. /* sends a message to an Inbox */
  368. {
  369. char *seckey = xs_dict_get(snac->key, "secret");
  370. return send_to_inbox_raw(snac->actor, seckey, inbox, msg, payload, p_size, timeout);
  371. }
  372. xs_str *get_actor_inbox(const char *actor)
  373. /* gets an actor's inbox */
  374. {
  375. xs *data = NULL;
  376. char *v = NULL;
  377. if (valid_status(actor_request(NULL, actor, &data))) {
  378. /* try first endpoints/sharedInbox */
  379. if ((v = xs_dict_get(data, "endpoints")))
  380. v = xs_dict_get(v, "sharedInbox");
  381. /* try then the regular inbox */
  382. if (xs_is_null(v))
  383. v = xs_dict_get(data, "inbox");
  384. }
  385. return xs_is_null(v) ? NULL : xs_dup(v);
  386. }
  387. int send_to_actor(snac *snac, const char *actor, const xs_dict *msg,
  388. xs_val **payload, int *p_size, int timeout)
  389. /* sends a message to an actor */
  390. {
  391. int status = 400;
  392. xs *inbox = get_actor_inbox(actor);
  393. if (!xs_is_null(inbox))
  394. status = send_to_inbox(snac, inbox, msg, payload, p_size, timeout);
  395. return status;
  396. }
  397. void post_message(snac *snac, const char *actor, const xs_dict *msg)
  398. /* posts a message immediately (bypassing the output queues) */
  399. {
  400. xs *payload = NULL;
  401. int p_size;
  402. int status = send_to_actor(snac, actor, msg, &payload, &p_size, 3);
  403. srv_log(xs_fmt("post_message to actor %s %d", actor, status));
  404. if (!valid_status(status))
  405. /* cannot send right now, enqueue */
  406. enqueue_message(snac, msg);
  407. }
  408. xs_list *recipient_list(snac *snac, const xs_dict *msg, int expand_public)
  409. /* returns the list of recipients for a message */
  410. {
  411. char *to = xs_dict_get(msg, "to");
  412. char *cc = xs_dict_get(msg, "cc");
  413. xs_set rcpts;
  414. int n;
  415. xs_set_init(&rcpts);
  416. char *lists[] = { to, cc, NULL };
  417. for (n = 0; lists[n]; n++) {
  418. char *l = lists[n];
  419. char *v;
  420. xs *tl = NULL;
  421. /* if it's a string, create a list with only one element */
  422. if (xs_type(l) == XSTYPE_STRING) {
  423. tl = xs_list_new();
  424. tl = xs_list_append(tl, l);
  425. l = tl;
  426. }
  427. while (xs_list_iter(&l, &v)) {
  428. if (expand_public && strcmp(v, public_address) == 0) {
  429. /* iterate the followers and add them */
  430. xs *fwers = follower_list(snac);
  431. char *actor;
  432. char *p = fwers;
  433. while (xs_list_iter(&p, &actor))
  434. xs_set_add(&rcpts, actor);
  435. }
  436. else
  437. xs_set_add(&rcpts, v);
  438. }
  439. }
  440. return xs_set_result(&rcpts);
  441. }
  442. int is_msg_public(const xs_dict *msg)
  443. /* checks if a message is public */
  444. {
  445. const char *to = xs_dict_get(msg, "to");
  446. const char *cc = xs_dict_get(msg, "cc");
  447. int n;
  448. const char *lists[] = { to, cc, NULL };
  449. for (n = 0; lists[n]; n++) {
  450. const xs_val *l = lists[n];
  451. if (xs_type(l) == XSTYPE_STRING) {
  452. if (strcmp(l, public_address) == 0)
  453. return 1;
  454. }
  455. else
  456. if (xs_type(l) == XSTYPE_LIST) {
  457. if (xs_list_in(l, public_address) != -1)
  458. return 1;
  459. }
  460. }
  461. return 0;
  462. }
  463. int is_msg_for_me(snac *snac, const xs_dict *c_msg)
  464. /* checks if this message is for me */
  465. {
  466. const char *type = xs_dict_get(c_msg, "type");
  467. const char *actor = xs_dict_get(c_msg, "actor");
  468. if (xs_match(type, "Like|Announce")) {
  469. const char *object = xs_dict_get(c_msg, "object");
  470. if (xs_type(object) == XSTYPE_DICT)
  471. object = xs_dict_get(object, "id");
  472. /* bad object id? reject */
  473. if (xs_type(object) != XSTYPE_STRING)
  474. return 0;
  475. /* if it's about one of our posts, accept it */
  476. if (xs_startswith(object, snac->actor))
  477. return 2;
  478. /* if it's by someone we don't follow, reject */
  479. return following_check(snac, actor);
  480. }
  481. /* if it's an Undo, it must be from someone related to us */
  482. if (xs_match(type, "Undo")) {
  483. return follower_check(snac, actor) || following_check(snac, actor);
  484. }
  485. /* if it's an Accept + Follow, it must be for a Follow we created */
  486. if (xs_match(type, "Accept")) {
  487. return following_check(snac, actor);
  488. }
  489. /* if it's a Follow, it must be explicitly for us */
  490. if (xs_match(type, "Follow")) {
  491. char *object = xs_dict_get(c_msg, "object");
  492. return !xs_is_null(object) && strcmp(snac->actor, object) == 0;
  493. }
  494. /* if it's not a Create or Update, allow as is */
  495. if (!xs_match(type, "Create|Update")) {
  496. return 1;
  497. }
  498. int pub_msg = is_msg_public(c_msg);
  499. /* if this message is public and we follow the actor of this post, allow */
  500. if (pub_msg && following_check(snac, actor))
  501. return 1;
  502. xs_dict *msg = xs_dict_get(c_msg, "object");
  503. xs *rcpts = recipient_list(snac, msg, 0);
  504. xs_list *p = rcpts;
  505. xs_str *v;
  506. xs *actor_followers = NULL;
  507. if (!pub_msg) {
  508. /* not a public message; get the actor and its followers list */
  509. xs *actor_obj = NULL;
  510. if (valid_status(object_get(actor, &actor_obj))) {
  511. if ((v = xs_dict_get(actor_obj, "followers")))
  512. actor_followers = xs_dup(v);
  513. }
  514. }
  515. while(xs_list_iter(&p, &v)) {
  516. /* explicitly for me? accept */
  517. if (strcmp(v, snac->actor) == 0)
  518. return 2;
  519. if (pub_msg) {
  520. /* a public message for someone we follow? (probably cc'ed) accept */
  521. if (following_check(snac, v))
  522. return 5;
  523. }
  524. else
  525. if (actor_followers && strcmp(v, actor_followers) == 0) {
  526. /* if this message is for this actor's followers, are we one of them? */
  527. if (following_check(snac, actor))
  528. return 6;
  529. }
  530. }
  531. /* accept if it's by someone we follow */
  532. char *atto = get_atto(msg);
  533. if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
  534. return 3;
  535. /* is this message a reply to another? */
  536. char *irt = xs_dict_get(msg, "inReplyTo");
  537. if (!xs_is_null(irt)) {
  538. xs *r_msg = NULL;
  539. /* try to get the replied message */
  540. if (valid_status(object_get(irt, &r_msg))) {
  541. atto = get_atto(r_msg);
  542. /* accept if the replied message is from someone we follow */
  543. if (pub_msg && !xs_is_null(atto) && following_check(snac, atto))
  544. return 4;
  545. }
  546. }
  547. return 0;
  548. }
  549. xs_str *process_tags(snac *snac, const char *content, xs_list **tag)
  550. /* parses mentions and tags from content */
  551. {
  552. xs_str *nc = xs_str_new(NULL);
  553. xs_list *tl = *tag;
  554. xs *split;
  555. xs_list *p;
  556. xs_val *v;
  557. int n = 0;
  558. /* create a default server for incomplete mentions */
  559. xs *def_srv = NULL;
  560. if (xs_list_len(tl)) {
  561. /* if there are any mentions, get the server from
  562. the first one, which is the inReplyTo author */
  563. p = tl;
  564. while (xs_list_iter(&p, &v)) {
  565. const char *type = xs_dict_get(v, "type");
  566. const char *name = xs_dict_get(v, "name");
  567. if (type && name && strcmp(type, "Mention") == 0) {
  568. xs *l = xs_split(name, "@");
  569. def_srv = xs_dup(xs_list_get(l, -1));
  570. break;
  571. }
  572. }
  573. }
  574. if (xs_is_null(def_srv))
  575. /* use this same server */
  576. def_srv = xs_dup(xs_dict_get(srv_config, "host"));
  577. split = xs_regex_split(content, "(@[A-Za-z0-9_]+(@[A-Za-z0-9\\.-]+)?|&#[0-9]+;|#[^ ,\\.:;<]+)");
  578. p = split;
  579. while (xs_list_iter(&p, &v)) {
  580. if ((n & 0x1)) {
  581. if (*v == '@') {
  582. xs *link = NULL;
  583. xs *wuid = NULL;
  584. if (strchr(v + 1, '@') == NULL) {
  585. /* only one @? it's a dumb Mastodon-like mention
  586. without server; add the default one */
  587. wuid = xs_fmt("%s@%s", v, def_srv);
  588. snac_debug(snac, 2, xs_fmt("mention without server '%s' '%s'", v, wuid));
  589. }
  590. else
  591. wuid = xs_dup(v);
  592. /* query the webfinger about this fellow */
  593. xs *actor = NULL;
  594. xs *uid = NULL;
  595. int status;
  596. status = webfinger_request(wuid, &actor, &uid);
  597. if (valid_status(status) && actor && uid) {
  598. xs *d = xs_dict_new();
  599. xs *n = xs_fmt("@%s", uid);
  600. d = xs_dict_append(d, "type", "Mention");
  601. d = xs_dict_append(d, "href", actor);
  602. d = xs_dict_append(d, "name", n);
  603. tl = xs_list_append(tl, d);
  604. link = xs_fmt("<a href=\"%s\" class=\"u-url mention\">%s</a>", actor, n);
  605. }
  606. if (!xs_is_null(link))
  607. nc = xs_str_cat(nc, link);
  608. else
  609. nc = xs_str_cat(nc, v);
  610. }
  611. else
  612. if (*v == '#') {
  613. /* hashtag */
  614. xs *d = xs_dict_new();
  615. xs *n = xs_tolower_i(xs_dup(v));
  616. xs *h = xs_fmt("%s?t=%s", srv_baseurl, n + 1);
  617. xs *l = xs_fmt("<a href=\"%s\" class=\"mention hashtag\" rel=\"tag\">%s</a>", h, v);
  618. d = xs_dict_append(d, "type", "Hashtag");
  619. d = xs_dict_append(d, "href", h);
  620. d = xs_dict_append(d, "name", n);
  621. tl = xs_list_append(tl, d);
  622. /* add the code */
  623. nc = xs_str_cat(nc, l);
  624. }
  625. else
  626. if (*v == '&') {
  627. /* HTML Unicode entity, probably part of an emoji */
  628. /* write as is */
  629. nc = xs_str_cat(nc, v);
  630. }
  631. }
  632. else
  633. nc = xs_str_cat(nc, v);
  634. n++;
  635. }
  636. *tag = tl;
  637. return nc;
  638. }
  639. void notify(snac *snac, const char *type, const char *utype, const char *actor, const xs_dict *msg)
  640. /* notifies the user of relevant events */
  641. {
  642. const char *id = xs_dict_get(msg, "id");
  643. if (strcmp(type, "Create") == 0) {
  644. /* only notify of notes specifically for us */
  645. xs *rcpts = recipient_list(snac, msg, 0);
  646. if (xs_list_in(rcpts, snac->actor) == -1)
  647. return;
  648. /* discard votes */
  649. const xs_dict *note = xs_dict_get(msg, "object");
  650. if (note && !xs_is_null(xs_dict_get(note, "name")))
  651. return;
  652. }
  653. if (strcmp(type, "Undo") == 0 && strcmp(utype, "Follow") != 0)
  654. return;
  655. /* get the object id */
  656. const char *objid = xs_dict_get(msg, "object");
  657. if (xs_type(objid) == XSTYPE_DICT)
  658. objid = xs_dict_get(objid, "id");
  659. if (strcmp(type, "Like") == 0 || strcmp(type, "Announce") == 0) {
  660. /* if it's not an admiration about something by us, done */
  661. if (xs_is_null(objid) || !xs_startswith(objid, snac->actor))
  662. return;
  663. }
  664. /* updated poll? */
  665. if (strcmp(type, "Update") == 0 && strcmp(utype, "Question") == 0) {
  666. const xs_dict *poll;
  667. const char *poll_id;
  668. if ((poll = xs_dict_get(msg, "object")) == NULL)
  669. return;
  670. /* if it's not closed, discard */
  671. if (xs_is_null(xs_dict_get(poll, "closed")))
  672. return;
  673. if ((poll_id = xs_dict_get(poll, "id")) == NULL)
  674. return;
  675. /* if it's not ours and we didn't vote, discard */
  676. if (!xs_startswith(poll_id, snac->actor) && !was_question_voted(snac, poll_id))
  677. return;
  678. }
  679. /* user will love to know about this! */
  680. /* prepare message body */
  681. xs *body = xs_fmt("User : @%s@%s\n",
  682. xs_dict_get(snac->config, "uid"),
  683. xs_dict_get(srv_config, "host")
  684. );
  685. if (strcmp(utype, "(null)") != 0) {
  686. xs *s1 = xs_fmt("Type : %s + %s\n", type, utype);
  687. body = xs_str_cat(body, s1);
  688. }
  689. else {
  690. xs *s1 = xs_fmt("Type : %s\n", type);
  691. body = xs_str_cat(body, s1);
  692. }
  693. {
  694. xs *s1 = xs_fmt("Actor : %s\n", actor);
  695. body = xs_str_cat(body, s1);
  696. }
  697. if (objid != NULL) {
  698. xs *s1 = xs_fmt("Object: %s\n", objid);
  699. body = xs_str_cat(body, s1);
  700. }
  701. /* email */
  702. const char *email = "[disabled by admin]";
  703. if (xs_type(xs_dict_get(srv_config, "disable_email_notifications")) != XSTYPE_TRUE) {
  704. email = xs_dict_get(snac->config_o, "email");
  705. if (xs_is_null(email)) {
  706. email = xs_dict_get(snac->config, "email");
  707. if (xs_is_null(email))
  708. email = "[empty]";
  709. }
  710. }
  711. if (*email != '\0' && *email != '[') {
  712. snac_debug(snac, 1, xs_fmt("email notify %s %s %s", type, utype, actor));
  713. xs *subject = xs_fmt("snac notify for @%s@%s",
  714. xs_dict_get(snac->config, "uid"), xs_dict_get(srv_config, "host"));
  715. xs *from = xs_fmt("snac-daemon <snac-daemon@%s>", xs_dict_get(srv_config, "host"));
  716. xs *header = xs_fmt(
  717. "From: %s\n"
  718. "To: %s\n"
  719. "Subject: %s\n"
  720. "\n",
  721. from, email, subject);
  722. xs *email_body = xs_fmt("%s%s", header, body);
  723. enqueue_email(email_body, 0);
  724. }
  725. /* telegram */
  726. char *bot = xs_dict_get(snac->config, "telegram_bot");
  727. char *chat_id = xs_dict_get(snac->config, "telegram_chat_id");
  728. if (!xs_is_null(bot) && !xs_is_null(chat_id) && *bot && *chat_id)
  729. enqueue_telegram(body, bot, chat_id);
  730. /* finally, store it in the notification folder */
  731. if (strcmp(type, "Follow") == 0)
  732. objid = id;
  733. else
  734. if (strcmp(utype, "Follow") == 0)
  735. objid = actor;
  736. /* ntfy */
  737. char *ntfy_server = xs_dict_get(snac->config, "ntfy_server");
  738. char *ntfy_token = xs_dict_get(snac->config, "ntfy_token");
  739. if (!xs_is_null(ntfy_server) && *ntfy_server)
  740. enqueue_ntfy(body, ntfy_server, ntfy_token);
  741. /* finally, store it in the notification folder */
  742. if (strcmp(type, "Follow") == 0)
  743. objid = id;
  744. else
  745. if (strcmp(utype, "Follow") == 0)
  746. objid = actor;
  747. notify_add(snac, type, utype, actor, objid != NULL ? objid : id);
  748. }
  749. /** messages **/
  750. xs_dict *msg_base(snac *snac, const char *type, const char *id,
  751. const char *actor, const char *date, const char *object)
  752. /* creates a base ActivityPub message */
  753. {
  754. xs *did = NULL;
  755. xs *published = NULL;
  756. xs *ntid = tid(0);
  757. const char *obj_id;
  758. if (xs_type(object) == XSTYPE_DICT)
  759. obj_id = xs_dict_get(object, "id");
  760. else
  761. obj_id = object;
  762. /* generated values */
  763. if (date && strcmp(date, "@now") == 0) {
  764. published = xs_str_utctime(0, ISO_DATE_SPEC);
  765. date = published;
  766. }
  767. if (id != NULL) {
  768. if (strcmp(id, "@dummy") == 0) {
  769. did = xs_fmt("%s/d/%s/%s", snac->actor, ntid, type);
  770. id = did;
  771. }
  772. else
  773. if (strcmp(id, "@object") == 0) {
  774. if (obj_id != NULL) {
  775. did = xs_fmt("%s/%s_%s", obj_id, type, ntid);
  776. id = did;
  777. }
  778. else
  779. id = NULL;
  780. }
  781. else
  782. if (strcmp(id, "@wrapper") == 0) {
  783. /* like @object, but always generate the same id */
  784. if (object != NULL) {
  785. date = xs_dict_get(object, "published");
  786. did = xs_fmt("%s/%s", obj_id, type);
  787. id = did;
  788. }
  789. else
  790. id = NULL;
  791. }
  792. }
  793. xs_dict *msg = xs_dict_new();
  794. msg = xs_dict_append(msg, "@context", "https:/" "/www.w3.org/ns/activitystreams");
  795. msg = xs_dict_append(msg, "type", type);
  796. if (id != NULL)
  797. msg = xs_dict_append(msg, "id", id);
  798. if (actor != NULL)
  799. msg = xs_dict_append(msg, "actor", actor);
  800. if (date != NULL)
  801. msg = xs_dict_append(msg, "published", date);
  802. if (object != NULL)
  803. msg = xs_dict_append(msg, "object", object);
  804. return msg;
  805. }
  806. xs_dict *msg_collection(snac *snac, char *id)
  807. /* creates an empty OrderedCollection message */
  808. {
  809. xs_dict *msg = msg_base(snac, "OrderedCollection", id, NULL, NULL, NULL);
  810. xs *ol = xs_list_new();
  811. msg = xs_dict_append(msg, "attributedTo", snac->actor);
  812. msg = xs_dict_append(msg, "orderedItems", ol);
  813. msg = xs_dict_append(msg, "totalItems", xs_stock_0);
  814. return msg;
  815. }
  816. xs_dict *msg_accept(snac *snac, char *object, char *to)
  817. /* creates an Accept message (as a response to a Follow) */
  818. {
  819. xs_dict *msg = msg_base(snac, "Accept", "@dummy", snac->actor, NULL, object);
  820. msg = xs_dict_append(msg, "to", to);
  821. return msg;
  822. }
  823. xs_dict *msg_update(snac *snac, xs_dict *object)
  824. /* creates an Update message */
  825. {
  826. xs_dict *msg = msg_base(snac, "Update", "@object", snac->actor, "@now", object);
  827. char *type = xs_dict_get(object, "type");
  828. if (strcmp(type, "Note") == 0) {
  829. msg = xs_dict_append(msg, "to", xs_dict_get(object, "to"));
  830. msg = xs_dict_append(msg, "cc", xs_dict_get(object, "cc"));
  831. }
  832. else
  833. if (strcmp(type, "Person") == 0) {
  834. msg = xs_dict_append(msg, "to", public_address);
  835. /* also spam the people being followed, so that
  836. they have the newest information about who we are */
  837. xs *cc = following_list(snac);
  838. msg = xs_dict_append(msg, "cc", cc);
  839. }
  840. else
  841. msg = xs_dict_append(msg, "to", public_address);
  842. return msg;
  843. }
  844. xs_dict *msg_admiration(snac *snac, char *object, char *type)
  845. /* creates a Like or Announce message */
  846. {
  847. xs *a_msg = NULL;
  848. xs_dict *msg = NULL;
  849. xs *wrk = NULL;
  850. /* call the object */
  851. timeline_request(snac, &object, &wrk, 0);
  852. if (valid_status(object_get(object, &a_msg))) {
  853. xs *rcpts = xs_list_new();
  854. msg = msg_base(snac, type, "@dummy", snac->actor, "@now", object);
  855. if (is_msg_public(a_msg))
  856. rcpts = xs_list_append(rcpts, public_address);
  857. rcpts = xs_list_append(rcpts, get_atto(a_msg));
  858. msg = xs_dict_append(msg, "to", rcpts);
  859. }
  860. else
  861. snac_log(snac, xs_fmt("msg_admiration cannot retrieve object %s", object));
  862. return msg;
  863. }
  864. xs_dict *msg_actor(snac *snac)
  865. /* create a Person message for this actor */
  866. {
  867. xs *ctxt = xs_list_new();
  868. xs *icon = xs_dict_new();
  869. xs *keys = xs_dict_new();
  870. xs *tags = xs_list_new();
  871. xs *avtr = NULL;
  872. xs *kid = NULL;
  873. xs *f_bio = NULL;
  874. xs_dict *msg = msg_base(snac, "Person", snac->actor, NULL, NULL, NULL);
  875. char *p;
  876. int n;
  877. /* change the @context (is this really necessary?) */
  878. ctxt = xs_list_append(ctxt, "https:/" "/www.w3.org/ns/activitystreams");
  879. ctxt = xs_list_append(ctxt, "https:/" "/w3id.org/security/v1");
  880. msg = xs_dict_set(msg, "@context", ctxt);
  881. msg = xs_dict_set(msg, "url", snac->actor);
  882. msg = xs_dict_set(msg, "name", xs_dict_get(snac->config, "name"));
  883. msg = xs_dict_set(msg, "preferredUsername", snac->uid);
  884. msg = xs_dict_set(msg, "published", xs_dict_get(snac->config, "published"));
  885. xs *f_bio_2 = not_really_markdown(xs_dict_get(snac->config, "bio"), NULL);
  886. f_bio = process_tags(snac, f_bio_2, &tags);
  887. msg = xs_dict_set(msg, "summary", f_bio);
  888. msg = xs_dict_set(msg, "tag", tags);
  889. char *folders[] = { "inbox", "outbox", "followers", "following", NULL };
  890. for (n = 0; folders[n]; n++) {
  891. xs *f = xs_fmt("%s/%s", snac->actor, folders[n]);
  892. msg = xs_dict_set(msg, folders[n], f);
  893. }
  894. p = xs_dict_get(snac->config, "avatar");
  895. if (*p == '\0')
  896. avtr = xs_fmt("%s/susie.png", srv_baseurl);
  897. else
  898. avtr = xs_dup(p);
  899. icon = xs_dict_append(icon, "type", "Image");
  900. icon = xs_dict_append(icon, "mediaType", xs_mime_by_ext(avtr));
  901. icon = xs_dict_append(icon, "url", avtr);
  902. msg = xs_dict_set(msg, "icon", icon);
  903. kid = xs_fmt("%s#main-key", snac->actor);
  904. keys = xs_dict_append(keys, "id", kid);
  905. keys = xs_dict_append(keys, "owner", snac->actor);
  906. keys = xs_dict_append(keys, "publicKeyPem", xs_dict_get(snac->key, "public"));
  907. msg = xs_dict_set(msg, "publicKey", keys);
  908. /* if the "bot" config field is set to true, change type to "Service" */
  909. if (xs_type(xs_dict_get(snac->config, "bot")) == XSTYPE_TRUE)
  910. msg = xs_dict_set(msg, "type", "Service");
  911. /* add the header image, if there is one defined */
  912. const char *header = xs_dict_get(snac->config, "header");
  913. if (!xs_is_null(header)) {
  914. xs *d = xs_dict_new();
  915. d = xs_dict_append(d, "type", "Image");
  916. d = xs_dict_append(d, "mediaType", xs_mime_by_ext(header));
  917. d = xs_dict_append(d, "url", header);
  918. msg = xs_dict_set(msg, "image", d);
  919. }
  920. /* add the metadata as attachments of PropertyValue */
  921. xs_dict *metadata = xs_dict_get(snac->config, "metadata");
  922. if (xs_type(metadata) == XSTYPE_DICT) {
  923. xs *attach = xs_list_new();
  924. xs_str *k;
  925. xs_str *v;
  926. while (xs_dict_iter(&metadata, &k, &v)) {
  927. xs *d = xs_dict_new();
  928. xs *k2 = encode_html(k);
  929. xs *v2 = NULL;
  930. if (xs_startswith(v, "https:")) {
  931. xs *t = encode_html(v);
  932. v2 = xs_fmt("<a href=\"%s\">%s</a>", t, t);
  933. }
  934. else
  935. v2 = encode_html(v);
  936. d = xs_dict_append(d, "type", "PropertyValue");
  937. d = xs_dict_append(d, "name", k2);
  938. d = xs_dict_append(d, "value", v2);
  939. attach = xs_list_append(attach, d);
  940. }
  941. msg = xs_dict_set(msg, "attachment", attach);
  942. }
  943. /* use shared inboxes? */
  944. if (xs_type(xs_dict_get(srv_config, "shared_inboxes")) == XSTYPE_TRUE) {
  945. xs *d = xs_dict_new();
  946. xs *si = xs_fmt("%s/shared-inbox", srv_baseurl);
  947. d = xs_dict_append(d, "sharedInbox", si);
  948. msg = xs_dict_set(msg, "endpoints", d);
  949. }
  950. return msg;
  951. }
  952. xs_dict *msg_create(snac *snac, const xs_dict *object)
  953. /* creates a 'Create' message */
  954. {
  955. xs_dict *msg = msg_base(snac, "Create", "@wrapper", snac->actor, NULL, object);
  956. xs_val *v;
  957. if ((v = get_atto(object)))
  958. msg = xs_dict_append(msg, "attributedTo", v);
  959. if ((v = xs_dict_get(object, "cc")))
  960. msg = xs_dict_append(msg, "cc", v);
  961. if ((v = xs_dict_get(object, "to")))
  962. msg = xs_dict_append(msg, "to", v);
  963. else
  964. msg = xs_dict_append(msg, "to", public_address);
  965. return msg;
  966. }
  967. xs_dict *msg_undo(snac *snac, char *object)
  968. /* creates an 'Undo' message */
  969. {
  970. xs_dict *msg = msg_base(snac, "Undo", "@object", snac->actor, "@now", object);
  971. const char *to;
  972. if (xs_type(object) == XSTYPE_DICT && (to = xs_dict_get(object, "object")))
  973. msg = xs_dict_append(msg, "to", to);
  974. return msg;
  975. }
  976. xs_dict *msg_delete(snac *snac, char *id)
  977. /* creates a 'Delete' + 'Tombstone' for a local entry */
  978. {
  979. xs *tomb = xs_dict_new();
  980. xs_dict *msg = NULL;
  981. /* sculpt the tombstone */
  982. tomb = xs_dict_append(tomb, "type", "Tombstone");
  983. tomb = xs_dict_append(tomb, "id", id);
  984. /* now create the Delete */
  985. msg = msg_base(snac, "Delete", "@object", snac->actor, "@now", tomb);
  986. msg = xs_dict_append(msg, "to", public_address);
  987. return msg;
  988. }
  989. xs_dict *msg_follow(snac *snac, const char *q)
  990. /* creates a 'Follow' message */
  991. {
  992. xs *actor_o = NULL;
  993. xs *actor = NULL;
  994. xs_dict *msg = NULL;
  995. int status;
  996. xs *url_or_uid = xs_strip_i(xs_str_new(q));
  997. if (xs_startswith(url_or_uid, "https:/"))
  998. actor = xs_dup(url_or_uid);
  999. else
  1000. if (!valid_status(webfinger_request(url_or_uid, &actor, NULL)) || actor == NULL) {
  1001. snac_log(snac, xs_fmt("cannot resolve user %s to follow", url_or_uid));
  1002. return NULL;
  1003. }
  1004. /* request the actor */
  1005. status = actor_request(snac, actor, &actor_o);
  1006. if (valid_status(status)) {
  1007. /* check if the actor is an alias */
  1008. char *r_actor = xs_dict_get(actor_o, "id");
  1009. if (r_actor && strcmp(actor, r_actor) != 0) {
  1010. snac_log(snac, xs_fmt("actor to follow is an alias %s -> %s", actor, r_actor));
  1011. }
  1012. msg = msg_base(snac, "Follow", "@dummy", snac->actor, NULL, r_actor);
  1013. }
  1014. else
  1015. snac_log(snac, xs_fmt("cannot get actor to follow %s %d", actor, status));
  1016. return msg;
  1017. }
  1018. xs_dict *msg_note(snac *snac, const xs_str *content, const xs_val *rcpts,
  1019. xs_str *in_reply_to, xs_list *attach, int priv)
  1020. /* creates a 'Note' message */
  1021. {
  1022. xs *ntid = tid(0);
  1023. xs *id = xs_fmt("%s/p/%s", snac->actor, ntid);
  1024. xs *ctxt = NULL;
  1025. xs *fc2 = NULL;
  1026. xs *fc1 = NULL;
  1027. xs *to = NULL;
  1028. xs *cc = xs_list_new();
  1029. xs *irt = NULL;
  1030. xs *tag = xs_list_new();
  1031. xs *atls = xs_list_new();
  1032. xs_dict *msg = msg_base(snac, "Note", id, NULL, "@now", NULL);
  1033. xs_list *p;
  1034. xs_val *v;
  1035. if (rcpts == NULL)
  1036. to = xs_list_new();
  1037. else {
  1038. if (xs_type(rcpts) == XSTYPE_STRING) {
  1039. to = xs_list_new();
  1040. to = xs_list_append(to, rcpts);
  1041. }
  1042. else
  1043. to = xs_dup(rcpts);
  1044. }
  1045. /* format the content */
  1046. fc2 = not_really_markdown(content, &atls);
  1047. if (in_reply_to != NULL && *in_reply_to) {
  1048. xs *p_msg = NULL;
  1049. xs *wrk = NULL;
  1050. /* demand this thing */
  1051. timeline_request(snac, &in_reply_to, &wrk, 0);
  1052. if (valid_status(object_get(in_reply_to, &p_msg))) {
  1053. /* add this author as recipient */
  1054. char *a, *v;
  1055. if ((a = get_atto(p_msg)) && xs_list_in(to, a) == -1)
  1056. to = xs_list_append(to, a);
  1057. /* add this author to the tag list as a mention */
  1058. if (!xs_is_null(a)) {
  1059. xs *l = xs_split(a, "/");
  1060. xs *actor_o = NULL;
  1061. if (xs_list_len(l) > 3 && valid_status(object_get(a, &actor_o))) {
  1062. char *uname = xs_dict_get(actor_o, "preferredUsername");
  1063. if (!xs_is_null(uname) && *uname) {
  1064. xs *handle = xs_fmt("@%s@%s", uname, xs_list_get(l, 2));
  1065. xs *t = xs_dict_new();
  1066. t = xs_dict_append(t, "type", "Mention");
  1067. t = xs_dict_append(t, "href", a);
  1068. t = xs_dict_append(t, "name", handle);
  1069. tag = xs_list_append(tag, t);
  1070. }
  1071. }
  1072. }
  1073. /* get the context, if there is one */
  1074. if ((v = xs_dict_get(p_msg, "context")))
  1075. ctxt = xs_dup(v);
  1076. /* propagate the conversation field, if there is one */
  1077. if ((v = xs_dict_get(p_msg, "conversation")))
  1078. msg = xs_dict_append(msg, "conversation", v);
  1079. /* if this message is public, ours will also be */
  1080. if (!priv && is_msg_public(p_msg) && xs_list_in(to, public_address) == -1)
  1081. to = xs_list_append(to, public_address);
  1082. }
  1083. irt = xs_dup(in_reply_to);
  1084. }
  1085. else
  1086. irt = xs_val_new(XSTYPE_NULL);
  1087. /* extract the mentions and hashtags and convert the content */
  1088. fc1 = process_tags(snac, fc2, &tag);
  1089. /* create the attachment list, if there are any */
  1090. if (!xs_is_null(attach)) {
  1091. while (xs_list_iter(&attach, &v)) {
  1092. xs *d = xs_dict_new();
  1093. const char *url = xs_list_get(v, 0);
  1094. const char *alt = xs_list_get(v, 1);
  1095. const char *mime = xs_mime_by_ext(url);
  1096. d = xs_dict_append(d, "mediaType", mime);
  1097. d = xs_dict_append(d, "url", url);
  1098. d = xs_dict_append(d, "name", alt);
  1099. d = xs_dict_append(d, "type",
  1100. xs_startswith(mime, "image/") ? "Image" : "Document");
  1101. atls = xs_list_append(atls, d);
  1102. }
  1103. }
  1104. if (ctxt == NULL)
  1105. ctxt = xs_fmt("%s#ctxt", id);
  1106. /* add all mentions to the cc */
  1107. p = tag;
  1108. while (xs_list_iter(&p, &v)) {
  1109. if (xs_type(v) == XSTYPE_DICT) {
  1110. char *t;
  1111. if (!xs_is_null(t = xs_dict_get(v, "type")) && strcmp(t, "Mention") == 0) {
  1112. if (!xs_is_null(t = xs_dict_get(v, "href")))
  1113. cc = xs_list_append(cc, t);
  1114. }
  1115. }
  1116. }
  1117. /* no recipients? must be for everybody */
  1118. if (!priv && xs_list_len(to) == 0)
  1119. to = xs_list_append(to, public_address);
  1120. /* delete all cc recipients that also are in the to */
  1121. p = to;
  1122. while (xs_list_iter(&p, &v)) {
  1123. int i;
  1124. if ((i = xs_list_in(cc, v)) != -1)
  1125. cc = xs_list_del(cc, i);
  1126. }
  1127. msg = xs_dict_append(msg, "attributedTo", snac->actor);
  1128. msg = xs_dict_append(msg, "summary", "");
  1129. msg = xs_dict_append(msg, "content", fc1);
  1130. msg = xs_dict_append(msg, "context", ctxt);
  1131. msg = xs_dict_append(msg, "url", id);
  1132. msg = xs_dict_append(msg, "to", to);
  1133. msg = xs_dict_append(msg, "cc", cc);
  1134. msg = xs_dict_append(msg, "inReplyTo", irt);
  1135. msg = xs_dict_append(msg, "tag", tag);
  1136. msg = xs_dict_append(msg, "sourceContent", content);
  1137. if (xs_list_len(atls))
  1138. msg = xs_dict_append(msg, "attachment", atls);
  1139. return msg;
  1140. }
  1141. xs_dict *msg_ping(snac *user, const char *rcpt)
  1142. /* creates a Ping message (https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt) */
  1143. {
  1144. xs_dict *msg = msg_base(user, "Ping", "@dummy", user->actor, NULL, NULL);
  1145. msg = xs_dict_append(msg, "to", rcpt);
  1146. return msg;
  1147. }
  1148. xs_dict *msg_pong(snac *user, const char *rcpt, const char *object)
  1149. /* creates a Pong message (https://humungus.tedunangst.com/r/honk/v/tip/f/docs/ping.txt) */
  1150. {
  1151. xs_dict *msg = msg_base(user, "Pong", "@dummy", user->actor, NULL, object);
  1152. msg = xs_dict_append(msg, "to", rcpt);
  1153. return msg;
  1154. }
  1155. xs_dict *msg_question(snac *user, const char *content, xs_list *attach,
  1156. const xs_list *opts, int multiple, int end_secs)
  1157. /* creates a Question message */
  1158. {
  1159. xs_dict *msg = msg_note(user, content, NULL, NULL, attach, 0);
  1160. int max = 8;
  1161. xs_set seen;
  1162. msg = xs_dict_set(msg, "type", "Question");
  1163. /* make it non-editable */
  1164. msg = xs_dict_del(msg, "sourceContent");
  1165. xs *o = xs_list_new();
  1166. xs_list *p = (xs_list *)opts;
  1167. xs_str *v;
  1168. xs *replies = xs_json_loads("{\"type\":\"Collection\",\"totalItems\":0}");
  1169. xs_set_init(&seen);
  1170. while (max && xs_list_iter(&p, &v)) {
  1171. if (*v) {
  1172. xs *v2 = xs_dup(v);
  1173. xs *d = xs_dict_new();
  1174. if (strlen(v2) > 60) {
  1175. v2[60] = '\0';
  1176. v2 = xs_str_cat(v2, "...");
  1177. }
  1178. if (xs_set_add(&seen, v2) == 1) {
  1179. d = xs_dict_append(d, "name", v2);
  1180. d = xs_dict_append(d, "replies", replies);
  1181. o = xs_list_append(o, d);
  1182. max--;
  1183. }
  1184. }
  1185. }
  1186. xs_set_free(&seen);
  1187. msg = xs_dict_append(msg, multiple ? "anyOf" : "oneOf", o);
  1188. /* set the end time */
  1189. time_t t = time(NULL) + end_secs;
  1190. xs *et = xs_str_utctime(t, ISO_DATE_SPEC);
  1191. msg = xs_dict_append(msg, "endTime", et);
  1192. return msg;
  1193. }
  1194. int update_question(snac *user, const char *id)
  1195. /* updates the poll counts */
  1196. {
  1197. xs *msg = NULL;
  1198. xs *rcnt = xs_dict_new();
  1199. xs *lopts = xs_list_new();
  1200. xs_list *opts;
  1201. xs_list *p;
  1202. xs_val *v;
  1203. /* get the object */
  1204. if (!valid_status(object_get(id, &msg)))
  1205. return -1;
  1206. /* closed? do nothing more */
  1207. if (xs_dict_get(msg, "closed"))
  1208. return -2;
  1209. /* get the options */
  1210. if ((opts = xs_dict_get(msg, "oneOf")) == NULL &&
  1211. (opts = xs_dict_get(msg, "anyOf")) == NULL)
  1212. return -3;
  1213. /* fill the initial count */
  1214. p = opts;
  1215. while (xs_list_iter(&p, &v)) {
  1216. const char *name = xs_dict_get(v, "name");
  1217. if (name) {
  1218. lopts = xs_list_append(lopts, name);
  1219. rcnt = xs_dict_set(rcnt, name, xs_stock_0);
  1220. }
  1221. }
  1222. xs_set s;
  1223. xs_set_init(&s);
  1224. /* iterate now the children (the votes) */
  1225. xs *chld = object_children(id);
  1226. p = chld;
  1227. while (xs_list_iter(&p, &v)) {
  1228. xs *obj = NULL;
  1229. if (!valid_status(object_get_by_md5(v, &obj)))
  1230. continue;
  1231. const char *name = xs_dict_get(obj, "name");
  1232. const char *atto = get_atto(obj);
  1233. if (name && atto) {
  1234. /* get the current count */
  1235. const xs_number *cnt = xs_dict_get(rcnt, name);
  1236. if (xs_type(cnt) == XSTYPE_NUMBER) {
  1237. /* if it exists, increment */
  1238. xs *ucnt = xs_number_new(xs_number_get(cnt) + 1);
  1239. rcnt = xs_dict_set(rcnt, name, ucnt);
  1240. xs_set_add(&s, atto);
  1241. }
  1242. }
  1243. }
  1244. xs *rcpts = xs_set_result(&s);
  1245. /* create a new list of options with their new counts */
  1246. xs *nopts = xs_list_new();
  1247. p = lopts;
  1248. while (xs_list_iter(&p, &v)) {
  1249. const xs_number *cnt = xs_dict_get(rcnt, v);
  1250. if (xs_type(cnt) == XSTYPE_NUMBER) {
  1251. xs *d1 = xs_dict_new();
  1252. xs *d2 = xs_dict_new();
  1253. d2 = xs_dict_append(d2, "type", "Collection");
  1254. d2 = xs_dict_append(d2, "totalItems", cnt);
  1255. d1 = xs_dict_append(d1, "type", "Note");
  1256. d1 = xs_dict_append(d1, "name", v);
  1257. d1 = xs_dict_append(d1, "replies", d2);
  1258. nopts = xs_list_append(nopts, d1);
  1259. }
  1260. }
  1261. /* update the list */
  1262. msg = xs_dict_set(msg, xs_dict_get(msg, "oneOf") != NULL ? "oneOf" : "anyOf", nopts);
  1263. /* due date? */
  1264. int closed = 0;
  1265. const char *end_time = xs_dict_get(msg, "endTime");
  1266. if (!xs_is_null(end_time)) {
  1267. xs *now = xs_str_utctime(0, ISO_DATE_SPEC);
  1268. /* is now greater than the endTime? */
  1269. if (strcmp(now, end_time) >= 0) {
  1270. xs *et = xs_dup(end_time);
  1271. msg = xs_dict_set(msg, "closed", et);
  1272. closed = 1;
  1273. }
  1274. }
  1275. /* update the count of voters */
  1276. xs *vcnt = xs_number_new(xs_list_len(rcpts));
  1277. msg = xs_dict_set(msg, "votersCount", vcnt);
  1278. msg = xs_dict_set(msg, "cc", rcpts);
  1279. /* store */
  1280. object_add_ow(id, msg);
  1281. snac_debug(user, 1, xs_fmt("recounted poll %s", id));
  1282. timeline_touch(user);
  1283. /* send an update message to all voters */
  1284. xs *u_msg = msg_update(user, msg);
  1285. u_msg = xs_dict_set(u_msg, "cc", rcpts);
  1286. enqueue_message(user, u_msg);
  1287. if (closed) {
  1288. xs *c_msg = msg_update(user, msg);
  1289. notify(user, "Update", "Question", user->actor, c_msg);
  1290. }
  1291. return 0;
  1292. }
  1293. /** queues **/
  1294. int process_input_message(snac *snac, xs_dict *msg, xs_dict *req)
  1295. /* processes an ActivityPub message from the input queue */
  1296. /* return values: -1, fatal error; 0, transient error, retry;
  1297. 1, processed and done; 2, propagate to users (only when no user is set) */
  1298. {
  1299. char *actor = xs_dict_get(msg, "actor");
  1300. char *type = xs_dict_get(msg, "type");
  1301. xs *actor_o = NULL;
  1302. int a_status;
  1303. int do_notify = 0;
  1304. if (xs_is_null(actor) || *actor == '\0') {
  1305. srv_debug(0, xs_fmt("malformed message (bad actor)"));
  1306. return -1;
  1307. }
  1308. /* question votes may not have a type */
  1309. if (xs_is_null(type))
  1310. type = "Note";
  1311. /* reject uninteresting messages right now */
  1312. if (xs_match(type, "Add|View")) {
  1313. srv_debug(0, xs_fmt("Ignored message of type '%s'", type));
  1314. return -1;
  1315. }
  1316. char *object, *utype;
  1317. object = xs_dict_get(msg, "object");
  1318. if (object != NULL && xs_type(object) == XSTYPE_DICT)
  1319. utype = xs_dict_get(object, "type");
  1320. else
  1321. utype = "(null)";
  1322. /* special case for Delete messages */
  1323. if (strcmp(type, "Delete") == 0) {
  1324. /* if the actor is not here, do not even try */
  1325. if (!object_here(actor)) {
  1326. srv_debug(1, xs_fmt("dropped 'Delete' message from unknown actor '%s'", actor));
  1327. return -1;
  1328. }
  1329. /* discard crap */
  1330. if (xs_is_null(object)) {
  1331. srv_log(xs_fmt("dropped 'Delete' message with invalid object from actor '%s'", actor));
  1332. return -1;
  1333. }
  1334. /* also discard if the object to be deleted is not here */
  1335. char *obj_id = object;
  1336. if (xs_type(obj_id) == XSTYPE_DICT)
  1337. obj_id = xs_dict_get(obj_id, "id");
  1338. if (!object_here(obj_id)) {
  1339. srv_debug(1, xs_fmt("dropped 'Delete' message from unknown object '%s'", obj_id));
  1340. return -1;
  1341. }
  1342. }
  1343. /* bring the actor */
  1344. a_status = actor_request(snac, actor, &actor_o);
  1345. /* do not retry permanent failures */
  1346. if (a_status == 404 || a_status == 410 || a_status < 0) {
  1347. srv_debug(1, xs_fmt("dropping message due to actor error %s %d", actor, a_status));
  1348. return -1;
  1349. }
  1350. if (!valid_status(a_status)) {
  1351. /* do not retry 'Delete' messages */
  1352. if (strcmp(type, "Delete") == 0) {
  1353. srv_debug(1, xs_fmt("dropping 'Delete' message due to actor error %s %d", actor, a_status));
  1354. return -1;
  1355. }
  1356. /* other actor download errors */
  1357. /* the actor may require a signed request; propagate if no user is set */
  1358. if (snac == NULL)
  1359. return 2;
  1360. /* may need a retry */
  1361. srv_debug(0, xs_fmt("error requesting actor %s %d -- retry later", actor, a_status));
  1362. return 0;
  1363. }
  1364. /* check the signature */
  1365. xs *sig_err = NULL;
  1366. if (!check_signature(req, &sig_err)) {
  1367. srv_log(xs_fmt("bad signature %s (%s)", actor, sig_err));
  1368. srv_archive_error("check_signature", sig_err, req, msg);
  1369. return -1;
  1370. }
  1371. /* if no user is set, no further checks can be done; propagate */
  1372. if (snac == NULL)
  1373. return 2;
  1374. /* reject messages that are not for this user */
  1375. if (!is_msg_for_me(snac, msg)) {
  1376. snac_debug(snac, 1, xs_fmt("message from %s of type '%s' not for us", actor, type));
  1377. return 1;
  1378. }
  1379. /* if it's a DM from someone we don't follow, reject the message */
  1380. if (xs_type(xs_dict_get(snac->config, "drop_dm_from_unknown")) == XSTYPE_TRUE) {
  1381. if (strcmp(utype, "Note") == 0 && !is_msg_public(msg) &&
  1382. !following_check(snac, actor)) {
  1383. snac_log(snac, xs_fmt("DM rejected from unknown actor %s", actor));
  1384. return 1;
  1385. }
  1386. }
  1387. if (strcmp(type, "Follow") == 0) { /** **/
  1388. if (!follower_check(snac, actor)) {
  1389. /* ensure the actor object is here */
  1390. if (!object_here(actor)) {
  1391. xs *actor_obj = NULL;
  1392. actor_request(snac, actor, &actor_obj);
  1393. object_add(actor, actor_obj);
  1394. }
  1395. xs *f_msg = xs_dup(msg);
  1396. xs *reply = msg_accept(snac, f_msg, actor);
  1397. post_message(snac, actor, reply);
  1398. if (xs_is_null(xs_dict_get(f_msg, "published"))) {
  1399. /* add a date if it doesn't include one (Mastodon) */
  1400. xs *date = xs_str_utctime(0, ISO_DATE_SPEC);
  1401. f_msg = xs_dict_set(f_msg, "published", date);
  1402. }
  1403. timeline_add(snac, xs_dict_get(f_msg, "id"), f_msg);
  1404. follower_add(snac, actor);
  1405. snac_log(snac, xs_fmt("new follower %s", actor));
  1406. do_notify = 1;
  1407. }
  1408. else
  1409. snac_log(snac, xs_fmt("repeated 'Follow' from %s", actor));
  1410. }
  1411. else
  1412. if (strcmp(type, "Undo") == 0) { /** **/
  1413. if (xs_type(object) != XSTYPE_DICT)
  1414. utype = "Follow";
  1415. if (strcmp(utype, "Follow") == 0) { /** **/
  1416. if (valid_status(follower_del(snac, actor))) {
  1417. snac_log(snac, xs_fmt("no longer following us %s", actor));
  1418. do_notify = 1;
  1419. }
  1420. else
  1421. snac_log(snac, xs_fmt("error deleting follower %s", actor));
  1422. }
  1423. else
  1424. snac_debug(snac, 1, xs_fmt("ignored 'Undo' for object type '%s'", utype));
  1425. }
  1426. else
  1427. if (strcmp(type, "Create") == 0) { /** **/
  1428. if (is_muted(snac, actor)) {
  1429. snac_log(snac, xs_fmt("ignored 'Create' + '%s' from muted actor %s", utype, actor));
  1430. return 1;
  1431. }
  1432. if (strcmp(utype, "Note") == 0) { /** **/
  1433. char *id = xs_dict_get(object, "id");
  1434. char *in_reply_to = xs_dict_get(object, "inReplyTo");
  1435. xs *wrk = NULL;
  1436. if (!xs_is_null(in_reply_to) && is_hidden(snac, in_reply_to)) {
  1437. snac_debug(snac, 0, xs_fmt("dropped reply %s to hidden post %s", id, in_reply_to));
  1438. }
  1439. else {
  1440. timeline_request(snac, &in_reply_to, &wrk, 0);
  1441. if (timeline_add(snac, id, object)) {
  1442. snac_log(snac, xs_fmt("new 'Note' %s %s", actor, id));
  1443. do_notify = 1;
  1444. }
  1445. /* if it has a "name" field, it may be a vote for a question */
  1446. const char *name = xs_dict_get(object, "name");
  1447. if (!xs_is_null(name) && *name && !xs_is_null(in_reply_to) && *in_reply_to)
  1448. update_question(snac, in_reply_to);
  1449. }
  1450. }
  1451. else
  1452. if (strcmp(utype, "Question") == 0) { /** **/
  1453. char *id = xs_dict_get(object, "id");
  1454. if (timeline_add(snac, id, object))
  1455. snac_log(snac, xs_fmt("new 'Question' %s %s", actor, id));
  1456. }
  1457. else
  1458. if (strcmp(utype, "Video") == 0) { /** **/
  1459. char *id = xs_dict_get(object, "id");
  1460. if (timeline_add(snac, id, object))
  1461. snac_log(snac, xs_fmt("new 'Video' %s %s", actor, id));
  1462. }
  1463. else
  1464. snac_debug(snac, 1, xs_fmt("ignored 'Create' for object type '%s'", utype));
  1465. }
  1466. else
  1467. if (strcmp(type, "Accept") == 0) { /** **/
  1468. if (strcmp(utype, "(null)") == 0) {
  1469. const char *obj_id = xs_dict_get(msg, "object");
  1470. /* if the accepted object id is a string that may
  1471. be created by us, it's a follow */
  1472. if (xs_type(obj_id) == XSTYPE_STRING &&
  1473. xs_startswith(obj_id, srv_baseurl) &&
  1474. xs_endswith(obj_id, "/Follow"))
  1475. utype = "Follow";
  1476. }
  1477. if (strcmp(utype, "Follow") == 0) { /** **/
  1478. if (following_check(snac, actor)) {
  1479. following_add(snac, actor, msg);
  1480. snac_log(snac, xs_fmt("confirmed follow from %s", actor));
  1481. }
  1482. else
  1483. snac_log(snac, xs_fmt("spurious follow accept from %s", actor));
  1484. }
  1485. else
  1486. if (strcmp(utype, "Create") == 0) {
  1487. /* some implementations send Create confirmations, go figure */
  1488. snac_debug(snac, 1, xs_dup("ignored 'Accept' + 'Create'"));
  1489. }
  1490. else {
  1491. srv_archive_error("accept", "ignored Accept", req, msg);
  1492. snac_debug(snac, 1, xs_fmt("ignored 'Accept' for object type '%s'", utype));
  1493. }
  1494. }
  1495. else
  1496. if (strcmp(type, "Like") == 0) { /** **/
  1497. if (xs_type(object) == XSTYPE_DICT)
  1498. object = xs_dict_get(object, "id");
  1499. timeline_admire(snac, object, actor, 1);
  1500. snac_log(snac, xs_fmt("new 'Like' %s %s", actor, object));
  1501. do_notify = 1;
  1502. }
  1503. else
  1504. if (strcmp(type, "Announce") == 0) { /** **/
  1505. if (xs_type(object) == XSTYPE_DICT)
  1506. object = xs_dict_get(object, "id");
  1507. if (is_muted(snac, actor) && !xs_startswith(object, snac->actor))
  1508. snac_log(snac, xs_fmt("dropped 'Announce' from muted actor %s", actor));
  1509. else
  1510. if (is_limited(snac, actor) && !xs_startswith(object, snac->actor))
  1511. snac_log(snac, xs_fmt("dropped 'Announce' from limited actor %s", actor));
  1512. else {
  1513. xs *a_msg = NULL;
  1514. xs *wrk = NULL;
  1515. timeline_request(snac, &object, &wrk, 0);
  1516. if (valid_status(object_get(object, &a_msg))) {
  1517. const char *who = get_atto(a_msg);
  1518. if (who && !is_muted(snac, who)) {
  1519. /* bring the actor */
  1520. xs *who_o = NULL;
  1521. if (valid_status(actor_request(snac, who, &who_o))) {
  1522. timeline_admire(snac, object, actor, 0);
  1523. snac_log(snac, xs_fmt("new 'Announce' %s %s", actor, object));
  1524. do_notify = 1;
  1525. }
  1526. else
  1527. snac_debug(snac, 1, xs_fmt("dropped 'Announce' on actor request error %s", who));
  1528. }
  1529. else
  1530. snac_log(snac, xs_fmt("ignored 'Announce' about muted actor %s", who));
  1531. }
  1532. else
  1533. snac_debug(snac, 2, xs_fmt("error requesting 'Announce' object %s", object));
  1534. }
  1535. }
  1536. else
  1537. if (strcmp(type, "Update") == 0) { /** **/
  1538. if (xs_match(utype, "Person|Service")) { /** **/
  1539. actor_add(actor, xs_dict_get(msg, "object"));
  1540. timeline_touch(snac);
  1541. snac_log(snac, xs_fmt("updated actor %s", actor));
  1542. }
  1543. else
  1544. if (xs_match(utype, "Note|Page|Article|Video")) { /** **/
  1545. const char *id = xs_dict_get(object, "id");
  1546. if (object_here(id)) {
  1547. object_add_ow(id, object);
  1548. timeline_touch(snac);
  1549. snac_log(snac, xs_fmt("updated '%s' %s", utype, id));
  1550. }
  1551. else
  1552. snac_log(snac, xs_fmt("dropped update for unknown '%s' %s", utype, id));
  1553. }
  1554. else
  1555. if (strcmp(utype, "Question") == 0) { /** **/
  1556. const char *id = xs_dict_get(object, "id");
  1557. const char *closed = xs_dict_get(object, "closed");
  1558. object_add_ow(id, object);
  1559. timeline_touch(snac);
  1560. snac_log(snac, xs_fmt("%s poll %s", closed == NULL ? "updated" : "closed", id));
  1561. if (closed != NULL)
  1562. do_notify = 1;
  1563. }
  1564. else {
  1565. srv_archive_error("unsupported_update", "unsupported_update", req, msg);
  1566. snac_log(snac, xs_fmt("ignored 'Update' for object type '%s'", utype));
  1567. }
  1568. }
  1569. else
  1570. if (strcmp(type, "Delete") == 0) { /** **/
  1571. if (xs_type(object) == XSTYPE_DICT)
  1572. object = xs_dict_get(object, "id");
  1573. if (object_here(object)) {
  1574. timeline_del(snac, object);
  1575. snac_debug(snac, 1, xs_fmt("new 'Delete' %s %s", actor, object));
  1576. }
  1577. else
  1578. snac_debug(snac, 1, xs_fmt("ignored 'Delete' for unknown object %s", object));
  1579. }
  1580. else
  1581. if (strcmp(type, "Pong") == 0) { /** **/
  1582. snac_log(snac, xs_fmt("'Pong' received from %s", actor));
  1583. }
  1584. else
  1585. if (strcmp(type, "Ping") == 0) { /** **/
  1586. snac_log(snac, xs_fmt("'Ping' requested from %s", actor));
  1587. xs *rsp = msg_pong(snac, actor, xs_dict_get(msg, "id"));
  1588. enqueue_output_by_actor(snac, rsp, actor, 0);
  1589. }
  1590. else {
  1591. srv_archive_error("unsupported_type", "unsupported_type", req, msg);
  1592. snac_debug(snac, 1, xs_fmt("process_input_message type '%s' ignored", type));
  1593. }
  1594. if (do_notify) {
  1595. notify(snac, type, utype, actor, msg);
  1596. timeline_touch(snac);
  1597. }
  1598. return 1;
  1599. }
  1600. int send_email(char *msg)
  1601. /* invoke sendmail with email headers and body in msg */
  1602. {
  1603. FILE *f;
  1604. int status;
  1605. int fds[2];
  1606. pid_t pid;
  1607. if (pipe(fds) == -1) return -1;
  1608. pid = vfork();
  1609. if (pid == -1) return -1;
  1610. else if (pid == 0) {
  1611. dup2(fds[0], 0);
  1612. close(fds[0]);
  1613. close(fds[1]);
  1614. execl("/usr/sbin/sendmail", "sendmail", "-t", (char *) NULL);
  1615. _exit(1);
  1616. }
  1617. close(fds[0]);
  1618. if ((f = fdopen(fds[1], "w")) == NULL) {
  1619. close(fds[1]);
  1620. return -1;
  1621. }
  1622. fprintf(f, "%s\n", msg);
  1623. fclose(f);
  1624. if (waitpid(pid, &status, 0) == -1) return -1;
  1625. return status;
  1626. }
  1627. void process_user_queue_item(snac *snac, xs_dict *q_item)
  1628. /* processes an item from the user queue */
  1629. {
  1630. char *type;
  1631. int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
  1632. if ((type = xs_dict_get(q_item, "type")) == NULL)
  1633. type = "output";
  1634. if (strcmp(type, "message") == 0) {
  1635. xs_dict *msg = xs_dict_get(q_item, "message");
  1636. xs *rcpts = recipient_list(snac, msg, 1);
  1637. xs_set inboxes;
  1638. xs_list *p;
  1639. xs_str *actor;
  1640. xs_set_init(&inboxes);
  1641. /* iterate the recipients */
  1642. p = rcpts;
  1643. while (xs_list_iter(&p, &actor)) {
  1644. xs *inbox = get_actor_inbox(actor);
  1645. if (inbox != NULL) {
  1646. /* add to the set and, if it's not there, send message */
  1647. if (xs_set_add(&inboxes, inbox) == 1)
  1648. enqueue_output(snac, msg, inbox, 0, 0);
  1649. }
  1650. else
  1651. snac_log(snac, xs_fmt("cannot find inbox for %s", actor));
  1652. }
  1653. /* if it's public, send to the collected inboxes */
  1654. if (is_msg_public(msg)) {
  1655. if (xs_type(xs_dict_get(srv_config, "disable_inbox_collection")) != XSTYPE_TRUE) {
  1656. xs *shibx = inbox_list();
  1657. xs_str *inbox;
  1658. p = shibx;
  1659. while (xs_list_iter(&p, &inbox)) {
  1660. if (xs_set_add(&inboxes, inbox) == 1)
  1661. enqueue_output(snac, msg, inbox, 0, 0);
  1662. }
  1663. }
  1664. }
  1665. xs_set_free(&inboxes);
  1666. }
  1667. else
  1668. if (strcmp(type, "input") == 0) {
  1669. /* process the message */
  1670. xs_dict *msg = xs_dict_get(q_item, "message");
  1671. xs_dict *req = xs_dict_get(q_item, "req");
  1672. int retries = xs_number_get(xs_dict_get(q_item, "retries"));
  1673. if (xs_is_null(msg))
  1674. return;
  1675. if (!process_input_message(snac, msg, req)) {
  1676. srv_archive_error("input", "process_input_message", req, msg);
  1677. if (retries > queue_retry_max)
  1678. snac_log(snac, xs_fmt("input giving up"));
  1679. else {
  1680. /* reenqueue */
  1681. enqueue_input(snac, msg, req, retries + 1);
  1682. snac_log(snac, xs_fmt("input requeue #%d", retries + 1));
  1683. }
  1684. }
  1685. }
  1686. else
  1687. if (strcmp(type, "close_question") == 0) {
  1688. /* the time for this question has ended */
  1689. const char *id = xs_dict_get(q_item, "message");
  1690. if (!xs_is_null(id))
  1691. update_question(snac, id);
  1692. }
  1693. else
  1694. if (strcmp(type, "request_replies") == 0) {
  1695. const char *id = xs_dict_get(q_item, "message");
  1696. if (!xs_is_null(id))
  1697. timeline_request_replies(snac, id);
  1698. }
  1699. else
  1700. snac_log(snac, xs_fmt("unexpected user q_item type '%s'", type));
  1701. }
  1702. int process_user_queue(snac *snac)
  1703. /* processes a user's queue */
  1704. {
  1705. int cnt = 0;
  1706. xs *list = user_queue(snac);
  1707. xs_list *p = list;
  1708. xs_str *fn;
  1709. while (xs_list_iter(&p, &fn)) {
  1710. xs *q_item = dequeue(fn);
  1711. if (q_item == NULL)
  1712. continue;
  1713. process_user_queue_item(snac, q_item);
  1714. cnt++;
  1715. }
  1716. return cnt;
  1717. }
  1718. void process_queue_item(xs_dict *q_item)
  1719. /* processes an item from the global queue */
  1720. {
  1721. char *type = xs_dict_get(q_item, "type");
  1722. int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
  1723. if (strcmp(type, "output") == 0) {
  1724. int status;
  1725. xs_str *inbox = xs_dict_get(q_item, "inbox");
  1726. xs_str *keyid = xs_dict_get(q_item, "keyid");
  1727. xs_str *seckey = xs_dict_get(q_item, "seckey");
  1728. xs_dict *msg = xs_dict_get(q_item, "message");
  1729. int retries = xs_number_get(xs_dict_get(q_item, "retries"));
  1730. int p_status = xs_number_get(xs_dict_get(q_item, "p_status"));
  1731. xs *payload = NULL;
  1732. int p_size = 0;
  1733. if (xs_is_null(inbox) || xs_is_null(msg) || xs_is_null(keyid) || xs_is_null(seckey)) {
  1734. srv_log(xs_fmt("output message error: missing fields"));
  1735. return;
  1736. }
  1737. if (is_instance_blocked(inbox)) {
  1738. srv_debug(0, xs_fmt("discarded output message to blocked instance %s", inbox));
  1739. return;
  1740. }
  1741. /* deliver (if previous error status was a timeout, try now longer) */
  1742. status = send_to_inbox_raw(keyid, seckey, inbox, msg,
  1743. &payload, &p_size, p_status == 599 ? 8 : 6);
  1744. if (payload) {
  1745. if (p_size > 64) {
  1746. /* trim the message */
  1747. payload[64] = '\0';
  1748. payload = xs_str_cat(payload, "...");
  1749. }
  1750. /* strip ugly control characters */
  1751. payload = xs_replace_i(payload, "\n", "");
  1752. payload = xs_replace_i(payload, "\r", "");
  1753. if (*payload)
  1754. payload = xs_str_wrap_i(" [", payload, "]");
  1755. }
  1756. else
  1757. payload = xs_str_new(NULL);
  1758. srv_log(xs_fmt("output message: sent to inbox %s %d%s", inbox, status, payload));
  1759. if (!valid_status(status)) {
  1760. retries++;
  1761. /* if it's not the first time it fails with a timeout,
  1762. penalize the server by skipping one retry */
  1763. if (p_status == status && status == 499)
  1764. retries++;
  1765. /* error sending; requeue? */
  1766. if (status == 400 || status == 404 || status == 405 || status == 410 || status < 0)
  1767. /* explicit error: discard */
  1768. srv_log(xs_fmt("output message: fatal error %s %d", inbox, status));
  1769. else
  1770. if (retries > queue_retry_max)
  1771. srv_log(xs_fmt("output message: giving up %s %d", inbox, status));
  1772. else {
  1773. /* requeue */
  1774. enqueue_output_raw(keyid, seckey, msg, inbox, retries, status);
  1775. srv_log(xs_fmt("output message: requeue %s #%d", inbox, retries));
  1776. }
  1777. }
  1778. }
  1779. else
  1780. if (strcmp(type, "email") == 0) {
  1781. /* send this email */
  1782. xs_str *msg = xs_dict_get(q_item, "message");
  1783. int retries = xs_number_get(xs_dict_get(q_item, "retries"));
  1784. if (!send_email(msg))
  1785. srv_debug(1, xs_fmt("email message sent"));
  1786. else {
  1787. retries++;
  1788. if (retries > queue_retry_max)
  1789. srv_log(xs_fmt("email giving up (errno: %d)", errno));
  1790. else {
  1791. /* requeue */
  1792. srv_log(xs_fmt(
  1793. "email requeue #%d (errno: %d)", retries, errno));
  1794. enqueue_email(msg, retries);
  1795. }
  1796. }
  1797. }
  1798. else
  1799. if (strcmp(type, "telegram") == 0) {
  1800. /* send this via telegram */
  1801. char *bot = xs_dict_get(q_item, "bot");
  1802. char *msg = xs_dict_get(q_item, "message");
  1803. xs *chat_id = xs_dup(xs_dict_get(q_item, "chat_id"));
  1804. int status = 0;
  1805. /* chat_id must start with a - */
  1806. if (!xs_startswith(chat_id, "-"))
  1807. chat_id = xs_str_wrap_i("-", chat_id, NULL);
  1808. xs *url = xs_fmt("https:/" "/api.telegram.org/bot%s/sendMessage", bot);
  1809. xs *body = xs_fmt("{\"chat_id\":%s,\"text\":\"%s\"}", chat_id, msg);
  1810. xs *headers = xs_dict_new();
  1811. headers = xs_dict_append(headers, "content-type", "application/json");
  1812. xs *rsp = xs_http_request("POST", url, headers,
  1813. body, strlen(body), &status, NULL, NULL, 0);
  1814. rsp = xs_free(rsp);
  1815. srv_debug(0, xs_fmt("telegram post %d", status));
  1816. }
  1817. else
  1818. if (strcmp(type, "ntfy") == 0) {
  1819. /* send this via ntfy */
  1820. char *ntfy_server = xs_dict_get(q_item, "ntfy_server");
  1821. char *msg = xs_dict_get(q_item, "message");
  1822. char *ntfy_token = xs_dict_get(q_item, "ntfy_token");
  1823. int status = 0;
  1824. xs *url = xs_fmt("%s", ntfy_server);
  1825. xs *body = xs_fmt("%s", msg);
  1826. xs *headers = xs_dict_new();
  1827. headers = xs_dict_append(headers, "content-type", "text/plain");
  1828. // Append the Authorization header only if ntfy_token is not NULL
  1829. if (ntfy_token != NULL) {
  1830. headers = xs_dict_append(headers, "Authorization", xs_fmt("Bearer %s", ntfy_token));
  1831. }
  1832. xs *rsp = xs_http_request("POST", url, headers,
  1833. body, strlen(body), &status, NULL, NULL, 0);
  1834. rsp = xs_free(rsp);
  1835. srv_debug(0, xs_fmt("ntfy post %d", status));
  1836. }
  1837. else
  1838. if (strcmp(type, "purge") == 0) {
  1839. srv_log(xs_dup("purge start"));
  1840. purge_all();
  1841. srv_log(xs_dup("purge end"));
  1842. }
  1843. else
  1844. if (strcmp(type, "input") == 0) {
  1845. xs_dict *msg = xs_dict_get(q_item, "message");
  1846. xs_dict *req = xs_dict_get(q_item, "req");
  1847. int retries = xs_number_get(xs_dict_get(q_item, "retries"));
  1848. /* do some instance-level checks */
  1849. int r = process_input_message(NULL, msg, req);
  1850. if (r == 0) {
  1851. /* transient error? retry */
  1852. int queue_retry_max = xs_number_get(xs_dict_get(srv_config, "queue_retry_max"));
  1853. if (retries > queue_retry_max)
  1854. srv_log(xs_fmt("shared input giving up"));
  1855. else {
  1856. /* reenqueue */
  1857. enqueue_shared_input(msg, req, retries + 1);
  1858. srv_log(xs_fmt("shared input requeue #%d", retries + 1));
  1859. }
  1860. }
  1861. else
  1862. if (r == 2) {
  1863. /* redistribute the input message to all users */
  1864. char *ntid = xs_dict_get(q_item, "ntid");
  1865. xs *tmpfn = xs_fmt("%s/tmp/%s.json", srv_basedir, ntid);
  1866. FILE *f;
  1867. if ((f = fopen(tmpfn, "w")) != NULL) {
  1868. xs_json_dump(q_item, 4, f);
  1869. fclose(f);
  1870. }
  1871. xs *users = user_list();
  1872. xs_list *p = users;
  1873. char *v;
  1874. int cnt = 0;
  1875. while (xs_list_iter(&p, &v)) {
  1876. snac user;
  1877. if (user_open(&user, v)) {
  1878. if (is_msg_for_me(&user, msg)) {
  1879. xs *fn = xs_fmt("%s/queue/%s.json", user.basedir, ntid);
  1880. snac_debug(&user, 1, xs_fmt("enqueue_input (from shared inbox) %s", fn));
  1881. if (link(tmpfn, fn) < 0)
  1882. srv_log(xs_fmt("link(%s, %s) error", tmpfn, fn));
  1883. cnt++;
  1884. }
  1885. user_free(&user);
  1886. }
  1887. }
  1888. unlink(tmpfn);
  1889. if (cnt == 0) {
  1890. srv_archive_qitem("no_valid_recipients", q_item);
  1891. srv_debug(1, xs_fmt("no valid recipients for %s", tmpfn));
  1892. }
  1893. }
  1894. }
  1895. else
  1896. srv_log(xs_fmt("unexpected q_item type '%s'", type));
  1897. }
  1898. int process_queue(void)
  1899. /* processes the global queue */
  1900. {
  1901. int cnt = 0;
  1902. xs *list = queue();
  1903. xs_list *p = list;
  1904. xs_str *fn;
  1905. while (xs_list_iter(&p, &fn)) {
  1906. xs *q_item = dequeue(fn);
  1907. if (q_item != NULL) {
  1908. job_post(q_item, 0);
  1909. cnt++;
  1910. }
  1911. }
  1912. return cnt;
  1913. }
  1914. /** HTTP handlers */
  1915. int activitypub_get_handler(const xs_dict *req, const char *q_path,
  1916. char **body, int *b_size, char **ctype)
  1917. {
  1918. int status = 200;
  1919. char *accept = xs_dict_get(req, "accept");
  1920. snac snac;
  1921. xs *msg = NULL;
  1922. if (accept == NULL)
  1923. return 0;
  1924. if (xs_str_in(accept, "application/activity+json") == -1 &&
  1925. xs_str_in(accept, "application/ld+json") == -1)
  1926. return 0;
  1927. xs *l = xs_split_n(q_path, "/", 2);
  1928. char *uid, *p_path;
  1929. uid = xs_list_get(l, 1);
  1930. if (!user_open(&snac, uid)) {
  1931. /* invalid user */
  1932. srv_debug(1, xs_fmt("activitypub_get_handler bad user %s", uid));
  1933. return 404;
  1934. }
  1935. p_path = xs_list_get(l, 2);
  1936. *ctype = "application/activity+json";
  1937. if (p_path == NULL) {
  1938. /* if there was no component after the user, it's an actor request */
  1939. msg = msg_actor(&snac);
  1940. *ctype = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
  1941. char *ua = xs_dict_get(req, "user-agent");
  1942. snac_debug(&snac, 0, xs_fmt("serving actor [%s]", ua ? ua : "No UA"));
  1943. }
  1944. else
  1945. if (strcmp(p_path, "outbox") == 0) {
  1946. xs *id = xs_fmt("%s/outbox", snac.actor);
  1947. xs *elems = timeline_simple_list(&snac, "public", 0, 20);
  1948. xs *list = xs_list_new();
  1949. msg = msg_collection(&snac, id);
  1950. char *p, *v;
  1951. p = elems;
  1952. while (xs_list_iter(&p, &v)) {
  1953. xs *i = NULL;
  1954. if (valid_status(object_get_by_md5(v, &i))) {
  1955. char *type = xs_dict_get(i, "type");
  1956. char *id = xs_dict_get(i, "id");
  1957. if (type && id && strcmp(type, "Note") == 0 && xs_startswith(id, snac.actor)) {
  1958. xs *c_msg = msg_create(&snac, i);
  1959. list = xs_list_append(list, c_msg);
  1960. }
  1961. }
  1962. }
  1963. /* replace the 'orderedItems' with the latest posts */
  1964. xs *items = xs_number_new(xs_list_len(list));
  1965. msg = xs_dict_set(msg, "orderedItems", list);
  1966. msg = xs_dict_set(msg, "totalItems", items);
  1967. }
  1968. else
  1969. if (strcmp(p_path, "followers") == 0 || strcmp(p_path, "following") == 0) {
  1970. xs *id = xs_fmt("%s/%s", snac.actor, p_path);
  1971. msg = msg_collection(&snac, id);
  1972. }
  1973. else
  1974. if (xs_startswith(p_path, "p/")) {
  1975. xs *id = xs_fmt("%s/%s", snac.actor, p_path);
  1976. status = object_get(id, &msg);
  1977. /* don't return non-public objects */
  1978. if (valid_status(status) && !is_msg_public(msg))
  1979. status = 404;
  1980. }
  1981. else
  1982. status = 404;
  1983. if (status == 200 && msg != NULL) {
  1984. *body = xs_json_dumps(msg, 4);
  1985. *b_size = strlen(*body);
  1986. }
  1987. snac_debug(&snac, 1, xs_fmt("activitypub_get_handler serving %s %d", q_path, status));
  1988. user_free(&snac);
  1989. return status;
  1990. }
  1991. int activitypub_post_handler(const xs_dict *req, const char *q_path,
  1992. char *payload, int p_size,
  1993. char **body, int *b_size, char **ctype)
  1994. /* processes an input message */
  1995. {
  1996. (void)b_size;
  1997. int status = 202; /* accepted */
  1998. char *i_ctype = xs_dict_get(req, "content-type");
  1999. snac snac;
  2000. char *v;
  2001. if (i_ctype == NULL) {
  2002. *body = xs_str_new("no content-type");
  2003. *ctype = "text/plain";
  2004. return 400;
  2005. }
  2006. if (xs_is_null(payload)) {
  2007. *body = xs_str_new("no payload");
  2008. *ctype = "text/plain";
  2009. return 400;
  2010. }
  2011. if (xs_str_in(i_ctype, "application/activity+json") == -1 &&
  2012. xs_str_in(i_ctype, "application/ld+json") == -1)
  2013. return 0;
  2014. /* decode the message */
  2015. xs *msg = xs_json_loads(payload);
  2016. const char *id = xs_dict_get(msg, "id");
  2017. if (msg == NULL) {
  2018. srv_log(xs_fmt("activitypub_post_handler JSON error %s", q_path));
  2019. srv_archive_error("activitypub_post_handler", "JSON error", req, payload);
  2020. *body = xs_str_new("JSON error");
  2021. *ctype = "text/plain";
  2022. return 400;
  2023. }
  2024. if (id && is_instance_blocked(id)) {
  2025. srv_debug(1, xs_fmt("full instance block for %s", id));
  2026. *body = xs_str_new("blocked");
  2027. *ctype = "text/plain";
  2028. return 403;
  2029. }
  2030. /* get the user and path */
  2031. xs *l = xs_split_n(q_path, "/", 2);
  2032. if (xs_list_len(l) == 2 && strcmp(xs_list_get(l, 1), "shared-inbox") == 0) {
  2033. enqueue_shared_input(msg, req, 0);
  2034. return 202;
  2035. }
  2036. if (xs_list_len(l) != 3 || strcmp(xs_list_get(l, 2), "inbox") != 0) {
  2037. /* strange q_path */
  2038. srv_debug(1, xs_fmt("activitypub_post_handler unsupported path %s", q_path));
  2039. return 404;
  2040. }
  2041. const char *uid = xs_list_get(l, 1);
  2042. if (!user_open(&snac, uid)) {
  2043. /* invalid user */
  2044. srv_debug(1, xs_fmt("activitypub_post_handler bad user %s", uid));
  2045. return 404;
  2046. }
  2047. /* if it has a digest, check it now, because
  2048. later the payload won't be exactly the same */
  2049. if ((v = xs_dict_get(req, "digest")) != NULL) {
  2050. xs *s1 = xs_sha256_base64(payload, p_size);
  2051. xs *s2 = xs_fmt("SHA-256=%s", s1);
  2052. if (strcmp(s2, v) != 0) {
  2053. srv_log(xs_fmt("digest check FAILED"));
  2054. *body = xs_str_new("bad digest");
  2055. *ctype = "text/plain";
  2056. status = 400;
  2057. }
  2058. }
  2059. /* if the message is from a muted actor, reject it right now */
  2060. if (!xs_is_null(v = xs_dict_get(msg, "actor")) && *v) {
  2061. if (is_muted(&snac, v)) {
  2062. snac_log(&snac, xs_fmt("rejected message from MUTEd actor %s", v));
  2063. *body = xs_str_new("rejected");
  2064. *ctype = "text/plain";
  2065. status = 403;
  2066. }
  2067. }
  2068. if (valid_status(status)) {
  2069. enqueue_input(&snac, msg, req, 0);
  2070. *ctype = "application/activity+json";
  2071. }
  2072. user_free(&snac);
  2073. return status;
  2074. }