1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
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
|