Implemented base64 encoder based on CamelStreams. Should the
1999-07-13 Miguel de Icaza <miguel@gnu.org> * camel/gmime-base64.c (gmime_encode_base64): Implemented base64 encoder based on CamelStreams. Should the encoder/decoder be a Stream itself? * camel/gmime-utils.c: include config.h here. * camel/url-util.c: ditto. * camel/gstring-util.c: ditto. * camel/gmime-content-field.c: ditto. * camel/camel-stream.c: ditto. * camel/camel-stream-fs.c: ditto. * camel/camel-store.c: ditto. * camel/camel-simple-data-wrapper.c: ditto. * camel/camel-session.c: ditto. * camel/camel-service.c: ditto. * camel/camel-mime-part.c: ditto. * camel/camel-mime-message.c: ditto. * camel/camel-log.c: ditto. * camel/camel-data-wrapper.c: ditto * camel/camel-folder.c: ditto. * camel/camel-stream.c (camel_stream_write): Moved api documentation to the places that they document. (camel_stream_class_init): Virtual classes do not need to have a default implementation. So null them all. (camel_stream_write): Return value from write. (camel_stream_available): implement. (camel_stream_write_strings): documented. * devel-docs/query/virtual-folder-in-depth.sgml: Small reformatting 1999-06-28 bertrand <Bertrand.Guiheneuf@inria.fr> * tests/test2.c (main): now use CamelDataWrapper::contruct_form_stream to test svn path=/trunk/; revision=1024
This commit is contained in:
committed by
Arturo Espinosa
parent
68f3afb7a2
commit
fce26238c4
33
ChangeLog
33
ChangeLog
@ -1,3 +1,36 @@
|
||||
1999-07-13 Miguel de Icaza <miguel@gnu.org>
|
||||
|
||||
* camel/gmime-base64.c (gmime_encode_base64): Implemented base64
|
||||
encoder based on CamelStreams. Should the encoder/decoder be a
|
||||
Stream itself?
|
||||
|
||||
* camel/gmime-utils.c: include config.h here.
|
||||
* camel/url-util.c: ditto.
|
||||
* camel/gstring-util.c: ditto.
|
||||
* camel/gmime-content-field.c: ditto.
|
||||
* camel/camel-stream.c: ditto.
|
||||
* camel/camel-stream-fs.c: ditto.
|
||||
* camel/camel-store.c: ditto.
|
||||
* camel/camel-simple-data-wrapper.c: ditto.
|
||||
* camel/camel-session.c: ditto.
|
||||
* camel/camel-service.c: ditto.
|
||||
* camel/camel-mime-part.c: ditto.
|
||||
* camel/camel-mime-message.c: ditto.
|
||||
* camel/camel-log.c: ditto.
|
||||
* camel/camel-data-wrapper.c: ditto
|
||||
* camel/camel-folder.c: ditto.
|
||||
|
||||
* camel/camel-stream.c (camel_stream_write): Moved api
|
||||
documentation to the places that they document.
|
||||
(camel_stream_class_init): Virtual classes do not need to have a
|
||||
default implementation. So null them all.
|
||||
(camel_stream_write): Return value from write.
|
||||
(camel_stream_available): implement.
|
||||
(camel_stream_write_strings): documented.
|
||||
|
||||
* devel-docs/query/virtual-folder-in-depth.sgml: Small
|
||||
reformatting
|
||||
|
||||
1999-06-28 bertrand <Bertrand.Guiheneuf@inria.fr>
|
||||
|
||||
* tests/test2.c (main): now use
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-data-wrapper.h"
|
||||
|
||||
static GtkObjectClass *parent_class=NULL;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-folder.h"
|
||||
#include "gstring-util.h"
|
||||
|
||||
@ -362,12 +362,17 @@ _create(CamelFolder *folder)
|
||||
g_assert(folder->parent_store);
|
||||
g_assert(folder->name);
|
||||
|
||||
if ( CF_CLASS(folder)->exists(folder) ) return TRUE;
|
||||
if (CF_CLASS(folder)->exists(folder))
|
||||
return TRUE;
|
||||
|
||||
sep = camel_store_get_separator(folder->parent_store);
|
||||
if (folder->parent_folder) camel_folder_create(folder->parent_folder);
|
||||
if (folder->parent_folder)
|
||||
camel_folder_create(folder->parent_folder);
|
||||
else {
|
||||
if (folder->full_name) {
|
||||
dich_result = g_string_dichotomy(folder->full_name, sep, &prefix, NULL, DICHOTOMY_STRIP_TRAILING | DICHOTOMY_RIGHT_DIR);
|
||||
dich_result = g_string_dichotomy(
|
||||
folder->full_name, sep, &prefix, NULL,
|
||||
GSTRING_DICHOTOMY_STRIP_TRAILING | GSTRING_DICHOTOMY_RIGHT_DIR);
|
||||
if (dich_result!='o') {
|
||||
g_warning("I have to handle the case where the path is not OK\n");
|
||||
return FALSE;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-log.h"
|
||||
|
||||
int camel_debug_level = 10;
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-mime-message.h"
|
||||
#include <stdio.h>
|
||||
#include "gmime-content-field.h"
|
||||
@ -564,7 +564,9 @@ _set_recipient_list_from_string (CamelMimeMessage *message, GString *recipient_t
|
||||
{
|
||||
GList *recipients_list;
|
||||
CAMEL_LOG (FULL_DEBUG,"CamelMimeMessage::_set_recipient_list_from_string parsing ##%s##\n", recipients_string->str);
|
||||
recipients_list = g_string_split (recipients_string, ',', "\t ", TRIM_STRIP_TRAILING | TRIM_STRIP_LEADING);
|
||||
recipients_list = g_string_split (
|
||||
recipients_string, ',', "\t ",
|
||||
GSTRING_TRIM_STRIP_TRAILING | GSTRING_TRIM_STRIP_LEADING);
|
||||
g_hash_table_insert (message->recipients, recipient_type, recipients_list);
|
||||
|
||||
}
|
||||
|
||||
@ -81,24 +81,35 @@ typedef struct {
|
||||
CamelMimePartClass parent_class;
|
||||
|
||||
/* Virtual methods */
|
||||
void (*set_received_date) (CamelMimeMessage *mime_message, GString *received_date);
|
||||
void (*set_received_date) (CamelMimeMessage *mime_message,
|
||||
GString *received_date);
|
||||
GString * (*get_received_date) (CamelMimeMessage *mime_message);
|
||||
GString * (*get_sent_date) (CamelMimeMessage *mime_message);
|
||||
void (*set_reply_to) (CamelMimeMessage *mime_message, GString *reply_to);
|
||||
GString * (*get_reply_to) (CamelMimeMessage *mime_message);
|
||||
void (*set_subject) (CamelMimeMessage *mime_message, GString *subject);
|
||||
GString * (*get_subject) (CamelMimeMessage *mime_message);
|
||||
void (*set_from) (CamelMimeMessage *mime_message, GString *from);
|
||||
GString * (*get_from) (CamelMimeMessage *mime_message);
|
||||
void (*add_recipient) (CamelMimeMessage *mime_message, GString *recipient_type, GString *recipient);
|
||||
void (*remove_recipient) (CamelMimeMessage *mime_message, GString *recipient_type, GString *recipient);
|
||||
GList * (*get_recipients) (CamelMimeMessage *mime_message, GString *recipient_type);
|
||||
void (*set_flag) (CamelMimeMessage *mime_message, GString *flag, gboolean value);
|
||||
gboolean (*get_flag) (CamelMimeMessage *mime_message, GString *flag);
|
||||
|
||||
void (*set_message_number) (CamelMimeMessage *mime_message, guint number);
|
||||
guint (*get_message_number) (CamelMimeMessage *mime_message);
|
||||
GString * (*get_sent_date) (CamelMimeMessage *mime_message);
|
||||
void (*set_reply_to) (CamelMimeMessage *mime_message,
|
||||
GString *reply_to);
|
||||
GString * (*get_reply_to) (CamelMimeMessage *mime_message);
|
||||
void (*set_subject) (CamelMimeMessage *mime_message,
|
||||
GString *subject);
|
||||
GString * (*get_subject) (CamelMimeMessage *mime_message);
|
||||
void (*set_from) (CamelMimeMessage *mime_message,
|
||||
GString *from);
|
||||
GString * (*get_from) (CamelMimeMessage *mime_message);
|
||||
void (*add_recipient) (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type,
|
||||
GString *recipient);
|
||||
void (*remove_recipient) (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type,
|
||||
GString *recipient);
|
||||
GList * (*get_recipients) (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type);
|
||||
void (*set_flag) (CamelMimeMessage *mime_message,
|
||||
GString *flag, gboolean value);
|
||||
gboolean (*get_flag) (CamelMimeMessage *mime_message,
|
||||
GString *flag);
|
||||
|
||||
void (*set_message_number)(CamelMimeMessage *mime_message,
|
||||
guint number);
|
||||
guint (*get_message_number)(CamelMimeMessage *mime_message);
|
||||
} CamelMimeMessageClass;
|
||||
|
||||
|
||||
@ -111,24 +122,34 @@ GtkType camel_mime_message_get_type (void);
|
||||
CamelMimeMessage *camel_mime_message_new_with_session (CamelSession *session);
|
||||
|
||||
|
||||
void camel_mime_message_set_received_date (CamelMimeMessage *mime_message, GString *received_date);
|
||||
GString *camel_mime_message_get_received_date (CamelMimeMessage *mime_message);
|
||||
GString *camel_mime_message_get_sent_date (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_reply_to (CamelMimeMessage *mime_message, GString *reply_to);
|
||||
GString *camel_mime_message_get_reply_to (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_subject (CamelMimeMessage *mime_message, GString *subject);
|
||||
GString *camel_mime_message_get_subject (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_from (CamelMimeMessage *mime_message, GString *from);
|
||||
GString *camel_mime_message_get_from (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_received_date (CamelMimeMessage *mime_message,
|
||||
GString *received_date);
|
||||
GString *camel_mime_message_get_received_date (CamelMimeMessage *mime_message);
|
||||
GString *camel_mime_message_get_sent_date (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_reply_to (CamelMimeMessage *mime_message,
|
||||
GString *reply_to);
|
||||
GString *camel_mime_message_get_reply_to (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_subject (CamelMimeMessage *mime_message,
|
||||
GString *subject);
|
||||
GString *camel_mime_message_get_subject (CamelMimeMessage *mime_message);
|
||||
void camel_mime_message_set_from (CamelMimeMessage *mime_message, GString *from);
|
||||
GString *camel_mime_message_get_from (CamelMimeMessage *mime_message);
|
||||
|
||||
void camel_mime_message_add_recipient (CamelMimeMessage *mime_message, GString *recipient_type, GString *recipient);
|
||||
void camel_mime_message_remove_recipient (CamelMimeMessage *mime_message, GString *recipient_type, GString *recipient);
|
||||
GList *camel_mime_message_get_recipients (CamelMimeMessage *mime_message, GString *recipient_type);
|
||||
void camel_mime_message_add_recipient (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type,
|
||||
GString *recipient);
|
||||
void camel_mime_message_remove_recipient (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type,
|
||||
GString *recipient);
|
||||
GList *camel_mime_message_get_recipients (CamelMimeMessage *mime_message,
|
||||
GString *recipient_type);
|
||||
|
||||
void camel_mime_message_set_flag (CamelMimeMessage *mime_message, GString *flag, gboolean value);
|
||||
gboolean camel_mime_message_get_flag (CamelMimeMessage *mime_message, GString *flag);
|
||||
void camel_mime_message_set_flag (CamelMimeMessage *mime_message,
|
||||
GString *flag, gboolean value);
|
||||
gboolean camel_mime_message_get_flag (CamelMimeMessage *mime_message,
|
||||
GString *flag);
|
||||
|
||||
guint camel_mime_message_get_message_number (CamelMimeMessage *mime_message);
|
||||
guint camel_mime_message_get_message_number (CamelMimeMessage *mime_message);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-mime-part.h"
|
||||
#include <stdio.h>
|
||||
#include "gmime-content-field.h"
|
||||
|
||||
@ -104,23 +104,33 @@ GtkType camel_mime_part_get_type (void);
|
||||
|
||||
|
||||
/* public methods */
|
||||
void camel_mime_part_add_header (CamelMimePart *mime_part, GString *header_name, GString *header_value);
|
||||
void camel_mime_part_remove_header (CamelMimePart *mime_part, GString *header_name);
|
||||
GString *camel_mime_part_get_header (CamelMimePart *mime_part, GString *header_name);
|
||||
void camel_mime_part_set_description (CamelMimePart *mime_part, GString *description);
|
||||
GString *camel_mime_part_get_description (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_disposition (CamelMimePart *mime_part, GString *disposition);
|
||||
GString *camel_mime_part_get_disposition (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_filename (CamelMimePart *mime_part, GString *filename);
|
||||
GString *camel_mime_part_get_filename (CamelMimePart *mime_part);
|
||||
GString *camel_mime_part_get_content_id (CamelMimePart *mime_part);
|
||||
GString *camel_mime_part_get_content_MD5 (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_encoding (CamelMimePart *mime_part, GString *encoding);
|
||||
GString *camel_mime_part_get_encoding (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_content_languages (CamelMimePart *mime_part, GList *content_languages);
|
||||
GList *camel_mime_part_get_content_languages (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_header_lines (CamelMimePart *mime_part, GList *header_lines);
|
||||
GList *camel_mime_part_get_header_lines (CamelMimePart *mime_part);
|
||||
void camel_mime_part_add_header (CamelMimePart *mime_part,
|
||||
GString *header_name,
|
||||
GString *header_value);
|
||||
void camel_mime_part_remove_header (CamelMimePart *mime_part,
|
||||
GString *header_name);
|
||||
GString *camel_mime_part_get_header (CamelMimePart *mime_part,
|
||||
GString *header_name);
|
||||
void camel_mime_part_set_description (CamelMimePart *mime_part,
|
||||
GString *description);
|
||||
GString *camel_mime_part_get_description (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_disposition (CamelMimePart *mime_part,
|
||||
GString *disposition);
|
||||
GString *camel_mime_part_get_disposition (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_filename (CamelMimePart *mime_part,
|
||||
GString *filename);
|
||||
GString *camel_mime_part_get_filename (CamelMimePart *mime_part);
|
||||
GString *camel_mime_part_get_content_id (CamelMimePart *mime_part);
|
||||
GString *camel_mime_part_get_content_MD5 (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_encoding (CamelMimePart *mime_part,
|
||||
GString *encoding);
|
||||
GString *camel_mime_part_get_encoding (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_content_languages (CamelMimePart *mime_part,
|
||||
GList *content_languages);
|
||||
GList *camel_mime_part_get_content_languages (CamelMimePart *mime_part);
|
||||
void camel_mime_part_set_header_lines (CamelMimePart *mime_part,
|
||||
GList *header_lines);
|
||||
GList *camel_mime_part_get_header_lines (CamelMimePart *mime_part);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-service.h"
|
||||
|
||||
static GtkObjectClass *parent_class=NULL;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-session.h"
|
||||
#include "gstring-util.h"
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-simple-data-wrapper.h"
|
||||
|
||||
static CamelDataWrapperClass *parent_class=NULL;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-store.h"
|
||||
|
||||
static GtkObjectClass *parent_class=NULL;
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-stream-fs.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -32,7 +32,7 @@
|
||||
static CamelStreamClass *parent_class=NULL;
|
||||
|
||||
|
||||
/* Returns the class for a CamelMimeMessage */
|
||||
/* Returns the class for a CamelStreamFS */
|
||||
#define CS_CLASS(so) CAMEL_STREAM_FS_CLASS (GTK_OBJECT(so)->klass)
|
||||
|
||||
static gint _read (CamelStream *stream, gchar *buffer, gint n);
|
||||
@ -101,13 +101,17 @@ camel_stream_fs_new_with_name (GString *name, CamelStreamFsMode mode)
|
||||
CAMEL_LOG (FULL_DEBUG, "Entering CamelStream::new_with_name, name=\"%s\", mode=%d\n", name->str, mode);
|
||||
v = stat (name->str, &s);
|
||||
|
||||
if (mode & CAMEL_STREAM_FS_READ)
|
||||
if (mode & CAMEL_STREAM_FS_WRITE) flags = O_RDWR | O_CREAT;
|
||||
else flags = O_RDONLY;
|
||||
else
|
||||
if (mode & CAMEL_STREAM_FS_WRITE) flags = O_WRONLY | O_CREAT;
|
||||
else return NULL;
|
||||
|
||||
if (mode & CAMEL_STREAM_FS_READ){
|
||||
if (mode & CAMEL_STREAM_FS_WRITE)
|
||||
flags = O_RDWR | O_CREAT;
|
||||
else
|
||||
flags = O_RDONLY;
|
||||
} else {
|
||||
if (mode & CAMEL_STREAM_FS_WRITE)
|
||||
flags = O_WRONLY | O_CREAT;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
if ( (mode & CAMEL_STREAM_FS_READ) && !(mode & CAMEL_STREAM_FS_WRITE) )
|
||||
if (v == -1) return NULL;
|
||||
|
||||
|
||||
@ -52,7 +52,6 @@ typedef struct
|
||||
CamelStream parent_object;
|
||||
GString *name;
|
||||
int fd;
|
||||
|
||||
} CamelStreamFs;
|
||||
|
||||
|
||||
|
||||
@ -21,22 +21,26 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "camel-stream.h"
|
||||
|
||||
static CamelStreamClass *parent_class=NULL;
|
||||
static CamelStreamClass *parent_class = NULL;
|
||||
|
||||
|
||||
/* Returns the class for a CamelMimeMessage */
|
||||
#define CS_CLASS(so) CAMEL_STREAM_CLASS (GTK_OBJECT(so)->klass)
|
||||
|
||||
static gint _read (CamelStream *stream, gchar *buffer, gint n);
|
||||
static gint _write (CamelStream *stream, gchar *buffer, gint n);
|
||||
static void _flush (CamelStream *stream);
|
||||
static gint _available (CamelStream *stream);
|
||||
static gboolean _eos (CamelStream *stream);
|
||||
static void _close (CamelStream *stream);
|
||||
static void
|
||||
default_camel_flush (CamelStream *stream)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void
|
||||
default_camel_close (CamelStream *stream)
|
||||
{
|
||||
/* nothing */
|
||||
}
|
||||
|
||||
static void
|
||||
camel_stream_class_init (CamelStreamClass *camel_stream_class)
|
||||
@ -45,19 +49,16 @@ camel_stream_class_init (CamelStreamClass *camel_stream_class)
|
||||
parent_class = gtk_type_class (gtk_object_get_type ());
|
||||
|
||||
/* virtual method definition */
|
||||
camel_stream_class->read = _read;
|
||||
camel_stream_class->write = _write;
|
||||
camel_stream_class->flush = _flush;
|
||||
camel_stream_class->available = _available;
|
||||
camel_stream_class->eos = _eos;
|
||||
camel_stream_class->close = _close;
|
||||
camel_stream_class->read = NULL;
|
||||
camel_stream_class->write = NULL;
|
||||
camel_stream_class->flush = default_camel_flush;
|
||||
camel_stream_class->available = NULL;
|
||||
camel_stream_class->eos = NULL;
|
||||
camel_stream_class->close = default_camel_close;
|
||||
|
||||
/* virtual method overload */
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
GtkType
|
||||
camel_stream_get_type (void)
|
||||
{
|
||||
@ -82,24 +83,17 @@ camel_stream_get_type (void)
|
||||
return camel_stream_type;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* _read: read bytes from a stream
|
||||
* @stream: stream
|
||||
* @buffer: buffer where bytes are stored
|
||||
* @n: max number of bytes to read
|
||||
*
|
||||
* camel_stream_read:
|
||||
* @stream: a CamelStream.
|
||||
* @buffer: buffer where bytes pulled from the stream are stored.
|
||||
* @n: max number of bytes to read.
|
||||
*
|
||||
* Read at most @n bytes from the @stream object and stores them
|
||||
* in the buffer pointed at by @buffer.
|
||||
*
|
||||
* Return value: number of bytes actually read.
|
||||
**/
|
||||
static gint
|
||||
_read (CamelStream *stream, gchar *buffer, gint n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
gint
|
||||
camel_stream_read (CamelStream *stream, gchar *buffer, gint n)
|
||||
{
|
||||
@ -107,98 +101,84 @@ camel_stream_read (CamelStream *stream, gchar *buffer, gint n)
|
||||
}
|
||||
|
||||
/**
|
||||
* _write: read bytes to a stream
|
||||
* @stream: the stream
|
||||
* @buffer: byte buffer
|
||||
* camel_stream_write:
|
||||
* @stream: a CamelStream object.
|
||||
* @buffer: buffer to write.
|
||||
* @n: number of bytes to write
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Write @n bytes from the buffer pointed at by @buffer into @stream.
|
||||
*
|
||||
* Return value: the number of bytes actually written
|
||||
* in the stream.
|
||||
**/
|
||||
static gint
|
||||
_write (CamelStream *stream, gchar *buffer, gint n)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
gint
|
||||
camel_stream_write (CamelStream *stream, gchar *buffer, gint n)
|
||||
{
|
||||
CS_CLASS (stream)->write (stream, buffer, n);
|
||||
return CS_CLASS (stream)->write (stream, buffer, n);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* _flush: flush pending changes
|
||||
* @stream: the stream
|
||||
*
|
||||
* camel_stream_flush:
|
||||
* @stream: a CamelStream object
|
||||
*
|
||||
* Flushes the contents of the stream to its backing store.
|
||||
**/
|
||||
static void
|
||||
_flush (CamelStream *stream)
|
||||
void
|
||||
camel_stream_flush (CamelStream *stream)
|
||||
{
|
||||
|
||||
return CS_CLASS (stream)->flush (stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* _available: return the number of bytes available for reading
|
||||
* @stream: the stream
|
||||
* camel_stream_available:
|
||||
* @stream: a CamelStream object
|
||||
*
|
||||
* Return the number of bytes available without blocking.
|
||||
*
|
||||
* Return value: the number of bytes available
|
||||
* Return value: the number of bytes available.
|
||||
**/
|
||||
static gint
|
||||
_available (CamelStream *stream)
|
||||
camel_stream_available (CamelStream *stream)
|
||||
{
|
||||
|
||||
return CS_CLASS (stream)->available (stream);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _eos: test if there are bytes left to read
|
||||
* @stream: the stream
|
||||
* camle_stream_eos:
|
||||
* @stream: a CamelStream object
|
||||
*
|
||||
* Test if there are bytes left to read on the @stream object.
|
||||
*
|
||||
*
|
||||
* Return value: true if all stream has been read
|
||||
* Return value: %TRUE if all the contents on the stream has been read, or
|
||||
* %FALSE if information is still available.
|
||||
**/
|
||||
static gboolean
|
||||
_eos (CamelStream *stream)
|
||||
camel_stream_eos (CamelStream *stream)
|
||||
{
|
||||
|
||||
return CS_CLASS (stream)->eos (stream);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* _close: close a stream
|
||||
* @stream: the stream
|
||||
*
|
||||
* camel_stram_close:
|
||||
* @stream: a CamelStream object.
|
||||
*
|
||||
* Close the @stream object.
|
||||
**/
|
||||
static void
|
||||
_close (CamelStream *stream)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
camel_stream_close (CamelStream *stream)
|
||||
{
|
||||
CS_CLASS (stream)->close (stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/***************** Utility functions ********************/
|
||||
|
||||
/**
|
||||
* came_stream_write_strings:
|
||||
* @stream: a CamelStream object.
|
||||
* @...: A %NULL terminated list of strings.
|
||||
*
|
||||
* This is a utility function that writes the list of
|
||||
* strings into the @stream object.
|
||||
*/
|
||||
void
|
||||
camel_stream_write_strings (CamelStream *stream, ... )
|
||||
{
|
||||
@ -209,7 +189,7 @@ camel_stream_write_strings (CamelStream *stream, ... )
|
||||
string = va_arg (args, char *);
|
||||
|
||||
while (string) {
|
||||
camel_stream_write_string(stream, string);
|
||||
camel_stream_write_string (stream, string);
|
||||
string = va_arg (args, char *);
|
||||
}
|
||||
va_end (args);
|
||||
|
||||
@ -52,13 +52,12 @@ typedef struct {
|
||||
GtkObjectClass parent_class;
|
||||
|
||||
/* Virtual methods */
|
||||
gint (*read) (CamelStream *stream, gchar *buffer, gint n);
|
||||
gint (*write) (CamelStream *stream, gchar *buffer, gint n);
|
||||
void (*flush) (CamelStream *stream);
|
||||
gint (*available) (CamelStream *stream);
|
||||
gboolean (*eos) (CamelStream *stream);
|
||||
void (*close) (CamelStream *stream);
|
||||
|
||||
gint (*read) (CamelStream *stream, gchar *buffer, gint n);
|
||||
gint (*write) (CamelStream *stream, gchar *buffer, gint n);
|
||||
void (*flush) (CamelStream *stream);
|
||||
gint (*available) (CamelStream *stream);
|
||||
gboolean (*eos) (CamelStream *stream);
|
||||
void (*close) (CamelStream *stream);
|
||||
} CamelStreamClass;
|
||||
|
||||
|
||||
@ -68,9 +67,12 @@ GtkType camel_stream_get_type (void);
|
||||
|
||||
|
||||
/* public methods */
|
||||
gint camel_stream_read (CamelStream *stream, gchar *buffer, gint n);
|
||||
gint camel_stream_write (CamelStream *stream, gchar *buffer, gint n);
|
||||
void camel_stream_close (CamelStream *stream);
|
||||
gint camel_stream_read (CamelStream *stream, gchar *buffer, gint n);
|
||||
gint camel_stream_write (CamelStream *stream, gchar *buffer, gint n);
|
||||
void camel_stream_flush (CamelStream *stream);
|
||||
gint camel_stream_available (CamelStream *stream);
|
||||
gboolean camel_stream_eos (CamelStream *stream);
|
||||
void camel_stream_close (CamelStream *stream);
|
||||
|
||||
/* utility macros and funcs */
|
||||
#define camel_stream_write_string(stream, string) camel_stream_write ((stream), (string), strlen (string))
|
||||
|
||||
@ -21,9 +21,7 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "gmime-content-field.h"
|
||||
#include "gstring-util.h"
|
||||
#include "camel-log.h"
|
||||
|
||||
@ -21,8 +21,7 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "gmime-utils.h"
|
||||
#include "gstring-util.h"
|
||||
#include "camel-log.h"
|
||||
@ -112,14 +111,18 @@ _store_header_pair_from_gstring (GHashTable *header_table, GString *header_line)
|
||||
|
||||
g_assert (header_table);
|
||||
if ( (header_line) && (header_line->str) ) {
|
||||
dich_result = g_string_dichotomy(header_line, ':', &header_name, &header_value, DICHOTOMY_NONE);
|
||||
dich_result = g_string_dichotomy (
|
||||
header_line, ':', &header_name, &header_value,
|
||||
GSTRING_DICHOTOMY_NONE);
|
||||
if (dich_result != 'o')
|
||||
camel_log(WARNING,
|
||||
"store_header_pair_from_gstring : dichotomy result is %c"
|
||||
"header line is :\n--\n%s\n--\n");
|
||||
|
||||
else {
|
||||
g_string_trim (header_value, " \t", TRIM_STRIP_LEADING | TRIM_STRIP_TRAILING);
|
||||
g_string_trim (
|
||||
header_value, " \t",
|
||||
GSTRING_TRIM_STRIP_LEADING | GSTRING_TRIM_STRIP_TRAILING);
|
||||
g_hash_table_insert (header_table, header_name, header_value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "gstring-util.h"
|
||||
#include "camel-log.h"
|
||||
|
||||
@ -64,7 +64,14 @@ g_string_clone(GString *string)
|
||||
|
||||
|
||||
/**
|
||||
* g_string_dichotomy : return the strings before and/or after
|
||||
* g_string_dichotomy:
|
||||
* @sep : separator
|
||||
* @prefix: pointer to be field by the prefix object
|
||||
* the prefix is not returned when the given pointer is NULL
|
||||
* @suffix: pointer to be field by the suffix object
|
||||
* the suffix is not returned when the given pointer is NULL
|
||||
*
|
||||
* Return the strings before and/or after
|
||||
* the last occurence of the specified separator
|
||||
*
|
||||
* This routine returns the string before and/or after
|
||||
@ -75,17 +82,12 @@ g_string_clone(GString *string)
|
||||
* suffix is set to NULL and result is set to 'n'
|
||||
* When the operation succedeed, the return value is 'o'
|
||||
*
|
||||
* @sep : separator
|
||||
* @prefix: pointer to be field by the prefix object
|
||||
* the prefix is not returned when the given pointer is NULL
|
||||
* @suffix: pointer to be field by the suffix object
|
||||
* the suffix is not returned when the given pointer is NULL
|
||||
*
|
||||
* @Return Value : result of the operation ('o', 'l' or 'n')
|
||||
*
|
||||
**/
|
||||
gchar
|
||||
g_string_dichotomy (GString *string, gchar sep, GString **prefix, GString **suffix, DichotomyOption options)
|
||||
g_string_dichotomy (GString *string, gchar sep, GString **prefix, GString **suffix,
|
||||
GStringDichotomyOption options)
|
||||
{
|
||||
gchar *str, *tmp;
|
||||
gint pos, len, first;
|
||||
@ -96,17 +98,19 @@ g_string_dichotomy (GString *string, gchar sep, GString **prefix, GString **suff
|
||||
g_assert( tmp=string->str );
|
||||
len = strlen(tmp);
|
||||
if (!len) {
|
||||
if (prefix) *prefix=NULL;
|
||||
if (suffix) *suffix=NULL;
|
||||
if (prefix)
|
||||
*prefix=NULL;
|
||||
if (suffix)
|
||||
*suffix=NULL;
|
||||
CAMEL_LOG(FULL_DEBUG,"string_dichotomy: string is empty\n");
|
||||
return 'n';
|
||||
}
|
||||
first = 0;
|
||||
|
||||
if ( (options & DICHOTOMY_STRIP_LEADING ) && (tmp[first] == sep) )
|
||||
if ( (options & GSTRING_DICHOTOMY_STRIP_LEADING ) && (tmp[first] == sep) )
|
||||
do {first++;} while ( (first<len) && (tmp[first] == sep) );
|
||||
|
||||
if (options & DICHOTOMY_STRIP_TRAILING )
|
||||
if (options & GSTRING_DICHOTOMY_STRIP_TRAILING )
|
||||
while (tmp[len-1] == sep)
|
||||
len--;
|
||||
|
||||
@ -117,7 +121,7 @@ g_string_dichotomy (GString *string, gchar sep, GString **prefix, GString **suff
|
||||
return 'n';
|
||||
}
|
||||
|
||||
if (options & DICHOTOMY_RIGHT_DIR) {
|
||||
if (options & GSTRING_DICHOTOMY_RIGHT_DIR) {
|
||||
pos = len;
|
||||
|
||||
do {
|
||||
@ -253,7 +257,7 @@ g_string_list_free (GList *string_list)
|
||||
|
||||
|
||||
GList *
|
||||
g_string_split (GString *string, char sep, gchar *trim_chars, TrimOption trim_options)
|
||||
g_string_split (GString *string, char sep, gchar *trim_chars, GStringTrimOption trim_options)
|
||||
{
|
||||
GList *result = NULL;
|
||||
gint first, last, pos;
|
||||
@ -296,10 +300,8 @@ g_string_split (GString *string, char sep, gchar *trim_chars, TrimOption trim_op
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void
|
||||
g_string_trim (GString *string, gchar *chars, TrimOption options)
|
||||
g_string_trim (GString *string, gchar *chars, GStringTrimOption options)
|
||||
{
|
||||
gint first_ok;
|
||||
gint last_ok;
|
||||
@ -308,24 +310,31 @@ g_string_trim (GString *string, gchar *chars, TrimOption options)
|
||||
|
||||
CAMEL_LOG(FULL_DEBUG,"**\nentering g_string_trim::\n");
|
||||
|
||||
if ((!string) || (!string->str)) return;
|
||||
if ((!string) || (!string->str))
|
||||
return;
|
||||
str = string->str;
|
||||
length = strlen (str);
|
||||
if (!length) return;
|
||||
if (!length)
|
||||
return;
|
||||
|
||||
first_ok = 0;
|
||||
last_ok = length - 1;
|
||||
|
||||
CAMEL_LOG (FULL_DEBUG,"g_string_trim:: trim_options:%d\n", options);
|
||||
if (options & TRIM_STRIP_LEADING)
|
||||
if (options & GSTRING_TRIM_STRIP_LEADING)
|
||||
while ( (first_ok <= last_ok) && (strchr (chars, str[first_ok])) )
|
||||
first_ok++;
|
||||
|
||||
if (options & TRIM_STRIP_TRAILING)
|
||||
if (options & GSTRING_TRIM_STRIP_TRAILING)
|
||||
while ( (first_ok <= last_ok) && (strchr (chars, str[last_ok])) )
|
||||
last_ok++;
|
||||
CAMEL_LOG (FULL_DEBUG,"g_string_trim::\n\t\"%s\":first ok:%d last_ok:%d\n", string->str, first_ok, last_ok);
|
||||
if (first_ok>0) g_string_erase (string, 0, first_ok);
|
||||
if (last_ok<length-1) g_string_truncate (string, last_ok - first_ok +1);
|
||||
CAMEL_LOG (FULL_DEBUG,"g_string_trim::\n\t\"%s\":first ok:%d last_ok:%d\n",
|
||||
string->str, first_ok, last_ok);
|
||||
|
||||
if (first_ok > 0)
|
||||
g_string_erase (string, 0, first_ok);
|
||||
|
||||
if (last_ok < length-1)
|
||||
g_string_truncate (string, last_ok - first_ok +1);
|
||||
|
||||
}
|
||||
|
||||
@ -35,33 +35,37 @@ extern "C" {
|
||||
#include <glib.h>
|
||||
|
||||
typedef enum {
|
||||
DICHOTOMY_NONE = 0,
|
||||
DICHOTOMY_RIGHT_DIR = 1,
|
||||
DICHOTOMY_STRIP_TRAILING = 2,
|
||||
DICHOTOMY_STRIP_LEADING = 4,
|
||||
GSTRING_DICHOTOMY_NONE = 0,
|
||||
GSTRING_DICHOTOMY_RIGHT_DIR = 1,
|
||||
GSTRING_DICHOTOMY_STRIP_TRAILING = 2,
|
||||
GSTRING_DICHOTOMY_STRIP_LEADING = 4,
|
||||
|
||||
} DichotomyOption;
|
||||
} GStringDichotomyOption;
|
||||
|
||||
typedef enum {
|
||||
TRIM_NONE = 0,
|
||||
TRIM_STRIP_TRAILING = 1,
|
||||
TRIM_STRIP_LEADING = 2,
|
||||
|
||||
} TrimOption;
|
||||
GSTRING_TRIM_NONE = 0,
|
||||
GSTRING_TRIM_STRIP_TRAILING = 1,
|
||||
GSTRING_TRIM_STRIP_LEADING = 2,
|
||||
} GStringTrimOption;
|
||||
|
||||
|
||||
gboolean g_string_equals(GString *string1, GString *string2);
|
||||
GString *g_string_clone(GString *string);
|
||||
gchar g_string_dichotomy( GString *string, gchar sep, GString **prefix, GString **suffix, DichotomyOption options);
|
||||
void g_string_append_g_string(GString *dest_string, GString *other_string);
|
||||
gboolean g_string_equals (GString *string1, GString *string2);
|
||||
GString *g_string_clone (GString *string);
|
||||
gchar g_string_dichotomy (GString *string, gchar sep,
|
||||
GString **prefix, GString **suffix,
|
||||
GStringDichotomyOption options);
|
||||
void g_string_append_g_string (GString *dest_string,
|
||||
GString *other_string);
|
||||
|
||||
gboolean g_string_equal_for_hash (gconstpointer v, gconstpointer v2);
|
||||
gboolean g_string_equal_for_hash (gconstpointer v, gconstpointer v2);
|
||||
gboolean g_string_equal_for_glist (gconstpointer v, gconstpointer v2);
|
||||
guint g_string_hash (gconstpointer v);
|
||||
void g_string_list_free (GList *string_list);
|
||||
guint g_string_hash (gconstpointer v);
|
||||
void g_string_list_free (GList *string_list);
|
||||
|
||||
GList *g_string_split (GString *string, char sep, gchar *trim_chars, TrimOption trim_options);
|
||||
void g_string_trim (GString *string, gchar *chars, TrimOption options);
|
||||
GList *g_string_split (GString *string, char sep,
|
||||
gchar *trim_chars, GStringTrimOption trim_options);
|
||||
void g_string_trim (GString *string, gchar *chars,
|
||||
GStringTrimOption options);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
/*
|
||||
XXX TODO: recover the words between #'s or ?'s after the path */
|
||||
|
||||
|
||||
#include <config.h>
|
||||
#include "url-util.h"
|
||||
|
||||
/* general item finder */
|
||||
|
||||
@ -3,393 +3,405 @@
|
||||
<!-- SGMLized by Bertrand <Bertrand.Guiheneuf@inria.fr> -->
|
||||
|
||||
<article id="index">
|
||||
<artheader>
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Giao</firstname>
|
||||
<surname>Nguyen</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<title>An in-depth look at the virtual folder mechanism</title>
|
||||
<abstract>
|
||||
<para>
|
||||
This document describes a different way of approaching mail
|
||||
organization and how all things are possible in this brave new
|
||||
world. This document does not describe physical storage issues nor
|
||||
interface issues.
|
||||
</para>
|
||||
<para>
|
||||
Historically mail has been organized into folders. These folders
|
||||
usually mapped to a single storage medium. The relationship between
|
||||
mail organization and storage medium was one to one. There was one
|
||||
mail organization for every storage medium. This scheme had its
|
||||
limitations.
|
||||
</para>
|
||||
<para>
|
||||
Efforts at categorizations are only meaningful at the instance that
|
||||
one categorized. To find any piece of data, regardless of how well
|
||||
it was categorized, required some amount of searching. Therefore, any
|
||||
attempts to nullify searching is doomed to fail. It's time to embrace
|
||||
searching as a way of life.
|
||||
</para>
|
||||
<para>
|
||||
These are the terms and their definitions. The example rules used are
|
||||
based on the syntax for VM (http://www.wonderworks.com/vm/) by Kyle
|
||||
Jones whose ideas form the basis for this. I'm only adding the
|
||||
existence of summary files to aid in scaling. I currently use VM and
|
||||
it's virtual-folder rules for my daily mail purposes. To date, my only
|
||||
complaints are speed (it has no caches) and for the unitiated, it's
|
||||
not very user-friendly.
|
||||
</para>
|
||||
<para>
|
||||
Comments, questions, rants, etc. should be directed at Giao Nguyen
|
||||
(grail@cafebabe.org) who will try to address issues in a timely
|
||||
manner.
|
||||
</para>
|
||||
</abstract>
|
||||
</artheader>
|
||||
<sect1 id="definitions">
|
||||
<title>Definitions</title>
|
||||
<sect2>
|
||||
<title>Store</title>
|
||||
<para>
|
||||
A location where mail can be found. This may be a file (Berkeley
|
||||
mbox), directory (MH), IMAP server, POP3 server, Exchange server,
|
||||
Lotus Notes server, a stack of Post-Its by your monitor fed through
|
||||
some OCR system.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Message</title>
|
||||
<para>
|
||||
An individual mail message.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Vfolder</title>
|
||||
<para>
|
||||
A group of messages sharing some commonality. This is the result of a
|
||||
query. The vfolder maybe contained in a store, but it is not necessary
|
||||
that a store holds only one vfolder. There is always an implicit
|
||||
vfolder rule which matches all messages. A store contains the vfolder
|
||||
which is the result of the query (any). It's short for virtual folder
|
||||
or maybe view folder. I dunno.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Default-vfolder</title>
|
||||
<para>
|
||||
The vfolder defined by (any) applied to the store. This is not the
|
||||
inbox. The inbox could easily be defined by a query. A default rule
|
||||
for the inbox could be (new) but it doesn't have to be. Mine happens
|
||||
to be (or (unread) (new)).
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Folder</title>
|
||||
<para>
|
||||
The classical mail folder approach: one message organization per
|
||||
store.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Query</title>
|
||||
<para>
|
||||
A search for messages. The result of this is a vfolder. There are two
|
||||
kinds of queries: named queries and lambda queries. More on this
|
||||
later.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Summary file </title>
|
||||
<para>
|
||||
An external file that contains pointers to messages which are matches
|
||||
for a named query. In addition to pointers, the summary file should
|
||||
also contain signatures of the store for sanity checks. When the term
|
||||
"index" is used as a verb, it means to build a summary file for a
|
||||
given name-value pair.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<artheader>
|
||||
<authorgroup>
|
||||
<author>
|
||||
<firstname>Giao</firstname>
|
||||
<surname>Nguyen</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
|
||||
<sect1>
|
||||
<title>Queries</title>
|
||||
<para>
|
||||
Named queries are analogous to classical mail folders. Because named
|
||||
queries maybe reused, summary files are kept as caches to reduce
|
||||
the overall cost of viewing a vfolder. Summary files are superior to
|
||||
folders in that they allow for the same messages to appear in multiple
|
||||
vfolders without message duplications. Duplications of messages
|
||||
defeats attempts at tagging a message with additional user information
|
||||
like annotations. Named queries will define folders.
|
||||
</para>
|
||||
<para>
|
||||
Lambda queries are similar to named queries except that they have no
|
||||
name. These are created on the fly by the user to filter out or
|
||||
include certain messages.
|
||||
</para>
|
||||
<para>
|
||||
All queries can be layered on top of each other. A lambda query can be
|
||||
layered on a named query and a named query can be layered on a lambda
|
||||
query. The possibilities are endless.
|
||||
</para>
|
||||
<para>
|
||||
The layerings can be done as boolean operations (and, or, not). Short
|
||||
circuiting should be used.
|
||||
</para>
|
||||
<para>
|
||||
Examples:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(unread))
|
||||
</programlisting>
|
||||
The (unread) query should only be evaluated on the results of (author
|
||||
"Giao").
|
||||
<programlisting>
|
||||
(or (author "Giao")
|
||||
(unread))
|
||||
</programlisting>
|
||||
Both of these queries should be evaluated. Any matches are added to the
|
||||
resulting vfolder.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Summary files</title>
|
||||
<para>
|
||||
Summary files are only meaningful when applied to the context of the
|
||||
default-vfolder of a store.
|
||||
</para>
|
||||
<para>
|
||||
Summary files should be generated for queries of the form:
|
||||
<programlisting>
|
||||
(function "constant value")
|
||||
</programlisting>
|
||||
Summary files should never be generated for queries of the form:
|
||||
<programlisting>
|
||||
(function (function1))
|
||||
|
||||
(and (function "value")
|
||||
(another-function "another value"))
|
||||
</programlisting>
|
||||
Given a query of the form:
|
||||
<programlisting>
|
||||
(and (function "value")
|
||||
(another-function "another value"))
|
||||
</programlisting>
|
||||
The system should use one summary file for (function "value") and
|
||||
another summary file for (another-function "another value"). I will
|
||||
call the prior form the "plain form".
|
||||
</para>
|
||||
<para>
|
||||
It should be noted that the signature of the store should be based on
|
||||
the assumption that new data may have been added to the store since
|
||||
the application generated the summary file. Signatures generated on
|
||||
the entirety of the store will most likely be meaningless for things
|
||||
like POP/IMAP servers.
|
||||
</para>
|
||||
</sect1>
|
||||
<title>An in-depth look at the virtual folder mechanism</title>
|
||||
<abstract>
|
||||
<para>
|
||||
This document describes a different way of approaching mail
|
||||
organization and how all things are possible in this brave new
|
||||
world. This document does not describe physical storage issues
|
||||
nor interface issues.
|
||||
</para>
|
||||
<para>
|
||||
Historically mail has been organized into folders. These
|
||||
folders usually mapped to a single storage medium. The
|
||||
relationship between mail organization and storage medium was
|
||||
one to one. There was one mail organization for every storage
|
||||
medium. This scheme had its limitations.
|
||||
</para>
|
||||
<para>
|
||||
Efforts at categorizations are only meaningful at the instance that
|
||||
one categorized. To find any piece of data, regardless of how well
|
||||
it was categorized, required some amount of searching. Therefore, any
|
||||
attempts to nullify searching is doomed to fail. It's time to embrace
|
||||
searching as a way of life.
|
||||
</para>
|
||||
<para>
|
||||
These are the terms and their definitions. The example rules used are
|
||||
based on the syntax for VM (http://www.wonderworks.com/vm/) by Kyle
|
||||
Jones whose ideas form the basis for this. I'm only adding the
|
||||
existence of summary files to aid in scaling. I currently use VM and
|
||||
it's virtual-folder rules for my daily mail purposes. To date, my only
|
||||
complaints are speed (it has no caches) and for the unitiated, it's
|
||||
not very user-friendly.
|
||||
</para>
|
||||
<para>
|
||||
Comments, questions, rants, etc. should be directed at Giao Nguyen
|
||||
(grail@cafebabe.org) who will try to address issues in a timely
|
||||
manner.
|
||||
</para>
|
||||
</abstract>
|
||||
</artheader>
|
||||
|
||||
<sect1>
|
||||
<title>Incremental indexing</title>
|
||||
<para>
|
||||
When new messages are detected, all known queries should be evaluated
|
||||
on the new messages. vfolders should be notified of new messages that
|
||||
are positive matches for their queries. The indexes generated by this
|
||||
process should be merged into the current indexes for the vfolder.
|
||||
</para>
|
||||
</sect1>
|
||||
<!-- Definitions -->
|
||||
<sect1 id="definitions">
|
||||
<title>Definitions</title>
|
||||
<sect2>
|
||||
<title>Store</title>
|
||||
<para>
|
||||
A location where mail can be found. This may be a file (Berkeley
|
||||
mbox), directory (MH), IMAP server, POP3 server, Exchange server,
|
||||
Lotus Notes server, a stack of Post-Its by your monitor fed through
|
||||
some OCR system.
|
||||
</para>
|
||||
</sect2>
|
||||
|
||||
<sect1>
|
||||
<title>Can I have multiple stores?</title>
|
||||
<para>
|
||||
I don't see why not. Again, the inbox is a vfolder so you can get a
|
||||
unified inbox consisting of all new mail sent to all your stores or
|
||||
your can get inboxes for each store or any combination your heart
|
||||
desire. You get your cake, eat it, and someone else cleans the dishes!
|
||||
</para>
|
||||
</sect1>
|
||||
<sect2>
|
||||
<title>Message</title>
|
||||
<para>
|
||||
An individual mail message.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Vfolder</title>
|
||||
<para>
|
||||
A group of messages sharing some commonality. This is the result of a
|
||||
query. The vfolder maybe contained in a store, but it is not necessary
|
||||
that a store holds only one vfolder. There is always an implicit
|
||||
vfolder rule which matches all messages. A store contains the vfolder
|
||||
which is the result of the query (any). It's short for virtual folder
|
||||
or maybe view folder. I dunno.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Default-vfolder</title>
|
||||
<para>
|
||||
The vfolder defined by (any) applied to the store. This is not the
|
||||
inbox. The inbox could easily be defined by a query. A default rule
|
||||
for the inbox could be (new) but it doesn't have to be. Mine happens
|
||||
to be (or (unread) (new)).
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Folder</title>
|
||||
<para>
|
||||
The classical mail folder approach: one message organization per
|
||||
store.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Query</title>
|
||||
<para>
|
||||
A search for messages. The result of this is a vfolder. There are two
|
||||
kinds of queries: named queries and lambda queries. More on this
|
||||
later.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Summary file </title>
|
||||
<para>
|
||||
An external file that contains pointers to messages which are matches
|
||||
for a named query. In addition to pointers, the summary file should
|
||||
also contain signatures of the store for sanity checks. When the term
|
||||
"index" is used as a verb, it means to build a summary file for a
|
||||
given name-value pair.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Why all this?</title>
|
||||
<para>
|
||||
Consider the dynamic nature of the following query:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
</programlisting>
|
||||
today-midnight would be a function that is evaluated at run-time to
|
||||
calculate the appropriate object.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Scenarios of usage and their solutions</title>
|
||||
<sect2>
|
||||
<title>Mesage alterations</title>
|
||||
<para>
|
||||
This is a fuzzy area that should be left to the UI to handle. Messages
|
||||
are altered. Read status are altered when a new message is read for
|
||||
example. How do we handle this if our query is for unread messages?
|
||||
Upon viewing the state would change.
|
||||
</para>
|
||||
<para>
|
||||
One idea is to not evaluate the queries unless we're changing between
|
||||
vfolder views. This assumes that one can only view a particular
|
||||
vfolder at a time. For multi-vfolder viewing, a message change should
|
||||
propagate through the vfolder system. Certain effects (as in our
|
||||
example) would not be intuitive.
|
||||
</para>
|
||||
<para>
|
||||
It would not be a clean solution to make special cases but they may be
|
||||
necessary where certain defined fields are ignored when they are
|
||||
changed. Some combination of the above rules can be used. I don't
|
||||
think it's an easy solution.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Message inclusion and exclusion</title>
|
||||
<para>
|
||||
Messages are included and excluded also with queries. The final query
|
||||
will have the form of:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(criteria value)
|
||||
(not (criteria other-value)))
|
||||
</programlisting>
|
||||
Userland criterias may be a label of some sort. These may be userland
|
||||
labels or Message-IDs. What are the performance issues involved in
|
||||
this? With short circuiting, it's not a major problem.
|
||||
</para>
|
||||
<para>
|
||||
The criterias and values are determined by the UI. The vfolder
|
||||
mechanism isn't concerned with such issues.
|
||||
</para>
|
||||
<para>
|
||||
Messages can be included and excluded at will. The idea is often
|
||||
called "arbitrary inclusion/exclusion". This can be done by
|
||||
Message-IDs or other fields. It's been noted that Message-IDs are not
|
||||
unique.
|
||||
</para>
|
||||
<para>
|
||||
I propose that any given vfolder is allocated an inclusion label and an
|
||||
exclusion label. These should be randomly generated. This should be
|
||||
part of the vfolder description. It should be noted that the vfolder
|
||||
description has not been drafted yet.
|
||||
</para>
|
||||
<para>
|
||||
The result is such that the rules for a given named query is:
|
||||
<programlisting>
|
||||
(and (user-query)
|
||||
(label inclusion-label)
|
||||
(not exclusion-label))
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Query scheduling</title>
|
||||
<para>
|
||||
Consider the following extremely dynamic queries:
|
||||
<programlisting>
|
||||
A:
|
||||
(and (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
|
||||
B:
|
||||
(and (sent-after (today-midnight))
|
||||
(author "Giao"))
|
||||
|
||||
C:
|
||||
(or (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
</programlisting>
|
||||
Query A would be significantly faster because (author "Giao") is not
|
||||
dynamic. A summary file could be generated for this query. Query B is
|
||||
slow and can be optimized if there was a query compiler of some
|
||||
sort. Query C demonstrates a query in which there is no good
|
||||
optimization which can be applied. These come with a certain amount of
|
||||
baggage.
|
||||
</para>
|
||||
<para>
|
||||
It seems then that for boolean 'and' operations, plain forms should be
|
||||
moved forward and other queries should be moved such that they are
|
||||
evaluated later. I would expect that the majority of queries would be
|
||||
of the plain form.
|
||||
</para>
|
||||
<para>
|
||||
First is that the summary file is tied to the query and the store
|
||||
where the query originates from. Second, a hashing function for
|
||||
strings needs to be calculated for the query so that the query and the
|
||||
summary file can be associated. This hashing function could be similar
|
||||
to the hashing function described in Rob Pike's "The Practice of
|
||||
Programming". (FIXME: Stick page number here)
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Archives</title>
|
||||
<para>
|
||||
Many people are concerned that archives won't be preserved, archives
|
||||
aren't supported, and many other archive related issues. This is the
|
||||
short version.
|
||||
</para>
|
||||
<para>
|
||||
Archives are just that, archives. Archives are stores. Take your
|
||||
vfolder, export it to a store. You are done. If you load up the store
|
||||
again, then the default-vfolder of that store is the view of the
|
||||
vfolder, except the query is different.
|
||||
</para>
|
||||
<para>
|
||||
The point to vfolder is not to do away with classical folder
|
||||
representation but to move the queries to the front where it would
|
||||
make data management easier for people who don't think in terms of
|
||||
files but in terms of queries because ordinary people don't think in
|
||||
terms of files.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Miscellany</title>
|
||||
<sect2>
|
||||
<title>Annotations</title>
|
||||
<para>
|
||||
There should be a scheme to add annotations to messages. Common mail
|
||||
user agents have used a tag in the message header to mark messages as
|
||||
read/unread for example. Extending on this we have the ability to add
|
||||
our own data to a message to add meaning to it. If we have a good
|
||||
scheme for doing this, new possibilities are opened.
|
||||
</para>
|
||||
<sect3>
|
||||
<title>Keywords</title>
|
||||
<para>
|
||||
When sending a message, a message could have certain keywords attached
|
||||
to it. While this can be done with the subject line, the subject line
|
||||
has a tendency to be munged by other mail applications. One popular
|
||||
example is the "[rR]e:" prefix. Using the subject line also breaks the
|
||||
"contract" with other mail user agents. Using keywords in another
|
||||
field in the message header allows the sender to assist the recipient
|
||||
in organizing data automatically. Note that the sender can only
|
||||
provide hints as the sender is unlikely to know the organization
|
||||
schemes of the recipient.
|
||||
<!-- Queries -->
|
||||
<sect1>
|
||||
<title>Queries</title>
|
||||
<para>
|
||||
Named queries are analogous to classical mail folders. Because named
|
||||
queries maybe reused, summary files are kept as caches to reduce
|
||||
the overall cost of viewing a vfolder. Summary files are superior to
|
||||
folders in that they allow for the same messages to appear in multiple
|
||||
vfolders without message duplications. Duplications of messages
|
||||
defeats attempts at tagging a message with additional user information
|
||||
like annotations. Named queries will define folders.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Scope</title>
|
||||
<para>
|
||||
Let us assume that we have multiple stores. Does a query work on a
|
||||
given store? Or does it work on all stores? Or is it configurable such
|
||||
that a query can work on a user-selected list of stores?
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<para>
|
||||
Lambda queries are similar to named queries except that they have no
|
||||
name. These are created on the fly by the user to filter out or
|
||||
include certain messages.
|
||||
</para>
|
||||
<para>
|
||||
All queries can be layered on top of each other. A lambda query can be
|
||||
layered on a named query and a named query can be layered on a lambda
|
||||
query. The possibilities are endless.
|
||||
</para>
|
||||
<para>
|
||||
The layerings can be done as boolean operations (and, or, not). Short
|
||||
circuiting should be used.
|
||||
</para>
|
||||
<para>
|
||||
Examples:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(unread))
|
||||
</programlisting>
|
||||
The (unread) query should only be evaluated on the results of (author
|
||||
"Giao").
|
||||
<programlisting>
|
||||
(or (author "Giao")
|
||||
(unread))
|
||||
</programlisting>
|
||||
Both of these queries should be evaluated. Any matches are added to the
|
||||
resulting vfolder.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1>
|
||||
<title>Alternatives to the above</title>
|
||||
<para>
|
||||
Jim Meyer (purp@selequa.com) is putting some notes on where
|
||||
annotations needs to be located. They'll be located here as well as
|
||||
any contributions I may have to them.
|
||||
</para>
|
||||
</sect1>
|
||||
<!-- Summary files -->
|
||||
<sect1>
|
||||
<title>Summary files</title>
|
||||
<para>
|
||||
Summary files are only meaningful when applied to the context of the
|
||||
default-vfolder of a store.
|
||||
</para>
|
||||
<para>
|
||||
Summary files should be generated for queries of the form:
|
||||
<programlisting>
|
||||
(function "constant value")
|
||||
</programlisting>
|
||||
Summary files should never be generated for queries of the form:
|
||||
<programlisting>
|
||||
(function (function1))
|
||||
|
||||
(and (function "value")
|
||||
(another-function "another value"))
|
||||
</programlisting>
|
||||
Given a query of the form:
|
||||
<programlisting>
|
||||
(and (function "value")
|
||||
(another-function "another value"))
|
||||
</programlisting>
|
||||
The system should use one summary file for (function "value") and
|
||||
another summary file for (another-function "another value"). I will
|
||||
call the prior form the "plain form".
|
||||
</para>
|
||||
<para>
|
||||
It should be noted that the signature of the store should be based on
|
||||
the assumption that new data may have been added to the store since
|
||||
the application generated the summary file. Signatures generated on
|
||||
the entirety of the store will most likely be meaningless for things
|
||||
like POP/IMAP servers.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<!-- Incremental Indexing -->
|
||||
<sect1>
|
||||
<title>Incremental indexing</title>
|
||||
<para>
|
||||
When new messages are detected, all known queries should be evaluated
|
||||
on the new messages. vfolders should be notified of new messages that
|
||||
are positive matches for their queries. The indexes generated by this
|
||||
process should be merged into the current indexes for the vfolder.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<!-- Can I have multiple stores -->
|
||||
<sect1>
|
||||
<title>Can I have multiple stores?</title>
|
||||
<para>
|
||||
I don't see why not. Again, the inbox is a vfolder so you can get a
|
||||
unified inbox consisting of all new mail sent to all your stores or
|
||||
your can get inboxes for each store or any combination your heart
|
||||
desire. You get your cake, eat it, and someone else cleans the dishes!
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<!-- Why all this? -->
|
||||
<sect1>
|
||||
<title>Why all this?</title>
|
||||
<para>
|
||||
Consider the dynamic nature of the following query:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
</programlisting>
|
||||
today-midnight would be a function that is evaluated at run-time to
|
||||
calculate the appropriate object.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<!-- Scenarios of usage and their solutions -->
|
||||
<sect1>
|
||||
<title>Scenarios of usage and their solutions</title>
|
||||
<sect2>
|
||||
<title>Mesage alterations</title>
|
||||
<para>
|
||||
This is a fuzzy area that should be left to the UI to handle. Messages
|
||||
are altered. Read status are altered when a new message is read for
|
||||
example. How do we handle this if our query is for unread messages?
|
||||
Upon viewing the state would change.
|
||||
</para>
|
||||
<para>
|
||||
One idea is to not evaluate the queries unless we're changing between
|
||||
vfolder views. This assumes that one can only view a particular
|
||||
vfolder at a time. For multi-vfolder viewing, a message change should
|
||||
propagate through the vfolder system. Certain effects (as in our
|
||||
example) would not be intuitive.
|
||||
</para>
|
||||
<para>
|
||||
It would not be a clean solution to make special cases but they may be
|
||||
necessary where certain defined fields are ignored when they are
|
||||
changed. Some combination of the above rules can be used. I don't
|
||||
think it's an easy solution.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Message inclusion and exclusion</title>
|
||||
<para>
|
||||
Messages are included and excluded also with queries. The final query
|
||||
will have the form of:
|
||||
<programlisting>
|
||||
(and (author "Giao")
|
||||
(criteria value)
|
||||
(not (criteria other-value)))
|
||||
</programlisting>
|
||||
Userland criterias may be a label of some sort. These may be userland
|
||||
labels or Message-IDs. What are the performance issues involved in
|
||||
this? With short circuiting, it's not a major problem.
|
||||
</para>
|
||||
<para>
|
||||
The criterias and values are determined by the UI. The vfolder
|
||||
mechanism isn't concerned with such issues.
|
||||
</para>
|
||||
<para>
|
||||
Messages can be included and excluded at will. The idea is often
|
||||
called "arbitrary inclusion/exclusion". This can be done by
|
||||
Message-IDs or other fields. It's been noted that Message-IDs are not
|
||||
unique.
|
||||
</para>
|
||||
<para>
|
||||
I propose that any given vfolder is allocated an inclusion label and an
|
||||
exclusion label. These should be randomly generated. This should be
|
||||
part of the vfolder description. It should be noted that the vfolder
|
||||
description has not been drafted yet.
|
||||
</para>
|
||||
<para>
|
||||
The result is such that the rules for a given named query is:
|
||||
<programlisting>
|
||||
(and (user-query)
|
||||
(label inclusion-label)
|
||||
(not exclusion-label))
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Query scheduling</title>
|
||||
<para>
|
||||
Consider the following extremely dynamic queries:
|
||||
<programlisting>
|
||||
A:
|
||||
(and (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
|
||||
B:
|
||||
(and (sent-after (today-midnight))
|
||||
(author "Giao"))
|
||||
|
||||
C:
|
||||
(or (author "Giao")
|
||||
(sent-after (today-midnight)))
|
||||
</programlisting>
|
||||
Query A would be significantly faster because (author "Giao") is not
|
||||
dynamic. A summary file could be generated for this query. Query B is
|
||||
slow and can be optimized if there was a query compiler of some
|
||||
sort. Query C demonstrates a query in which there is no good
|
||||
optimization which can be applied. These come with a certain amount of
|
||||
baggage.
|
||||
</para>
|
||||
<para>
|
||||
It seems then that for boolean 'and' operations, plain forms should be
|
||||
moved forward and other queries should be moved such that they are
|
||||
evaluated later. I would expect that the majority of queries would be
|
||||
of the plain form.
|
||||
</para>
|
||||
<para>
|
||||
First is that the summary file is tied to the query and the store
|
||||
where the query originates from. Second, a hashing function for
|
||||
strings needs to be calculated for the query so that the query and the
|
||||
summary file can be associated. This hashing function could be similar
|
||||
to the hashing function described in Rob Pike's "The Practice of
|
||||
Programming". (FIXME: Stick page number here)
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Archives</title>
|
||||
<para>
|
||||
Many people are concerned that archives won't be preserved, archives
|
||||
aren't supported, and many other archive related issues. This is the
|
||||
short version.
|
||||
</para>
|
||||
<para>
|
||||
Archives are just that, archives. Archives are stores. Take your
|
||||
vfolder, export it to a store. You are done. If you load up the store
|
||||
again, then the default-vfolder of that store is the view of the
|
||||
vfolder, except the query is different.
|
||||
</para>
|
||||
<para>
|
||||
The point to vfolder is not to do away with classical folder
|
||||
representation but to move the queries to the front where it would
|
||||
make data management easier for people who don't think in terms of
|
||||
files but in terms of queries because ordinary people don't think in
|
||||
terms of files.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<!-- Miscellany -->
|
||||
<sect1>
|
||||
<title>Miscellany</title>
|
||||
<sect2>
|
||||
<title>Annotations</title>
|
||||
<para>
|
||||
There should be a scheme to add annotations to messages. Common mail
|
||||
user agents have used a tag in the message header to mark messages as
|
||||
read/unread for example. Extending on this we have the ability to add
|
||||
our own data to a message to add meaning to it. If we have a good
|
||||
scheme for doing this, new possibilities are opened.
|
||||
</para>
|
||||
<sect3>
|
||||
<title>Keywords</title>
|
||||
<para>
|
||||
When sending a message, a message could have certain keywords attached
|
||||
to it. While this can be done with the subject line, the subject line
|
||||
has a tendency to be munged by other mail applications. One popular
|
||||
example is the "[rR]e:" prefix. Using the subject line also breaks the
|
||||
"contract" with other mail user agents. Using keywords in another
|
||||
field in the message header allows the sender to assist the recipient
|
||||
in organizing data automatically. Note that the sender can only
|
||||
provide hints as the sender is unlikely to know the organization
|
||||
schemes of the recipient.
|
||||
</para>
|
||||
</sect3>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Scope</title>
|
||||
<para>
|
||||
Let us assume that we have multiple stores. Does a query work on a
|
||||
given store? Or does it work on all stores? Or is it configurable such
|
||||
that a query can work on a user-selected list of stores?
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<!-- Alternatives to the above -->
|
||||
<sect1>
|
||||
<title>Alternatives to the above</title>
|
||||
<para>
|
||||
Jim Meyer (purp@selequa.com) is putting some notes on where
|
||||
annotations needs to be located. They'll be located here as well as
|
||||
any contributions I may have to them.
|
||||
</para>
|
||||
</sect1>
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user