diff options
Diffstat (limited to 'camel/providers/nntp/camel-nntp-newsrc.c')
-rw-r--r-- | camel/providers/nntp/camel-nntp-newsrc.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/camel/providers/nntp/camel-nntp-newsrc.c b/camel/providers/nntp/camel-nntp-newsrc.c new file mode 100644 index 0000000000..fc0f4c2a1a --- /dev/null +++ b/camel/providers/nntp/camel-nntp-newsrc.c @@ -0,0 +1,461 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-newsrc.c - .newsrc parsing/regurgitating code */ +/* + * + * Copyright (C) 2000 Helix Code, Inc. <toshok@helixcode.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <glib.h> +#include "camel-nntp-newsrc.h" + +typedef struct { + guint low; + guint high; +} ArticleRange; + +typedef struct { + char *name; + GArray *ranges; + gboolean subscribed; +} NewsrcGroup; + +struct CamelNNTPNewsrc { + gchar *filename; + GHashTable *groups; + GHashTable *subscribed_groups; + gboolean dirty; +} ; + +static NewsrcGroup * +camel_nntp_newsrc_group_add (CamelNNTPNewsrc *newsrc, char *group_name, gboolean subscribed) +{ + NewsrcGroup *new_group = g_malloc(sizeof(NewsrcGroup)); + + new_group->name = g_strdup(group_name); + new_group->subscribed = subscribed; + new_group->ranges = g_array_new (FALSE, FALSE, sizeof (ArticleRange)); + + g_hash_table_insert (newsrc->groups, new_group->name, new_group); + if (subscribed) + g_hash_table_insert (newsrc->subscribed_groups, new_group->name, new_group); + + newsrc->dirty = TRUE; + + return new_group; +} + +static long +camel_nntp_newsrc_group_get_highest_article_read(CamelNNTPNewsrc *newsrc, NewsrcGroup *group) +{ + if (group->ranges->len == 0) + return 0; + + return g_array_index(group->ranges, ArticleRange, group->ranges->len - 1).high; +} + +static void +camel_nntp_newsrc_group_mark_range_read(CamelNNTPNewsrc *newsrc, NewsrcGroup *group, long low, long high) +{ + int i; + + if (group->ranges->len == 1 + && g_array_index (group->ranges, ArticleRange, 0).low == 0 + && g_array_index (group->ranges, ArticleRange, 0).high == 0) { + g_array_index (group->ranges, ArticleRange, 0).low = low; + g_array_index (group->ranges, ArticleRange, 0).high = high; + + newsrc->dirty = TRUE; + } + else { + ArticleRange tmp_range; + + for (i = 0; i < group->ranges->len; i ++) { + guint range_low = g_array_index (group->ranges, ArticleRange, i).low; + guint range_high = g_array_index (group->ranges, ArticleRange, i).high; + + /* if it's already part of a range, return immediately. */ + if (low >= range_low && + low <= range_high && + high >= range_low && + high <= range_high) { + return; + } + /* if we have a new lower bound for this range, set it. */ + else if (low <= range_low + && high >= range_low + && high <= range_high) { + g_array_index (group->ranges, ArticleRange, i).low = low; + newsrc->dirty = TRUE; + return; + } + /* if we have a new upper bound for this range, set it. */ + else if (high >= range_high + && low >= range_low + && low <= range_high) { + g_array_index (group->ranges, ArticleRange, i).high = high; + newsrc->dirty = TRUE; + return; + } + /* if we would be inserting another range that + starts one index higher than an existing + one, make the upper value of the existing + range the upper value of the new one. */ + else if (low == range_high + 1) { + g_array_index (group->ranges, ArticleRange, i).high = high; + newsrc->dirty = TRUE; + return; + } + /* if we would be inserting another range that + ends one index lower than an existing one, + group the existing range by setting its low + to the new low */ + else if (high == range_low - 1) { + g_array_index (group->ranges, ArticleRange, i).low = low; + newsrc->dirty = TRUE; + return; + } + /* if the range lies entirely outside another + range, doesn't coincide with it's + endpoints, and has lower values, insert it + into the middle of the list. */ + else if (low < range_low + && high < range_low) { + tmp_range.low = low; + tmp_range.high = high; + + group->ranges = g_array_insert_val (group->ranges, i, tmp_range); + newsrc->dirty = TRUE; + + return; + } + } + + /* if we made it here, the range needs to go at the end */ + tmp_range.low = low; + tmp_range.high = high; + group->ranges = g_array_append_val (group->ranges, tmp_range); + newsrc->dirty = TRUE; + } +} + +int +camel_nntp_newsrc_get_highest_article_read (CamelNNTPNewsrc *newsrc, char *group_name) +{ + NewsrcGroup *group; + + group = g_hash_table_lookup (newsrc->groups, group_name); + + return camel_nntp_newsrc_group_get_highest_article_read (newsrc, group); +} + +void +camel_nntp_newsrc_mark_article_read (CamelNNTPNewsrc *newsrc, char *group_name, int num) +{ + camel_nntp_newsrc_mark_range_read (newsrc, group_name, num, num); +} + +void +camel_nntp_newsrc_mark_range_read(CamelNNTPNewsrc *newsrc, char *group_name, long low, long high) +{ + NewsrcGroup *group; + + /* swap them if they're in the wrong order. */ + if (low > high) { + long tmp; + + tmp = high; + high = low; + low = tmp; + } + + group = g_hash_table_lookup (newsrc->groups, group_name); + + camel_nntp_newsrc_group_mark_range_read (newsrc, group, low, high); +} + +gboolean +camel_nntp_newsrc_article_is_read (CamelNNTPNewsrc *newsrc, char *group_name, long num) +{ + int i; + NewsrcGroup *group; + + group = g_hash_table_lookup (newsrc->groups, group_name); + + for (i = 0; i < group->ranges->len; i++) { + if (num >= g_array_index (group->ranges, ArticleRange, i).low && + num <= g_array_index (group->ranges, ArticleRange, i).high) { + return TRUE; + } + } + + return FALSE; +} + +struct newsrc_ptr_array { + GPtrArray *ptr_array; + gboolean subscribed_only; +}; + +static void +get_group_foreach (char *group_name, NewsrcGroup *group, struct newsrc_ptr_array *npa) +{ + if (group->subscribed || !npa->subscribed_only) { + g_ptr_array_add (npa->ptr_array, group_name); + } +} + +GPtrArray * +camel_nntp_newsrc_get_subscribed_group_names (CamelNNTPNewsrc *newsrc) +{ + struct newsrc_ptr_array npa; + + npa.ptr_array = g_ptr_array_new(); + npa.subscribed_only = TRUE; + + g_hash_table_foreach (newsrc->subscribed_groups, + (GHFunc)get_group_foreach, &npa); + + return npa.ptr_array; +} + +GPtrArray * +camel_nntp_newsrc_get_all_group_names (CamelNNTPNewsrc *newsrc) +{ + struct newsrc_ptr_array npa; + + npa.ptr_array = g_ptr_array_new(); + npa.subscribed_only = FALSE; + + g_hash_table_foreach (newsrc->groups, + (GHFunc)get_group_foreach, &npa); + + return npa.ptr_array; +} + +void +camel_nntp_newsrc_free_group_names (CamelNNTPNewsrc *newsrc, GPtrArray *group_names) +{ + g_ptr_array_free (group_names, TRUE); +} + +struct newsrc_fp { + CamelNNTPNewsrc *newsrc; + FILE *fp; +}; + +static void +camel_nntp_newsrc_write_group_line(gpointer key, NewsrcGroup *group, struct newsrc_fp *newsrc_fp) +{ + CamelNNTPNewsrc *newsrc; + FILE *fp; + int i; + int line_length = 0; + + fp = newsrc_fp->fp; + newsrc = newsrc_fp->newsrc; + + fprintf (fp, "%s%c", group->name, group->subscribed ? ':' : '!'); + + line_length += strlen(group->name) + 1; + + if (group->ranges->len == 1 + && g_array_index (group->ranges, ArticleRange, 0).low == 0 + && g_array_index (group->ranges, ArticleRange, 0).high == 0) { + fprintf (fp, "\n"); + + return; /* special case since our parsing code will insert this + bogus range if there were no read articles. The code + to add a range is smart enough to remove this one if we + ever mark an article read, but we still need to deal with + it if that code doesn't get hit. */ + } + + fprintf (fp, " "); + line_length += 1; + + for (i = 0; i < group->ranges->len; i ++) { + char range_buffer[100]; + guint low = g_array_index (group->ranges, ArticleRange, i).low; + guint high = g_array_index (group->ranges, ArticleRange, i).high; + + if (low == high) + sprintf(range_buffer, "%d", low); + else if (low == high - 1) + sprintf(range_buffer, "%d,%d", low, high); + else + sprintf(range_buffer, "%d-%d", low, high); + + if (i != group->ranges->len - 1) + strcat(range_buffer, ","); + + /* this constant (991) gives the same line breaking as faried's .newsrc file */ + if (line_length + strlen(range_buffer) > 991 /*XXX*/) { + char range_buffer2[101]; + int num_to_print = 991 - line_length; + + strcpy(range_buffer2, range_buffer); + range_buffer2[num_to_print] = '!'; + range_buffer2[num_to_print+1] = '\n'; + range_buffer2[num_to_print+2] = '\0'; + + fprintf (fp, range_buffer2); + + fprintf (fp, range_buffer + num_to_print); + + line_length = strlen(range_buffer) - num_to_print; + } + else { + fprintf (fp, range_buffer); + line_length += strlen(range_buffer); + } + } + + fprintf (fp, "\n"); +} + +void +camel_nntp_newsrc_write_to_file(CamelNNTPNewsrc *newsrc, FILE *fp) +{ + struct newsrc_fp newsrc_fp; + + newsrc_fp.newsrc = newsrc; + newsrc_fp.fp = fp; + + g_hash_table_foreach (newsrc->groups, + (GHFunc)camel_nntp_newsrc_write_group_line, + &newsrc_fp); +} + +void +camel_nntp_newsrc_write(CamelNNTPNewsrc *newsrc) +{ + FILE *fp; + + if (!newsrc->dirty) + return; + + if ((fp = fopen(newsrc->filename, "w")) == NULL) { + g_warning ("Couldn't open newsrc file '%s'.\n", newsrc->filename); + return; + } + + camel_nntp_newsrc_write_to_file(newsrc, fp); + + fclose(fp); +} + +static void +camel_nntp_newsrc_parse_line(CamelNNTPNewsrc *newsrc, char *line) +{ + char *p, sep, *comma, *dash; + gboolean is_subscribed; + NewsrcGroup *group; + + p = strchr(line, ':'); + + if (p) { + is_subscribed = TRUE; + } + else { + p = strchr(line, '!'); + if (p) + is_subscribed = FALSE; + else + return; /* bogus line. */ + } + + sep = *p; + *p = '\0'; + + group = camel_nntp_newsrc_group_add (newsrc, line, is_subscribed); + + *p = sep; + + p++; + + do { + guint high, low; + + comma = strchr(p, ','); + + if (comma) + *comma = '\0'; + + dash = strchr(p, '-'); + + if (!dash) { /* there wasn't a dash. must be just one number */ + high = low = atol(p); + } + else { /* there was a dash. */ + *dash = '\0'; + low = atol(p); + *dash = '-'; + p = dash + 1; + high = atol(p); + } + + camel_nntp_newsrc_group_mark_range_read (newsrc, group, low, high); + + if (comma) { + *comma = ','; + p = comma + 1; + } + + } while(comma); +} + +#define MAX_LINE_LENGTH 1500 +#define BUFFER_LENGTH (20 * MAX_LINE_LENGTH) + +CamelNNTPNewsrc * +camel_nntp_newsrc_read_for_server (const char *server) +{ + FILE *fp; + char buf[BUFFER_LENGTH]; + CamelNNTPNewsrc *newsrc = g_new0(CamelNNTPNewsrc, 1); + + newsrc->filename = g_strdup_printf ("%s/.newsrc-%s", g_get_home_dir(), server); + newsrc->groups = g_hash_table_new (g_str_hash, g_str_equal); + newsrc->subscribed_groups = g_hash_table_new (g_str_hash, g_str_equal); + + if ((fp = fopen(newsrc->filename, "r")) == NULL) { + g_free (newsrc->filename); + g_free (newsrc); + return NULL; + } + + while (fgets(buf, MAX_LINE_LENGTH, fp) != NULL) { + /* we silently ignore (and lose!) lines longer than 20 * 1500 chars. + Too bad for them. */ + while(strlen(buf) < sizeof(buf) + && buf[strlen(buf) - 2] == '!') { + fgets(&buf[strlen(buf) - 2], MAX_LINE_LENGTH, fp); + } + + camel_nntp_newsrc_parse_line(newsrc, buf); + } + + fclose(fp); + + return newsrc; +} |