diff options
Diffstat (limited to 'rss.c')
| -rw-r--r-- | rss.c | 124 |
1 files changed, 124 insertions, 0 deletions
| @@ -5,6 +5,8 @@ | |||
| 5 | #include "xs_html.h" | 5 | #include "xs_html.h" |
| 6 | #include "xs_regex.h" | 6 | #include "xs_regex.h" |
| 7 | #include "xs_time.h" | 7 | #include "xs_time.h" |
| 8 | #include "xs_match.h" | ||
| 9 | #include "xs_curl.h" | ||
| 8 | 10 | ||
| 9 | #include "snac.h" | 11 | #include "snac.h" |
| 10 | 12 | ||
| @@ -103,3 +105,125 @@ xs_str *rss_from_timeline(snac *user, const xs_list *timeline, | |||
| 103 | 105 | ||
| 104 | return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | 106 | return xs_html_render_s(rss, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); |
| 105 | } | 107 | } |
| 108 | |||
| 109 | |||
| 110 | void rss_to_timeline(snac *user, const char *url) | ||
| 111 | /* reads an RSS and inserts all ActivityPub posts into the user's timeline */ | ||
| 112 | { | ||
| 113 | xs *hdrs = xs_dict_new(); | ||
| 114 | hdrs = xs_dict_set(hdrs, "accept", "application/rss+xml"); | ||
| 115 | hdrs = xs_dict_set(hdrs, "user-agent", USER_AGENT); | ||
| 116 | |||
| 117 | xs *payload = NULL; | ||
| 118 | int status; | ||
| 119 | int p_size; | ||
| 120 | |||
| 121 | xs *rsp = xs_http_request("GET", url, hdrs, NULL, 0, &status, &payload, &p_size, 0); | ||
| 122 | |||
| 123 | if (!valid_status(status) || !xs_is_string(payload)) | ||
| 124 | return; | ||
| 125 | |||
| 126 | /* not an RSS? done */ | ||
| 127 | const char *ctype = xs_dict_get(rsp, "content-type"); | ||
| 128 | if (!xs_is_string(ctype) || xs_str_in(ctype, "application/rss+xml") == -1) | ||
| 129 | return; | ||
| 130 | |||
| 131 | snac_log(user, xs_fmt("parsing RSS %s", url)); | ||
| 132 | |||
| 133 | /* yes, parsing is done with regexes (now I have two problems blah blah blah) */ | ||
| 134 | xs *links = xs_regex_select(payload, "<link>[^<]+</link>"); | ||
| 135 | const char *link; | ||
| 136 | |||
| 137 | xs_list_foreach(links, link) { | ||
| 138 | xs *l = xs_replace(link, "<link>", ""); | ||
| 139 | char *p = strchr(l, '<'); | ||
| 140 | |||
| 141 | if (p == NULL) | ||
| 142 | continue; | ||
| 143 | *p = '\0'; | ||
| 144 | |||
| 145 | /* skip this same URL */ | ||
| 146 | if (strcmp(l, url) == 0) | ||
| 147 | continue; | ||
| 148 | |||
| 149 | snac_debug(user, 1, xs_fmt("RSS link: %s", l)); | ||
| 150 | |||
| 151 | if (timeline_here(user, l)) { | ||
| 152 | snac_debug(user, 1, xs_fmt("RSS entry already in timeline %s", l)); | ||
| 153 | continue; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* special trick for Mastodon: convert from the alternate format */ | ||
| 157 | if (strchr(l, '@') != NULL) { | ||
| 158 | xs *l2 = xs_split(l, "/"); | ||
| 159 | |||
| 160 | if (xs_list_len(l2) == 5) { | ||
| 161 | const char *uid = xs_list_get(l2, 3); | ||
| 162 | if (*uid == '@') { | ||
| 163 | xs *guessed_id = xs_fmt("https:/" "/%s/users/%s/statuses/%s", | ||
| 164 | xs_list_get(l2, 2), uid + 1, xs_list_get(l2, -1)); | ||
| 165 | |||
| 166 | if (timeline_here(user, guessed_id)) { | ||
| 167 | snac_debug(user, 1, xs_fmt("RSS entry already in timeline (alt) %s", guessed_id)); | ||
| 168 | continue; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | xs *obj = NULL; | ||
| 175 | |||
| 176 | if (!valid_status(object_get(l, &obj))) { | ||
| 177 | /* object is not here: bring it */ | ||
| 178 | if (!valid_status(activitypub_request(user, l, &obj))) | ||
| 179 | continue; | ||
| 180 | } | ||
| 181 | |||
| 182 | if (xs_is_dict(obj)) { | ||
| 183 | const char *id = xs_dict_get(obj, "id"); | ||
| 184 | const char *type = xs_dict_get(obj, "type"); | ||
| 185 | const char *attr_to = get_atto(obj); | ||
| 186 | |||
| 187 | if (!xs_is_string(id) || !xs_is_string(type) || !xs_is_string(attr_to)) | ||
| 188 | continue; | ||
| 189 | |||
| 190 | if (!xs_match(type, POSTLIKE_OBJECT_TYPE)) | ||
| 191 | continue; | ||
| 192 | |||
| 193 | if (timeline_here(user, id)) { | ||
| 194 | snac_debug(user, 1, xs_fmt("RSS entry already in timeline (id) %s", id)); | ||
| 195 | continue; | ||
| 196 | } | ||
| 197 | |||
| 198 | if (!valid_status(actor_request(user, attr_to, NULL))) | ||
| 199 | continue; | ||
| 200 | |||
| 201 | timeline_add(user, id, obj); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | void rss_process(void) | ||
| 208 | /* parses all RSS from all users */ | ||
| 209 | { | ||
| 210 | xs *list = user_list(); | ||
| 211 | const char *uid; | ||
| 212 | |||
| 213 | xs_list_foreach(list, uid) { | ||
| 214 | snac user; | ||
| 215 | |||
| 216 | if (user_open(&user, uid)) { | ||
| 217 | const xs_list *rss = xs_dict_get(user.config, "rss"); | ||
| 218 | |||
| 219 | if (xs_is_list(rss)) { | ||
| 220 | const char *url; | ||
| 221 | |||
| 222 | xs_list_foreach(rss, url) | ||
| 223 | rss_to_timeline(&user, url); | ||
| 224 | } | ||
| 225 | |||
| 226 | user_free(&user); | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||