diff options
author | Chris Toshok <toshok@src.gnome.org> | 2000-04-15 03:05:33 +0800 |
---|---|---|
committer | Chris Toshok <toshok@src.gnome.org> | 2000-04-15 03:05:33 +0800 |
commit | ab0e676fd9b1d4946911704a3446eb9b54e5335b (patch) | |
tree | 687ae389e10e3b221fec361a0e7850c9e570ad2e /camel/providers/nntp/camel-nntp-store.c | |
parent | afe5b492b34f40626421942dbf8acbd71d339e21 (diff) | |
download | gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.gz gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.tar.zst gsoc2013-evolution-ab0e676fd9b1d4946911704a3446eb9b54e5335b.zip |
initial import of nntp store.
svn path=/trunk/; revision=2430
Diffstat (limited to 'camel/providers/nntp/camel-nntp-store.c')
-rw-r--r-- | camel/providers/nntp/camel-nntp-store.c | 466 |
1 files changed, 466 insertions, 0 deletions
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; +} |