aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Toshok <toshok@src.gnome.org>2000-04-15 03:05:33 +0800
committerChris Toshok <toshok@src.gnome.org>2000-04-15 03:05:33 +0800
commitab0e676fd9b1d4946911704a3446eb9b54e5335b (patch)
tree687ae389e10e3b221fec361a0e7850c9e570ad2e
parentafe5b492b34f40626421942dbf8acbd71d339e21 (diff)
downloadgsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.gz
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.bz2
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.lz
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.xz
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.zst
gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.zip
initial import of nntp store.
svn path=/trunk/; revision=2430
-rw-r--r--camel/providers/nntp/.cvsignore7
-rw-r--r--camel/providers/nntp/Makefile.am31
-rw-r--r--camel/providers/nntp/camel-nntp-folder.c687
-rw-r--r--camel/providers/nntp/camel-nntp-folder.h73
-rw-r--r--camel/providers/nntp/camel-nntp-provider.c54
-rw-r--r--camel/providers/nntp/camel-nntp-store.c466
-rw-r--r--camel/providers/nntp/camel-nntp-store.h85
-rw-r--r--camel/providers/nntp/camel-nntp-summary.c381
-rw-r--r--camel/providers/nntp/camel-nntp-summary.h81
-rw-r--r--camel/providers/nntp/camel-nntp-utils.c107
-rw-r--r--camel/providers/nntp/camel-nntp-utils.h42
11 files changed, 2014 insertions, 0 deletions
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 <toshok@helixcode.com>
+ *
+ * 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 <config.h>
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+
+#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;i<message_info_array->len;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; i<message_info_array->len; 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 <toshok@helixcode.com>
+ *
+ * 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 <gtk/gtk.h>
+#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 <danw@helixcode.com>
+ *
+ * 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. <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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#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. <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
+ */
+
+
+#ifndef CAMEL_NNTP_STORE_H
+#define CAMEL_NNTP_STORE_H 1
+
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus }*/
+
+#include <gtk/gtk.h>
+#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 <toshok@helixcode.com>
+ *
+ * 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 <config.h>
+
+#include "camel-log.h"
+#include "camel-exception.h"
+#include "camel-nntp-folder.h"
+#include "camel-nntp-summary.h"
+#include "md5-utils.h"
+
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/in.h>
+
+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 <toshok@helixcode.com>
+ *
+ * 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 <camel-folder-summary.h>
+
+#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 <toshok@helixcode.com>
+ *
+ * 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 <stdlib.h>
+
+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 <toshok@helixcode.com>
+ *
+ * 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 */