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:
Miguel de Icaza
1999-07-15 00:11:56 +00:00
committed by Arturo Espinosa
parent 68f3afb7a2
commit fce26238c4
22 changed files with 679 additions and 597 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -20,7 +20,7 @@
* USA
*/
#include <config.h>
#include "camel-log.h"
int camel_debug_level = 10;

View File

@ -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);
}

View File

@ -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
}

View File

@ -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"

View File

@ -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
}

View File

@ -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;

View File

@ -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"

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -52,7 +52,6 @@ typedef struct
CamelStream parent_object;
GString *name;
int fd;
} CamelStreamFs;

View File

@ -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);

View File

@ -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))

View File

@ -21,9 +21,7 @@
* USA
*/
#include <config.h>
#include "gmime-content-field.h"
#include "gstring-util.h"
#include "camel-log.h"

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -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 */

View File

@ -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>