2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2005-2023 the Claws Mail Team and Andrej Kacian <andrej@kacian.sk>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 /* Claws Mail includes */
29 #include <common/claws.h>
30 #include <procheader.h>
31 #include <common/utils.h>
35 #include "libfeed/feed.h"
36 #include "libfeed/feeditem.h"
37 #include "libfeed/date.h"
39 #include "rssyl_feed.h"
40 #include "rssyl_parse_feed.h"
43 /* rssyl_parse_folder_item_file()
45 * Parse a RFC822-formatted feed item given by "path", and returns a
46 * pointer to a newly-allocated FeedItem struct, which contains all required data.
49 FeedItem *rssyl_parse_folder_item_file(gchar *path)
51 gchar *contents, **lines, **line, **splid, *tmp, *tmp2;
57 gboolean parsing_headers = TRUE, past_html_tag = FALSE, past_endhtml_tag = FALSE;
58 gboolean started_author = FALSE, started_subject = FALSE;
59 gboolean started_link = FALSE, started_clink = FALSE, got_original_title = FALSE;
61 debug_print("RSSyl: parsing '%s'\n", path);
63 if( !g_file_get_contents(path, &contents, NULL, &error) ) {
64 g_warning("error: '%s'", error->message);
68 if( contents != NULL ) {
69 lines = strsplit_no_copy(contents, '\n');
71 g_warning("badly formatted file found, ignoring: '%s'", path);
75 ctx = g_new0(RFeedCtx, 1);
76 ctx->path = g_strdup(path); /* store filesystem path to source file */
79 item = feed_item_new(NULL);
83 if( parsing_headers && lines[i] && !strlen(lines[i]) ) {
84 parsing_headers = FALSE;
85 debug_print("RSSyl: finished parsing headers\n");
88 if( parsing_headers ) {
89 line = g_strsplit(lines[i], ": ", 2);
90 if( line[0] && line[1] && strlen(line[0]) && lines[i][0] != ' ') {
91 started_author = FALSE;
92 started_subject = FALSE;
94 started_clink = FALSE;
97 if( !strcmp(line[0], "From") ) {
98 feed_item_set_author(item, line[1]);
99 debug_print("RSSyl: got author '%s'\n", feed_item_get_author(item));
100 started_author = TRUE;
103 /* Date (set both FeedItem timestamps) */
104 if( !strcmp(line[0], "Date") ) {
105 feed_item_set_date_modified(item,
106 procheader_date_parse(NULL, line[1], 0));
107 feed_item_set_date_published(item,
108 feed_item_get_date_modified(item));
109 debug_print("RSSyl: got date \n" );
113 if( !strcmp(line[0], "Subject") && !got_original_title ) {
114 feed_item_set_title(item,line[1]);
115 debug_print("RSSyl: got title '%s'\n", feed_item_get_title(item));
116 started_subject = TRUE;
119 /* Original (including HTML) title - Atom feeds */
120 if( !strcmp(line[0], "X-RSSyl-OrigTitle") ) {
121 feed_item_set_title(item, line[1]);
122 debug_print("RSSyl: got original title '%s'\n",
123 feed_item_get_title(item));
124 got_original_title = TRUE;
128 if( !strcmp(line[0], "X-RSSyl-URL") ) {
129 feed_item_set_url(item, line[1]);
130 debug_print("RSSyl: got link '%s'\n", feed_item_get_url(item));
134 /* Last-Seen timestamp */
135 if( !strcmp(line[0], "X-RSSyl-Last-Seen") ) {
136 ctx->last_seen = atol(line[1]);
137 debug_print("RSSyl: got last_seen timestamp %"CM_TIME_FORMAT"\n", ctx->last_seen);
141 if( !strcmp(line[0], "Message-ID") ) {
142 if (line[1][0] != '<' || line[1][strlen(line[1])-1] != '>') {
143 debug_print("RSSyl: malformed Message-ID, ignoring...\n");
145 /* Get the ID from within < and >. */
147 tmp2 = g_strndup(tmp, strlen(tmp) - 1);
148 feed_item_set_id(item, tmp2);
154 if( !strcmp(line[0], "X-RSSyl-Comments") ) {
155 feed_item_set_comments_url(item, line[1]);
156 debug_print("RSSyl: got clink '%s'\n", feed_item_get_comments_url(item));
157 started_clink = TRUE;
161 if( !strcmp(line[0], "References") ) {
162 splid = g_strsplit_set(line[1], "<>", 3);
163 if( strlen(splid[1]) != 0 )
164 feed_item_set_parent_id(item, line[1]);
168 } else if (lines[i][0] == ' ') {
170 /* continuation line */
171 if (started_author) {
172 tmp = g_strdup_printf("%s %s", feed_item_get_author(item), lines[i]+1);
173 feed_item_set_author(item, tmp);
174 debug_print("RSSyl: updated author to '%s'\n", tmp);
176 } else if (started_subject) {
177 tmp = g_strdup_printf("%s %s", feed_item_get_title(item), lines[i]+1);
178 feed_item_set_title(item, tmp);
179 debug_print("RSSyl: updated title to '%s'\n", tmp);
181 } else if (started_link) {
182 tmp = g_strdup_printf("%s%s", feed_item_get_url(item), lines[i]+1);
183 feed_item_set_url(item, tmp);
184 debug_print("RSSyl: updated link to '%s'\n", tmp);
186 } else if (started_clink) {
187 tmp = g_strdup_printf("%s%s", feed_item_get_comments_url(item), lines[i]+1);
188 feed_item_set_comments_url(item, tmp);
189 debug_print("RSSyl: updated comments_link to '%s'\n", tmp);
194 if( !strcmp(lines[i], RSSYL_TEXT_START) ) {
195 debug_print("RSSyl: Leading html tag found at line %d\n", i);
196 past_html_tag = TRUE;
199 g_warning("unexpected leading html tag found at line %d", i);
200 g_string_free(body, TRUE);
202 body = g_string_new("");
206 while( past_html_tag && !past_endhtml_tag && lines[i] ) {
207 if( !strcmp(lines[i], RSSYL_TEXT_END) ) {
208 debug_print("RSSyl: Trailing html tag found at line %d\n", i);
209 past_endhtml_tag = TRUE;
214 body = g_string_append_c(body, '\n');
215 body = g_string_append(body, lines[i]);
226 if (past_html_tag && past_endhtml_tag && body->str != NULL)
227 feed_item_set_text(item, body->str);
228 g_string_free(body, TRUE);
236 static void rssyl_flush_folder_func(gpointer data, gpointer user_data)
238 FeedItem *item = (FeedItem *)data;
239 RFeedCtx *ctx = (RFeedCtx *)item->data;
241 if( ctx != NULL && ctx->path != NULL) {
244 feed_item_free(item);
247 static void rssyl_folder_read_existing_real(RFolderItem *ritem)
249 gchar *path = NULL, *fname = NULL;
252 GError *error = NULL;
254 FeedItem *item = NULL;
257 g_return_if_fail(ritem != NULL);
259 path = folder_item_get_path(&ritem->item);
260 g_return_if_fail(path != NULL);
262 debug_print("RSSyl: reading existing items from '%s'\n", path);
264 /* Flush contents if any, so we can add new */
265 if( g_slist_length(ritem->items) > 0 ) {
266 g_slist_foreach(ritem->items, (GFunc)rssyl_flush_folder_func, NULL);
267 g_slist_free(ritem->items);
270 ritem->last_update = 0;
272 if( (dp = g_dir_open(path, 0, &error)) == NULL ) {
273 debug_print("g_dir_open on \"%s\" failed with error %d (%s)\n",
274 path, error->code, error->message);
280 while( (d = g_dir_read_name(dp)) != NULL ) {
281 if( claws_is_exiting() ) {
287 if( d[0] != '.' && (num = to_number(d)) > 0 ) {
288 fname = g_strdup_printf("%s%c%s", path, G_DIR_SEPARATOR, d);
289 if (!g_file_test(fname, G_FILE_TEST_IS_REGULAR)) {
290 debug_print("RSSyl: not a regular file: '%s', ignoring it\n", fname);
295 debug_print("RSSyl: starting to parse '%s'\n", d);
296 if( (item = rssyl_parse_folder_item_file(fname)) != NULL ) {
297 /* Find latest timestamp */
298 ctx = (RFeedCtx *)item->data;
299 if( ritem->last_update < ctx->last_seen )
300 ritem->last_update = ctx->last_seen;
301 debug_print("RSSyl: Appending '%s'\n", feed_item_get_title(item));
302 ritem->items = g_slist_prepend(ritem->items, item);
311 ritem->items = g_slist_reverse(ritem->items);
315 static void *rssyl_read_existing_thr(void *arg)
317 RParseCtx *ctx = (RParseCtx *)arg;
319 rssyl_folder_read_existing_real(ctx->ritem);
325 void rssyl_folder_read_existing(RFolderItem *ritem)
332 g_return_if_fail(ritem != NULL);
336 ctx = g_new0(RParseCtx, 1);
340 if( pthread_create(&pt, NULL, rssyl_read_existing_thr,
341 (void *)ctx) != 0 ) {
342 /* Couldn't create thread, let's continue non-threaded. */
343 rssyl_folder_read_existing_real(ritem);
345 /* Thread started, wait until it is done. */
346 debug_print("RSSyl: waiting for thread to finish\n");
347 while( !ctx->ready ) {
351 debug_print("RSSyl: thread finished\n");
352 pthread_join(pt, NULL);
357 rssyl_folder_read_existing_real(ritem);