2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4 * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 /* Claws Mail includes */
31 #include <common/claws.h>
32 #include <procheader.h>
33 #include <common/utils.h>
37 #include "libfeed/feed.h"
38 #include "libfeed/feeditem.h"
39 #include "libfeed/date.h"
41 #include "rssyl_feed.h"
42 #include "rssyl_parse_feed.h"
45 /* rssyl_parse_folder_item_file()
47 * Parse a RFC822-formatted feed item given by "path", and returns a
48 * pointer to a newly-allocated FeedItem struct, which contains all required data.
51 FeedItem *rssyl_parse_folder_item_file(gchar *path)
53 gchar *contents, **lines, **line, **splid, *tmp, *tmp2;
59 gboolean parsing_headers = TRUE, past_html_tag = FALSE, past_endhtml_tag = FALSE;
60 gboolean started_author = FALSE, started_subject = FALSE;
61 gboolean started_link = FALSE, started_clink = FALSE, got_original_title = FALSE;
63 debug_print("RSSyl: parsing '%s'\n", path);
65 if( !g_file_get_contents(path, &contents, NULL, &error) ) {
66 g_warning("error: '%s'", error->message);
70 if( contents != NULL ) {
71 lines = strsplit_no_copy(contents, '\n');
73 g_warning("badly formatted file found, ignoring: '%s'", path);
77 ctx = g_new0(RFeedCtx, 1);
78 ctx->path = g_strdup(path); /* store filesystem path to source file */
81 item = feed_item_new(NULL);
85 if( parsing_headers && lines[i] && !strlen(lines[i]) ) {
86 parsing_headers = FALSE;
87 debug_print("RSSyl: finished parsing headers\n");
90 if( parsing_headers ) {
91 line = g_strsplit(lines[i], ": ", 2);
92 if( line[0] && line[1] && strlen(line[0]) && lines[i][0] != ' ') {
93 started_author = FALSE;
94 started_subject = FALSE;
96 started_clink = FALSE;
99 if( !strcmp(line[0], "From") ) {
100 feed_item_set_author(item, line[1]);
101 debug_print("RSSyl: got author '%s'\n", feed_item_get_author(item));
102 started_author = TRUE;
105 /* Date (set both FeedItem timestamps) */
106 if( !strcmp(line[0], "Date") ) {
107 feed_item_set_date_modified(item,
108 procheader_date_parse(NULL, line[1], 0));
109 feed_item_set_date_published(item,
110 feed_item_get_date_modified(item));
111 debug_print("RSSyl: got date \n" );
115 if( !strcmp(line[0], "Subject") && !got_original_title ) {
116 feed_item_set_title(item,line[1]);
117 debug_print("RSSyl: got title '%s'\n", feed_item_get_title(item));
118 started_subject = TRUE;
121 /* Original (including HTML) title - Atom feeds */
122 if( !strcmp(line[0], "X-RSSyl-OrigTitle") ) {
123 feed_item_set_title(item, line[1]);
124 debug_print("RSSyl: got original title '%s'\n",
125 feed_item_get_title(item));
126 got_original_title = TRUE;
130 if( !strcmp(line[0], "X-RSSyl-URL") ) {
131 feed_item_set_url(item, line[1]);
132 debug_print("RSSyl: got link '%s'\n", feed_item_get_url(item));
136 /* Last-Seen timestamp */
137 if( !strcmp(line[0], "X-RSSyl-Last-Seen") ) {
138 ctx->last_seen = atol(line[1]);
139 debug_print("RSSyl: got last_seen timestamp %"CM_TIME_FORMAT"\n", ctx->last_seen);
143 if( !strcmp(line[0], "Message-ID") ) {
144 if (line[1][0] != '<' || line[1][strlen(line[1])-1] != '>') {
145 debug_print("RSSyl: malformed Message-ID, ignoring...\n");
147 /* Get the ID from within < and >. */
149 tmp2 = g_strndup(tmp, strlen(tmp) - 1);
150 feed_item_set_id(item, tmp2);
156 if( !strcmp(line[0], "X-RSSyl-Comments") ) {
157 feed_item_set_comments_url(item, line[1]);
158 debug_print("RSSyl: got clink '%s'\n", feed_item_get_comments_url(item));
159 started_clink = TRUE;
163 if( !strcmp(line[0], "References") ) {
164 splid = g_strsplit_set(line[1], "<>", 3);
165 if( strlen(splid[1]) != 0 )
166 feed_item_set_parent_id(item, line[1]);
170 } else if (lines[i][0] == ' ') {
172 /* continuation line */
173 if (started_author) {
174 tmp = g_strdup_printf("%s %s", feed_item_get_author(item), lines[i]+1);
175 feed_item_set_author(item, tmp);
176 debug_print("RSSyl: updated author to '%s'\n", tmp);
178 } else if (started_subject) {
179 tmp = g_strdup_printf("%s %s", feed_item_get_title(item), lines[i]+1);
180 feed_item_set_title(item, tmp);
181 debug_print("RSSyl: updated title to '%s'\n", tmp);
183 } else if (started_link) {
184 tmp = g_strdup_printf("%s%s", feed_item_get_url(item), lines[i]+1);
185 feed_item_set_url(item, tmp);
186 debug_print("RSSyl: updated link to '%s'\n", tmp);
188 } else if (started_clink) {
189 tmp = g_strdup_printf("%s%s", feed_item_get_comments_url(item), lines[i]+1);
190 feed_item_set_comments_url(item, tmp);
191 debug_print("RSSyl: updated comments_link to '%s'\n", tmp);
196 if( !strcmp(lines[i], RSSYL_TEXT_START) ) {
197 debug_print("RSSyl: Leading html tag found at line %d\n", i);
198 past_html_tag = TRUE;
201 g_warning("unexpected leading html tag found at line %d", i);
202 g_string_free(body, TRUE);
204 body = g_string_new("");
208 while( past_html_tag && !past_endhtml_tag && lines[i] ) {
209 if( !strcmp(lines[i], RSSYL_TEXT_END) ) {
210 debug_print("RSSyl: Trailing html tag found at line %d\n", i);
211 past_endhtml_tag = TRUE;
216 body = g_string_append_c(body, '\n');
217 body = g_string_append(body, lines[i]);
228 if (past_html_tag && past_endhtml_tag && body->str != NULL)
229 feed_item_set_text(item, body->str);
230 g_string_free(body, TRUE);
238 static void rssyl_flush_folder_func(gpointer data, gpointer user_data)
240 FeedItem *item = (FeedItem *)data;
241 RFeedCtx *ctx = (RFeedCtx *)item->data;
243 if( ctx != NULL && ctx->path != NULL) {
246 feed_item_free(item);
249 static void rssyl_folder_read_existing_real(RFolderItem *ritem)
251 gchar *path = NULL, *fname = NULL;
254 GError *error = NULL;
256 FeedItem *item = NULL;
259 g_return_if_fail(ritem != NULL);
261 path = folder_item_get_path(&ritem->item);
262 g_return_if_fail(path != NULL);
264 debug_print("RSSyl: reading existing items from '%s'\n", path);
266 /* Flush contents if any, so we can add new */
267 if( g_slist_length(ritem->items) > 0 ) {
268 g_slist_foreach(ritem->items, (GFunc)rssyl_flush_folder_func, NULL);
269 g_slist_free(ritem->items);
272 ritem->last_update = 0;
274 if( (dp = g_dir_open(path, 0, &error)) == NULL ) {
275 debug_print("g_dir_open on \"%s\" failed with error %d (%s)\n",
276 path, error->code, error->message);
282 while( (d = g_dir_read_name(dp)) != NULL ) {
283 if( claws_is_exiting() ) {
289 if( d[0] != '.' && (num = to_number(d)) > 0 ) {
290 fname = g_strdup_printf("%s%c%s", path, G_DIR_SEPARATOR, d);
291 if (!g_file_test(fname, G_FILE_TEST_IS_REGULAR)) {
292 debug_print("RSSyl: not a regular file: '%s', ignoring it\n", fname);
297 debug_print("RSSyl: starting to parse '%s'\n", d);
298 if( (item = rssyl_parse_folder_item_file(fname)) != NULL ) {
299 /* Find latest timestamp */
300 ctx = (RFeedCtx *)item->data;
301 if( ritem->last_update < ctx->last_seen )
302 ritem->last_update = ctx->last_seen;
303 debug_print("RSSyl: Appending '%s'\n", feed_item_get_title(item));
304 ritem->items = g_slist_prepend(ritem->items, item);
313 ritem->items = g_slist_reverse(ritem->items);
317 static void *rssyl_read_existing_thr(void *arg)
319 RParseCtx *ctx = (RParseCtx *)arg;
321 rssyl_folder_read_existing_real(ctx->ritem);
327 void rssyl_folder_read_existing(RFolderItem *ritem)
334 g_return_if_fail(ritem != NULL);
338 ctx = g_new0(RParseCtx, 1);
342 if( pthread_create(&pt, NULL, rssyl_read_existing_thr,
343 (void *)ctx) != 0 ) {
344 /* Couldn't create thread, let's continue non-threaded. */
345 rssyl_folder_read_existing_real(ritem);
347 /* Thread started, wait until it is done. */
348 debug_print("RSSyl: waiting for thread to finish\n");
349 while( !ctx->ready ) {
353 debug_print("RSSyl: thread finished\n");
354 pthread_join(pt, NULL);
359 rssyl_folder_read_existing_real(ritem);