From ab0e676fd9b1d4946911704a3446eb9b54e5335b Mon Sep 17 00:00:00 2001 From: Chris Toshok Date: Fri, 14 Apr 2000 19:05:33 +0000 Subject: initial import of nntp store. svn path=/trunk/; revision=2430 --- camel/providers/nntp/.cvsignore | 7 + camel/providers/nntp/Makefile.am | 31 ++ camel/providers/nntp/camel-nntp-folder.c | 687 +++++++++++++++++++++++++++++ camel/providers/nntp/camel-nntp-folder.h | 73 +++ camel/providers/nntp/camel-nntp-provider.c | 54 +++ camel/providers/nntp/camel-nntp-store.c | 466 +++++++++++++++++++ camel/providers/nntp/camel-nntp-store.h | 85 ++++ camel/providers/nntp/camel-nntp-summary.c | 381 ++++++++++++++++ camel/providers/nntp/camel-nntp-summary.h | 81 ++++ camel/providers/nntp/camel-nntp-utils.c | 107 +++++ camel/providers/nntp/camel-nntp-utils.h | 42 ++ 11 files changed, 2014 insertions(+) create mode 100644 camel/providers/nntp/.cvsignore create mode 100644 camel/providers/nntp/Makefile.am create mode 100644 camel/providers/nntp/camel-nntp-folder.c create mode 100644 camel/providers/nntp/camel-nntp-folder.h create mode 100644 camel/providers/nntp/camel-nntp-provider.c create mode 100644 camel/providers/nntp/camel-nntp-store.c create mode 100644 camel/providers/nntp/camel-nntp-store.h create mode 100644 camel/providers/nntp/camel-nntp-summary.c create mode 100644 camel/providers/nntp/camel-nntp-summary.h create mode 100644 camel/providers/nntp/camel-nntp-utils.c create mode 100644 camel/providers/nntp/camel-nntp-utils.h (limited to 'camel/providers/nntp') diff --git a/camel/providers/nntp/.cvsignore b/camel/providers/nntp/.cvsignore new file mode 100644 index 0000000000..fd6b811c68 --- /dev/null +++ b/camel/providers/nntp/.cvsignore @@ -0,0 +1,7 @@ +.deps +Makefile +Makefile.in +.libs +.deps +*.lo +*.la diff --git a/camel/providers/nntp/Makefile.am b/camel/providers/nntp/Makefile.am new file mode 100644 index 0000000000..2aabff29ef --- /dev/null +++ b/camel/providers/nntp/Makefile.am @@ -0,0 +1,31 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = + +libcamelnntpincludedir = $(includedir)/camel + +providerdir = $(pkglibdir)/camel-providers/$(VERSION) + +provider_LTLIBRARIES = libcamelnntp.la + +INCLUDES = -I.. -I$(srcdir)/.. -I$(includedir) \ + -I$(top_srcdir)/intl \ + $(GTK_INCLUDEDIR) -I$(top_srcdir)/camel \ + -I$(srcdir)/../mbox + +libcamelnntp_la_SOURCES = \ + camel-nntp-folder.c \ + camel-nntp-provider.c \ + camel-nntp-store.c \ + camel-nntp-summary.c \ + camel-nntp-utils.c + +libcamelnntpinclude_HEADERS = \ + camel-nntp-folder.h \ + camel-nntp-store.h \ + camel-nntp-summary.h + + +libcamelnntp_la_LDFLAGS = -version-info 0:0:0 -rpath $(libdir) + +EXTRA_DIST = diff --git a/camel/providers/nntp/camel-nntp-folder.c b/camel/providers/nntp/camel-nntp-folder.c new file mode 100644 index 0000000000..17328bfd38 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-folder.c @@ -0,0 +1,687 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-folder.c : Abstract class for an email folder */ + +/* + * Author : Chris Toshok + * + * Copyright (C) 2000 Helix Code . + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camel-nntp-store.h" +#include "camel-nntp-folder.h" +#include "camel-nntp-store.h" +#include "camel-nntp-utils.h" +#include "camel-nntp-summary.h" + +#include "string-utils.h" +#include "camel-log.h" +#include "camel-stream-mem.h" +#include "camel-stream-buffer.h" +#include "gmime-utils.h" +#include "camel-data-wrapper.h" +#include "camel-mime-message.h" + +#include "camel-exception.h" + +static CamelFolderClass *parent_class=NULL; + +/* Returns the class for a CamelNNTPFolder */ +#define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS (GTK_OBJECT(so)->klass) +#define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT(so)->klass) +#define CNNTPS_CLASS(so) CAMEL_STORE_CLASS (GTK_OBJECT(so)->klass) + + +static void _init (CamelFolder *folder, CamelStore *parent_store, + CamelFolder *parent_folder, const gchar *name, + gchar separator, CamelException *ex); +static void _set_name(CamelFolder *folder, const gchar *name, CamelException *ex); + + +static void _open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex); +static void _close (CamelFolder *folder, gboolean expunge, CamelException *ex); +static gboolean _exists (CamelFolder *folder, CamelException *ex); +static gboolean _create(CamelFolder *folder, CamelException *ex); +static gboolean _delete (CamelFolder *folder, gboolean recurse, CamelException *ex); +static gboolean _delete_messages (CamelFolder *folder, CamelException *ex); +static GList *_list_subfolders (CamelFolder *folder, CamelException *ex); +static CamelMimeMessage *_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex); +static gint _get_message_count (CamelFolder *folder, CamelException *ex); +static void _append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex); +static GList *_get_uid_list (CamelFolder *folder, CamelException *ex); +static CamelMimeMessage *_get_message_by_uid (CamelFolder *folder, const gchar *uid, CamelException *ex); +#if 0 +static void _expunge (CamelFolder *folder, CamelException *ex); +static void _copy_message_to (CamelFolder *folder, CamelMimeMessage *message, CamelFolder *dest_folder, CamelException *ex); +static const gchar *_get_message_uid (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex); +#endif + +static void _finalize (GtkObject *object); + +static void +camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class) +{ + CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_nntp_folder_class); + GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (camel_folder_class); + + parent_class = gtk_type_class (camel_folder_get_type ()); + + /* virtual method definition */ + + /* virtual method overload */ + camel_folder_class->init = _init; + camel_folder_class->set_name = _set_name; + camel_folder_class->open = _open; + camel_folder_class->close = _close; + camel_folder_class->exists = _exists; + camel_folder_class->create = _create; + camel_folder_class->delete = _delete; + camel_folder_class->delete_messages = _delete_messages; + camel_folder_class->list_subfolders = _list_subfolders; + camel_folder_class->get_message_by_number = _get_message_by_number; + camel_folder_class->get_message_count = _get_message_count; +#if 0 + camel_folder_class->append_message = _append_message; + camel_folder_class->get_uid_list = _get_uid_list; + camel_folder_class->expunge = _expunge; + camel_folder_class->copy_message_to = _copy_message_to; + camel_folder_class->get_message_uid = _get_message_uid; + camel_folder_class->get_message_by_uid = _get_message_by_uid; +#endif + + gtk_object_class->finalize = _finalize; + +} + + + +static void +_finalize (GtkObject *object) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (object); + + CAMEL_LOG_FULL_DEBUG ("Entering CamelFolder::finalize\n"); + + + g_free (nntp_folder->summary_file_path); + + GTK_OBJECT_CLASS (parent_class)->finalize (object); + CAMEL_LOG_FULL_DEBUG ("Leaving CamelFolder::finalize\n"); +} + + + + + +GtkType +camel_nntp_folder_get_type (void) +{ + static GtkType camel_nntp_folder_type = 0; + + if (!camel_nntp_folder_type) { + GtkTypeInfo camel_nntp_folder_info = + { + "CamelNNTPFolder", + sizeof (CamelNNTPFolder), + sizeof (CamelNNTPFolderClass), + (GtkClassInitFunc) camel_nntp_folder_class_init, + (GtkObjectInitFunc) NULL, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + camel_nntp_folder_type = gtk_type_unique (CAMEL_FOLDER_TYPE, &camel_nntp_folder_info); + } + + return camel_nntp_folder_type; +} + + + + + + +static void +_init (CamelFolder *folder, CamelStore *parent_store, + CamelFolder *parent_folder, const gchar *name, gchar separator, + CamelException *ex) +{ + + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::init_with_store\n"); + + /* call parent method */ + parent_class->init (folder, parent_store, parent_folder, + name, separator, ex); + if (camel_exception_get_id (ex)) return; + + /* we assume that the parent init + method checks for the existance of @folder */ + + if (!strcmp(name, "/")) + { + folder->has_summary_capability = FALSE; + folder->can_hold_messages = FALSE; + folder->can_hold_folders = TRUE; + } + else + { + folder->has_summary_capability = TRUE; + folder->can_hold_messages = TRUE; + folder->can_hold_folders = TRUE; + } + + folder->has_uid_capability = FALSE; + folder->has_search_capability = FALSE; + folder->summary = NULL; + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::init_with_store\n"); +} + + + +/* internal method used to : + - test for the existence of a summary file + - test the sync between the summary and the newsgroup + - load the summary or create it if necessary +*/ +static void +_check_get_or_maybe_generate_summary_file (CamelNNTPFolder *nntp_folder, + CamelException *ex) +{ + CamelFolder *folder = CAMEL_FOLDER (nntp_folder); + CamelNNTPSummary *summ = NULL; + GArray *message_info_array; + + folder->summary = NULL; + + /* Test for the existence and up-to-dateness of the summary file. */ + if (access (nntp_folder->summary_file_path, F_OK) == 0) { + summ = camel_nntp_summary_load (CAMEL_FOLDER(nntp_folder)->name, + nntp_folder->summary_file_path, ex); + if (summ) { + folder->summary = CAMEL_FOLDER_SUMMARY (summ); + } else { + /* Bad summary file */ + if (camel_exception_get_id (ex) != + CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID) + return; + camel_exception_clear (ex); + } + } + + /* In the case where the summary does not exist (or was the + * wrong version), or is not in sync with the nntp file, + * regenerate it. + */ + if (folder->summary == NULL) { + message_info_array = + camel_nntp_get_headers (CAMEL_FOLDER( folder )->parent_store, nntp_folder, ex); + if (camel_exception_get_id (ex)) + return; + + summ = CAMEL_NNTP_SUMMARY (gtk_object_new (camel_nntp_summary_get_type (), NULL)); + summ->message_info = message_info_array; + summ->nb_message = summ->message_info->len; + + folder->summary = CAMEL_FOLDER_SUMMARY (summ); + } +} + + +static void +_open (CamelFolder *folder, CamelFolderOpenMode mode, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); + + /* call parent class */ + parent_class->open (folder, mode, ex); + if (camel_exception_get_id(ex)) + return; + +#if 0 + /* get (or create) uid list */ + if (!(nntp_load_uid_list (nntp_folder) > 0)) + nntp_generate_uid_list (nntp_folder); +#endif + + _check_get_or_maybe_generate_summary_file (nntp_folder, ex); +} + + +static void +_close (CamelFolder *folder, gboolean expunge, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); + CamelNNTPSummary *nntp_summary = CAMEL_NNTP_SUMMARY (folder->summary); + + /* call parent implementation */ + parent_class->close (folder, expunge, ex); + + camel_nntp_summary_save (nntp_summary, + nntp_folder->summary_file_path, ex); +} + + + + +static void +_set_name (CamelFolder *folder, const gchar *name, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); + const gchar *root_dir_path; + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::set_name\n"); + + /* call default implementation */ + parent_class->set_name (folder, name, ex); + if (camel_exception_get_id (ex)) return; + + root_dir_path = camel_nntp_store_get_toplevel_dir (CAMEL_NNTP_STORE(folder->parent_store)); + + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::set_name full_name is %s\n", folder->full_name); + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::set_name root_dir_path is %s\n", root_dir_path); + + nntp_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary", root_dir_path, folder->name); + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::set_name\n"); +} + + + + + + +static gboolean +_exists (CamelFolder *folder, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder; + struct stat stat_buf; + gint stat_error; + gboolean exists; + + g_assert(folder != NULL); + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::exists\n"); + + nntp_folder = CAMEL_NNTP_FOLDER (folder); + + /* check if the nntp summary path is determined */ + if (!nntp_folder->summary_file_path) { + camel_exception_set (ex, + CAMEL_EXCEPTION_FOLDER_INVALID, + "undetermined folder summary path. Maybe use set_name ?"); + return FALSE; + } + + /* check if the nntp file exists */ + stat_error = stat (nntp_folder->summary_file_path, &stat_buf); + if (stat_error == -1) + return FALSE; + + exists = S_ISREG (stat_buf.st_mode); + /* we should check the rights here */ + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::exists\n"); + return exists; +} + + + + + + + + +static gboolean +_create (CamelFolder *folder, CamelException *ex) +{ +#if 0 + CamelNNTPSummary *summary; + g_assert(folder != NULL); + + /* call default implementation */ + parent_class->create (folder, ex); + + /* create the summary object */ + summary = CAMEL_NNTP_SUMMARY (gtk_object_new (camel_nntp_summary_get_type (), NULL)); + summary->nb_message = 0; + summary->next_uid = 1; + summary->nntp_file_size = 0; + summary->message_info = g_array_new (FALSE, FALSE, sizeof (CamelNNTPSummaryInformation)); +#endif + + return TRUE; +} + + + +static gboolean +_delete (CamelFolder *folder, gboolean recurse, CamelException *ex) +{ + gboolean folder_already_exists; + + g_assert(folder != NULL); + + /* check if the folder object exists */ + + /* in the case where the folder does not exist, + return immediatly */ + folder_already_exists = camel_folder_exists (folder, ex); + if (camel_exception_get_id (ex)) + return FALSE; + + if (!folder_already_exists) + return TRUE; + + + /* call default implementation. + It should delete the messages in the folder + and recurse the operation to subfolders */ + parent_class->delete (folder, recurse, ex); + + return TRUE; +} + + + + +gboolean +_delete_messages (CamelFolder *folder, CamelException *ex) +{ + + gboolean folder_already_exists; + + g_assert(folder!=NULL); + + /* in the case where the folder does not exist, + return immediatly */ + folder_already_exists = camel_folder_exists (folder, ex); + if (camel_exception_get_id (ex)) return FALSE; + + if (!folder_already_exists) return TRUE; + + return TRUE; +} + + +static GList * +_list_subfolders (CamelFolder *folder, CamelException *ex) +{ + /* newsgroups don't have subfolders */ + return NULL; +} + + + + +static gint +_get_message_count (CamelFolder *folder, CamelException *ex) +{ + gint message_count = 0; + + g_assert (folder); +#if 0 + g_assert (folder->summary); + + message_count = CAMEL_NNTP_SUMMARY (folder->summary)->nb_message; +#endif + + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::get_message_count found %d messages\n", message_count); + return message_count; +} + + +#if 0 +static void +_append_message (CamelFolder *folder, CamelMimeMessage *message, CamelException *ex) +{ + CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder); +#if 0 + CamelNNTPSummary *summary = CAMEL_NNTP_SUMMARY (folder->summary); +#endif + CamelStream *output_stream; + guint32 tmp_file_size; + guint32 next_uid; + gint tmp_file_fd; + GArray *message_info_array; +#if 0 + GArray *nntp_summary_info; +#endif + gchar *tmp_message_filename; + gint fd1, fd2; + int i; + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::append_message\n"); + + /* write the message itself */ + output_stream = camel_stream_fs_new_with_name (tmp_message_filename, + CAMEL_STREAM_FS_WRITE); + if (output_stream != NULL) { + camel_stream_write_string (output_stream, "From - \n"); + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (message), output_stream); + } + camel_stream_close (output_stream); + gtk_object_unref (GTK_OBJECT (output_stream)); + + /* at this point we have saved the message to a + temporary file, now, we have to add the x-evolution + field and also update the main summary */ + + /* + First : parse the nntp file, but only from the + position where the message has been added, + wich happens to be the last postion in the + nntp file before we added the message. + This position is still stored in the summary + for the moment + */ + next_uid = summary->next_uid; + tmp_file_fd = open (tmp_message_filename, O_RDONLY); + message_info_array = + camel_nntp_parse_file (tmp_file_fd, "From - ", 0, + &tmp_file_size, &next_uid, TRUE, + NULL, 0, ex); + + close (tmp_file_fd); + + /* get the value of the last available UID + as saved in the summary file, again */ + next_uid = summary->next_uid; + + /* make sure all our of message info's have 0 uid - ignore any + set elsewhere */ + for (i=0;ilen;i++) { + g_array_index(message_info_array, CamelNNTPParserMessageInfo, i).uid = 0; + } + + /* + OK, this is not very efficient, we should not use the same + method as for parsing an entire mail file, + but I have no time to write a simpler parser + */ +#if 0 + next_uid = camel_nntp_write_xev (nntp_folder, tmp_message_filename, + message_info_array, &tmp_file_size, next_uid, ex); + +#endif + if (camel_exception_get_id (ex)) { + /* ** FIXME : free the preparsed information */ + return; + } + +#if 0 + nntp_summary_info = + parsed_information_to_nntp_summary (message_info_array); +#endif + + /* store the number of messages as well as the summary array */ + summary->nb_message += 1; + summary->next_uid = next_uid; + + ((CamelNNTPSummaryInformation *)(nntp_summary_info->data))->position += + summary->nntp_file_size; + summary->nntp_file_size += tmp_file_size; + + camel_nntp_summary_append_entries (summary, nntp_summary_info); + g_array_free (nntp_summary_info, TRUE); + + + /* append the temporary file message to the nntp file */ + fd1 = open (tmp_message_filename, O_RDONLY); + fd2 = open (nntp_folder->folder_file_path, + O_WRONLY | O_CREAT | O_APPEND, + 0600); + + if (fd2 == -1) { + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, + "could not open the nntp folder file for appending the message\n" + "\t%s\n" + "Full error is : %s\n", + nntp_folder->folder_file_path, + strerror (errno)); + return; + } + + camel_nntp_copy_file_chunk (fd1, + fd2, + tmp_file_size, + ex); + close (fd1); + close (fd2); + + /* remove the temporary file */ + unlink (tmp_message_filename); + + g_free (tmp_message_filename); + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::append_message\n"); +} +#endif + + + +#if 0 +static GList * +_get_uid_list (CamelFolder *folder, CamelException *ex) +{ + GArray *message_info_array; + CamelNNTPSummaryInformation *message_info; + GList *uid_list = NULL; + int i; + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::get_uid_list\n"); + + message_info_array = + CAMEL_NNTP_SUMMARY (folder->summary)->message_info; + + for (i=0; ilen; i++) { + message_info = (CamelNNTPSummaryInformation *)(message_info_array->data) + i; + uid_list = g_list_prepend (uid_list, g_strdup_printf ("%u", message_info->uid)); + } + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::get_uid_list\n"); + + return uid_list; +} +#endif + + + +static CamelMimeMessage * +_get_message_by_number (CamelFolder *folder, gint number, CamelException *ex) +{ + + CamelStream *nntp_istream; + CamelStream *message_stream; + CamelMimeMessage *message = NULL; + CamelStore *parent_store; + char *buf; + int buf_len; + int buf_alloc; + int status; + gboolean done; + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPFolder::get_message_by_uid\n"); + + /* get the parent store */ + parent_store = camel_folder_get_parent_store (folder, ex); + if (camel_exception_get_id (ex)) { + return NULL; + } + + nntp_istream = CAMEL_NNTP_STORE (parent_store)->istream; + + status = camel_nntp_command (CAMEL_NNTP_STORE( parent_store ), NULL, "ARTICLE %d", number); + + /* if the uid was not found, raise an exception and return */ + if (status != CAMEL_NNTP_OK) { + camel_exception_setv (ex, + CAMEL_EXCEPTION_FOLDER_INVALID_UID, + "message %d not found in the group", + number); + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::get_message_by_number\n"); + return NULL; + } + + /* XXX ick ick ick. read the entire message into a buffer and + then create a stream_mem for it. */ + buf_alloc = 2048; + buf_len = 0; + buf = malloc(buf_alloc); + done = FALSE; + + while (!done) { + char *line = camel_stream_buffer_read_line ( CAMEL_STREAM_BUFFER ( nntp_istream )); + int line_length = strlen ( line ); + + if (*line == '.') { + done = TRUE; + g_free (line); + } + else { + if (buf_len + line_length > buf_alloc) { + buf_alloc *= 2; + buf = realloc (buf, buf_alloc); + } + } + } + + /* create a stream bound to the message */ + message_stream = camel_stream_mem_new_with_buffer(buf, + buf_len, + CAMEL_STREAM_MEM_READ); + + message = camel_mime_message_new_with_session (camel_service_get_session (CAMEL_SERVICE (parent_store))); + camel_data_wrapper_set_input_stream (CAMEL_DATA_WRAPPER (message), message_stream); + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPFolder::get_message_by_number\n"); + return message; +} diff --git a/camel/providers/nntp/camel-nntp-folder.h b/camel/providers/nntp/camel-nntp-folder.h new file mode 100644 index 0000000000..7029023a62 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-folder.h @@ -0,0 +1,73 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-folder.h : NNTP group (folder) support. */ + +/* + * + * Author : Chris Toshok + * + * Copyright (C) 2000 Helix Code . + * + * 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 + */ + + +#ifndef CAMEL_NNTP_FOLDER_H +#define CAMEL_NNTP_FOLDER_H 1 + + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include +#include "camel-folder.h" +#include "camel-nntp-summary.h" + +/* #include "camel-store.h" */ + +#define CAMEL_NNTP_FOLDER_TYPE (camel_nntp_folder_get_type ()) +#define CAMEL_NNTP_FOLDER(obj) (GTK_CHECK_CAST((obj), CAMEL_NNTP_FOLDER_TYPE, CamelNNTPFolder)) +#define CAMEL_NNTP_FOLDER_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_NNTP_FOLDER_TYPE, CamelNNTPFolderClass)) +#define IS_CAMEL_NNTP_FOLDER(o) (GTK_CHECK_TYPE((o), CAMEL_NNTP_FOLDER_TYPE)) + + +typedef struct { + CamelFolder parent_object; + + gchar *summary_file_path; /* contains the messages summary */ +} CamelNNTPFolder; + + + +typedef struct { + CamelFolderClass parent_class; + + /* Virtual methods */ + +} CamelNNTPFolderClass; + + +/* public methods */ + +/* Standard Gtk function */ +GtkType camel_nntp_folder_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_NNTP_FOLDER_H */ diff --git a/camel/providers/nntp/camel-nntp-provider.c b/camel/providers/nntp/camel-nntp-provider.c new file mode 100644 index 0000000000..4159026d4f --- /dev/null +++ b/camel/providers/nntp/camel-nntp-provider.c @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-provider.c: nntp provider registration code */ + +/* + * Authors : + * Dan Winship + * + * Copyright (C) 2000 Helix Code, Inc. (www.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 "camel-nntp-store.h" +#include "camel-provider.h" +#include "camel-log.h" + + +static CamelProvider _nntp_provider = { + (GtkType) 0, + PROVIDER_STORE, + PROVIDER_REMOTE, + "news", + "Camel default NNTP provider", + "This is a provider that reads from a NNTP server.", + (GModule *) NULL +}; + +CamelProvider * +camel_provider_module_init (void); + + +CamelProvider * +camel_provider_module_init (void) +{ + _nntp_provider.object_type = camel_nntp_store_get_type(); + return &_nntp_provider; +} + + + diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c new file mode 100644 index 0000000000..0c300be197 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-store.c @@ -0,0 +1,466 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-store.c : class for an nntp store */ + +/* + * + * Copyright (C) 2000 Helix Code, Inc. + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "camel-log.h" + +#include "libgnome/libgnome.h" + +#include "camel-nntp-store.h" +#include "camel-nntp-folder.h" +#include "camel-stream-buffer.h" +#include "camel-stream-fs.h" +#include "camel-exception.h" +#include "camel-url.h" +#include "string-utils.h" + +#define NNTP_PORT 119 + +static CamelServiceClass *service_class = NULL; + +/* Returns the class for a CamelNNTPStore */ +#define CNNTPS_CLASS(so) CAMEL_NNTP_STORE_CLASS (GTK_OBJECT(so)->klass) +#define CF_CLASS(so) CAMEL_FOLDER_CLASS (GTK_OBJECT(so)->klass) +#define CNNTPF_CLASS(so) CAMEL_NNTP_FOLDER_CLASS (GTK_OBJECT(so)->klass) + +static CamelFolder *_get_folder (CamelStore *store, const gchar *folder_name, CamelException *ex); +static char *_get_folder_name (CamelStore *store, const char *folder_name, + CamelException *ex); + +static gboolean nntp_connect (CamelService *service, CamelException *ex); +static gboolean nntp_disconnect (CamelService *service, CamelException *ex); + +static void +camel_nntp_store_class_init (CamelNNTPStoreClass *camel_nntp_store_class) +{ + CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS (camel_nntp_store_class); + + service_class = gtk_type_class (camel_service_get_type ()); + + /* virtual method overload */ + camel_store_class->get_folder = _get_folder; + camel_store_class->get_folder_name = _get_folder_name; +} + + + +static void +camel_nntp_store_init (gpointer object, gpointer klass) +{ + CamelService *service = CAMEL_SERVICE (object); + + service->url_flags = CAMEL_SERVICE_URL_NEED_HOST; +} + +GtkType +camel_nntp_store_get_type (void) +{ + static GtkType camel_nntp_store_type = 0; + + if (!camel_nntp_store_type) { + GtkTypeInfo camel_nntp_store_info = + { + "CamelNNTPStore", + sizeof (CamelNNTPStore), + sizeof (CamelNNTPStoreClass), + (GtkClassInitFunc) camel_nntp_store_class_init, + (GtkObjectInitFunc) camel_nntp_store_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + camel_nntp_store_type = gtk_type_unique (CAMEL_STORE_TYPE, &camel_nntp_store_info); + } + + return camel_nntp_store_type; +} + +static CamelFolder * +_get_folder (CamelStore *store, const gchar *folder_name, CamelException *ex) +{ + CamelNNTPFolder *new_nntp_folder; + CamelFolder *new_folder; + + /* check if folder has already been created */ + /* call the standard routine for that when */ + /* it is done ... */ + + CAMEL_LOG_FULL_DEBUG ("Entering CamelNNTPStore::get_folder\n"); + new_nntp_folder = gtk_type_new (CAMEL_NNTP_FOLDER_TYPE); + new_folder = CAMEL_FOLDER (new_nntp_folder); + + /* XXX We shouldn't be passing NULL here, but it's equivalent to + * what was there before, and there's no + * CamelNNTPFolder::get_subfolder yet anyway... + */ + CF_CLASS (new_folder)->init (new_folder, store, NULL, + folder_name, '/', ex); + + CAMEL_LOG_FULL_DEBUG ("Leaving CamelNNTPStore::get_folder\n"); + + return new_folder; +} + + +static char * +_get_folder_name (CamelStore *store, const char *folder_name, + CamelException *ex) +{ + return g_strdup (folder_name); +} + +/** + * camel_nntp_store_open: Connect to the server if we are currently + * disconnected. + * @store: the store + * @ex: a CamelException + * + **/ +void +camel_nntp_store_open (CamelNNTPStore *store, CamelException *ex) +{ + CamelService *service = CAMEL_SERVICE (store); + + if (!camel_service_is_connected (service)) + nntp_connect (service, ex); +} + +/** + * camel_nntp_store_close: Close the connection to the server + * @store: the store + * @ex: a CamelException + * + **/ +void +camel_nntp_store_close (CamelNNTPStore *store, gboolean expunge, + CamelException *ex) +{ + camel_nntp_command (store, NULL, "QUIT"); + + nntp_disconnect (CAMEL_SERVICE (store), ex); +} + +static gboolean +nntp_connect (CamelService *service, CamelException *ex) +{ + struct hostent *h; + struct sockaddr_in sin; + int fd, status, apoplen; + char *buf, *apoptime, *pass; + CamelNNTPStore *store = CAMEL_NNTP_STORE (service); + + if (!service_class->connect (service, ex)) + return FALSE; + + h = camel_service_gethost (service, ex); + if (!h) + return FALSE; + + sin.sin_family = h->h_addrtype; + sin.sin_port = htons (service->url->port ? service->url->port : NNTP_PORT); + memcpy (&sin.sin_addr, h->h_addr, sizeof (sin.sin_addr)); + + fd = socket (h->h_addrtype, SOCK_STREAM, 0); + if (fd == -1 || + connect (fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE, + "Could not connect to %s (port %s): %s", + service->url->host, service->url->port, + strerror(errno)); + if (fd > -1) + close (fd); + return FALSE; + } + + store->ostream = camel_stream_fs_new_with_fd (fd); + store->istream = camel_stream_buffer_new (store->ostream, + CAMEL_STREAM_BUFFER_READ); + + /* Read the greeting */ + buf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream)); + if (!buf) { + return -1; + } + + g_free (buf); + + /* get a list of extensions that the server supports */ + + return TRUE; +} + +static gboolean +nntp_disconnect (CamelService *service, CamelException *ex) +{ + CamelNNTPStore *store = CAMEL_NNTP_STORE (service); + + if (!service->connected) + return TRUE; + + if (!service_class->disconnect (service, ex)) + return FALSE; + + /* Closing the buffered write stream will close the + * unbuffered read stream wrapped inside it as well. + */ + camel_stream_close (store->ostream); + gtk_object_unref (GTK_OBJECT (store->ostream)); + store->ostream = NULL; + store->istream = NULL; + return TRUE; +} + +/** + * camel_nntp_command: Send a command to a NNTP server. + * @store: the NNTP store + * @ret: a pointer to return the full server response in + * @fmt: a printf-style format string, followed by arguments + * + * This command sends the command specified by @fmt and the following + * arguments to the connected NNTP store specified by @store. It then + * reads the server's response and parses out the status code. If + * the caller passed a non-NULL pointer for @ret, camel_nntp_command + * will set it to point to an buffer containing the rest of the + * response from the NNTP server. (If @ret was passed but there was + * no extended response, @ret will be set to NULL.) The caller must + * free this buffer when it is done with it. + * + * Return value: one of CAMEL_NNTP_OK (command executed successfully), + * CAMEL_NNTP_ERR (command encounted an error), or CAMEL_NNTP_FAIL + * (a protocol-level error occurred, and Camel is uncertain of the + * result of the command.) + **/ +int +camel_nntp_command (CamelNNTPStore *store, char **ret, char *fmt, ...) +{ + char *cmdbuf, *respbuf; + va_list ap; + int status; + int resp_code; + + va_start (ap, fmt); + cmdbuf = g_strdup_vprintf (fmt, ap); + va_end (ap); + + /* Send the command */ + camel_stream_write (store->ostream, cmdbuf, strlen (cmdbuf)); + g_free (cmdbuf); + camel_stream_write (store->ostream, "\r\n", 2); + + /* Read the response */ + respbuf = camel_stream_buffer_read_line (CAMEL_STREAM_BUFFER (store->istream)); + resp_code = atoi (respbuf); + + if (resp_code < 400) + status = CAMEL_NNTP_OK; + else if (resp_code < 500) + status = CAMEL_NNTP_ERR; + else + status = CAMEL_NNTP_FAIL; + + if (ret) { + *ret = strchr (respbuf, ' '); + if (*ret) + *ret = g_strdup (*ret + 1); + } + g_free (respbuf); + + return status; +} + +/** + * camel_nntp_command_get_additional_data: get "additional data" from + * a NNTP command. + * @store: the NNTP store + * + * This command gets the additional data returned by "multi-line" POP + * commands, such as LIST, RETR, TOP, and UIDL. This command _must_ + * be called after a successful (CAMEL_NNTP_OK) call to + * camel_nntp_command for a command that has a multi-line response. + * The returned data is un-byte-stuffed, and has lines termined by + * newlines rather than CR/LF pairs. + * + * Return value: the data, which the caller must free. + **/ +char * +camel_nntp_command_get_additional_data (CamelNNTPStore *store) +{ + CamelStreamBuffer *stream = CAMEL_STREAM_BUFFER (store->istream); + GPtrArray *data; + char *buf; + int i, status = CAMEL_NNTP_OK; + + data = g_ptr_array_new (); + while (1) { + buf = camel_stream_buffer_read_line (stream); + if (!buf) { + status = CAMEL_NNTP_FAIL; + break; + } + + if (!strcmp (buf, ".")) + break; + if (*buf == '.') + memmove (buf, buf + 1, strlen (buf)); + g_ptr_array_add (data, buf); + } + + if (status == CAMEL_NNTP_OK) { + /* Append an empty string to the end of the array + * so when we g_strjoinv it, we get a "\n" after + * the last real line. + */ + g_ptr_array_add (data, ""); + g_ptr_array_add (data, NULL); + buf = g_strjoinv ("\n", (char **)data->pdata); + } else + buf = NULL; + + for (i = 0; i < data->len - 2; i++) + g_free (data->pdata[i]); + g_ptr_array_free (data, TRUE); + + return buf; +} + +void +camel_nntp_store_subscribe_group (CamelStore *store, + const gchar *group_name) +{ + gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); + char *ret = NULL; + CamelException *ex = camel_exception_new(); + + camel_nntp_store_open(CAMEL_NNTP_STORE(store), ex); + + if (camel_exception_get_id (ex)) { + g_free (root_dir); + camel_exception_free (ex); + return; + } + + if (CAMEL_NNTP_OK == camel_nntp_command ( CAMEL_NNTP_STORE (store), + &ret, "GROUP %s", group_name)) { + gchar *summary_file; + summary_file = g_strdup_printf ("%s/%s-ev-summary", root_dir, group_name); + } + if (ret) g_free (ret); + + g_free (root_dir); + camel_exception_free (ex); +} + +void +camel_nntp_store_unsubscribe_group (CamelStore *store, + const gchar *group_name) +{ + gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); + gchar *summary_file; + + summary_file = g_strdup_printf ("%s/%s-ev-summary", root_dir, group_name); + if (g_file_exists (summary_file)) + unlink (summary_file); + g_free (summary_file); + + g_free (root_dir); +} + +GList * +camel_nntp_store_list_subscribed_groups(CamelStore *store) +{ + GList *group_name_list = NULL; + struct stat stat_buf; + gint stat_error = 0; + gchar *entry_name; + gchar *full_entry_name; + gchar *real_group_name; + struct dirent *dir_entry; + DIR *dir_handle; + gchar *root_dir = camel_nntp_store_get_toplevel_dir(CAMEL_NNTP_STORE(store)); + + dir_handle = opendir (root_dir); + + /* read the first entry in the directory */ + dir_entry = readdir (dir_handle); + while ((stat_error != -1) && (dir_entry != NULL)) { + + /* get the name of the next entry in the dir */ + entry_name = dir_entry->d_name; + full_entry_name = g_strdup_printf ("%s/%s", root_dir, entry_name); + stat_error = stat (full_entry_name, &stat_buf); + g_free (full_entry_name); + + /* is it a normal file ending in -ev-summary ? */ + if ((stat_error != -1) && S_ISREG (stat_buf.st_mode)) { + gboolean summary_suffix_found; + + real_group_name = string_prefix (entry_name, "-ev-summary", + &summary_suffix_found); + + if (summary_suffix_found) + /* add the folder name to the list */ + group_name_list = g_list_append (group_name_list, + real_group_name); + } + /* read next entry */ + dir_entry = readdir (dir_handle); + } + + closedir (dir_handle); + + return group_name_list; +} + +gchar * +camel_nntp_store_get_toplevel_dir (CamelNNTPStore *store) +{ + CamelURL *url = CAMEL_SERVICE (store)->url; + char *news_dir; + char *top_dir; + + g_assert(url != NULL); + + news_dir = gnome_util_prepend_user_home ("evolution/news"); + + top_dir = g_strdup_printf( "%s/%s", + news_dir, + url->host ); + + g_free (news_dir); + + return top_dir; +} diff --git a/camel/providers/nntp/camel-nntp-store.h b/camel/providers/nntp/camel-nntp-store.h new file mode 100644 index 0000000000..257772d7cb --- /dev/null +++ b/camel/providers/nntp/camel-nntp-store.h @@ -0,0 +1,85 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-store.h : class for an nntp store */ + +/* + * + * Copyright (C) 2000 Helix Code, Inc. + * + * 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 + */ + + +#ifndef CAMEL_NNTP_STORE_H +#define CAMEL_NNTP_STORE_H 1 + + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include +#include "camel-store.h" + +#define CAMEL_NNTP_STORE_TYPE (camel_nntp_store_get_type ()) +#define CAMEL_NNTP_STORE(obj) (GTK_CHECK_CAST((obj), CAMEL_NNTP_STORE_TYPE, CamelNNTPStore)) +#define CAMEL_NNTP_STORE_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_NNTP_STORE_TYPE, CamelNNTPStoreClass)) +#define IS_CAMEL_NNTP_STORE(o) (GTK_CHECK_TYPE((o), CAMEL_NNTP_STORE_TYPE)) + + +typedef struct { + CamelStore parent_object; + +#define CAMEL_NNTP_EXT_XOVER 0x01 + guint32 extensions; + + CamelStream *istream, *ostream; +} CamelNNTPStore; + + + +typedef struct { + CamelStoreClass parent_class; + +} CamelNNTPStoreClass; + + +/* public methods */ +void camel_nntp_store_open (CamelNNTPStore *store, CamelException *ex); +void camel_nntp_store_close (CamelNNTPStore *store, gboolean expunge, + CamelException *ex); + +void camel_nntp_store_subscribe_group (CamelStore *store, const gchar *group_name); +void camel_nntp_store_unsubscribe_group (CamelStore *store, const gchar *group_name); +GList *camel_nntp_store_list_subscribed_groups(CamelStore *store); + +gchar *camel_nntp_store_get_toplevel_dir (CamelNNTPStore *store); + +/* support functions */ +enum { CAMEL_NNTP_OK, CAMEL_NNTP_ERR, CAMEL_NNTP_FAIL }; +int camel_nntp_command (CamelNNTPStore *store, char **ret, char *fmt, ...); +char *camel_nntp_command_get_additional_data (CamelNNTPStore *store); + +/* Standard Gtk function */ +GtkType camel_nntp_store_get_type (void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_NNTP_STORE_H */ + + diff --git a/camel/providers/nntp/camel-nntp-summary.c b/camel/providers/nntp/camel-nntp-summary.c new file mode 100644 index 0000000000..079c1dddab --- /dev/null +++ b/camel/providers/nntp/camel-nntp-summary.c @@ -0,0 +1,381 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-utils.c : summary support for nntp groups. */ + +/* + * Author : Chris Toshok + * + * Copyright (C) 2000 Helix Code . + + * 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 + +#include "camel-log.h" +#include "camel-exception.h" +#include "camel-nntp-folder.h" +#include "camel-nntp-summary.h" +#include "md5-utils.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static CamelFolderSummaryClass *parent_class = NULL; + +static int count_messages (CamelFolderSummary *summary); +static int count_subfolders (CamelFolderSummary *summary); +static GPtrArray *get_subfolder_info (CamelFolderSummary *summary, + int first, int count); +static GPtrArray *get_message_info (CamelFolderSummary *summary, + int first, int count); +static void finalize (GtkObject *object); + +static void +camel_nntp_summary_class_init (CamelNNTPSummaryClass *camel_nntp_summary_class) +{ + GtkObjectClass *gtk_object_class = + GTK_OBJECT_CLASS (camel_nntp_summary_class); + CamelFolderSummaryClass *camel_folder_summary_class = + CAMEL_FOLDER_SUMMARY_CLASS (camel_nntp_summary_class); + + parent_class = gtk_type_class (camel_folder_summary_get_type ()); + + /* virtual method override */ + camel_folder_summary_class->count_messages = count_messages; + camel_folder_summary_class->count_subfolders = count_subfolders; + camel_folder_summary_class->get_subfolder_info = get_subfolder_info; + camel_folder_summary_class->get_message_info = get_message_info; + + gtk_object_class->finalize = finalize; +} + + +GtkType +camel_nntp_summary_get_type (void) +{ + static GtkType camel_nntp_summary_type = 0; + + if (!camel_nntp_summary_type) { + GtkTypeInfo camel_nntp_summary_info = + { + "CamelNNTPSummary", + sizeof (CamelNNTPSummary), + sizeof (CamelNNTPSummaryClass), + (GtkClassInitFunc) camel_nntp_summary_class_init, + (GtkObjectInitFunc) NULL, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + camel_nntp_summary_type = gtk_type_unique (camel_folder_summary_get_type (), &camel_nntp_summary_info); + } + + return camel_nntp_summary_type; +} + +static void +finalize (GtkObject *object) +{ + CamelNNTPSummary *summary = CAMEL_NNTP_SUMMARY (object); + CamelNNTPSummaryInformation *info; + int i; + + for (i = 0; i < summary->message_info->len; i++) { + info = &(((CamelNNTPSummaryInformation *)summary->message_info->data)[i]); + g_free (info->headers.subject); + g_free (info->headers.sender); + g_free (info->headers.to); + g_free (info->headers.sent_date); + g_free (info->headers.received_date); + g_free (info->headers.uid); + } + g_array_free (summary->message_info, TRUE); + + GTK_OBJECT_CLASS (parent_class)->finalize (object); +} + +static int +count_messages (CamelFolderSummary *summary) +{ + return CAMEL_NNTP_SUMMARY (summary)->nb_message; +} + +static int +count_subfolders (CamelFolderSummary *summary) +{ + return 0; +} + +static GPtrArray * +get_subfolder_info (CamelFolderSummary *summary, int first, int count) +{ + return NULL; +} + +static GPtrArray * +get_message_info (CamelFolderSummary *summary, int first, int count) +{ + CamelNNTPSummary *nntp_summary = CAMEL_NNTP_SUMMARY (summary); + CamelNNTPSummaryInformation *info; + GPtrArray *arr; + + /* XXX bounds check */ + + arr = g_ptr_array_new (); + for (; count; count--) { + info = &((CamelNNTPSummaryInformation *)nntp_summary->message_info->data)[first++]; + g_ptr_array_add (arr, info); + } + + return arr; +} + +/** + * camel_nntp_summary_save: + * @summary: + * @filename: + * @ex: + * + * save the summary into a file + **/ +void +camel_nntp_summary_save (CamelNNTPSummary *summary, const gchar *filename, + CamelException *ex) +{ + CamelNNTPSummaryInformation *msg_info; + guint cur_msg; + guint field_length; + gint fd; + gint write_result; /* XXX use this */ + guint32 data; + + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::save_summary entering \n"); + + fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + if (fd == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, + "could not create the nntp summary " + "file\n\t%s\nFull error is : %s\n", + filename, + strerror (errno)); + return; + } + + /* We write the file out in network byte order, not because + * that makes sense, but because it's easy. + */ + + data = htonl (CAMEL_NNTP_SUMMARY_VERSION); + write (fd, &data, sizeof (data)); + + data = htonl (summary->nb_message); + write (fd, &data, sizeof (data)); + + for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) { + msg_info = (CamelNNTPSummaryInformation *) + (summary->message_info->data) + cur_msg; + + /* Write meta-info. */ + data = htonl (msg_info->size); + write (fd, &data, sizeof (data)); + write (fd, &msg_info->status, 1); + + /* Write subject. */ + if (msg_info->headers.subject) + field_length = strlen (msg_info->headers.subject); + else + field_length = 0; + data = htonl (field_length); + write (fd, &data, sizeof (data)); + if (msg_info->headers.subject) + write (fd, msg_info->headers.subject, field_length); + + /* Write sender. */ + if (msg_info->headers.sender) + field_length = strlen (msg_info->headers.sender); + else + field_length = 0; + data = htonl (field_length); + write (fd, &data, sizeof (data)); + if (msg_info->headers.sender) + write (fd, msg_info->headers.sender, field_length); + + /* Write sent date. */ + if (msg_info->headers.sent_date) + field_length = strlen (msg_info->headers.sent_date); + else + field_length = 0; + data = htonl (field_length); + write (fd, &data, sizeof (data)); + if (msg_info->headers.sent_date) + write (fd, msg_info->headers.sent_date, field_length); + + /* Write received date. */ + if (msg_info->headers.received_date) + field_length = strlen (msg_info->headers.received_date); + else + field_length = 0; + data = htonl (field_length); + write (fd, &data, sizeof (data)); + if (msg_info->headers.received_date) + write (fd, msg_info->headers.received_date, field_length); + } + + close (fd); + + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::save_summary leaving \n"); +} + + + +/** + * camel_nntp_summary_load: + * @filename: + * @ex: + * + * load the summary from a file + * + * Return value: + **/ +CamelNNTPSummary * +camel_nntp_summary_load (const gchar *newsgroup, const gchar *filename, CamelException *ex) +{ + CamelNNTPSummaryInformation *msg_info; + guint cur_msg; + guint field_length; + gint fd; + CamelNNTPSummary *summary; + gint read_result; + guint32 data; + + CAMEL_LOG_FULL_DEBUG ("CamelNNTPFolder::save_summary entering \n"); + + fd = open (filename, O_RDONLY); + if (fd == -1) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_INSUFFICIENT_PERMISSION, + "could not open the nntp summary file\n" + "\t%s\nFull error is : %s\n", + filename, strerror (errno)); + return NULL; + } + + /* Verify version number. */ + read (fd, &data, sizeof(data)); + data = ntohl (data); + + if (data != CAMEL_NNTP_SUMMARY_VERSION) { + camel_exception_setv (ex, CAMEL_EXCEPTION_FOLDER_SUMMARY_INVALID, + "This folder summary was written by " + "%s version of this software.", + data < CAMEL_NNTP_SUMMARY_VERSION ? + "an older" : "a newer"); + return NULL; + } + + summary = CAMEL_NNTP_SUMMARY (gtk_object_new (camel_nntp_summary_get_type (), NULL)); + + read (fd, &data, sizeof(data)); + summary->nb_message = ntohl (data); + + summary->message_info = + g_array_new (FALSE, FALSE, + sizeof (CamelNNTPSummaryInformation)); + g_array_set_size (summary->message_info, summary->nb_message); + + for (cur_msg = 0; cur_msg < summary->nb_message; cur_msg++) { + msg_info = (CamelNNTPSummaryInformation *) + (summary->message_info->data) + cur_msg; + + /* Read the meta-info. */ + read (fd, &data, sizeof(data)); + msg_info->size = ntohl (data); + read (fd, &msg_info->status, 1); + + /* Read the subject. */ + read (fd, &field_length, sizeof (field_length)); + field_length = ntohl (field_length); + if (field_length > 0) { + msg_info->headers.subject = + g_new0 (gchar, field_length + 1); + read (fd, msg_info->headers.subject, field_length); + } else + msg_info->headers.subject = NULL; + + /* Read the sender. */ + read (fd, &field_length, sizeof (field_length)); + field_length = ntohl (field_length); + if (field_length > 0) { + msg_info->headers.sender = + g_new0 (gchar, field_length + 1); + read (fd, msg_info->headers.sender, field_length); + } else + msg_info->headers.sender = NULL; + + msg_info->headers.to = g_strdup (newsgroup); + + /* Read the sent date field. */ + read (fd, &field_length, sizeof (field_length)); + field_length = ntohl (field_length); + if (field_length > 0) { + msg_info->headers.sent_date = + g_new0 (gchar, field_length + 1); + read (fd, msg_info->headers.sent_date, field_length); + } else + msg_info->headers.sent_date = NULL; + + /* Read the received date field. */ + read (fd, &field_length, sizeof (field_length)); + field_length = ntohl (field_length); + if (field_length > 0) { + msg_info->headers.received_date = + g_new0 (gchar, field_length + 1); + read (fd, msg_info->headers.received_date, + field_length); + } else + msg_info->headers.received_date = NULL; + } + + close (fd); + return summary; +} + + +/** + * camel_nntp_summary_append_entries: + * @summary: + * @entries: + * + * append an entry to a summary + **/ +void +camel_nntp_summary_append_entries (CamelNNTPSummary *summary, GArray *entries) +{ + + summary->message_info = g_array_append_vals (summary->message_info, + entries->data, + entries->len); +} diff --git a/camel/providers/nntp/camel-nntp-summary.h b/camel/providers/nntp/camel-nntp-summary.h new file mode 100644 index 0000000000..6c54f6cb33 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-summary.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-utils.c : summary support for nntp groups. */ + +/* + * + * Author : Chris Toshok + * + * Copyright (C) 2000 Helix Code (http://www.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 + */ + +#ifndef NNTP_SUMMARY_H +#define NNTP_SUMMARY_H 1 + +#include + +#define CAMEL_NNTP_SUMMARY_TYPE (camel_nntp_summary_get_type ()) +#define CAMEL_NNTP_SUMMARY(obj) (GTK_CHECK_CAST((obj), CAMEL_NNTP_SUMMARY_TYPE, CamelNNTPSummary)) +#define CAMEL_NNTP_SUMMARY_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), CAMEL_NNTP_SUMMARY_TYPE, CamelNNTPSummaryClass)) +#define CAMEL_IS_NNTP_SUMMARY(o) (GTK_CHECK_TYPE((o), CAMEL_NNTP_SUMMARY_TYPE)) + + +#define CAMEL_NNTP_SUMMARY_VERSION 1 + + +typedef struct { + CamelMessageInfo headers; + + guint size; + guchar status; + +} CamelNNTPSummaryInformation; + + +/* this contains informations about the whole nntp file */ +typedef struct { + CamelFolderSummary parent_object; + + guint first_message; /* the first message number in the summary */ + guint nb_message; /* number of messages in the summary */ + + GArray *message_info; /* array of CamelNNTPSummaryInformation */ + +} CamelNNTPSummary; + +typedef struct { + CamelFolderSummaryClass parent_class; + +} CamelNNTPSummaryClass; + + +GtkType camel_nntp_summary_get_type (void); + +void camel_nntp_summary_save (CamelNNTPSummary *summary, + const gchar *filename, CamelException *ex); +CamelNNTPSummary * +camel_nntp_summary_load (const gchar *newsgroup, const gchar *filename, CamelException *ex); + +gboolean camel_nntp_summary_check_sync (gchar *summary_filename, + gchar *nntp_filename, + CamelException *ex); + +void camel_nntp_summary_append_entries (CamelNNTPSummary *summary, + GArray *entries); + + +#endif /* NNTP_SUMMARY_H */ diff --git a/camel/providers/nntp/camel-nntp-utils.c b/camel/providers/nntp/camel-nntp-utils.c new file mode 100644 index 0000000000..def5cecd23 --- /dev/null +++ b/camel/providers/nntp/camel-nntp-utils.c @@ -0,0 +1,107 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-utils.c : utilities used by the nntp code. */ + +/* + * Author : Chris Toshok + * + * Copyright (C) 2000 Helix Code . + * + * 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 "camel-nntp-folder.h" +#include "camel-nntp-store.h" +#include "camel-nntp-summary.h" +#include "camel-nntp-utils.h" +#include "camel-stream-buffer.h" + +#include + +GArray * +camel_nntp_get_headers (CamelStore *store, + CamelNNTPFolder *nntp_folder, + CamelException *ex) +{ + CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store); + CamelFolder *folder = CAMEL_FOLDER (nntp_folder); + + if (TRUE /* nntp_store->extensions & CAMEL_NNTP_EXT_XOVER */) { + int status; + char *ret; + int first_message, nb_message, last_message; + + status = camel_nntp_command (nntp_store, &ret, + "GROUP %s", folder->name); + + if (status != CAMEL_NNTP_OK) + return NULL; + + sscanf (ret, "%d %d %d", &nb_message, &first_message, &last_message); + g_free (ret); + + status = camel_nntp_command (nntp_store, NULL, + "XOVER %d-%d", + first_message, + last_message); + + if (status == CAMEL_NNTP_OK) { + CamelStream *nntp_istream = nntp_store->istream; + GArray *array; + gboolean done = FALSE; + + array = g_array_new(FALSE, FALSE, sizeof(CamelNNTPSummaryInformation)); + + while (!done) { + char *line; + + line = camel_stream_buffer_read_line ( + CAMEL_STREAM_BUFFER ( nntp_istream )); + + if (*line == '.') { + done = TRUE; + } + else { + CamelNNTPSummaryInformation new_info; + char **split_line = g_strsplit (line, "\t", 7); + + new_info.headers.subject = g_strdup(split_line[1]); + new_info.headers.sender = g_strdup(split_line[2]); + new_info.headers.to = g_strdup(folder->name); + new_info.headers.sent_date = g_strdup(split_line[3]); + /* XXX do we need to fill in both dates? */ + new_info.headers.received_date = g_strdup(split_line[3]); + new_info.headers.size = atoi(split_line[5]); + new_info.headers.uid = g_strdup(split_line[4]); + g_strfreev (split_line); + + printf ("%s\t%s\t%s\n", new_info.headers.subject, + new_info.headers.sender, + new_info.headers.uid); + g_array_append_val(array, new_info); + } + g_free (line); + } + + return array; + } + } + else { + /* do HEAD stuff for the range of articles */ + } + + return NULL; +} + diff --git a/camel/providers/nntp/camel-nntp-utils.h b/camel/providers/nntp/camel-nntp-utils.h new file mode 100644 index 0000000000..52617fa0ce --- /dev/null +++ b/camel/providers/nntp/camel-nntp-utils.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* camel-nntp-utils.h : Utilities for the NNTP provider */ + +/* + * + * Author : Chris Toshok + * + * Copyright (C) 1999 Helix Code . + * + * 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 + */ + + +#ifndef CAMEL_NNTP_UTILS_H +#define CAMEL_NNTP_UTILS_H 1 + + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +GArray *camel_nntp_get_headers (CamelStore *store, CamelNNTPFolder *nntp_folder, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_NNTP_UTILS_H */ -- cgit v1.2.3