diff options
Diffstat (limited to 'maildir/maildirkeywords.h')
| -rw-r--r-- | maildir/maildirkeywords.h | 532 | 
1 files changed, 532 insertions, 0 deletions
| diff --git a/maildir/maildirkeywords.h b/maildir/maildirkeywords.h new file mode 100644 index 0000000..55b4ecc --- /dev/null +++ b/maildir/maildirkeywords.h @@ -0,0 +1,532 @@ +/* +** Copyright 2003 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#ifndef	maildirkeywords_h +#define	maildirkeywords_h + +#include	"config.h" +#include	<stdio.h> +#include	<string.h> + +#ifdef  __cplusplus +extern "C" { +#endif + + +/* +** IMAP keywords.  This data structure is designed so that it is possible to: +** A) Find all messages that have the given keyword, +** B) Find all keywords that are set for the given message, +** C) Optimize memory usage, even with many keywords set for many msgs. +*/ + +/* A doubly-linked list of keywords */ + +struct libmail_keywordEntry { +	struct libmail_keywordEntry *prev, *next; /* Doubly-linked list */ + + +#define keywordName(ke) ((char *)((ke)+1)) + +	union { +		void *userPtr; /* Misc ptr */ +		unsigned long userNum; /* Or misc num */ +	} u; + +	struct libmail_kwMessageEntry *firstMsg, *lastMsg; +	/* Doubly-linked list of messages that use this keyword */ +}; + + +/* The main keyword hash table */ + +struct libmail_kwHashtable { + +	struct libmail_keywordEntry heads[43], tails[43]; +	/* Dummy head/tail nodes for each hash bucket */ + +	int keywordAddedRemoved; +}; + +struct libmail_kwMessageEntry { +	struct libmail_kwMessageEntry *next, *prev; +	/* Doubly-linked list of all keywords set for this message */ + +	struct libmail_kwMessageEntry *keywordNext, *keywordPrev; +	/* Doubly-linked list of all entries for the same keyword */ + +	struct libmail_keywordEntry *libmail_keywordEntryPtr; /* The keyword */ +	struct libmail_kwMessage *libmail_kwMessagePtr; /* The message */ +}; + +struct libmail_kwMessage { + +	struct libmail_kwMessageEntry *firstEntry, *lastEntry; +	/* Doubly-linked list of all keywords set for this message */ + +	union { +		void *userPtr; /* Misc ptr */ +		unsigned long userNum; /* Or misc num */ +	} u; +}; + +/* +** Initialize a libmail_kwHashtable +*/ +void libmail_kwhInit(struct libmail_kwHashtable *); + + +/* +** Returns 0 if libmail_kwHashtable is empty.  Return -1, errno=EIO if it's +** not (sanity check). +*/ +int libmail_kwhCheck(struct libmail_kwHashtable *); + +/* +** Enumerate all defined keywords. +*/ +int libmail_kwEnumerate(struct libmail_kwHashtable *h, +			int (*callback_func)(struct libmail_keywordEntry *, +					     void *), +			void *callback_arg); + +/* +** Find a keyword in the hashtable, optionally create it.  If createIfNew, +** then we MUST add the returned result to some keywordMessage, else there'll +** be a cleanup problem. +*/ +struct libmail_keywordEntry * +libmail_kweFind(struct libmail_kwHashtable *ht, +			 const char *name, +			 int createIfNew); + + +extern const char *libmail_kwVerbotten; +/* +** Optional list of chars prohibited in keyword names.  They get automagically +** replaced with underscores. +*/ +extern int libmail_kwCaseSensitive; +/* +** Non zero if keyword names are case sensitive. +*/ + +/* +** Clear a reference to a particular keyword, from a particular message. +*/ +void libmail_kweClear(struct libmail_keywordEntry *); + +/* +** Create an abstract message object, with no keywords currently defined. +*/ +struct libmail_kwMessage *libmail_kwmCreate(); + +/* +** Destroy the message object, automatically removing any keywords that were +** used by the object. +*/ +void libmail_kwmDestroy(struct libmail_kwMessage *); + +/* +** Link a keyword to a message. +*/ +int libmail_kwmSet(struct libmail_kwMessage *, struct libmail_keywordEntry *); + +/* +** Link a keyword to a message, by keyword's name. +*/ +int libmail_kwmSetName(struct libmail_kwHashtable *, +		       struct libmail_kwMessage *, const char *); + +/* +** Compare two messages, return 0 if they have the same keywords. +*/ +int libmail_kwmCmp(struct libmail_kwMessage *, +		   struct libmail_kwMessage *); + +/* +** Clear a keyword from a message. +*/ +int libmail_kwmClearName(struct libmail_kwMessage *, const char *); +/* +** libmail_kwmClearName is for INTERNAL USE ONLY -- the name doesn't get vetted +** by libmail_kweFind. +*/ + +/* +** Clear a keyword from a message, the public version. +*/ +int libmail_kwmClear(struct libmail_kwMessage *, struct libmail_keywordEntry *); +/* +** +*/ +int libmail_kwmClearEntry(struct libmail_kwMessageEntry *); + + +/***************************************************************************** + +The above is low-level stuff.  And now, a high level maildir storage API: + +*****************************************************************************/ + +/* +** Read keywords from a maildir.  The application presumably has read and +** compiled a list of messages it found in the maildir's cur (and new) +** directories. +** +** The function maildir_kwRead() will now read the keywords associated with +** each message.  How the application maintains the list of messages in the +** maildir is irrelevant.  The application simply needs to allocate a pointer +** to a libmail_kwMessage structure, one pointer for each message in the +** maildir.  Each pointer must be initialized to a NULL, and the application +** provides a set of callback functions, as defined below, that return +** a pointer to this pointer (pay attention now), given the filename. +** maildir_kwRead() invokes the callback functions, as appropriate, while +** it's doing its business. +** +** There's other callback functions too, so let's get to business. +** The application must initialize the following structure before calling +** maildir_kwRead().  This is where the pointers to all callback functions +** are provided: +*/ + +struct maildir_kwReadInfo { + +	struct libmail_kwMessage **(*findMessageByFilename)(const char +							    *filename, +							    int autocreate, +							    size_t *indexNum, +							    void *voidarg); +	/* +	** Return a pointer to a libmail_kwMessage * that's associated with +	** the message whose name is 'filename'.  'filename' will not have +	** :2, and the message's flags, so the application needs to be +	** careful. +	** +	** All libmail_kwMessage *s are initially NULL.  If autocreate is not +	** zero, the application must use libmail_kwmCreate() to initialize +	** the pointer, before returning.  Otherwise, the application should +	** return a pointer to a NULL libmail_kwMessage *. +	** +	** The application may use libmail_kwMessage.u for its own purposes. +	** +	** The application should return NULL if it can't find 'filename' +	** in its list of messages in the maildir.  That is a defined +	** possibility, and occur in certain race conditions (which are +	** properly handled, of course). +	** +	** If indexNum is not NULL, the application should set *indexNum to +	** the found message's index (if the application does not return NULL). +	** All messages the application has must be consecutively numbered, +	** beginning with 0 and up to, but not including, whatever the +	** application returns in getMessageCount(). +	*/ + +	size_t (*getMessageCount)(void *voidarg); +	/* +	** The application should return the number of messages it thinks +	** there are in the maildir. +	*/ + +	struct libmail_kwMessage **(*findMessageByIndex)(size_t indexNum, +							 int autocreate, +							 void *voidarg); +	/* +	** This one is like the findMessageByFilename() callback, except that +	** instead of filename the applicationg gets indexNum which ranges +	** between 0 and getMessageCount()-1. +	** The return code from this callback is identical to the return code +	** from findMessageByFilename(), and autocreate's semantics are also +	** the same. +	*/ + +	const char *(*getMessageFilename)(size_t n, void *voidarg); +	/* +	** The application should return the filename for message #n.  The +	** application may or may not include :2, in the returned ptr. +	*/ + +	struct libmail_kwHashtable * (*getKeywordHashtable)(void *voidarg); +	/* +	** The application should return the libmail_kwHashtable that it +	** allocated to store all the keyword stuff.  Read keywords are +	** allocated from this hashtable. +	*/ + +	void (*updateKeywords)(size_t n, struct libmail_kwMessage *kw, +			       void *voidarg); +	/* +	** The updateKeywords callback gets invoked by maildir_kwRead() +	** if it needs to throw out the list of keywords it already read for +	** a given message, and replace it, instead, with another set of +	** keywords.  This can happen in certain circumstances. +	** +	** The application should retrieve the libmail_kwMessage pointer for +	** message #n.  It may or may not be null.  If it's not null, the +	** application must use libmail_kwmDestroy().  Then, afterwards, +	** the application should save 'kw' as the new pointer. +	** +	** This callback is provided so that the application may save whatever +	** it wants to save in kw->u.userPtr or kw->u.userNum, because 'kw' +	** was created by libmail_kwRead(), and not one of the two findMessage +	** callbacks. +	*/ + +	void *voidarg; +	/* +	** All of the above callbacks receive this voidarg as their last +	** argument. +	*/ + +	int tryagain; +	/* +	** libmail_kwRead() returns 0 for success, or -1 for a system failure +	** (check errno). +	** +	** If libmail_kwRead() returned 0, the application MUST check +	** tryagain. +	** +	** If tryagain is not 0, the application MUST: +	**     A) Take any non-NULL libmail_kwMessage pointers that are +	**        associated with each message in the maildir, use +	**        libmail_kwmDestroy() to blow them away, and reset each +	**        pointer to NULL. +	** +	**     B) Invoke libmail_kwRead() again. +	** +	** A non-0 tryagain indicates a recoverable race condition. +	*/ + + +	/* Everything below is internal */ + +	int updateNeeded; +	int errorOccured; +}; + + +int maildir_kwRead(const char *maildir, +		   struct maildir_kwReadInfo *rki); + +/* +** maildir_kwSave saves new keywords for a particular message: +*/ + +int maildir_kwSave(const char *maildir, /* The maildir */ +		   const char *filename, +		    /* The message.  :2, is allowed, and ignored */ + +		   struct libmail_kwMessage *newKeyword, +		    /* New keywords.  The ptr may be NULL */ + +		   char **tmpname, +		   char **newname, + +		   int tryAtomic); + +int maildir_kwSaveArray(const char *maildir, +			const char *filename, +			const char **flags, +			char **tmpname, +			char **newname, +			int tryAtomic); + +/* +** maildir_kwSave returns -1 for an error.  If it return 0, it will initialize +** *tmpname and *newname, both will be full path filenames.  The application +** needs to simply call rename() with both filenames, and free() them, to +** effect the change.  Example: +** +**  char *tmpname, *newname; +** +**  if (maildir_kwSave( ..., &tmpname, &newname) == 0) +**  { +**         rename(tmpname, newname); +** +**         free(tmpname); +**         free(newname); +**  } +** +**  Of course, rename()s return code should also be checked. +** +**  If 'tryAtomic' is non-zero, the semantics are going to be slightly +**  different.  tryAtomic is non-zero when we want to update keywords +**  atomically.  To do that, first, use maildir_kwRead()  (or, most likely +**  maildir_kwgReadMaildir) to read the existing keywords, update the keywords +**  for the particular message, use maildir_kwSave(), but instead of rename() +**  use link().  Whether link succeeds or not, use unlink(tmpname) in any +**  case.  If link() failed with EEXIST, we had a race condition, so try +**  again. +**  Note - in NFS environments, must check not only that links succeeds, but +**  if stat-ing the tmpfile the number of links also must be 2. +*/ + +/* +** Set libmail_kwEnabled to ZERO in order to silently disable all maildir +** keyword functionality.  It's optional in Courier-IMAP.  Setting this flag +** to zero disables all actual keyword read/write functions, however all the +** necessary data still gets created (namely the courierimapkeywords +** subdirectory. +*/ + +extern int libmail_kwEnabled; + + +/* +** The following functions are "semi-internal". +** +** maildir_kwExport() uses the same struct maildir_kwReadInfo, to "export" +** the list of keywords assigned to all messages into a file. +** +** maildir_kwImport() imports the saved keyword list. +** +** These functions are meant to save a "snapshot" of the keywords into a +** flag file, nothing else. +*/ + +int maildir_kwExport(FILE *, struct maildir_kwReadInfo *); +int maildir_kwImport(FILE *, struct maildir_kwReadInfo *); + + +/**************************************************************************** + +Generic maildir_kwRead implementation. + +****************************************************************************/ + +struct libmail_kwGeneric { + +	struct libmail_kwHashtable kwHashTable; + +	size_t nMessages; + +	struct libmail_kwGenericEntry **messages; +	int messagesValid; + +	struct libmail_kwGenericEntry *messageHashTable[99]; +}; + +struct libmail_kwGenericEntry { + +	struct libmail_kwGenericEntry *next; /* On the hash table */ + +	char *filename; +	size_t messageNum; +	struct libmail_kwMessage *keywords; +}; + +void libmail_kwgInit(struct libmail_kwGeneric *g); +int libmail_kwgDestroy(struct libmail_kwGeneric *g); +int libmail_kwgReadMaildir(struct libmail_kwGeneric *g, +			   const char *maildir); + +struct libmail_kwGenericEntry * +libmail_kwgFindByName(struct libmail_kwGeneric *g, const char *filename); + +struct libmail_kwGenericEntry * +libmail_kwgFindByIndex(struct libmail_kwGeneric *g, size_t n); + +#ifdef  __cplusplus +} + +#include <set> +#include <string> + +/* Some C++ wrappers */ + +namespace mail { +	namespace keywords { + +		class Hashtable { + +		public: +			struct libmail_kwHashtable kwh; + +			Hashtable(); +			~Hashtable(); + +			Hashtable(const Hashtable &); /* UNDEFINED */ +			Hashtable &operator=(const Hashtable &); +			/* UNDEFINED */ +		}; + + +		class MessageBase { +		public: +			struct libmail_kwMessage *km; +			size_t refCnt; + +			MessageBase(); +			~MessageBase(); + +			MessageBase(const MessageBase &); /* UNDEFINED */ +			MessageBase &operator=(const MessageBase *); +			/* UNDEFINED */ +		}; + +		class Message { + +			MessageBase *b; + +			bool copyOnWrite(); + +		public: +			Message(); +			~Message(); + +			Message(const Message &); +			Message &operator=(const Message &); + +			void getFlags(std::set<std::string> &) const; +			/* Extract list of flags */ + +			bool setFlags(Hashtable &, +				      const std::set<std::string> &); +			/* Set the flags. */ + +			bool addFlag(Hashtable &, std::string); +			bool remFlag(std::string); + +			bool empty() const { +				return b->km == NULL +					|| b->km->firstEntry == NULL; +			} + +			bool operator==(struct libmail_kwMessage *m) const { +				return b->km == NULL ? +					m == NULL || m->firstEntry == NULL: +					m && libmail_kwmCmp(b->km, m) == 0; +			} + +			bool operator !=(struct libmail_kwMessage *m) const { +				return ! operator==(m); +			} + +			void replace(struct libmail_kwMessage *p) +				{ +					if (b->km) +						libmail_kwmDestroy(b->km); +					b->km=p; +				} + +		}; +	} +} + + +/* BONUS: */ + +int maildir_kwSave(const char *maildir, +		   const char *filename, +		   std::set<std::string> keywords, + +		   char **tmpname, +		   char **newname, + +		   int tryAtomic); + +#endif + +#endif | 
