Completely new implementation of NNTP.
2001-11-30 Not Zed <NotZed@Ximian.com> * providers/nntp/camel-nntp-*.c: Completely new implementation of NNTP. Doesn't support subscriptions yet (lists all folders), but should be more reliable (faster?), and has an integrated cache. * camel-exception.c (camel_exception_new): Use e_memchunks for exception blocks. (camel_exception_free): Same. * camel-data-cache.[ch]: New object for managing on-disk caches of anything that can be stored in a camel-stream. * camel-file-utils.c (camel_file_util_mkdir): New function, just a nicer place to put this (than camel-store), should be removed from camel-store. (camel_file_util_safe_filename): New function to url-encode a filename. * camel-mime-parser.c (drop_states): New func to drop the parser state to initial state. (folder_scan_init_with_fd): (folder_scan_init_with_stream): Call above func to reset state if the stream is changed on us so we can change streams to reuse a parser object. svn path=/trunk/; revision=14822
This commit is contained in:
@ -1,3 +1,31 @@
|
|||||||
|
2001-11-30 Not Zed <NotZed@Ximian.com>
|
||||||
|
|
||||||
|
* providers/nntp/camel-nntp-*.c:
|
||||||
|
Completely new implementation of NNTP.
|
||||||
|
|
||||||
|
Doesn't support subscriptions yet (lists all folders), but should
|
||||||
|
be more reliable (faster?), and has an integrated cache.
|
||||||
|
|
||||||
|
* camel-exception.c (camel_exception_new): Use e_memchunks for
|
||||||
|
exception blocks.
|
||||||
|
(camel_exception_free): Same.
|
||||||
|
|
||||||
|
* camel-data-cache.[ch]: New object for managing on-disk caches of
|
||||||
|
anything that can be stored in a camel-stream.
|
||||||
|
|
||||||
|
* camel-file-utils.c (camel_file_util_mkdir): New function, just a
|
||||||
|
nicer place to put this (than camel-store), should be removed from
|
||||||
|
camel-store.
|
||||||
|
(camel_file_util_safe_filename): New function to url-encode a
|
||||||
|
filename.
|
||||||
|
|
||||||
|
* camel-mime-parser.c (drop_states): New func to drop the parser
|
||||||
|
state to initial state.
|
||||||
|
(folder_scan_init_with_fd):
|
||||||
|
(folder_scan_init_with_stream): Call above func to reset state if
|
||||||
|
the stream is changed on us so we can change streams to reuse a
|
||||||
|
parser object.
|
||||||
|
|
||||||
2001-11-25 Not Zed <NotZed@Ximian.com>
|
2001-11-25 Not Zed <NotZed@Ximian.com>
|
||||||
|
|
||||||
* providers/nntp/camel-nntp-folder.c (nntp_folder_get_message): If
|
* providers/nntp/camel-nntp-folder.c (nntp_folder_get_message): If
|
||||||
|
@ -28,6 +28,7 @@ libcamel_la_SOURCES = \
|
|||||||
camel-address.c \
|
camel-address.c \
|
||||||
camel-cipher-context.c \
|
camel-cipher-context.c \
|
||||||
camel-cms-context.c \
|
camel-cms-context.c \
|
||||||
|
camel-data-cache.c \
|
||||||
camel-data-wrapper.c \
|
camel-data-wrapper.c \
|
||||||
camel-digest-folder.c \
|
camel-digest-folder.c \
|
||||||
camel-disco-diary.c \
|
camel-disco-diary.c \
|
||||||
@ -114,6 +115,7 @@ libcamelinclude_HEADERS = \
|
|||||||
camel-charset-map.h \
|
camel-charset-map.h \
|
||||||
camel-cipher-context.h \
|
camel-cipher-context.h \
|
||||||
camel-cms-context.h \
|
camel-cms-context.h \
|
||||||
|
camel-data-cache.h \
|
||||||
camel-data-wrapper.h \
|
camel-data-wrapper.h \
|
||||||
camel-digest-folder.h \
|
camel-digest-folder.h \
|
||||||
camel-disco-diary.h \
|
camel-disco-diary.h \
|
||||||
|
476
camel/camel-data-cache.c
Normal file
476
camel/camel-data-cache.c
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* camel-message-cache.c: Class for a Camel cache.
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Ximian, Inc. (www.ximian.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <alloca.h>
|
||||||
|
|
||||||
|
#include "camel-data-cache.h"
|
||||||
|
#include "camel-exception.h"
|
||||||
|
#include "camel-stream-fs.h"
|
||||||
|
#include "camel-stream-mem.h"
|
||||||
|
#include "camel-file-utils.h"
|
||||||
|
|
||||||
|
extern int camel_verbose_debug;
|
||||||
|
#define dd(x) (camel_verbose_debug?(x):0)
|
||||||
|
|
||||||
|
/* how many 'bits' of hash are used to key the toplevel directory */
|
||||||
|
#define CAMEL_DATA_CACHE_BITS (6)
|
||||||
|
#define CAMEL_DATA_CACHE_MASK ((1<<CAMEL_DATA_CACHE_BITS)-1)
|
||||||
|
|
||||||
|
/* timeout before a cache dir is checked again for expired entries,
|
||||||
|
once an hour should be enough */
|
||||||
|
#define CAMEL_DATA_CACHE_CYCLE_TIME (60*60)
|
||||||
|
|
||||||
|
struct _CamelDataCachePrivate {
|
||||||
|
GHashTable *busy_stream;
|
||||||
|
GHashTable *busy_path;
|
||||||
|
|
||||||
|
int expire_inc;
|
||||||
|
time_t expire_last[1<<CAMEL_DATA_CACHE_BITS];
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
GMutex *lock;
|
||||||
|
#define CDC_LOCK(c, l) g_mutex_lock(((CamelDataCache *)(c))->priv->l)
|
||||||
|
#define CDC_UNLOCK(c, l) g_mutex_unlock(((CamelDataCache *)(c))->priv->l)
|
||||||
|
#else
|
||||||
|
#define CDC_LOCK(c, l)
|
||||||
|
#define CDC_UNLOCK(c, l)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static CamelObject *camel_data_cache_parent;
|
||||||
|
|
||||||
|
static void data_cache_class_init(CamelDataCacheClass *klass)
|
||||||
|
{
|
||||||
|
camel_data_cache_parent = (CamelObject *)camel_type_get_global_classfuncs (camel_object_get_type ());
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
klass->add = data_cache_add;
|
||||||
|
klass->get = data_cache_get;
|
||||||
|
klass->close = data_cache_close;
|
||||||
|
klass->remove = data_cache_remove;
|
||||||
|
klass->clear = data_cache_clear;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_cache_init(CamelDataCache *cdc, CamelDataCacheClass *klass)
|
||||||
|
{
|
||||||
|
struct _CamelDataCachePrivate *p;
|
||||||
|
|
||||||
|
p = cdc->priv = g_malloc0(sizeof(*cdc->priv));
|
||||||
|
|
||||||
|
p->busy_stream = g_hash_table_new(NULL, NULL);
|
||||||
|
p->busy_path = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
p->lock = g_mutex_new();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_busy(CamelStream *stream, char *path, void *data)
|
||||||
|
{
|
||||||
|
camel_object_unref((CamelObject *)stream);
|
||||||
|
g_free(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void data_cache_finalise(CamelDataCache *cdc)
|
||||||
|
{
|
||||||
|
struct _CamelDataCachePrivate *p;
|
||||||
|
|
||||||
|
p = cdc->priv;
|
||||||
|
|
||||||
|
g_hash_table_foreach(p->busy_stream, (GHFunc)free_busy, NULL);
|
||||||
|
g_hash_table_destroy(p->busy_path);
|
||||||
|
g_hash_table_destroy(p->busy_stream);
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
g_mutex_free(p->lock);
|
||||||
|
#endif
|
||||||
|
g_free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
CamelType
|
||||||
|
camel_data_cache_get_type(void)
|
||||||
|
{
|
||||||
|
static CamelType camel_data_cache_type = CAMEL_INVALID_TYPE;
|
||||||
|
|
||||||
|
if (camel_data_cache_type == CAMEL_INVALID_TYPE) {
|
||||||
|
camel_data_cache_type = camel_type_register(
|
||||||
|
CAMEL_OBJECT_TYPE, "CamelDataCache",
|
||||||
|
sizeof (CamelDataCache),
|
||||||
|
sizeof (CamelDataCacheClass),
|
||||||
|
(CamelObjectClassInitFunc) data_cache_class_init,
|
||||||
|
NULL,
|
||||||
|
(CamelObjectInitFunc) data_cache_init,
|
||||||
|
(CamelObjectFinalizeFunc) data_cache_finalise);
|
||||||
|
}
|
||||||
|
|
||||||
|
return camel_data_cache_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_new:
|
||||||
|
* @path: Base path of cache, subdirectories will be created here.
|
||||||
|
* @flags: Open flags, none defined.
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Create a new data cache.
|
||||||
|
*
|
||||||
|
* Return value: A new cache object, or NULL if the base path cannot
|
||||||
|
* be written to.
|
||||||
|
**/
|
||||||
|
CamelDataCache *
|
||||||
|
camel_data_cache_new(const char *path, guint32 flags, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelDataCache *cdc;
|
||||||
|
|
||||||
|
if (camel_file_util_mkdir(path, 0700) == -1) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
|
||||||
|
_("Unable to create cache path"));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdc = (CamelDataCache *)camel_object_new(CAMEL_DATA_CACHE_TYPE);
|
||||||
|
|
||||||
|
cdc->path = g_strdup(path);
|
||||||
|
cdc->flags = flags;
|
||||||
|
cdc->expire_age = -1;
|
||||||
|
cdc->expire_access = -1;
|
||||||
|
|
||||||
|
return cdc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_set_expire_age:
|
||||||
|
* @cdc:
|
||||||
|
* @when: Timeout for age expiry, or -1 to disable.
|
||||||
|
*
|
||||||
|
* Set the cache expiration policy for aged entries.
|
||||||
|
*
|
||||||
|
* Items in the cache older than @when seconds may be
|
||||||
|
* flushed at any time. Items are expired in a lazy
|
||||||
|
* manner, so it is indeterminate when the items will
|
||||||
|
* physically be removed.
|
||||||
|
*
|
||||||
|
* Note you can set both an age and an access limit. The
|
||||||
|
* age acts as a hard limit on cache entries.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
camel_data_cache_set_expire_age(CamelDataCache *cdc, time_t when)
|
||||||
|
{
|
||||||
|
cdc->expire_age = when;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_set_expire_access:
|
||||||
|
* @cdc:
|
||||||
|
* @when: Timeout for access, or -1 to disable access expiry.
|
||||||
|
*
|
||||||
|
* Set the cache expiration policy for access times.
|
||||||
|
*
|
||||||
|
* Items in the cache which haven't been accessed for @when
|
||||||
|
* seconds may be expired at any time. Items are expired in a lazy
|
||||||
|
* manner, so it is indeterminate when the items will
|
||||||
|
* physically be removed.
|
||||||
|
*
|
||||||
|
* Note you can set both an age and an access limit. The
|
||||||
|
* age acts as a hard limit on cache entries.
|
||||||
|
**/
|
||||||
|
void
|
||||||
|
camel_data_cache_set_expire_access(CamelDataCache *cdc, time_t when)
|
||||||
|
{
|
||||||
|
cdc->expire_access = when;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
data_cache_expire(CamelDataCache *cdc, const char *path, const char *keep, time_t now)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *d;
|
||||||
|
GString *s;
|
||||||
|
struct stat st;
|
||||||
|
char *oldpath;
|
||||||
|
CamelStream *stream;
|
||||||
|
|
||||||
|
dir = opendir(path);
|
||||||
|
if (dir == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
s = g_string_new("");
|
||||||
|
while ( (d = readdir(dir)) ) {
|
||||||
|
if (strcmp(d->d_name, keep) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
g_string_sprintf(s, "%s/%s", path, d->d_name);
|
||||||
|
dd(printf("Checking '%s' for expiry\n", s->str));
|
||||||
|
if (stat(s->str, &st) == 0
|
||||||
|
&& S_ISREG(st.st_mode)
|
||||||
|
&& ((cdc->expire_age != -1 && st.st_mtime + cdc->expire_age < now)
|
||||||
|
|| (cdc->expire_access != -1 && st.st_atime + cdc->expire_access < now))) {
|
||||||
|
dd(printf("Has expired! Removing!\n"));
|
||||||
|
unlink(s->str);
|
||||||
|
if (g_hash_table_lookup_extended(cdc->priv->busy_path, s->str, (void **)&oldpath, (void **)&stream)) {
|
||||||
|
g_hash_table_remove(cdc->priv->busy_path, path);
|
||||||
|
g_hash_table_remove(cdc->priv->busy_stream, stream);
|
||||||
|
g_free(oldpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g_string_free(s, TRUE);
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Since we have to stat the directory anyway, we use this opportunity to
|
||||||
|
lazily expire old data.
|
||||||
|
If it is this directories 'turn', and we haven't done it for CYCLE_TIME seconds,
|
||||||
|
then we perform an expiry run */
|
||||||
|
static char *
|
||||||
|
data_cache_path(CamelDataCache *cdc, int create, const char *path, const char *key)
|
||||||
|
{
|
||||||
|
char *dir, *real, *tmp;
|
||||||
|
guint32 hash;
|
||||||
|
|
||||||
|
hash = g_str_hash(key);
|
||||||
|
hash = (hash>>5)&CAMEL_DATA_CACHE_MASK;
|
||||||
|
dir = alloca(strlen(cdc->path) + strlen(path) + 8);
|
||||||
|
sprintf(dir, "%s/%s/%02x", cdc->path, path, hash);
|
||||||
|
if (access(dir, F_OK) == -1) {
|
||||||
|
if (create)
|
||||||
|
camel_file_util_mkdir(dir, 0700);
|
||||||
|
} else if (cdc->priv->expire_inc == hash
|
||||||
|
&& (cdc->expire_age != -1 || cdc->expire_access != -1)) {
|
||||||
|
time_t now;
|
||||||
|
|
||||||
|
dd(printf("Checking expire cycle time on dir '%s'\n", dir));
|
||||||
|
|
||||||
|
now = time(0);
|
||||||
|
if (cdc->priv->expire_last[hash] + CAMEL_DATA_CACHE_CYCLE_TIME < now) {
|
||||||
|
data_cache_expire(cdc, dir, key, now);
|
||||||
|
cdc->priv->expire_last[hash] = now;
|
||||||
|
}
|
||||||
|
cdc->priv->expire_inc = (cdc->priv->expire_inc + 1) & CAMEL_DATA_CACHE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp = camel_file_util_safe_filename(key);
|
||||||
|
real = g_strdup_printf("%s/%s", dir, tmp);
|
||||||
|
g_free(tmp);
|
||||||
|
|
||||||
|
return real;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stream_finalised(CamelObject *o, void *event_data, void *data)
|
||||||
|
{
|
||||||
|
CamelDataCache *cdc = data;
|
||||||
|
char *key;
|
||||||
|
|
||||||
|
CDC_LOCK(cdc, lock);
|
||||||
|
key = g_hash_table_lookup(cdc->priv->busy_stream, o);
|
||||||
|
if (key) {
|
||||||
|
g_hash_table_remove(cdc->priv->busy_path, key);
|
||||||
|
g_hash_table_remove(cdc->priv->busy_path, o);
|
||||||
|
g_free(key);
|
||||||
|
}
|
||||||
|
CDC_UNLOCK(cdc, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_add:
|
||||||
|
* @cdc:
|
||||||
|
* @path: Relative path of item to add.
|
||||||
|
* @key: Key of item to add.
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Add a new item to the cache.
|
||||||
|
*
|
||||||
|
* The key and the path combine to form a unique key used to store
|
||||||
|
* the item.
|
||||||
|
*
|
||||||
|
* Potentially, expiry processing will be performed while this call
|
||||||
|
* is executing.
|
||||||
|
*
|
||||||
|
* Return value: A CamelStream (file) opened in read-write mode.
|
||||||
|
* The caller must unref this when finished.
|
||||||
|
**/
|
||||||
|
CamelStream *
|
||||||
|
camel_data_cache_add(CamelDataCache *cdc, const char *path, const char *key, CamelException *ex)
|
||||||
|
{
|
||||||
|
char *real, *oldpath;
|
||||||
|
CamelStream *stream;
|
||||||
|
|
||||||
|
CDC_LOCK(cdc, lock);
|
||||||
|
|
||||||
|
real = data_cache_path(cdc, TRUE, path, key);
|
||||||
|
if (g_hash_table_lookup_extended(cdc->priv->busy_path, real, (void **)&oldpath, (void **)&stream)) {
|
||||||
|
g_hash_table_remove(cdc->priv->busy_path, oldpath);
|
||||||
|
g_hash_table_remove(cdc->priv->busy_stream, stream);
|
||||||
|
unlink(oldpath);
|
||||||
|
g_free(oldpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = camel_stream_fs_new_with_name(real, O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||||
|
if (stream) {
|
||||||
|
camel_object_hook_event((CamelObject *)stream, "finalize", stream_finalised, cdc);
|
||||||
|
g_hash_table_insert(cdc->priv->busy_stream, stream, real);
|
||||||
|
g_hash_table_insert(cdc->priv->busy_path, real, stream);
|
||||||
|
} else {
|
||||||
|
g_free(real);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDC_UNLOCK(cdc, lock);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_get:
|
||||||
|
* @cdc:
|
||||||
|
* @path: Path to the (sub) cache the item exists in.
|
||||||
|
* @key: Key for the cache item.
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Lookup an item in the cache. If the item exists, a stream
|
||||||
|
* is returned for the item. The stream may be shared by
|
||||||
|
* multiple callers, so ensure the stream is in a valid state
|
||||||
|
* through external locking.
|
||||||
|
*
|
||||||
|
* Return value: A cache item, or NULL if the cache item does not exist.
|
||||||
|
**/
|
||||||
|
CamelStream *
|
||||||
|
camel_data_cache_get(CamelDataCache *cdc, const char *path, const char *key, CamelException *ex)
|
||||||
|
{
|
||||||
|
char *real;
|
||||||
|
CamelStream *stream;
|
||||||
|
|
||||||
|
CDC_LOCK(cdc, lock);
|
||||||
|
|
||||||
|
real = data_cache_path(cdc, FALSE, path, key);
|
||||||
|
stream = g_hash_table_lookup(cdc->priv->busy_path, real);
|
||||||
|
if (stream) {
|
||||||
|
camel_object_ref((CamelObject *)stream);
|
||||||
|
g_free(real);
|
||||||
|
} else {
|
||||||
|
stream = camel_stream_fs_new_with_name(real, O_RDWR, 0600);
|
||||||
|
if (stream) {
|
||||||
|
camel_object_hook_event((CamelObject *)stream, "finalize", stream_finalised, cdc);
|
||||||
|
g_hash_table_insert(cdc->priv->busy_stream, stream, real);
|
||||||
|
g_hash_table_insert(cdc->priv->busy_path, real, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CDC_UNLOCK(cdc, lock);
|
||||||
|
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_remove:
|
||||||
|
* @cdc:
|
||||||
|
* @path:
|
||||||
|
* @key:
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Remove/expire a cache item.
|
||||||
|
*
|
||||||
|
* Return value:
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
camel_data_cache_remove(CamelDataCache *cdc, const char *path, const char *key, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelStream *stream;
|
||||||
|
char *real, *oldpath;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
CDC_LOCK(cdc, lock);
|
||||||
|
|
||||||
|
real = data_cache_path(cdc, FALSE, path, key);
|
||||||
|
if (g_hash_table_lookup_extended(cdc->priv->busy_path, real, (void **)&oldpath, (void **)&stream)) {
|
||||||
|
g_hash_table_remove(cdc->priv->busy_path, path);
|
||||||
|
g_hash_table_remove(cdc->priv->busy_stream, stream);
|
||||||
|
g_free(oldpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* maybe we were a mem stream */
|
||||||
|
if (unlink(real) == -1 && errno != ENOENT) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
|
||||||
|
_("Could not remove cache entry: %s: %s"),
|
||||||
|
real, strerror(errno));
|
||||||
|
ret = -1;
|
||||||
|
} else {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(real);
|
||||||
|
|
||||||
|
CDC_UNLOCK(cdc, lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_rename:
|
||||||
|
* @cache:
|
||||||
|
* @old:
|
||||||
|
* @new:
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Rename a cache path. All cache items accessed from the old path
|
||||||
|
* are accessible using the new path.
|
||||||
|
*
|
||||||
|
* CURRENTLY UNIMPLEMENTED
|
||||||
|
*
|
||||||
|
* Return value: -1 on error.
|
||||||
|
**/
|
||||||
|
int camel_data_cache_rename(CamelDataCache *cache,
|
||||||
|
const char *old, const char *new, CamelException *ex)
|
||||||
|
{
|
||||||
|
/* blah dont care yet */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_data_cache_clear:
|
||||||
|
* @cache:
|
||||||
|
* @path: Path to clear, or NULL to clear all items in
|
||||||
|
* all paths.
|
||||||
|
* @ex:
|
||||||
|
*
|
||||||
|
* Clear all items in a given cache path or all items in the cache.
|
||||||
|
*
|
||||||
|
* CURRENTLY_UNIMPLEMENTED
|
||||||
|
*
|
||||||
|
* Return value: -1 on error.
|
||||||
|
**/
|
||||||
|
int
|
||||||
|
camel_data_cache_clear(CamelDataCache *cache, const char *path, CamelException *ex)
|
||||||
|
{
|
||||||
|
/* nor for this? */
|
||||||
|
return -1;
|
||||||
|
}
|
98
camel/camel-data-cache.h
Normal file
98
camel/camel-data-cache.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
|
/* camel-data-cache.h: Class for a Camel filesystem cache
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Ximian, Inc. (www.ximian.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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_DATA_CACHE_H
|
||||||
|
#define CAMEL_DATA_CACHE_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#pragma }
|
||||||
|
#endif /* __cplusplus }*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <camel/camel-stream.h>
|
||||||
|
#include <camel/camel-exception.h>
|
||||||
|
|
||||||
|
#define CAMEL_DATA_CACHE_TYPE (camel_data_cache_get_type ())
|
||||||
|
#define CAMEL_DATA_CACHE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_DATA_CACHE_TYPE, CamelFolder))
|
||||||
|
#define CAMEL_DATA_CACHE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_DATA_CACHE_TYPE, CamelFolderClass))
|
||||||
|
#define CAMEL_IS_DATA_CACHE(o) (CAMEL_CHECK_TYPE((o), CAMEL_DATA_CACHE_TYPE))
|
||||||
|
|
||||||
|
typedef struct _CamelDataCache CamelDataCache;
|
||||||
|
typedef struct _CamelDataCacheClass CamelDataCacheClass;
|
||||||
|
|
||||||
|
struct _CamelDataCache {
|
||||||
|
CamelObject parent_object;
|
||||||
|
|
||||||
|
struct _CamelDataCachePrivate *priv;
|
||||||
|
|
||||||
|
char *path;
|
||||||
|
guint32 flags;
|
||||||
|
|
||||||
|
time_t expire_age;
|
||||||
|
time_t expire_access;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _CamelDataCacheClass {
|
||||||
|
CamelObjectClass parent_class;
|
||||||
|
|
||||||
|
/* None are virtual yet */
|
||||||
|
#if 0
|
||||||
|
/* Virtual methods */
|
||||||
|
CamelStream *(*add)(CamelDataCache *cmc, const char *path, const char *key, CamelException *ex);
|
||||||
|
CamelStream *(*get)(CamelDataCache *cmc, const char *path, const char *key, CamelException *ex);
|
||||||
|
int (*close)(CamelDataCache *cmc, CamelStream *stream, CamelException *ex);
|
||||||
|
int (*remove)(CamelDataCache *cmc, const char *path, const char *key, CamelException *ex);
|
||||||
|
|
||||||
|
int (*clear)(CamelDataCache *cmc, const char *path, CamelException *ex);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/* public methods */
|
||||||
|
CamelDataCache *camel_data_cache_new(const char *path, guint32 flags, CamelException *ex);
|
||||||
|
|
||||||
|
void camel_data_cache_set_expire_age(CamelDataCache *cache, time_t when);
|
||||||
|
void camel_data_cache_set_expire_access(CamelDataCache *cdc, time_t when);
|
||||||
|
|
||||||
|
int camel_data_cache_rename(CamelDataCache *cache,
|
||||||
|
const char *old, const char *new, CamelException *ex);
|
||||||
|
|
||||||
|
CamelStream *camel_data_cache_add(CamelDataCache *cache,
|
||||||
|
const char *path, const char *key, CamelException *ex);
|
||||||
|
CamelStream *camel_data_cache_get(CamelDataCache *cache,
|
||||||
|
const char *path, const char *key, CamelException *ex);
|
||||||
|
int camel_data_cache_remove(CamelDataCache *cache,
|
||||||
|
const char *path, const char *key, CamelException *ex);
|
||||||
|
|
||||||
|
int camel_data_cache_clear(CamelDataCache *cache,
|
||||||
|
const char *path, CamelException *ex);
|
||||||
|
|
||||||
|
/* Standard Camel function */
|
||||||
|
CamelType camel_data_cache_get_type (void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* CAMEL_DATA_CACHE_H */
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "camel-exception.h"
|
#include "camel-exception.h"
|
||||||
|
#include "e-util/e-memory.h"
|
||||||
|
|
||||||
/* i dont know why gthread_mutex stuff even exists, this is easier */
|
/* i dont know why gthread_mutex stuff even exists, this is easier */
|
||||||
|
|
||||||
@ -46,6 +47,8 @@ static pthread_mutex_t exception_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|||||||
#define CAMEL_EXCEPTION_UNLOCK(e)
|
#define CAMEL_EXCEPTION_UNLOCK(e)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static EMemChunk *exception_chunks = NULL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* camel_exception_new: allocate a new exception object.
|
* camel_exception_new: allocate a new exception object.
|
||||||
*
|
*
|
||||||
@ -59,12 +62,19 @@ camel_exception_new (void)
|
|||||||
{
|
{
|
||||||
CamelException *ex;
|
CamelException *ex;
|
||||||
|
|
||||||
ex = g_new (CamelException, 1);
|
CAMEL_EXCEPTION_LOCK(exception);
|
||||||
|
|
||||||
|
if (exception_chunks == NULL)
|
||||||
|
exception_chunks = e_memchunk_new(16, sizeof(CamelException));
|
||||||
|
|
||||||
|
ex = e_memchunk_alloc(exception_chunks);
|
||||||
ex->desc = NULL;
|
ex->desc = NULL;
|
||||||
|
|
||||||
/* set the Exception Id to NULL */
|
/* set the Exception Id to NULL */
|
||||||
ex->id = CAMEL_EXCEPTION_NONE;
|
ex->id = CAMEL_EXCEPTION_NONE;
|
||||||
|
|
||||||
|
CAMEL_EXCEPTION_UNLOCK(exception);
|
||||||
|
|
||||||
return ex;
|
return ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +139,12 @@ camel_exception_free (CamelException *exception)
|
|||||||
|
|
||||||
if (exception->desc)
|
if (exception->desc)
|
||||||
g_free (exception->desc);
|
g_free (exception->desc);
|
||||||
g_free (exception);
|
|
||||||
|
CAMEL_EXCEPTION_LOCK(exception);
|
||||||
|
|
||||||
|
e_memchunk_free(exception_chunks, exception);
|
||||||
|
|
||||||
|
CAMEL_EXCEPTION_UNLOCK(exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
*
|
||||||
/*
|
|
||||||
* Authors:
|
* Authors:
|
||||||
* Michael Zucchi <notzed@ximian.com>
|
* Michael Zucchi <notzed@ximian.com>
|
||||||
* Dan Winship <danw@ximian.com>
|
* Dan Winship <danw@ximian.com>
|
||||||
@ -24,6 +23,14 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "camel-file-utils.h"
|
#include "camel-file-utils.h"
|
||||||
|
#include "camel-url.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
@ -279,4 +286,37 @@ camel_file_util_decode_string (FILE *in, char **str)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make a directory heirarchy.
|
||||||
|
Always use full paths */
|
||||||
|
int
|
||||||
|
camel_file_util_mkdir(const char *path, mode_t mode)
|
||||||
|
{
|
||||||
|
char *copy, *p;
|
||||||
|
|
||||||
|
g_assert(path && path[0] == '/');
|
||||||
|
|
||||||
|
p = copy = alloca(strlen(path)+1);
|
||||||
|
strcpy(copy, path);
|
||||||
|
do {
|
||||||
|
p = strchr(p + 1, '/');
|
||||||
|
if (p)
|
||||||
|
*p = '\0';
|
||||||
|
if (access(copy, F_OK) == -1) {
|
||||||
|
if (mkdir(copy, mode) == -1)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (p)
|
||||||
|
*p = '/';
|
||||||
|
} while (p);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
camel_file_util_safe_filename(const char *name)
|
||||||
|
{
|
||||||
|
if (name == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return camel_url_encode(name, TRUE, "/?()'*");
|
||||||
|
}
|
||||||
|
@ -47,6 +47,9 @@ int camel_file_util_decode_off_t (FILE *in, off_t *);
|
|||||||
int camel_file_util_encode_string (FILE *out, const char *);
|
int camel_file_util_encode_string (FILE *out, const char *);
|
||||||
int camel_file_util_decode_string (FILE *in, char **);
|
int camel_file_util_decode_string (FILE *in, char **);
|
||||||
|
|
||||||
|
int camel_file_util_mkdir(const char *path, mode_t mode);
|
||||||
|
char *camel_file_util_safe_filename(const char *name);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
@ -1523,6 +1523,16 @@ folder_scan_init(void)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
drop_states(struct _header_scan_state *s)
|
||||||
|
{
|
||||||
|
while (s->parts) {
|
||||||
|
folder_scan_drop_step(s);
|
||||||
|
}
|
||||||
|
s->unstep = 0;
|
||||||
|
s->state = HSCAN_INITIAL;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
folder_scan_init_with_fd(struct _header_scan_state *s, int fd)
|
folder_scan_init_with_fd(struct _header_scan_state *s, int fd)
|
||||||
{
|
{
|
||||||
@ -1530,6 +1540,7 @@ folder_scan_init_with_fd(struct _header_scan_state *s, int fd)
|
|||||||
|
|
||||||
len = read(fd, s->inbuf, SCAN_BUF);
|
len = read(fd, s->inbuf, SCAN_BUF);
|
||||||
if (len>=0) {
|
if (len>=0) {
|
||||||
|
drop_states(s);
|
||||||
s->inend = s->inbuf+len;
|
s->inend = s->inbuf+len;
|
||||||
s->inptr = s->inbuf;
|
s->inptr = s->inbuf;
|
||||||
s->inend[0] = '\n';
|
s->inend[0] = '\n';
|
||||||
@ -1555,6 +1566,7 @@ folder_scan_init_with_stream(struct _header_scan_state *s, CamelStream *stream)
|
|||||||
|
|
||||||
len = camel_stream_read(stream, s->inbuf, SCAN_BUF);
|
len = camel_stream_read(stream, s->inbuf, SCAN_BUF);
|
||||||
if (len >= 0) {
|
if (len >= 0) {
|
||||||
|
drop_states(s);
|
||||||
s->inend = s->inbuf+len;
|
s->inend = s->inbuf+len;
|
||||||
s->inptr = s->inbuf;
|
s->inptr = s->inbuf;
|
||||||
s->inend[0] = '\n';
|
s->inend[0] = '\n';
|
||||||
|
@ -18,23 +18,37 @@ INCLUDES = -I../.. \
|
|||||||
-DG_LOG_DOMAIN=\"camel-nntp-provider\"
|
-DG_LOG_DOMAIN=\"camel-nntp-provider\"
|
||||||
|
|
||||||
libcamelnntp_la_SOURCES = \
|
libcamelnntp_la_SOURCES = \
|
||||||
camel-nntp-auth.c \
|
|
||||||
camel-nntp-folder.c \
|
|
||||||
camel-nntp-grouplist.c \
|
|
||||||
camel-nntp-newsrc.c \
|
|
||||||
camel-nntp-provider.c \
|
camel-nntp-provider.c \
|
||||||
camel-nntp-store.c \
|
camel-nntp-store.c \
|
||||||
camel-nntp-utils.c
|
camel-nntp-folder.c \
|
||||||
|
camel-nntp-stream.c \
|
||||||
|
camel-nntp-summary.c
|
||||||
|
|
||||||
|
# camel-nntp-auth.c \
|
||||||
|
# camel-nntp-folder.c \
|
||||||
|
# camel-nntp-grouplist.c \
|
||||||
|
# camel-nntp-newsrc.c \
|
||||||
|
# camel-nntp-provider.c \
|
||||||
|
# camel-nntp-store.c \
|
||||||
|
# camel-nntp-utils.c
|
||||||
|
|
||||||
libcamelnntpinclude_HEADERS = \
|
libcamelnntpinclude_HEADERS = \
|
||||||
camel-nntp-auth.h \
|
|
||||||
camel-nntp-folder.h \
|
|
||||||
camel-nntp-grouplist.h \
|
|
||||||
camel-nntp-newsrc.h \
|
|
||||||
camel-nntp-resp-codes.h \
|
|
||||||
camel-nntp-store.h \
|
camel-nntp-store.h \
|
||||||
camel-nntp-types.h \
|
camel-nntp-folder.h \
|
||||||
camel-nntp-utils.h
|
camel-nntp-stream.h \
|
||||||
|
camel-nntp-summary.h
|
||||||
|
|
||||||
|
# camel-nntp-auth.h \
|
||||||
|
# camel-nntp-folder.h \
|
||||||
|
# camel-nntp-grouplist.h \
|
||||||
|
# camel-nntp-newsrc.h \
|
||||||
|
# camel-nntp-resp-codes.h \
|
||||||
|
# camel-nntp-store.h \
|
||||||
|
# camel-nntp-types.h \
|
||||||
|
# camel-nntp-utils.h
|
||||||
|
|
||||||
|
noinst_HEADERS = \
|
||||||
|
camel-nntp-private.h
|
||||||
|
|
||||||
libcamelnntp_la_LDFLAGS = -version-info 0:0:0
|
libcamelnntp_la_LDFLAGS = -version-info 0:0:0
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||||||
/* camel-nntp-folder.c : Abstract class for an email folder */
|
/* camel-nntp-folder.c : Class for a news folder
|
||||||
|
|
||||||
/*
|
|
||||||
* Author : Chris Toshok <toshok@ximian.com>
|
|
||||||
*
|
*
|
||||||
* Copyright (C) 2000 Ximian .
|
* Authors : Chris Toshok <toshok@ximian.com>
|
||||||
|
* Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2001 Ximian .
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of version 2 of the GNU General Public
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
@ -33,21 +33,20 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "camel-folder-summary.h"
|
#include "camel/string-utils.h"
|
||||||
#include "camel-nntp-resp-codes.h"
|
#include "camel/camel-stream-mem.h"
|
||||||
|
#include "camel/camel-data-wrapper.h"
|
||||||
|
#include "camel/camel-mime-message.h"
|
||||||
|
#include "camel/camel-folder-search.h"
|
||||||
|
#include "camel/camel-exception.h"
|
||||||
|
#include "camel/camel-session.h"
|
||||||
|
#include "camel/camel-data-cache.h"
|
||||||
|
|
||||||
|
#include "camel-nntp-summary.h"
|
||||||
#include "camel-nntp-store.h"
|
#include "camel-nntp-store.h"
|
||||||
#include "camel-nntp-folder.h"
|
#include "camel-nntp-folder.h"
|
||||||
#include "camel-nntp-store.h"
|
#include "camel-nntp-store.h"
|
||||||
#include "camel-nntp-utils.h"
|
#include "camel-nntp-private.h"
|
||||||
|
|
||||||
#include "string-utils.h"
|
|
||||||
#include "camel-stream-mem.h"
|
|
||||||
#include "camel-data-wrapper.h"
|
|
||||||
#include "camel-mime-message.h"
|
|
||||||
#include "camel-folder-summary.h"
|
|
||||||
#include "camel-folder-search.h"
|
|
||||||
|
|
||||||
#include "camel-exception.h"
|
|
||||||
|
|
||||||
static CamelFolderClass *parent_class=NULL;
|
static CamelFolderClass *parent_class=NULL;
|
||||||
|
|
||||||
@ -56,131 +55,131 @@ static CamelFolderClass *parent_class=NULL;
|
|||||||
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
#define CF_CLASS(so) CAMEL_FOLDER_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||||
#define CNNTPS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
#define CNNTPS_CLASS(so) CAMEL_STORE_CLASS (CAMEL_OBJECT_GET_CLASS(so))
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nntp_folder_sync (CamelFolder *folder, gboolean expunge,
|
nntp_folder_sync (CamelFolder *folder, gboolean expunge, CamelException *ex)
|
||||||
CamelException *ex)
|
|
||||||
{
|
{
|
||||||
CamelNNTPStore *store;
|
CamelNNTPStore *nntp_store;
|
||||||
|
CamelFolderChangeInfo *changes = NULL;
|
||||||
|
CamelNNTPFolder *nntp_folder;
|
||||||
|
|
||||||
camel_folder_summary_save (folder->summary);
|
nntp_store = (CamelNNTPStore *)folder->parent_store;
|
||||||
|
nntp_folder = (CamelNNTPFolder *)folder;
|
||||||
|
|
||||||
store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder));
|
CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
|
||||||
|
|
||||||
if (store->newsrc)
|
if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, nntp_folder->changes, ex) != -1)
|
||||||
camel_nntp_newsrc_write (store->newsrc);
|
camel_folder_summary_save (folder->summary);
|
||||||
|
|
||||||
|
if (camel_folder_change_info_changed(nntp_folder->changes)) {
|
||||||
|
changes = nntp_folder->changes;
|
||||||
|
nntp_folder->changes = camel_folder_change_info_new();
|
||||||
|
}
|
||||||
|
|
||||||
|
CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes);
|
||||||
|
camel_folder_change_info_free(changes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nntp_folder_set_message_flags (CamelFolder *folder, const char *uid,
|
nntp_folder_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set)
|
||||||
guint32 flags, guint32 set)
|
|
||||||
{
|
{
|
||||||
((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set);
|
((CamelFolderClass *)parent_class)->set_message_flags(folder, uid, flags, set);
|
||||||
|
|
||||||
if (flags & set & CAMEL_MESSAGE_SEEN) {
|
|
||||||
int article_num;
|
|
||||||
CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (camel_folder_get_parent_store (folder));
|
|
||||||
|
|
||||||
sscanf (uid, "%d", &article_num);
|
|
||||||
|
|
||||||
camel_nntp_newsrc_mark_article_read (nntp_store->newsrc,
|
|
||||||
folder->name,
|
|
||||||
article_num);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static CamelMimeMessage *
|
static CamelMimeMessage *
|
||||||
nntp_folder_get_message (CamelFolder *folder, const gchar *uid, CamelException *ex)
|
nntp_folder_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
|
||||||
{
|
{
|
||||||
CamelStream *message_stream = NULL;
|
|
||||||
CamelMimeMessage *message = NULL;
|
CamelMimeMessage *message = NULL;
|
||||||
CamelStore *parent_store;
|
CamelNNTPStore *nntp_store;
|
||||||
char *buf;
|
CamelFolderChangeInfo *changes;
|
||||||
int buf_len;
|
CamelNNTPFolder *nntp_folder;
|
||||||
int buf_alloc;
|
CamelStream *stream = NULL;
|
||||||
int status;
|
int ret;
|
||||||
gboolean done;
|
char *line;
|
||||||
char *message_id;
|
const char *msgid;
|
||||||
|
|
||||||
/* get the parent store */
|
nntp_store = (CamelNNTPStore *)folder->parent_store;
|
||||||
parent_store = camel_folder_get_parent_store (folder);
|
nntp_folder = (CamelNNTPFolder *)folder;
|
||||||
|
|
||||||
message_id = strchr (uid, ',');
|
CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
|
||||||
if (message_id) {
|
|
||||||
message_id++;
|
msgid = strchr(uid, ',');
|
||||||
status = camel_nntp_command (CAMEL_NNTP_STORE( parent_store ), ex, NULL, "ARTICLE %s", message_id);
|
if (msgid == 0) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
|
||||||
|
_("Internal error: uid in invalid format: %s"), uid);
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
msgid++;
|
||||||
|
|
||||||
/* if the message_id was not found, raise an exception and return */
|
/* Lookup in cache, NEWS is global messageid's so use a global cache path */
|
||||||
if (message_id == NULL || status == NNTP_NO_SUCH_ARTICLE) {
|
stream = camel_data_cache_get(nntp_store->cache, "cache", msgid, NULL);
|
||||||
camel_exception_setv (ex,
|
if (stream == NULL) {
|
||||||
CAMEL_EXCEPTION_FOLDER_INVALID_UID,
|
/* Not in cache, retrieve and put in cache */
|
||||||
_("Message %s not found."),
|
if (camel_nntp_store_set_folder(nntp_store, folder, nntp_folder->changes, ex) == -1)
|
||||||
uid);
|
goto fail;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else if (status != NNTP_ARTICLE_FOLLOWS) {
|
|
||||||
/* XXX */
|
|
||||||
g_warning ("weird nntp error %d\n", status);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this could probably done fairly easily with an nntp stream that
|
ret = camel_nntp_command(nntp_store, &line, "article %s", msgid);
|
||||||
returns eof after '.' */
|
if (ret == -1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
/* XXX ick ick ick. read the entire message into a buffer and
|
if (ret == 220) {
|
||||||
then create a stream_mem for it. */
|
stream = camel_data_cache_add(nntp_store->cache, "cache", msgid, NULL);
|
||||||
buf_alloc = 2048;
|
if (stream) {
|
||||||
buf_len = 0;
|
if (camel_stream_write_to_stream((CamelStream *)nntp_store->stream, stream) == -1)
|
||||||
buf = g_malloc(buf_alloc);
|
goto error;
|
||||||
done = FALSE;
|
if (camel_stream_reset(stream) == -1)
|
||||||
|
goto error;
|
||||||
buf[0] = 0;
|
} else {
|
||||||
|
stream = (CamelStream *)nntp_store->stream;
|
||||||
while (!done) {
|
camel_object_ref((CamelObject *)stream);
|
||||||
int line_length;
|
|
||||||
char *line;
|
|
||||||
|
|
||||||
if (camel_remote_store_recv_line (CAMEL_REMOTE_STORE (parent_store), &line, ex) < 0) {
|
|
||||||
g_warning ("recv_line failed while building message\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* XXX check exception */
|
|
||||||
|
|
||||||
line_length = strlen ( line );
|
|
||||||
|
|
||||||
if (!strcmp(line, ".")) {
|
|
||||||
done = TRUE;
|
|
||||||
g_free (line);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (buf_len + line_length > buf_alloc) {
|
|
||||||
buf_alloc *= 2;
|
|
||||||
buf = g_realloc (buf, buf_alloc);
|
|
||||||
}
|
}
|
||||||
strcat(buf, line);
|
|
||||||
strcat(buf, "\n");
|
|
||||||
buf_len += strlen(line) + 1;
|
|
||||||
g_free (line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a stream bound to the message */
|
if (stream) {
|
||||||
message_stream = camel_stream_mem_new_with_buffer(buf, buf_len);
|
message = camel_mime_message_new();
|
||||||
|
if (camel_data_wrapper_construct_from_stream((CamelDataWrapper *)message, stream) == -1)
|
||||||
|
goto error;
|
||||||
|
|
||||||
message = camel_mime_message_new ();
|
CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
|
||||||
camel_data_wrapper_construct_from_stream (CAMEL_DATA_WRAPPER(message), message_stream);
|
|
||||||
|
|
||||||
camel_object_unref (CAMEL_OBJECT (message_stream));
|
camel_object_unref((CamelObject *)stream);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, line);
|
||||||
gtk_signal_connect (CAMEL_OBJECT (message), "message_changed", message_changed, folder);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_free (buf);
|
error:
|
||||||
|
if (errno == EINTR)
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("User cancelled"));
|
||||||
|
else
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot get message %s: %s"), uid, strerror(errno));
|
||||||
|
|
||||||
return message;
|
fail:
|
||||||
|
if (message)
|
||||||
|
camel_object_unref((CamelObject *)message);
|
||||||
|
|
||||||
|
if (stream)
|
||||||
|
camel_object_unref((CamelObject *)stream);
|
||||||
|
|
||||||
|
if (camel_folder_change_info_changed(nntp_folder->changes)) {
|
||||||
|
changes = nntp_folder->changes;
|
||||||
|
nntp_folder->changes = camel_folder_change_info_new();
|
||||||
|
} else {
|
||||||
|
changes = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes);
|
||||||
|
camel_folder_change_info_free(changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GPtrArray*
|
static GPtrArray*
|
||||||
@ -189,6 +188,8 @@ nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, C
|
|||||||
CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
|
CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
|
||||||
GPtrArray *matches, *summary;
|
GPtrArray *matches, *summary;
|
||||||
|
|
||||||
|
CAMEL_NNTP_FOLDER_LOCK(nntp_folder, search_lock);
|
||||||
|
|
||||||
if(nntp_folder->search == NULL)
|
if(nntp_folder->search == NULL)
|
||||||
nntp_folder->search = camel_folder_search_new();
|
nntp_folder->search = camel_folder_search_new();
|
||||||
|
|
||||||
@ -198,11 +199,54 @@ nntp_folder_search_by_expression (CamelFolder *folder, const char *expression, C
|
|||||||
|
|
||||||
matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex);
|
matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex);
|
||||||
|
|
||||||
|
CAMEL_NNTP_FOLDER_UNLOCK(nntp_folder, search_lock);
|
||||||
|
|
||||||
camel_folder_free_summary(folder, summary);
|
camel_folder_free_summary(folder, summary);
|
||||||
|
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GPtrArray *
|
||||||
|
nntp_folder_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER(folder);
|
||||||
|
GPtrArray *summary, *matches;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* NOTE: could get away without the search lock by creating a new
|
||||||
|
search object each time */
|
||||||
|
|
||||||
|
summary = g_ptr_array_new();
|
||||||
|
for (i=0;i<uids->len;i++) {
|
||||||
|
CamelMessageInfo *info;
|
||||||
|
|
||||||
|
info = camel_folder_get_message_info(folder, uids->pdata[i]);
|
||||||
|
if (info)
|
||||||
|
g_ptr_array_add(summary, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (summary->len == 0)
|
||||||
|
return summary;
|
||||||
|
|
||||||
|
CAMEL_NNTP_FOLDER_LOCK(folder, search_lock);
|
||||||
|
|
||||||
|
if (nntp_folder->search == NULL)
|
||||||
|
nntp_folder->search = camel_folder_search_new();
|
||||||
|
|
||||||
|
camel_folder_search_set_folder(nntp_folder->search, folder);
|
||||||
|
camel_folder_search_set_summary(nntp_folder->search, summary);
|
||||||
|
|
||||||
|
matches = camel_folder_search_execute_expression(nntp_folder->search, expression, ex);
|
||||||
|
|
||||||
|
CAMEL_NNTP_FOLDER_UNLOCK(folder, search_lock);
|
||||||
|
|
||||||
|
for (i=0;i<summary->len;i++)
|
||||||
|
camel_folder_free_message_info(folder, summary->pdata[i]);
|
||||||
|
g_ptr_array_free(summary, TRUE);
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nntp_folder_search_free(CamelFolder *folder, GPtrArray *result)
|
nntp_folder_search_free(CamelFolder *folder, GPtrArray *result)
|
||||||
{
|
{
|
||||||
@ -213,15 +257,19 @@ nntp_folder_search_free(CamelFolder *folder, GPtrArray *result)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
nntp_folder_finalize (CamelObject *object)
|
nntp_folder_init(CamelNNTPFolder *nntp_folder, CamelNNTPFolderClass *klass)
|
||||||
{
|
{
|
||||||
CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (object);
|
nntp_folder->changes = camel_folder_change_info_new();
|
||||||
|
|
||||||
g_free (nntp_folder->summary_file_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class)
|
nntp_folder_finalise (CamelNNTPFolder *nntp_folder)
|
||||||
|
{
|
||||||
|
g_free(nntp_folder->storage_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class)
|
||||||
{
|
{
|
||||||
CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_nntp_folder_class);
|
CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_nntp_folder_class);
|
||||||
|
|
||||||
@ -234,6 +282,7 @@ camel_nntp_folder_class_init (CamelNNTPFolderClass *camel_nntp_folder_class)
|
|||||||
camel_folder_class->set_message_flags = nntp_folder_set_message_flags;
|
camel_folder_class->set_message_flags = nntp_folder_set_message_flags;
|
||||||
camel_folder_class->get_message = nntp_folder_get_message;
|
camel_folder_class->get_message = nntp_folder_get_message;
|
||||||
camel_folder_class->search_by_expression = nntp_folder_search_by_expression;
|
camel_folder_class->search_by_expression = nntp_folder_search_by_expression;
|
||||||
|
camel_folder_class->search_by_uids = nntp_folder_search_by_uids;
|
||||||
camel_folder_class->search_free = nntp_folder_search_free;
|
camel_folder_class->search_free = nntp_folder_search_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,46 +295,97 @@ camel_nntp_folder_get_type (void)
|
|||||||
camel_nntp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelNNTPFolder",
|
camel_nntp_folder_type = camel_type_register (CAMEL_FOLDER_TYPE, "CamelNNTPFolder",
|
||||||
sizeof (CamelNNTPFolder),
|
sizeof (CamelNNTPFolder),
|
||||||
sizeof (CamelNNTPFolderClass),
|
sizeof (CamelNNTPFolderClass),
|
||||||
(CamelObjectClassInitFunc) camel_nntp_folder_class_init,
|
(CamelObjectClassInitFunc) nntp_folder_class_init,
|
||||||
NULL,
|
NULL,
|
||||||
(CamelObjectInitFunc) NULL,
|
(CamelObjectInitFunc) nntp_folder_init,
|
||||||
(CamelObjectFinalizeFunc) nntp_folder_finalize);
|
(CamelObjectFinalizeFunc) nntp_folder_finalise);
|
||||||
}
|
}
|
||||||
|
|
||||||
return camel_nntp_folder_type;
|
return camel_nntp_folder_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* not yet */
|
||||||
|
/* Idea is we update in stages, but this requires a different xover command, etc */
|
||||||
|
#ifdef ASYNC_SUMMARY
|
||||||
|
struct _folder_check_msg {
|
||||||
|
CamelSessionThreadMsg msg;
|
||||||
|
CamelNNTPFolder *folder;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
folder_check(CamelSession *session, CamelSessionThreadMsg *msg)
|
||||||
|
{
|
||||||
|
struct _folder_check_msg *m = (struct _folder_check_msg *)msg;
|
||||||
|
CamelException *ex;
|
||||||
|
CamelNNTPStore *nntp_store;
|
||||||
|
|
||||||
|
nntp_store = (CamelNNTPStore *)m->folder->parent.parent_store;
|
||||||
|
|
||||||
|
CAMEL_NNTP_STORE_LOCK(nntp_store, command_lock);
|
||||||
|
|
||||||
|
ex = camel_exception_new();
|
||||||
|
camel_nntp_summary_check((CamelNNTPSummary *)m->folder->parent.summary, m->folder->changes, ex);
|
||||||
|
camel_exception_free(ex);
|
||||||
|
|
||||||
|
CAMEL_NNTP_STORE_UNLOCK(nntp_store, command_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
folder_check_free(CamelSession *session, CamelSessionThreadMsg *msg)
|
||||||
|
{
|
||||||
|
struct _folder_check_msg *m = (struct _folder_check_msg *)msg;
|
||||||
|
|
||||||
|
camel_object_unref((CamelObject *)m->folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
static CamelSessionThreadOps folder_check_ops = {
|
||||||
|
folder_check,
|
||||||
|
folder_check_free,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
CamelFolder *
|
CamelFolder *
|
||||||
camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelException *ex)
|
camel_nntp_folder_new (CamelStore *parent, const char *folder_name, CamelException *ex)
|
||||||
{
|
{
|
||||||
CamelFolder *folder = CAMEL_FOLDER (camel_object_new (CAMEL_NNTP_FOLDER_TYPE));
|
CamelFolder *folder;
|
||||||
CamelNNTPFolder *nntp_folder = CAMEL_NNTP_FOLDER (folder);
|
CamelNNTPFolder *nntp_folder;
|
||||||
const gchar *root_dir_path;
|
char *root;
|
||||||
|
CamelService *service;
|
||||||
|
#ifdef ASYNC_SUMMARY
|
||||||
|
struct _folder_check_msg *m;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
service = (CamelService *)parent;
|
||||||
|
root = camel_session_get_storage_path(service->session, service, ex);
|
||||||
|
if (root == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* If this doesn't work, stuff wont save, but let it continue anyway */
|
||||||
|
(void) camel_mkdir_hier(root, 0777);
|
||||||
|
|
||||||
|
folder = (CamelFolder *) camel_object_new (CAMEL_NNTP_FOLDER_TYPE);
|
||||||
|
nntp_folder = (CamelNNTPFolder *)folder;
|
||||||
|
|
||||||
camel_folder_construct (folder, parent, folder_name, folder_name);
|
camel_folder_construct (folder, parent, folder_name, folder_name);
|
||||||
folder->folder_flags |= (CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY |
|
folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY|CAMEL_FOLDER_HAS_SEARCH_CAPABILITY;
|
||||||
CAMEL_FOLDER_HAS_SEARCH_CAPABILITY);
|
|
||||||
|
|
||||||
root_dir_path = camel_nntp_store_get_toplevel_dir (CAMEL_NNTP_STORE(folder->parent_store));
|
nntp_folder->storage_path = g_strdup_printf("%s/%s", root, folder->full_name);
|
||||||
nntp_folder->summary_file_path = g_strdup_printf ("%s/%s-ev-summary",
|
g_free(root);
|
||||||
root_dir_path,
|
|
||||||
folder->name);
|
|
||||||
|
|
||||||
folder->summary = camel_folder_summary_new ();
|
|
||||||
camel_folder_summary_set_filename (folder->summary,
|
|
||||||
nntp_folder->summary_file_path);
|
|
||||||
|
|
||||||
|
folder->summary = (CamelFolderSummary *)camel_nntp_summary_new(nntp_folder);
|
||||||
camel_folder_summary_load (folder->summary);
|
camel_folder_summary_load (folder->summary);
|
||||||
|
#ifdef ASYNC_SUMMARY
|
||||||
camel_nntp_get_headers (CAMEL_FOLDER( folder )->parent_store,
|
m = camel_session_thread_msg_new(service->session, &folder_check_ops, sizeof(*m));
|
||||||
nntp_folder, ex);
|
m->folder = nntp_folder;
|
||||||
if (camel_exception_get_id (ex)) {
|
camel_object_ref((CamelObject *)folder);
|
||||||
camel_object_unref (CAMEL_OBJECT (folder));
|
camel_session_thread_queue(service->session, &m->msg, 0);
|
||||||
return NULL;
|
#else
|
||||||
|
if (camel_nntp_summary_check((CamelNNTPSummary *)folder->summary, nntp_folder->changes, ex) == -1) {
|
||||||
|
camel_object_unref((CamelObject *)folder);
|
||||||
|
folder = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* XXX check return value */
|
|
||||||
camel_folder_summary_save (folder->summary);
|
|
||||||
|
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ extern "C" {
|
|||||||
#pragma }
|
#pragma }
|
||||||
#endif /* __cplusplus }*/
|
#endif /* __cplusplus }*/
|
||||||
|
|
||||||
#include "camel-folder.h"
|
#include "camel/camel-folder.h"
|
||||||
|
|
||||||
/* #include "camel-store.h" */
|
/* #include "camel-store.h" */
|
||||||
|
|
||||||
@ -41,25 +41,23 @@ extern "C" {
|
|||||||
#define CAMEL_NNTP_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_NNTP_FOLDER_TYPE, CamelNNTPFolderClass))
|
#define CAMEL_NNTP_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_NNTP_FOLDER_TYPE, CamelNNTPFolderClass))
|
||||||
#define CAMEL_IS_NNTP_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_FOLDER_TYPE))
|
#define CAMEL_IS_NNTP_FOLDER(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_FOLDER_TYPE))
|
||||||
|
|
||||||
|
typedef struct _CamelNNTPFolder {
|
||||||
|
CamelFolder parent;
|
||||||
|
|
||||||
typedef struct {
|
struct _CamelNNTPFolderPrivate *priv;
|
||||||
CamelFolder parent_object;
|
|
||||||
|
|
||||||
gchar *summary_file_path; /* contains the messages summary */
|
struct _CamelFolderChangeInfo *changes;
|
||||||
CamelFolderSummary *summary;
|
char *storage_path;
|
||||||
CamelFolderSearch *search;
|
CamelFolderSearch *search;
|
||||||
} CamelNNTPFolder;
|
} CamelNNTPFolder;
|
||||||
|
|
||||||
|
typedef struct _CamelNNTPFolderClass {
|
||||||
|
CamelFolderClass parent;
|
||||||
typedef struct {
|
|
||||||
CamelFolderClass parent_class;
|
|
||||||
|
|
||||||
/* Virtual methods */
|
/* Virtual methods */
|
||||||
|
|
||||||
} CamelNNTPFolderClass;
|
} CamelNNTPFolderClass;
|
||||||
|
|
||||||
|
|
||||||
/* public methods */
|
/* public methods */
|
||||||
|
|
||||||
/* Standard Camel function */
|
/* Standard Camel function */
|
||||||
|
74
camel/providers/nntp/camel-nntp-private.h
Normal file
74
camel/providers/nntp/camel-nntp-private.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||||
|
* camel-nntp-private.h: Private info for nntp.
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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_PRIVATE_H
|
||||||
|
#define CAMEL_NNTP_PRIVATE_H 1
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#pragma }
|
||||||
|
#endif /* __cplusplus }*/
|
||||||
|
|
||||||
|
/* need a way to configure and save this data, if this header is to
|
||||||
|
be installed. For now, dont install it */
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
#include "e-util/e-msgport.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _CamelNNTPStorePrivate {
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
EMutex *command_lock; /* for locking the command stream for a complete operation */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
#define CAMEL_NNTP_STORE_LOCK(f, l) (e_mutex_lock(((CamelNNTPStore *)f)->priv->l))
|
||||||
|
#define CAMEL_NNTP_STORE_UNLOCK(f, l) (e_mutex_unlock(((CamelNNTPStore *)f)->priv->l))
|
||||||
|
#else
|
||||||
|
#define CAMEL_NNTP_STORE_LOCK(f, l)
|
||||||
|
#define CAMEL_NNTP_STORE_UNLOCK(f, l)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct _CamelNNTPFolderPrivate {
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
GMutex *search_lock; /* for locking the search object */
|
||||||
|
GMutex *cache_lock; /* for locking the cache object */
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef ENABLE_THREADS
|
||||||
|
#define CAMEL_NNTP_FOLDER_LOCK(f, l) (g_mutex_lock(((CamelNNTPFolder *)f)->priv->l))
|
||||||
|
#define CAMEL_NNTP_FOLDER_UNLOCK(f, l) (g_mutex_unlock(((CamelNNTPFolder *)f)->priv->l))
|
||||||
|
#else
|
||||||
|
#define CAMEL_NNTP_FOLDER_LOCK(f, l)
|
||||||
|
#define CAMEL_NNTP_FOLDER_UNLOCK(f, l)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
#endif /* CAMEL_NNTP_PRIVATE_H */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -30,41 +30,15 @@ extern "C" {
|
|||||||
#pragma }
|
#pragma }
|
||||||
#endif /* __cplusplus }*/
|
#endif /* __cplusplus }*/
|
||||||
|
|
||||||
#include "camel-remote-store.h"
|
#include "camel/camel-remote-store.h"
|
||||||
#include "camel-nntp-newsrc.h"
|
#include "camel/camel-exception.h"
|
||||||
#include "camel-nntp-types.h"
|
#include "camel/camel-folder.h"
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#ifdef ENABLE_THREADS
|
|
||||||
#include "e-util/e-msgport.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CAMEL_NNTP_STORE_TYPE (camel_nntp_store_get_type ())
|
#define CAMEL_NNTP_STORE_TYPE (camel_nntp_store_get_type ())
|
||||||
#define CAMEL_NNTP_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_NNTP_STORE_TYPE, CamelNNTPStore))
|
#define CAMEL_NNTP_STORE(obj) (CAMEL_CHECK_CAST((obj), CAMEL_NNTP_STORE_TYPE, CamelNNTPStore))
|
||||||
#define CAMEL_NNTP_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_NNTP_STORE_TYPE, CamelNNTPStoreClass))
|
#define CAMEL_NNTP_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_NNTP_STORE_TYPE, CamelNNTPStoreClass))
|
||||||
#define CAMEL_IS_NNTP_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_STORE_TYPE))
|
#define CAMEL_IS_NNTP_STORE(o) (CAMEL_CHECK_TYPE((o), CAMEL_NNTP_STORE_TYPE))
|
||||||
|
|
||||||
enum {
|
|
||||||
CAMEL_NNTP_OVER_FROM,
|
|
||||||
CAMEL_NNTP_OVER_SUBJECT,
|
|
||||||
CAMEL_NNTP_OVER_DATE,
|
|
||||||
CAMEL_NNTP_OVER_MESSAGE_ID,
|
|
||||||
CAMEL_NNTP_OVER_REFERENCES,
|
|
||||||
CAMEL_NNTP_OVER_BYTES,
|
|
||||||
|
|
||||||
CAMEL_NNTP_OVER_LAST
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CamelNNTPOverField {
|
|
||||||
int index;
|
|
||||||
gboolean full; /* full in the OVER sense - the field name
|
|
||||||
precedes the ':' in the XOVER list. */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CamelNNTPStore {
|
|
||||||
CamelRemoteStore parent_object;
|
|
||||||
|
|
||||||
#define CAMEL_NNTP_EXT_SEARCH (1<<0)
|
#define CAMEL_NNTP_EXT_SEARCH (1<<0)
|
||||||
#define CAMEL_NNTP_EXT_SETGET (1<<1)
|
#define CAMEL_NNTP_EXT_SETGET (1<<1)
|
||||||
#define CAMEL_NNTP_EXT_OVER (1<<2)
|
#define CAMEL_NNTP_EXT_OVER (1<<2)
|
||||||
@ -73,51 +47,38 @@ struct CamelNNTPStore {
|
|||||||
#define CAMEL_NNTP_EXT_LISTMOTD (1<<5)
|
#define CAMEL_NNTP_EXT_LISTMOTD (1<<5)
|
||||||
#define CAMEL_NNTP_EXT_LISTSUBSCR (1<<6)
|
#define CAMEL_NNTP_EXT_LISTSUBSCR (1<<6)
|
||||||
#define CAMEL_NNTP_EXT_LISTPNAMES (1<<7)
|
#define CAMEL_NNTP_EXT_LISTPNAMES (1<<7)
|
||||||
|
|
||||||
|
typedef struct _CamelNNTPStore CamelNNTPStore;
|
||||||
|
typedef struct _CamelNNTPStoreClass CamelNNTPStoreClass;
|
||||||
|
|
||||||
|
struct _CamelNNTPStore {
|
||||||
|
CamelRemoteStore parent_object;
|
||||||
|
|
||||||
|
struct _CamelNNTPStorePrivate *priv;
|
||||||
|
|
||||||
guint32 extensions;
|
guint32 extensions;
|
||||||
|
|
||||||
gboolean posting_allowed;
|
gboolean posting_allowed;
|
||||||
|
|
||||||
int num_overview_fields;
|
struct _CamelNNTPStream *stream;
|
||||||
CamelNNTPOverField overview_field[ CAMEL_NNTP_OVER_LAST ];
|
struct _CamelStreamMem *mem;
|
||||||
|
|
||||||
CamelNNTPNewsrc *newsrc;
|
struct _CamelDataCache *cache;
|
||||||
CamelNNTPGroupList *group_list;
|
|
||||||
|
|
||||||
#ifdef ENABLE_THREADS
|
char *current_folder;
|
||||||
EMutex *command_lock;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CamelNNTPStoreClass {
|
struct _CamelNNTPStoreClass {
|
||||||
CamelRemoteStoreClass parent_class;
|
CamelRemoteStoreClass parent_class;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_THREADS
|
|
||||||
#define CAMEL_NNTP_STORE_LOCK(f) (e_mutex_lock(((CamelNNTPStore *) f)->command_lock))
|
|
||||||
#define CAMEL_NNTP_STORE_UNLOCK(f) (e_mutex_unlock(((CamelNNTPStore *) f)->command_lock))
|
|
||||||
#else
|
|
||||||
#define CAMEL_NNTP_STORE_LOCK(f)
|
|
||||||
#define CAMEL_NNTP_STORE_UNLOCK(f)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
int camel_nntp_command (CamelNNTPStore *store, CamelException *ex, char **ret, char *fmt, ...);
|
|
||||||
|
|
||||||
/* Standard Camel function */
|
/* Standard Camel function */
|
||||||
CamelType camel_nntp_store_get_type (void);
|
CamelType camel_nntp_store_get_type (void);
|
||||||
|
|
||||||
|
int camel_nntp_command(CamelNNTPStore *store, char **line, const char *fmt, ...);
|
||||||
|
int camel_nntp_store_set_folder(CamelNNTPStore *store, CamelFolder *folder, CamelFolderChangeInfo *changes, CamelException *ex);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
462
camel/providers/nntp/camel-nntp-stream.c
Normal file
462
camel/providers/nntp/camel-nntp-stream.c
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*-
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* Copyright 1999, 2000 Ximian, Inc. (www.ximian.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include "camel-nntp-stream.h"
|
||||||
|
|
||||||
|
extern int camel_verbose_debug;
|
||||||
|
#define dd(x) (camel_verbose_debug?(x):0)
|
||||||
|
|
||||||
|
static CamelObjectClass *parent_class = NULL;
|
||||||
|
|
||||||
|
/* Returns the class for a CamelStream */
|
||||||
|
#define CS_CLASS(so) CAMEL_NNTP_STREAM_CLASS(CAMEL_OBJECT_GET_CLASS(so))
|
||||||
|
|
||||||
|
#define CAMEL_NNTP_STREAM_SIZE (4096)
|
||||||
|
#define CAMEL_NNTP_STREAM_LINE (1024) /* maximum line size */
|
||||||
|
|
||||||
|
static int
|
||||||
|
stream_fill(CamelNNTPStream *is)
|
||||||
|
{
|
||||||
|
int left = 0;
|
||||||
|
|
||||||
|
if (is->source) {
|
||||||
|
left = is->end - is->ptr;
|
||||||
|
memcpy(is->buf, is->ptr, left);
|
||||||
|
is->end = is->buf + left;
|
||||||
|
is->ptr = is->buf;
|
||||||
|
left = camel_stream_read(is->source, is->end, CAMEL_NNTP_STREAM_SIZE - (is->end - is->buf));
|
||||||
|
if (left > 0) {
|
||||||
|
is->end += left;
|
||||||
|
is->end[0] = '\n';
|
||||||
|
return is->end - is->ptr;
|
||||||
|
} else {
|
||||||
|
dd(printf("NNTP_STREAM_FILL(ERROR): '%s'\n", strerror(errno)));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
stream_read(CamelStream *stream, char *buffer, size_t n)
|
||||||
|
{
|
||||||
|
CamelNNTPStream *is = (CamelNNTPStream *)stream;
|
||||||
|
char *o, *oe;
|
||||||
|
unsigned char *p, *e, c;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
if (is->mode != CAMEL_NNTP_STREAM_DATA || n == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
o = buffer;
|
||||||
|
oe = buffer + n;
|
||||||
|
state = is->state;
|
||||||
|
|
||||||
|
/* Need to copy/strip '.'s and whatnot */
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
|
||||||
|
switch(state) {
|
||||||
|
state_0:
|
||||||
|
case 0: /* start of line, always read at least 3 chars */
|
||||||
|
while (e - p < 3) {
|
||||||
|
is->ptr = p;
|
||||||
|
if (stream_fill(is) == -1)
|
||||||
|
return -1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
}
|
||||||
|
if (p[0] == '.') {
|
||||||
|
if (p[1] == '\r' && p[2] == '\n') {
|
||||||
|
is->ptr = p+3;
|
||||||
|
is->mode = CAMEL_NNTP_STREAM_EOD;
|
||||||
|
is->state = 0;
|
||||||
|
dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", o-buffer, o-buffer, buffer));
|
||||||
|
return o-buffer;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
state = 1;
|
||||||
|
/* FALLS THROUGH */
|
||||||
|
case 1: /* looking for next sol */
|
||||||
|
while (o < oe) {
|
||||||
|
c = *p++;
|
||||||
|
if (c == '\n') {
|
||||||
|
/* end of input sentinal check */
|
||||||
|
if (p > e) {
|
||||||
|
is->ptr = e;
|
||||||
|
if (stream_fill(is) == -1)
|
||||||
|
return -1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
} else {
|
||||||
|
*o++ = '\n';
|
||||||
|
state = 0;
|
||||||
|
goto state_0;
|
||||||
|
}
|
||||||
|
} else if (c != '\r') {
|
||||||
|
*o++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
is->ptr = p;
|
||||||
|
is->state = state;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_READ(%d):\n%.*s\n", o-buffer, o-buffer, buffer));
|
||||||
|
|
||||||
|
return o-buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
stream_write(CamelStream *stream, const char *buffer, size_t n)
|
||||||
|
{
|
||||||
|
CamelNNTPStream *is = (CamelNNTPStream *)stream;
|
||||||
|
|
||||||
|
return camel_stream_write(is->source, buffer, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stream_close(CamelStream *stream)
|
||||||
|
{
|
||||||
|
/* nop? */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stream_flush(CamelStream *stream)
|
||||||
|
{
|
||||||
|
/* nop? */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
stream_eos(CamelStream *stream)
|
||||||
|
{
|
||||||
|
CamelNNTPStream *is = (CamelNNTPStream *)stream;
|
||||||
|
|
||||||
|
return is->mode != CAMEL_NNTP_STREAM_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
stream_reset(CamelStream *stream)
|
||||||
|
{
|
||||||
|
/* nop? reset literal mode? */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_stream_class_init (CamelStreamClass *camel_nntp_stream_class)
|
||||||
|
{
|
||||||
|
CamelStreamClass *camel_stream_class = (CamelStreamClass *)camel_nntp_stream_class;
|
||||||
|
|
||||||
|
parent_class = camel_type_get_global_classfuncs( CAMEL_OBJECT_TYPE );
|
||||||
|
|
||||||
|
/* virtual method definition */
|
||||||
|
camel_stream_class->read = stream_read;
|
||||||
|
camel_stream_class->write = stream_write;
|
||||||
|
camel_stream_class->close = stream_close;
|
||||||
|
camel_stream_class->flush = stream_flush;
|
||||||
|
camel_stream_class->eos = stream_eos;
|
||||||
|
camel_stream_class->reset = stream_reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_stream_init(CamelNNTPStream *is, CamelNNTPStreamClass *isclass)
|
||||||
|
{
|
||||||
|
/* +1 is room for appending a 0 if we need to for a line */
|
||||||
|
is->ptr = is->end = is->buf = g_malloc(CAMEL_NNTP_STREAM_SIZE+1);
|
||||||
|
is->lineptr = is->linebuf = g_malloc(CAMEL_NNTP_STREAM_LINE+1);
|
||||||
|
is->lineend = is->linebuf + CAMEL_NNTP_STREAM_LINE;
|
||||||
|
|
||||||
|
/* init sentinal */
|
||||||
|
is->ptr[0] = '\n';
|
||||||
|
|
||||||
|
is->state = 0;
|
||||||
|
is->mode = CAMEL_NNTP_STREAM_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_stream_finalise(CamelNNTPStream *is)
|
||||||
|
{
|
||||||
|
g_free(is->buf);
|
||||||
|
g_free(is->linebuf);
|
||||||
|
if (is->source)
|
||||||
|
camel_object_unref((CamelObject *)is->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
CamelType
|
||||||
|
camel_nntp_stream_get_type (void)
|
||||||
|
{
|
||||||
|
static CamelType camel_nntp_stream_type = CAMEL_INVALID_TYPE;
|
||||||
|
|
||||||
|
if (camel_nntp_stream_type == CAMEL_INVALID_TYPE) {
|
||||||
|
camel_nntp_stream_type = camel_type_register( camel_stream_get_type(),
|
||||||
|
"CamelNNTPStream",
|
||||||
|
sizeof( CamelNNTPStream ),
|
||||||
|
sizeof( CamelNNTPStreamClass ),
|
||||||
|
(CamelObjectClassInitFunc) camel_nntp_stream_class_init,
|
||||||
|
NULL,
|
||||||
|
(CamelObjectInitFunc) camel_nntp_stream_init,
|
||||||
|
(CamelObjectFinalizeFunc) camel_nntp_stream_finalise );
|
||||||
|
}
|
||||||
|
|
||||||
|
return camel_nntp_stream_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* camel_nntp_stream_new:
|
||||||
|
*
|
||||||
|
* Returns a NULL stream. A null stream is always at eof, and
|
||||||
|
* always returns success for all reads and writes.
|
||||||
|
*
|
||||||
|
* Return value: the stream
|
||||||
|
**/
|
||||||
|
CamelStream *
|
||||||
|
camel_nntp_stream_new(CamelStream *source)
|
||||||
|
{
|
||||||
|
CamelNNTPStream *is;
|
||||||
|
|
||||||
|
is = (CamelNNTPStream *)camel_object_new(camel_nntp_stream_get_type ());
|
||||||
|
camel_object_ref((CamelObject *)source);
|
||||||
|
is->source = source;
|
||||||
|
|
||||||
|
return (CamelStream *)is;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get one line from the nntp stream */
|
||||||
|
int
|
||||||
|
camel_nntp_stream_line(CamelNNTPStream *is, unsigned char **data, unsigned int *len)
|
||||||
|
{
|
||||||
|
register unsigned char c, *p, *o, *oe;
|
||||||
|
int newlen, oldlen;
|
||||||
|
unsigned char *e;
|
||||||
|
|
||||||
|
if (is->mode == CAMEL_NNTP_STREAM_EOD) {
|
||||||
|
*data = is->linebuf;
|
||||||
|
*len = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = is->linebuf;
|
||||||
|
oe = is->lineend - 1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
|
||||||
|
/* Data mode, convert leading '..' to '.', and stop when we reach a solitary '.' */
|
||||||
|
if (is->mode == CAMEL_NNTP_STREAM_DATA) {
|
||||||
|
/* need at least 3 chars in buffer */
|
||||||
|
while (e-p < 3) {
|
||||||
|
is->ptr = p;
|
||||||
|
if (stream_fill(is) == -1)
|
||||||
|
return -1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check for isolated '.\r\n' or begging of line '.' */
|
||||||
|
if (p[0] == '.') {
|
||||||
|
if (p[1] == '\r' && p[2] == '\n') {
|
||||||
|
is->ptr = p+3;
|
||||||
|
is->mode = CAMEL_NNTP_STREAM_EOD;
|
||||||
|
*data = is->linebuf;
|
||||||
|
*len = 0;
|
||||||
|
is->linebuf[0] = 0;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_LINE(END)\n"));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
while (o < oe) {
|
||||||
|
c = *p++;
|
||||||
|
if (c == '\n') {
|
||||||
|
/* sentinal? */
|
||||||
|
if (p> e) {
|
||||||
|
is->ptr = e;
|
||||||
|
if (stream_fill(is) == -1)
|
||||||
|
return -1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
} else {
|
||||||
|
is->ptr = p;
|
||||||
|
*data = is->linebuf;
|
||||||
|
*len = o - is->linebuf;
|
||||||
|
*o = 0;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_LINE(%d): '%s'\n", *len, *data));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} else if (c != '\r') {
|
||||||
|
*o++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* limit this for bad server data? */
|
||||||
|
oldlen = o - is->linebuf;
|
||||||
|
newlen = (is->lineend - is->linebuf) * 3 / 2;
|
||||||
|
is->lineptr = is->linebuf = g_realloc(is->linebuf, newlen);
|
||||||
|
is->lineend = is->linebuf + newlen;
|
||||||
|
oe = is->lineend - 1;
|
||||||
|
o = is->linebuf + oldlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns -1 on error, 0 if last lot of data, >0 if more remaining */
|
||||||
|
int camel_nntp_stream_gets(CamelNNTPStream *is, unsigned char **start, unsigned int *len)
|
||||||
|
{
|
||||||
|
int max;
|
||||||
|
unsigned char *end;
|
||||||
|
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
max = is->end - is->ptr;
|
||||||
|
if (max == 0) {
|
||||||
|
max = stream_fill(is);
|
||||||
|
if (max <= 0)
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
*start = is->ptr;
|
||||||
|
end = memchr(is->ptr, '\n', max);
|
||||||
|
if (end)
|
||||||
|
max = (end - is->ptr) + 1;
|
||||||
|
*start = is->ptr;
|
||||||
|
*len = max;
|
||||||
|
is->ptr += max;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_GETS(%s,%d): '%.*s'\n", end==NULL?"more":"last", *len, (int)*len, *start));
|
||||||
|
|
||||||
|
return end == NULL?1:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void camel_nntp_stream_set_mode(CamelNNTPStream *is, camel_nntp_stream_mode_t mode)
|
||||||
|
{
|
||||||
|
is->mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns -1 on erorr, 0 if last data, >0 if more data left */
|
||||||
|
int camel_nntp_stream_getd(CamelNNTPStream *is, unsigned char **start, unsigned int *len)
|
||||||
|
{
|
||||||
|
unsigned char *p, *e, *s;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
if (is->mode == CAMEL_NNTP_STREAM_EOD)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (is->mode == CAMEL_NNTP_STREAM_LINE) {
|
||||||
|
g_warning("nntp_stream reading data in line mode\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = is->state;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
|
||||||
|
while (e - p < 3) {
|
||||||
|
is->ptr = p;
|
||||||
|
if (stream_fill(is) == -1)
|
||||||
|
return -1;
|
||||||
|
p = is->ptr;
|
||||||
|
e = is->end;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = p;
|
||||||
|
|
||||||
|
do {
|
||||||
|
switch(state) {
|
||||||
|
case 0:
|
||||||
|
/* check leading '.', ... */
|
||||||
|
if (p[0] == '.') {
|
||||||
|
if (p[1] == '\r' && p[2] == '\n') {
|
||||||
|
is->ptr = p+3;
|
||||||
|
*len = p-s;
|
||||||
|
*start = s;
|
||||||
|
is->mode = CAMEL_NNTP_STREAM_EOD;
|
||||||
|
is->state = 0;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "last", *len, (int)*len, *start));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If at start, just skip '.', else return data upto '.' but skip it */
|
||||||
|
if (p == s) {
|
||||||
|
s++;
|
||||||
|
p++;
|
||||||
|
} else {
|
||||||
|
is->ptr = p+1;
|
||||||
|
*len = p-s;
|
||||||
|
*start = s;
|
||||||
|
is->state = 1;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = 1;
|
||||||
|
case 1:
|
||||||
|
/* Scan for sentinal */
|
||||||
|
while ((*p++)!='\n')
|
||||||
|
;
|
||||||
|
|
||||||
|
if (p > e) {
|
||||||
|
p = e;
|
||||||
|
} else {
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while ((e-p) >= 3);
|
||||||
|
|
||||||
|
is->state = state;
|
||||||
|
is->ptr = p;
|
||||||
|
*len = p-s;
|
||||||
|
*start = s;
|
||||||
|
|
||||||
|
dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
66
camel/providers/nntp/camel-nntp-stream.h
Normal file
66
camel/providers/nntp/camel-nntp-stream.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2001 Ximian Inc.
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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_STREAM_H
|
||||||
|
#define _CAMEL_NNTP_STREAM_H
|
||||||
|
|
||||||
|
#include <camel/camel-stream.h>
|
||||||
|
|
||||||
|
#define CAMEL_NNTP_STREAM(obj) CAMEL_CHECK_CAST (obj, camel_nntp_stream_get_type (), CamelNNTPStream)
|
||||||
|
#define CAMEL_NNTP_STREAM_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_nntp_stream_get_type (), CamelNNTPStreamClass)
|
||||||
|
#define CAMEL_IS_NNTP_STREAM(obj) CAMEL_CHECK_TYPE (obj, camel_nntp_stream_get_type ())
|
||||||
|
|
||||||
|
typedef struct _CamelNNTPStreamClass CamelNNTPStreamClass;
|
||||||
|
typedef struct _CamelNNTPStream CamelNNTPStream;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CAMEL_NNTP_STREAM_LINE,
|
||||||
|
CAMEL_NNTP_STREAM_DATA,
|
||||||
|
CAMEL_NNTP_STREAM_EOD, /* end of data, acts as if end of stream */
|
||||||
|
} camel_nntp_stream_mode_t;
|
||||||
|
|
||||||
|
struct _CamelNNTPStream {
|
||||||
|
CamelStream parent;
|
||||||
|
|
||||||
|
CamelStream *source;
|
||||||
|
|
||||||
|
camel_nntp_stream_mode_t mode;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
unsigned char *buf, *ptr, *end;
|
||||||
|
unsigned char *linebuf, *lineptr, *lineend;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _CamelNNTPStreamClass {
|
||||||
|
CamelStreamClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
guint camel_nntp_stream_get_type (void);
|
||||||
|
|
||||||
|
CamelStream *camel_nntp_stream_new (CamelStream *source);
|
||||||
|
|
||||||
|
|
||||||
|
void camel_nntp_stream_set_mode (CamelNNTPStream *is, camel_nntp_stream_mode_t mode);
|
||||||
|
|
||||||
|
int camel_nntp_stream_line (CamelNNTPStream *is, unsigned char **data, unsigned int *len);
|
||||||
|
int camel_nntp_stream_gets (CamelNNTPStream *is, unsigned char **start, unsigned int *len);
|
||||||
|
int camel_nntp_stream_getd (CamelNNTPStream *is, unsigned char **start, unsigned int *len);
|
||||||
|
|
||||||
|
#endif /* ! _CAMEL_NNTP_STREAM_H */
|
581
camel/providers/nntp/camel-nntp-summary.c
Normal file
581
camel/providers/nntp/camel-nntp-summary.c
Normal file
@ -0,0 +1,581 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2000 Ximian Inc.
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "camel/camel-file-utils.h"
|
||||||
|
#include "camel/camel-mime-message.h"
|
||||||
|
#include "camel/camel-stream-null.h"
|
||||||
|
#include "camel/camel-operation.h"
|
||||||
|
#include "camel/camel-data-cache.h"
|
||||||
|
|
||||||
|
#include "camel-nntp-summary.h"
|
||||||
|
#include "camel-nntp-folder.h"
|
||||||
|
#include "camel-nntp-store.h"
|
||||||
|
#include "camel-nntp-stream.h"
|
||||||
|
|
||||||
|
#define w(x)
|
||||||
|
#define io(x)
|
||||||
|
#define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
|
||||||
|
extern int camel_verbose_debug;
|
||||||
|
#define dd(x) (camel_verbose_debug?(x):0)
|
||||||
|
|
||||||
|
#define CAMEL_NNTP_SUMMARY_VERSION (0x200)
|
||||||
|
|
||||||
|
static int xover_setup(CamelNNTPSummary *cns, CamelException *ex);
|
||||||
|
static int add_range_xover(CamelNNTPSummary *cns, unsigned int high, unsigned int low, CamelFolderChangeInfo *changes, CamelException *ex);
|
||||||
|
static int add_range_head(CamelNNTPSummary *cns, unsigned int high, unsigned int low, CamelFolderChangeInfo *changes, CamelException *ex);
|
||||||
|
|
||||||
|
enum _xover_t {
|
||||||
|
XOVER_STRING = 0,
|
||||||
|
XOVER_MSGID,
|
||||||
|
XOVER_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _xover_header {
|
||||||
|
struct _xover_header *next;
|
||||||
|
|
||||||
|
const char *name;
|
||||||
|
unsigned int skip:8;
|
||||||
|
enum _xover_t type:8;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _CamelNNTPSummaryPrivate {
|
||||||
|
char *uid;
|
||||||
|
|
||||||
|
struct _xover_header *xover; /* xoverview format */
|
||||||
|
int xover_setup;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _PRIVATE(o) (((CamelNNTPSummary *)(o))->priv)
|
||||||
|
|
||||||
|
static CamelMessageInfo * message_info_new (CamelFolderSummary *, struct _header_raw *);
|
||||||
|
static int summary_header_load(CamelFolderSummary *, FILE *);
|
||||||
|
static int summary_header_save(CamelFolderSummary *, FILE *);
|
||||||
|
|
||||||
|
static void camel_nntp_summary_class_init (CamelNNTPSummaryClass *klass);
|
||||||
|
static void camel_nntp_summary_init (CamelNNTPSummary *obj);
|
||||||
|
static void camel_nntp_summary_finalise (CamelObject *obj);
|
||||||
|
static CamelFolderSummaryClass *camel_nntp_summary_parent;
|
||||||
|
|
||||||
|
CamelType
|
||||||
|
camel_nntp_summary_get_type(void)
|
||||||
|
{
|
||||||
|
static CamelType type = CAMEL_INVALID_TYPE;
|
||||||
|
|
||||||
|
if (type == CAMEL_INVALID_TYPE) {
|
||||||
|
type = camel_type_register(camel_folder_summary_get_type(), "CamelNNTPSummary",
|
||||||
|
sizeof (CamelNNTPSummary),
|
||||||
|
sizeof (CamelNNTPSummaryClass),
|
||||||
|
(CamelObjectClassInitFunc) camel_nntp_summary_class_init,
|
||||||
|
NULL,
|
||||||
|
(CamelObjectInitFunc) camel_nntp_summary_init,
|
||||||
|
(CamelObjectFinalizeFunc) camel_nntp_summary_finalise);
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_summary_class_init(CamelNNTPSummaryClass *klass)
|
||||||
|
{
|
||||||
|
CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *) klass;
|
||||||
|
|
||||||
|
camel_nntp_summary_parent = CAMEL_FOLDER_SUMMARY_CLASS(camel_type_get_global_classfuncs(camel_folder_summary_get_type()));
|
||||||
|
|
||||||
|
sklass->message_info_new = message_info_new;
|
||||||
|
sklass->summary_header_load = summary_header_load;
|
||||||
|
sklass->summary_header_save = summary_header_save;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_summary_init(CamelNNTPSummary *obj)
|
||||||
|
{
|
||||||
|
struct _CamelNNTPSummaryPrivate *p;
|
||||||
|
struct _CamelFolderSummary *s = (CamelFolderSummary *)obj;
|
||||||
|
|
||||||
|
p = _PRIVATE(obj) = g_malloc0(sizeof(*p));
|
||||||
|
|
||||||
|
/* subclasses need to set the right instance data sizes */
|
||||||
|
s->message_info_size = sizeof(CamelMessageInfo);
|
||||||
|
s->content_info_size = sizeof(CamelMessageContentInfo);
|
||||||
|
|
||||||
|
/* and a unique file version */
|
||||||
|
s->version += CAMEL_NNTP_SUMMARY_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camel_nntp_summary_finalise(CamelObject *obj)
|
||||||
|
{
|
||||||
|
CamelNNTPSummary *cns = CAMEL_NNTP_SUMMARY(obj);
|
||||||
|
struct _xover_header *xover, *xn;
|
||||||
|
|
||||||
|
xover = cns->priv->xover;
|
||||||
|
while (xover) {
|
||||||
|
xn = xover->next;
|
||||||
|
g_free(xover);
|
||||||
|
xover = xn;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(cns->priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
CamelNNTPSummary *
|
||||||
|
camel_nntp_summary_new(CamelNNTPFolder *folder)
|
||||||
|
{
|
||||||
|
CamelNNTPSummary *cns = (CamelNNTPSummary *)camel_object_new(camel_nntp_summary_get_type());
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
cns->folder = folder;
|
||||||
|
path = g_strdup_printf("%s.ev-summary", folder->storage_path);
|
||||||
|
camel_folder_summary_set_filename((CamelFolderSummary *)cns, path);
|
||||||
|
g_free(path);
|
||||||
|
|
||||||
|
camel_folder_summary_set_build_content((CamelFolderSummary *)cns, FALSE);
|
||||||
|
|
||||||
|
return cns;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CamelMessageInfo *
|
||||||
|
message_info_new(CamelFolderSummary *s, struct _header_raw *h)
|
||||||
|
{
|
||||||
|
CamelMessageInfo *mi;
|
||||||
|
CamelNNTPSummary *cns = (CamelNNTPSummary *)s;
|
||||||
|
|
||||||
|
/* error to call without this setup */
|
||||||
|
if (cns->priv->uid == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* we shouldn't be here if we already have this uid */
|
||||||
|
g_assert(camel_folder_summary_uid(s, cns->priv->uid) == NULL);
|
||||||
|
|
||||||
|
mi = ((CamelFolderSummaryClass *)camel_nntp_summary_parent)->message_info_new(s, h);
|
||||||
|
if (mi) {
|
||||||
|
camel_message_info_set_uid(mi, cns->priv->uid);
|
||||||
|
cns->priv->uid = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int summary_header_load(CamelFolderSummary *s, FILE *in)
|
||||||
|
{
|
||||||
|
CamelNNTPSummary *cns = CAMEL_NNTP_SUMMARY(s);
|
||||||
|
|
||||||
|
if (((CamelFolderSummaryClass *)camel_nntp_summary_parent)->summary_header_load(s, in) == -1
|
||||||
|
|| camel_file_util_decode_fixed_int32(in, &cns->high) == -1
|
||||||
|
|| camel_file_util_decode_fixed_int32(in, &cns->low) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int summary_header_save(CamelFolderSummary *s, FILE *out)
|
||||||
|
{
|
||||||
|
CamelNNTPSummary *cns = CAMEL_NNTP_SUMMARY(s);
|
||||||
|
|
||||||
|
if (((CamelFolderSummaryClass *)camel_nntp_summary_parent)->summary_header_save(s, out) == -1
|
||||||
|
|| camel_file_util_encode_fixed_int32(out, cns->high) == -1
|
||||||
|
|| camel_file_util_encode_fixed_int32(out, cns->low) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assumes we have the stream */
|
||||||
|
int camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *changes, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelNNTPStore *store;
|
||||||
|
CamelFolder *folder;
|
||||||
|
CamelFolderSummary *s;
|
||||||
|
int ret, i;
|
||||||
|
char *line;
|
||||||
|
unsigned int n, f, l;
|
||||||
|
int count;
|
||||||
|
|
||||||
|
if (xover_setup(cns, ex) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
folder = (CamelFolder *)cns->folder;
|
||||||
|
store = (CamelNNTPStore *)folder->parent_store;
|
||||||
|
s = (CamelFolderSummary *)cns;
|
||||||
|
|
||||||
|
ret = camel_nntp_command(store, &line, "group %s", folder->full_name);
|
||||||
|
if (ret == 411) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_FOLDER_INVALID,
|
||||||
|
_("No such folder: %s"), line);
|
||||||
|
return -1;
|
||||||
|
} else if (ret != 211) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SERVICE_UNAVAILABLE,
|
||||||
|
_("Could not get group: %s"), line);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
line +=3;
|
||||||
|
n = strtoul(line, &line, 10);
|
||||||
|
f = strtoul(line, &line, 10);
|
||||||
|
l = strtoul(line, &line, 10);
|
||||||
|
|
||||||
|
dd(printf("nntp_summary: got last '%u' first '%u'\n"
|
||||||
|
"nntp_summary: high '%u' low '%u'\n", l, f, cns->high, cns->low));
|
||||||
|
|
||||||
|
if (cns->low == f && cns->high == l) {
|
||||||
|
dd(printf("nntp_summary: no work to do!\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Need to work out what to do with our messages */
|
||||||
|
|
||||||
|
/* Check for messages no longer on the server */
|
||||||
|
if (cns->low != f) {
|
||||||
|
count = camel_folder_summary_count(s);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
CamelMessageInfo *mi = camel_folder_summary_index(s, i);
|
||||||
|
|
||||||
|
if (mi) {
|
||||||
|
const char *uid = camel_message_info_uid(mi);
|
||||||
|
const char *msgid;
|
||||||
|
|
||||||
|
n = strtoul(uid, NULL, 10);
|
||||||
|
if (n < f || n > l) {
|
||||||
|
dd(printf("nntp_summary: %u is lower/higher than lowest/highest article, removed\n", n));
|
||||||
|
/* Since we use a global cache this could prematurely remove
|
||||||
|
a cached message that might be in another folder - not that important as
|
||||||
|
it is a true cache */
|
||||||
|
msgid = strchr(uid, ',');
|
||||||
|
if (msgid)
|
||||||
|
camel_data_cache_remove(store->cache, "cache", msgid+1, NULL);
|
||||||
|
camel_folder_change_info_remove_uid(changes, uid);
|
||||||
|
camel_folder_summary_remove(s, mi);
|
||||||
|
count--;
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
camel_folder_summary_info_free(s, mi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cns->low = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cns->high < l) {
|
||||||
|
if (cns->high < f)
|
||||||
|
cns->high = f-1;
|
||||||
|
|
||||||
|
if (cns->priv->xover) {
|
||||||
|
ret = add_range_xover(cns, l, cns->high+1, changes, ex);
|
||||||
|
} else {
|
||||||
|
ret = add_range_head(cns, l, cns->high+1, changes, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
camel_folder_summary_touch(s);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
const char *name;
|
||||||
|
int type;
|
||||||
|
} headers[] = {
|
||||||
|
{ "subject", 0 },
|
||||||
|
{ "from", 0 },
|
||||||
|
{ "date", 0 },
|
||||||
|
{ "message-id", 1 },
|
||||||
|
{ "references", 0 },
|
||||||
|
{ "bytes", 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
xover_setup(CamelNNTPSummary *cns, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelNNTPStore *store;
|
||||||
|
CamelFolder *folder;
|
||||||
|
CamelFolderSummary *s;
|
||||||
|
int ret, i;
|
||||||
|
char *line;
|
||||||
|
unsigned int len;
|
||||||
|
unsigned char c, *p;
|
||||||
|
struct _xover_header *xover, *last;
|
||||||
|
|
||||||
|
if (cns->priv->xover_setup)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* manual override */
|
||||||
|
if (getenv("CAMEL_NNTP_DISABLE_XOVER") != NULL) {
|
||||||
|
cns->priv->xover_setup = TRUE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
folder = (CamelFolder *)cns->folder;
|
||||||
|
store = (CamelNNTPStore *)folder->parent_store;
|
||||||
|
s = (CamelFolderSummary *)cns;
|
||||||
|
|
||||||
|
ret = camel_nntp_command(store, &line, "list overview.fmt");
|
||||||
|
if (ret == -1) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM,
|
||||||
|
_("NNTP Command failed: %s"), strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cns->priv->xover_setup = TRUE;
|
||||||
|
|
||||||
|
/* unsupported command? */
|
||||||
|
if (ret != 215)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
last = (struct _xover_header *)&cns->priv->xover;
|
||||||
|
|
||||||
|
/* supported command */
|
||||||
|
while ((ret = camel_nntp_stream_line(store->stream, (unsigned char **)&line, &len)) > 0) {
|
||||||
|
p = line;
|
||||||
|
xover = g_malloc0(sizeof(*xover));
|
||||||
|
last->next = xover;
|
||||||
|
last = xover;
|
||||||
|
while ((c = *p++)) {
|
||||||
|
if (c == ':') {
|
||||||
|
p[-1] = 0;
|
||||||
|
for (i=0;i<sizeof(headers)/sizeof(headers[0]);i++) {
|
||||||
|
if (strcmp(line, headers[i].name) == 0) {
|
||||||
|
xover->name = headers[i].name;
|
||||||
|
if (strncmp(p, "full", 4) == 0)
|
||||||
|
xover->skip = strlen(xover->name)+1;
|
||||||
|
else
|
||||||
|
xover->skip = 0;
|
||||||
|
xover->type = headers[i].type;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
p[-1] = tolower(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_range_xover(CamelNNTPSummary *cns, unsigned int high, unsigned int low, CamelFolderChangeInfo *changes, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelNNTPStore *store;
|
||||||
|
CamelFolder *folder;
|
||||||
|
CamelFolderSummary *s;
|
||||||
|
CamelMessageInfo *mi;
|
||||||
|
struct _header_raw *headers = NULL;
|
||||||
|
char *line, *tab;
|
||||||
|
int len, ret;
|
||||||
|
unsigned int n, count, total, size;
|
||||||
|
struct _xover_header *xover;
|
||||||
|
time_t last, now;
|
||||||
|
|
||||||
|
folder = (CamelFolder *)cns->folder;
|
||||||
|
store = (CamelNNTPStore *)folder->parent_store;
|
||||||
|
s = (CamelFolderSummary *)cns;
|
||||||
|
|
||||||
|
camel_operation_start(NULL, _("%s: Scanning new messages"), ((CamelService *)store)->url->host);
|
||||||
|
|
||||||
|
ret = camel_nntp_command(store, &line, "xover %r", low, high);
|
||||||
|
if (ret != 224) {
|
||||||
|
camel_operation_end(NULL);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
last = time(0);
|
||||||
|
count = 0;
|
||||||
|
total = high-low+1;
|
||||||
|
while ((ret = camel_nntp_stream_line(store->stream, (unsigned char **)&line, &len)) > 0) {
|
||||||
|
camel_operation_progress(NULL, (count * 100) / total);
|
||||||
|
count++;
|
||||||
|
n = strtoul(line, &tab, 10);
|
||||||
|
if (*tab != '\t')
|
||||||
|
continue;
|
||||||
|
tab++;
|
||||||
|
xover = cns->priv->xover;
|
||||||
|
size = 0;
|
||||||
|
for (;tab[0] && xover;xover = xover->next) {
|
||||||
|
line = tab;
|
||||||
|
tab = strchr(line, '\t');
|
||||||
|
if (tab)
|
||||||
|
*tab++ = 0;
|
||||||
|
else
|
||||||
|
tab = line+strlen(line);
|
||||||
|
|
||||||
|
/* do we care about this column? */
|
||||||
|
if (xover->name) {
|
||||||
|
line += xover->skip;
|
||||||
|
if (line < tab) {
|
||||||
|
header_raw_append(&headers, xover->name, line, -1);
|
||||||
|
switch(xover->type) {
|
||||||
|
case XOVER_STRING:
|
||||||
|
break;
|
||||||
|
case XOVER_MSGID:
|
||||||
|
cns->priv->uid = g_strdup_printf("%u,%s", n, line);
|
||||||
|
break;
|
||||||
|
case XOVER_SIZE:
|
||||||
|
size = strtoul(line, NULL, 10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* truncated line? ignore? */
|
||||||
|
if (xover == NULL) {
|
||||||
|
mi = camel_folder_summary_uid(s, cns->priv->uid);
|
||||||
|
if (mi == NULL) {
|
||||||
|
mi = camel_folder_summary_add_from_header(s, headers);
|
||||||
|
if (mi) {
|
||||||
|
mi->size = size;
|
||||||
|
cns->high = n;
|
||||||
|
camel_folder_change_info_add_uid(changes, camel_message_info_uid(mi));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
camel_folder_summary_info_free(s, mi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cns->priv->uid) {
|
||||||
|
g_free(cns->priv->uid);
|
||||||
|
cns->priv->uid = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
header_raw_clear(&headers);
|
||||||
|
|
||||||
|
now = time(0);
|
||||||
|
if (last + 2 < now) {
|
||||||
|
camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes);
|
||||||
|
camel_folder_change_info_clear(changes);
|
||||||
|
last = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
camel_operation_end(NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
add_range_head(CamelNNTPSummary *cns, unsigned int high, unsigned int low, CamelFolderChangeInfo *changes, CamelException *ex)
|
||||||
|
{
|
||||||
|
CamelNNTPStore *store;
|
||||||
|
CamelFolder *folder;
|
||||||
|
CamelFolderSummary *s;
|
||||||
|
int i, ret = -1;
|
||||||
|
char *line, *msgid;
|
||||||
|
unsigned int n, count, total;
|
||||||
|
CamelMessageInfo *mi;
|
||||||
|
CamelMimeParser *mp;
|
||||||
|
time_t now, last;
|
||||||
|
|
||||||
|
folder = (CamelFolder *)cns->folder;
|
||||||
|
store = (CamelNNTPStore *)folder->parent_store;
|
||||||
|
s = (CamelFolderSummary *)cns;
|
||||||
|
|
||||||
|
mp = camel_mime_parser_new();
|
||||||
|
|
||||||
|
camel_operation_start(NULL, _("%s: Scanning new messages"), ((CamelService *)store)->url->host);
|
||||||
|
|
||||||
|
last = time(0);
|
||||||
|
count = 0;
|
||||||
|
total = high-low+1;
|
||||||
|
for (i=low;i<high+1;i++) {
|
||||||
|
camel_operation_progress(NULL, (count * 100) / total);
|
||||||
|
count++;
|
||||||
|
ret = camel_nntp_command(store, &line, "head %u", i);
|
||||||
|
/* unknown article, ignore */
|
||||||
|
if (ret == 423)
|
||||||
|
continue;
|
||||||
|
else if (ret == -1)
|
||||||
|
goto error;
|
||||||
|
else if (ret != 221) {
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Unknown server response: %s"), line);
|
||||||
|
goto ioerror;
|
||||||
|
}
|
||||||
|
line += 3;
|
||||||
|
n = strtoul(line, &line, 10);
|
||||||
|
if (n != i)
|
||||||
|
g_warning("retrieved message '%d' when i expected '%d'?\n", n, i);
|
||||||
|
|
||||||
|
if ((msgid = strchr(line, '<')) && (line = strchr(msgid+1, '>'))){
|
||||||
|
line[1] = 0;
|
||||||
|
cns->priv->uid = g_strdup_printf("%u,%s\n", n, msgid);
|
||||||
|
mi = camel_folder_summary_uid(s, cns->priv->uid);
|
||||||
|
if (mi == NULL) {
|
||||||
|
if (camel_mime_parser_init_with_stream(mp, (CamelStream *)store->stream) == -1)
|
||||||
|
goto error;
|
||||||
|
mi = camel_folder_summary_add_from_parser(s, mp);
|
||||||
|
while (camel_mime_parser_step(mp, NULL, NULL) != HSCAN_EOF)
|
||||||
|
;
|
||||||
|
if (mi == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
cns->high = i;
|
||||||
|
camel_folder_change_info_add_uid(changes, camel_message_info_uid(mi));
|
||||||
|
} else {
|
||||||
|
/* already have, ignore */
|
||||||
|
camel_folder_summary_info_free(s, mi);
|
||||||
|
}
|
||||||
|
if (cns->priv->uid) {
|
||||||
|
g_free(cns->priv->uid);
|
||||||
|
cns->priv->uid = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
now = time(0);
|
||||||
|
if (last + 2 < now) {
|
||||||
|
camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes);
|
||||||
|
camel_folder_change_info_clear(changes);
|
||||||
|
last = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
error:
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_USER_CANCEL, _("Use cancel"));
|
||||||
|
else
|
||||||
|
camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Operation failed: %s"), strerror(errno));
|
||||||
|
}
|
||||||
|
ioerror:
|
||||||
|
|
||||||
|
if (cns->priv->uid) {
|
||||||
|
g_free(cns->priv->uid);
|
||||||
|
cns->priv->uid = NULL;
|
||||||
|
}
|
||||||
|
camel_object_unref((CamelObject *)mp);
|
||||||
|
|
||||||
|
camel_operation_end(NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
67
camel/providers/nntp/camel-nntp-summary.h
Normal file
67
camel/providers/nntp/camel-nntp-summary.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2000 Ximian Inc.
|
||||||
|
*
|
||||||
|
* Authors: Michael Zucchi <notzed@ximian.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of version 2 of the GNU General Public
|
||||||
|
* License as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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_SUMMARY_H
|
||||||
|
#define _CAMEL_NNTP_SUMMARY_H
|
||||||
|
|
||||||
|
#include <camel/camel-folder-summary.h>
|
||||||
|
#include <camel/camel-folder.h>
|
||||||
|
#include <camel/camel-exception.h>
|
||||||
|
#include <libibex/ibex.h>
|
||||||
|
|
||||||
|
#define CAMEL_NNTP_SUMMARY(obj) CAMEL_CHECK_CAST (obj, camel_nntp_summary_get_type (), CamelNNTPSummary)
|
||||||
|
#define CAMEL_NNTP_SUMMARY_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, camel_nntp_summary_get_type (), CamelNNTPSummaryClass)
|
||||||
|
#define CAMEL_IS_LOCAL_SUMMARY(obj) CAMEL_CHECK_TYPE (obj, camel_nntp_summary_get_type ())
|
||||||
|
|
||||||
|
typedef struct _CamelNNTPSummary CamelNNTPSummary;
|
||||||
|
typedef struct _CamelNNTPSummaryClass CamelNNTPSummaryClass;
|
||||||
|
|
||||||
|
struct _CamelNNTPSummary {
|
||||||
|
CamelFolderSummary parent;
|
||||||
|
|
||||||
|
struct _CamelNNTPSummaryPrivate *priv;
|
||||||
|
|
||||||
|
struct _CamelNNTPFolder *folder;
|
||||||
|
|
||||||
|
guint32 high, low;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _CamelNNTPSummaryClass {
|
||||||
|
CamelFolderSummaryClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
guint camel_nntp_summary_get_type (void);
|
||||||
|
CamelNNTPSummary *camel_nntp_summary_new(struct _CamelNNTPFolder *folder);
|
||||||
|
|
||||||
|
int camel_nntp_summary_check(CamelNNTPSummary *cns, CamelFolderChangeInfo *, CamelException *ex);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* load/check the summary */
|
||||||
|
int camel_nntp_summary_load(CamelNNTPSummary *cls, CamelException *ex);
|
||||||
|
/* check for new/removed messages */
|
||||||
|
int camel_nntp_summary_check(CamelNNTPSummary *cls, CamelFolderChangeInfo *, CamelException *ex);
|
||||||
|
/* perform a folder sync or expunge, if needed */
|
||||||
|
int camel_nntp_summary_sync(CamelNNTPSummary *cls, gboolean expunge, CamelFolderChangeInfo *, CamelException *ex);
|
||||||
|
/* add a new message to the summary */
|
||||||
|
CamelMessageInfo *camel_nntp_summary_add(CamelNNTPSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* ! _CAMEL_NNTP_SUMMARY_H */
|
||||||
|
|
Reference in New Issue
Block a user