Back to index

courier  0.68.2
maildirkeywords.h
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2003 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #ifndef       maildirkeywords_h
00007 #define       maildirkeywords_h
00008 
00009 #include      "config.h"
00010 #include      <stdio.h>
00011 #include      <string.h>
00012 
00013 #ifdef  __cplusplus
00014 extern "C" {
00015 #endif
00016 
00017 
00018 /*
00019 ** IMAP keywords.  This data structure is designed so that it is possible to:
00020 ** A) Find all messages that have the given keyword,
00021 ** B) Find all keywords that are set for the given message,
00022 ** C) Optimize memory usage, even with many keywords set for many msgs.
00023 */
00024 
00025 /* A doubly-linked list of keywords */
00026 
00027 struct libmail_keywordEntry {
00028        struct libmail_keywordEntry *prev, *next; /* Doubly-linked list */
00029 
00030 
00031 #define keywordName(ke) ((char *)((ke)+1))
00032 
00033        union {
00034               void *userPtr; /* Misc ptr */
00035               unsigned long userNum; /* Or misc num */
00036        } u;
00037 
00038        struct libmail_kwMessageEntry *firstMsg, *lastMsg;
00039        /* Doubly-linked list of messages that use this keyword */
00040 };
00041 
00042 
00043 /* The main keyword hash table */
00044 
00045 struct libmail_kwHashtable {
00046 
00047        struct libmail_keywordEntry heads[43], tails[43];
00048        /* Dummy head/tail nodes for each hash bucket */
00049 
00050        int keywordAddedRemoved;
00051 };
00052 
00053 struct libmail_kwMessageEntry {
00054        struct libmail_kwMessageEntry *next, *prev;
00055        /* Doubly-linked list of all keywords set for this message */
00056 
00057        struct libmail_kwMessageEntry *keywordNext, *keywordPrev;
00058        /* Doubly-linked list of all entries for the same keyword */
00059 
00060        struct libmail_keywordEntry *libmail_keywordEntryPtr; /* The keyword */
00061        struct libmail_kwMessage *libmail_kwMessagePtr; /* The message */
00062 };
00063 
00064 struct libmail_kwMessage {
00065 
00066        struct libmail_kwMessageEntry *firstEntry, *lastEntry;
00067        /* Doubly-linked list of all keywords set for this message */
00068 
00069        union {
00070               void *userPtr; /* Misc ptr */
00071               unsigned long userNum; /* Or misc num */
00072        } u;
00073 };
00074 
00075 /*
00076 ** Initialize a libmail_kwHashtable
00077 */
00078 void libmail_kwhInit(struct libmail_kwHashtable *);
00079 
00080 
00081 /*
00082 ** Returns 0 if libmail_kwHashtable is empty.  Return -1, errno=EIO if it's
00083 ** not (sanity check).
00084 */
00085 int libmail_kwhCheck(struct libmail_kwHashtable *);
00086 
00087 /*
00088 ** Enumerate all defined keywords.
00089 */
00090 int libmail_kwEnumerate(struct libmail_kwHashtable *h,
00091                      int (*callback_func)(struct libmail_keywordEntry *,
00092                                         void *),
00093                      void *callback_arg);
00094 
00095 /*
00096 ** Find a keyword in the hashtable, optionally create it.  If createIfNew,
00097 ** then we MUST add the returned result to some keywordMessage, else there'll
00098 ** be a cleanup problem.
00099 */
00100 struct libmail_keywordEntry *
00101 libmail_kweFind(struct libmail_kwHashtable *ht,
00102                       const char *name,
00103                       int createIfNew);
00104 
00105 
00106 extern const char *libmail_kwVerbotten;
00107 /*
00108 ** Optional list of chars prohibited in keyword names.  They get automagically
00109 ** replaced with underscores.
00110 */
00111 extern int libmail_kwCaseSensitive;
00112 /*
00113 ** Non zero if keyword names are case sensitive.
00114 */
00115 
00116 /*
00117 ** Clear a reference to a particular keyword, from a particular message.
00118 */
00119 void libmail_kweClear(struct libmail_keywordEntry *);
00120 
00121 /*
00122 ** Create an abstract message object, with no keywords currently defined.
00123 */
00124 struct libmail_kwMessage *libmail_kwmCreate();
00125 
00126 /*
00127 ** Destroy the message object, automatically removing any keywords that were
00128 ** used by the object.
00129 */
00130 void libmail_kwmDestroy(struct libmail_kwMessage *);
00131 
00132 /*
00133 ** Link a keyword to a message.
00134 */
00135 int libmail_kwmSet(struct libmail_kwMessage *, struct libmail_keywordEntry *);
00136 
00137 /*
00138 ** Link a keyword to a message, by keyword's name.
00139 */
00140 int libmail_kwmSetName(struct libmail_kwHashtable *,
00141                      struct libmail_kwMessage *, const char *);
00142 
00143 /*
00144 ** Compare two messages, return 0 if they have the same keywords.
00145 */
00146 int libmail_kwmCmp(struct libmail_kwMessage *,
00147                  struct libmail_kwMessage *);
00148 
00149 /*
00150 ** Clear a keyword from a message.
00151 */
00152 int libmail_kwmClearName(struct libmail_kwMessage *, const char *);
00153 /*
00154 ** libmail_kwmClearName is for INTERNAL USE ONLY -- the name doesn't get vetted
00155 ** by libmail_kweFind.
00156 */
00157 
00158 /*
00159 ** Clear a keyword from a message, the public version.
00160 */
00161 int libmail_kwmClear(struct libmail_kwMessage *, struct libmail_keywordEntry *);
00162 /*
00163 **
00164 */
00165 int libmail_kwmClearEntry(struct libmail_kwMessageEntry *);
00166 
00167 
00168 /*****************************************************************************
00169 
00170 The above is low-level stuff.  And now, a high level maildir storage API:
00171 
00172 *****************************************************************************/
00173 
00174 /*
00175 ** Read keywords from a maildir.  The application presumably has read and
00176 ** compiled a list of messages it found in the maildir's cur (and new)
00177 ** directories.
00178 **
00179 ** The function maildir_kwRead() will now read the keywords associated with
00180 ** each message.  How the application maintains the list of messages in the
00181 ** maildir is irrelevant.  The application simply needs to allocate a pointer
00182 ** to a libmail_kwMessage structure, one pointer for each message in the
00183 ** maildir.  Each pointer must be initialized to a NULL, and the application
00184 ** provides a set of callback functions, as defined below, that return
00185 ** a pointer to this pointer (pay attention now), given the filename.
00186 ** maildir_kwRead() invokes the callback functions, as appropriate, while
00187 ** it's doing its business.
00188 **
00189 ** There's other callback functions too, so let's get to business.
00190 ** The application must initialize the following structure before calling
00191 ** maildir_kwRead().  This is where the pointers to all callback functions
00192 ** are provided:
00193 */
00194 
00195 struct maildir_kwReadInfo {
00196 
00197        struct libmail_kwMessage **(*findMessageByFilename)(const char
00198                                                      *filename,
00199                                                      int autocreate,
00200                                                      size_t *indexNum,
00201                                                      void *voidarg);
00202        /*
00203        ** Return a pointer to a libmail_kwMessage * that's associated with
00204        ** the message whose name is 'filename'.  'filename' will not have
00205        ** :2, and the message's flags, so the application needs to be
00206        ** careful.
00207        **
00208        ** All libmail_kwMessage *s are initially NULL.  If autocreate is not
00209        ** zero, the application must use libmail_kwmCreate() to initialize
00210        ** the pointer, before returning.  Otherwise, the application should
00211        ** return a pointer to a NULL libmail_kwMessage *.
00212        **
00213        ** The application may use libmail_kwMessage.u for its own purposes.
00214        **
00215        ** The application should return NULL if it can't find 'filename'
00216        ** in its list of messages in the maildir.  That is a defined
00217        ** possibility, and occur in certain race conditions (which are
00218        ** properly handled, of course).
00219        **
00220        ** If indexNum is not NULL, the application should set *indexNum to
00221        ** the found message's index (if the application does not return NULL).
00222        ** All messages the application has must be consecutively numbered,
00223        ** beginning with 0 and up to, but not including, whatever the
00224        ** application returns in getMessageCount().
00225        */
00226 
00227        size_t (*getMessageCount)(void *voidarg);
00228        /*
00229        ** The application should return the number of messages it thinks
00230        ** there are in the maildir.
00231        */
00232 
00233        struct libmail_kwMessage **(*findMessageByIndex)(size_t indexNum,
00234                                                   int autocreate,
00235                                                   void *voidarg);
00236        /*
00237        ** This one is like the findMessageByFilename() callback, except that
00238        ** instead of filename the applicationg gets indexNum which ranges
00239        ** between 0 and getMessageCount()-1.
00240        ** The return code from this callback is identical to the return code
00241        ** from findMessageByFilename(), and autocreate's semantics are also
00242        ** the same.
00243        */
00244 
00245        const char *(*getMessageFilename)(size_t n, void *voidarg);
00246        /*
00247        ** The application should return the filename for message #n.  The
00248        ** application may or may not include :2, in the returned ptr.
00249        */
00250 
00251        struct libmail_kwHashtable * (*getKeywordHashtable)(void *voidarg);
00252        /*
00253        ** The application should return the libmail_kwHashtable that it
00254        ** allocated to store all the keyword stuff.  Read keywords are
00255        ** allocated from this hashtable.
00256        */
00257 
00258        void (*updateKeywords)(size_t n, struct libmail_kwMessage *kw,
00259                             void *voidarg);
00260        /*
00261        ** The updateKeywords callback gets invoked by maildir_kwRead()
00262        ** if it needs to throw out the list of keywords it already read for
00263        ** a given message, and replace it, instead, with another set of
00264        ** keywords.  This can happen in certain circumstances.
00265        **
00266        ** The application should retrieve the libmail_kwMessage pointer for
00267        ** message #n.  It may or may not be null.  If it's not null, the
00268        ** application must use libmail_kwmDestroy().  Then, afterwards,
00269        ** the application should save 'kw' as the new pointer.
00270        **
00271        ** This callback is provided so that the application may save whatever
00272        ** it wants to save in kw->u.userPtr or kw->u.userNum, because 'kw'
00273        ** was created by libmail_kwRead(), and not one of the two findMessage
00274        ** callbacks.
00275        */
00276 
00277        void *voidarg;
00278        /*
00279        ** All of the above callbacks receive this voidarg as their last
00280        ** argument.
00281        */
00282 
00283        int tryagain;
00284        /*
00285        ** libmail_kwRead() returns 0 for success, or -1 for a system failure
00286        ** (check errno).
00287        **
00288        ** If libmail_kwRead() returned 0, the application MUST check
00289        ** tryagain.
00290        **
00291        ** If tryagain is not 0, the application MUST:
00292        **     A) Take any non-NULL libmail_kwMessage pointers that are
00293        **        associated with each message in the maildir, use
00294        **        libmail_kwmDestroy() to blow them away, and reset each
00295        **        pointer to NULL.
00296        **
00297        **     B) Invoke libmail_kwRead() again.
00298        **
00299        ** A non-0 tryagain indicates a recoverable race condition.
00300        */
00301 
00302 
00303        /* Everything below is internal */
00304 
00305        int updateNeeded;
00306        int errorOccured;
00307 };
00308 
00309 
00310 int maildir_kwRead(const char *maildir,
00311                  struct maildir_kwReadInfo *rki);
00312 
00313 /*
00314 ** maildir_kwSave saves new keywords for a particular message:
00315 */
00316 
00317 int maildir_kwSave(const char *maildir, /* The maildir */
00318                  const char *filename,
00319                   /* The message.  :2, is allowed, and ignored */
00320 
00321                  struct libmail_kwMessage *newKeyword,
00322                   /* New keywords.  The ptr may be NULL */
00323 
00324                  char **tmpname,
00325                  char **newname,
00326 
00327                  int tryAtomic);
00328 
00329 int maildir_kwSaveArray(const char *maildir,
00330                      const char *filename,
00331                      const char **flags,
00332                      char **tmpname,
00333                      char **newname,
00334                      int tryAtomic);
00335 
00336 /*
00337 ** maildir_kwSave returns -1 for an error.  If it return 0, it will initialize
00338 ** *tmpname and *newname, both will be full path filenames.  The application
00339 ** needs to simply call rename() with both filenames, and free() them, to
00340 ** effect the change.  Example:
00341 **
00342 **  char *tmpname, *newname;
00343 **
00344 **  if (maildir_kwSave( ..., &tmpname, &newname) == 0)
00345 **  {
00346 **         rename(tmpname, newname);
00347 **
00348 **         free(tmpname);
00349 **         free(newname);
00350 **  }
00351 **
00352 **  Of course, rename()s return code should also be checked.
00353 **
00354 **  If 'tryAtomic' is non-zero, the semantics are going to be slightly
00355 **  different.  tryAtomic is non-zero when we want to update keywords
00356 **  atomically.  To do that, first, use maildir_kwRead()  (or, most likely
00357 **  maildir_kwgReadMaildir) to read the existing keywords, update the keywords
00358 **  for the particular message, use maildir_kwSave(), but instead of rename()
00359 **  use link().  Whether link succeeds or not, use unlink(tmpname) in any
00360 **  case.  If link() failed with EEXIST, we had a race condition, so try
00361 **  again.
00362 **  Note - in NFS environments, must check not only that links succeeds, but
00363 **  if stat-ing the tmpfile the number of links also must be 2.
00364 */
00365 
00366 /*
00367 ** Set libmail_kwEnabled to ZERO in order to silently disable all maildir
00368 ** keyword functionality.  It's optional in Courier-IMAP.  Setting this flag
00369 ** to zero disables all actual keyword read/write functions, however all the
00370 ** necessary data still gets created (namely the courierimapkeywords
00371 ** subdirectory.
00372 */
00373 
00374 extern int libmail_kwEnabled;
00375 
00376 
00377 /*
00378 ** The following functions are "semi-internal".
00379 **
00380 ** maildir_kwExport() uses the same struct maildir_kwReadInfo, to "export"
00381 ** the list of keywords assigned to all messages into a file.
00382 **
00383 ** maildir_kwImport() imports the saved keyword list.
00384 **
00385 ** These functions are meant to save a "snapshot" of the keywords into a
00386 ** flag file, nothing else.
00387 */
00388 
00389 int maildir_kwExport(FILE *, struct maildir_kwReadInfo *);
00390 int maildir_kwImport(FILE *, struct maildir_kwReadInfo *);
00391 
00392 
00393 /****************************************************************************
00394 
00395 Generic maildir_kwRead implementation.
00396 
00397 ****************************************************************************/
00398 
00399 struct libmail_kwGeneric {
00400 
00401        struct libmail_kwHashtable kwHashTable;
00402 
00403        size_t nMessages;
00404 
00405        struct libmail_kwGenericEntry **messages;
00406        int messagesValid;
00407 
00408        struct libmail_kwGenericEntry *messageHashTable[99];
00409 };
00410 
00411 struct libmail_kwGenericEntry {
00412 
00413        struct libmail_kwGenericEntry *next; /* On the hash table */
00414 
00415        char *filename;
00416        size_t messageNum;
00417        struct libmail_kwMessage *keywords;
00418 };
00419 
00420 void libmail_kwgInit(struct libmail_kwGeneric *g);
00421 int libmail_kwgDestroy(struct libmail_kwGeneric *g);
00422 int libmail_kwgReadMaildir(struct libmail_kwGeneric *g,
00423                         const char *maildir);
00424 
00425 struct libmail_kwGenericEntry *
00426 libmail_kwgFindByName(struct libmail_kwGeneric *g, const char *filename);
00427 
00428 struct libmail_kwGenericEntry *
00429 libmail_kwgFindByIndex(struct libmail_kwGeneric *g, size_t n);
00430 
00431 #ifdef  __cplusplus
00432 }
00433 
00434 #include <set>
00435 #include <string>
00436 
00437 /* Some C++ wrappers */
00438 
00439 namespace mail {
00440        namespace keywords {
00441 
00442               class Hashtable {
00443 
00444               public:
00445                      struct libmail_kwHashtable kwh;
00446 
00447                      Hashtable();
00448                      ~Hashtable();
00449 
00450                      Hashtable(const Hashtable &); /* UNDEFINED */
00451                      Hashtable &operator=(const Hashtable &);
00452                      /* UNDEFINED */
00453               };
00454 
00455 
00456               class MessageBase {
00457               public:
00458                      struct libmail_kwMessage *km;
00459                      size_t refCnt;
00460 
00461                      MessageBase();
00462                      ~MessageBase();
00463 
00464                      MessageBase(const MessageBase &); /* UNDEFINED */
00465                      MessageBase &operator=(const MessageBase *);
00466                      /* UNDEFINED */
00467               };
00468 
00469               class Message {
00470 
00471                      MessageBase *b;
00472 
00473                      bool copyOnWrite();
00474 
00475               public:
00476                      Message();
00477                      ~Message();
00478 
00479                      Message(const Message &);
00480                      Message &operator=(const Message &);
00481 
00482                      void getFlags(std::set<std::string> &) const;
00483                      /* Extract list of flags */
00484 
00485                      bool setFlags(Hashtable &,
00486                                   const std::set<std::string> &);
00487                      /* Set the flags. */
00488 
00489                      bool addFlag(Hashtable &, std::string);
00490                      bool remFlag(std::string);
00491 
00492                      bool empty() const {
00493                             return b->km == NULL
00494                                    || b->km->firstEntry == NULL;
00495                      }
00496 
00497                      bool operator==(struct libmail_kwMessage *m) const {
00498                             return b->km == NULL ?
00499                                    m == NULL || m->firstEntry == NULL:
00500                                    m && libmail_kwmCmp(b->km, m) == 0;
00501                      }
00502 
00503                      bool operator !=(struct libmail_kwMessage *m) const {
00504                             return ! operator==(m);
00505                      }
00506 
00507                      void replace(struct libmail_kwMessage *p)
00508                             {
00509                                    if (b->km)
00510                                           libmail_kwmDestroy(b->km);
00511                                    b->km=p;
00512                             }
00513 
00514               };
00515        }
00516 }
00517 
00518 
00519 /* BONUS: */
00520 
00521 int maildir_kwSave(const char *maildir,
00522                  const char *filename,
00523                  std::set<std::string> keywords,
00524 
00525                  char **tmpname,
00526                  char **newname,
00527 
00528                  int tryAtomic);
00529 
00530 #endif
00531 
00532 #endif