Commit 47546d96 47546d96e3c7659f831f96626dcb13662803f3ab by Alain Magloire

* imap4d/select.c(imap4_select_status): According to RFC2060

	There is no parenthesis when we send the UIDVALIDITY/UNSEEN.

	* mailbox/header.c (header_free_cache): New function.
	(fill_blurb): Call header_free_cache to release memory.
	(header_destroy): Move the code to header_free_cache().
	(header_set_fvalue): Accept a NULL value, it will permanent
	failure i.e. the header does not exist.
	(header_get_value): Check the cache provided by the header object
	then the cache from _get_fvalue() and fall back to downloading
	the entire header.
	* mailbox/mbx_imap.c (imap_hader_get_fvalue): Implement a cache
	in IMAP for the most use headers like "FROM, SUBJECT ....".
	* mailbox/include/imap0.h: New field in _m_imap to hold the
	header cache.
	* mailbox/mapfile_stream.c: signed and unsigned cast.
1 parent dcd0814e
1 2001-10-04 Alain Magloire
2
3 * imap4d/select.c(imap4_select_status): According to RFC2060
4 There is no parenthesis when we send the UIDVALIDITY/UNSEEN.
5
6 * mailbox/header.c (header_free_cache): New function.
7 (fill_blurb): Call header_free_cache to release memory.
8 (header_destroy): Move the code to header_free_cache().
9 (header_set_fvalue): Accept a NULL value, it will permanent
10 failure i.e. the header does not exist.
11 (header_get_value): Check the cache provided by the header object
12 then the cache from _get_fvalue() and fall back to downloading
13 the entire header.
14 * mailbox/mbx_imap.c (imap_hader_get_fvalue): Implement a cache
15 in IMAP for the most use headers like "FROM, SUBJECT ....".
16 * mailbox/include/imap0.h: New field in _m_imap to hold the
17 header cache.
18 * mailbox/mapfile_stream.c: signed and unsigned cast.
19
1 2001-10-03 Alain Magloire 20 2001-10-03 Alain Magloire
2 21
3 * mailbox/mbx_imap.c (imap_attr_set_flags): Do not send 22 * mailbox/mbx_imap.c (imap_attr_set_flags): Do not send
......
...@@ -111,11 +111,11 @@ imap4d_select_status() ...@@ -111,11 +111,11 @@ imap4d_select_status()
111 mailbox_message_unseen (mbox, &unseen); 111 mailbox_message_unseen (mbox, &unseen);
112 util_out (RESP_NONE, "%d EXISTS", count); 112 util_out (RESP_NONE, "%d EXISTS", count);
113 util_out (RESP_NONE, "%d RECENT", recent); 113 util_out (RESP_NONE, "%d RECENT", recent);
114 util_out (RESP_OK, "[UIDVALIDITY (%d)] UID valididy status", 114 util_out (RESP_OK, "[UIDVALIDITY %d] UID valididy status",
115 uidvalidity); 115 uidvalidity);
116 util_out (RESP_OK, "[UIDNEXT %d] Predicted next uid", uidnext); 116 util_out (RESP_OK, "[UIDNEXT %d] Predicted next uid", uidnext);
117 if (unseen) 117 if (unseen)
118 util_out (RESP_OK, "[UNSEEN (%d)] first unseen messsage ", unseen); 118 util_out (RESP_OK, "[UNSEEN %d] first unseen messsage ", unseen);
119 util_out (RESP_NONE, "FLAGS (%s)", mflags); 119 util_out (RESP_NONE, "FLAGS (%s)", mflags);
120 /* FIXME: 120 /* FIXME:
121 - '\*' can be supported if we use the attribute_set userflag() 121 - '\*' can be supported if we use the attribute_set userflag()
......
...@@ -40,6 +40,7 @@ static int header_readline __P ((stream_t, char *, size_t, off_t, size_t *)); ...@@ -40,6 +40,7 @@ static int header_readline __P ((stream_t, char *, size_t, off_t, size_t *));
40 static int header_write __P ((stream_t, const char *, size_t, off_t, 40 static int header_write __P ((stream_t, const char *, size_t, off_t,
41 size_t *)); 41 size_t *));
42 static int fill_blurb __P ((header_t)); 42 static int fill_blurb __P ((header_t));
43 static void header_free_cache __P ((header_t));
43 44
44 int 45 int
45 header_create (header_t *ph, const char *blurb, size_t len, void *owner) 46 header_create (header_t *ph, const char *blurb, size_t len, void *owner)
...@@ -59,6 +60,26 @@ header_create (header_t *ph, const char *blurb, size_t len, void *owner) ...@@ -59,6 +60,26 @@ header_create (header_t *ph, const char *blurb, size_t len, void *owner)
59 return status; 60 return status;
60 } 61 }
61 62
63 static void
64 header_free_cache (header_t header)
65 {
66 /* Clean up our fast header cache. */
67 if (header->fhdr)
68 {
69 size_t i;
70 for (i = 0; i < header->fhdr_count; i++)
71 {
72 if (header->fhdr[i].fn)
73 free (header->fhdr[i].fn);
74 if (header->fhdr[i].fv)
75 free (header->fhdr[i].fv);
76 }
77 free (header->fhdr);
78 header->fhdr = NULL;
79 header->fhdr_count = 0;
80 }
81 }
82
62 void 83 void
63 header_destroy (header_t *ph, void *owner) 84 header_destroy (header_t *ph, void *owner)
64 { 85 {
...@@ -77,19 +98,7 @@ header_destroy (header_t *ph, void *owner) ...@@ -77,19 +98,7 @@ header_destroy (header_t *ph, void *owner)
77 if (header->blurb) 98 if (header->blurb)
78 free (header->blurb); 99 free (header->blurb);
79 100
80 /* Clean up the fast header cache. */ 101 header_free_cache (header);
81 if (header->fhdr)
82 {
83 size_t i;
84 for (i = 0; i < header->fhdr_count; i++)
85 {
86 if (header->fhdr[i].fn)
87 free (header->fhdr[i].fn);
88 if (header->fhdr[i].fv)
89 free (header->fhdr[i].fv);
90 }
91 free (header->fhdr);
92 }
93 102
94 if (header->property) 103 if (header->property)
95 property_destroy (&(header->property), header); 104 property_destroy (&(header->property), header);
...@@ -343,6 +352,10 @@ header_set_value (header_t header, const char *fn, const char *fv, int replace) ...@@ -343,6 +352,10 @@ header_set_value (header_t header, const char *fn, const char *fv, int replace)
343 return 0; 352 return 0;
344 } 353 }
345 354
355 /* We try to cache the headers here to reduce networking access
356 especially for IMAP. When the buffer is NULL it means that
357 the field does not exist on the server and we should not
358 attempt to contact the server again for this field. */
346 static int 359 static int
347 header_set_fvalue (header_t header, const char *name, char *buffer) 360 header_set_fvalue (header_t header, const char *name, char *buffer)
348 { 361 {
...@@ -359,6 +372,8 @@ header_set_fvalue (header_t header, const char *name, char *buffer) ...@@ -359,6 +372,8 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
359 thdr[header->fhdr_count].fn = field; 372 thdr[header->fhdr_count].fn = field;
360 thdr[header->fhdr_count].fn_end = field + len; 373 thdr[header->fhdr_count].fn_end = field + len;
361 374
375 if (buffer)
376 {
362 len = strlen (buffer); 377 len = strlen (buffer);
363 field = malloc (len + 1); 378 field = malloc (len + 1);
364 if (field == NULL) 379 if (field == NULL)
...@@ -367,6 +382,12 @@ header_set_fvalue (header_t header, const char *name, char *buffer) ...@@ -367,6 +382,12 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
367 field[len] = '\0'; 382 field[len] = '\0';
368 thdr[header->fhdr_count].fv = field; 383 thdr[header->fhdr_count].fv = field;
369 thdr[header->fhdr_count].fv_end = field + len; 384 thdr[header->fhdr_count].fv_end = field + len;
385 }
386 else
387 {
388 thdr[header->fhdr_count].fv = NULL;
389 thdr[header->fhdr_count].fv_end = NULL;
390 }
370 header->fhdr_count++; 391 header->fhdr_count++;
371 header->fhdr = thdr; 392 header->fhdr = thdr;
372 return 0; 393 return 0;
...@@ -374,6 +395,9 @@ header_set_fvalue (header_t header, const char *name, char *buffer) ...@@ -374,6 +395,9 @@ header_set_fvalue (header_t header, const char *name, char *buffer)
374 return ENOMEM; 395 return ENOMEM;
375 } 396 }
376 397
398 /* For the cache header if the field exist but with no corresponding
399 value, it is a permanent failure i.e. the field does not exist
400 in the header return EINVAL to notify header_get_value(). */
377 static int 401 static int
378 header_get_fvalue (header_t header, const char *name, char *buffer, 402 header_get_fvalue (header_t header, const char *name, char *buffer,
379 size_t buflen, size_t *pn) 403 size_t buflen, size_t *pn)
...@@ -382,9 +406,6 @@ header_get_fvalue (header_t header, const char *name, char *buffer, ...@@ -382,9 +406,6 @@ header_get_fvalue (header_t header, const char *name, char *buffer,
382 size_t name_len; 406 size_t name_len;
383 int err = ENOENT; 407 int err = ENOENT;
384 408
385 if (header->_get_fvalue)
386 return header->_get_fvalue (header, name, buffer, buflen, pn);
387
388 for (i = 0, name_len = strlen (name); i < header->fhdr_count; i++) 409 for (i = 0, name_len = strlen (name); i < header->fhdr_count; i++)
389 { 410 {
390 fn_len = header->fhdr[i].fn_end - header->fhdr[i].fn; 411 fn_len = header->fhdr[i].fn_end - header->fhdr[i].fn;
...@@ -392,6 +413,11 @@ header_get_fvalue (header_t header, const char *name, char *buffer, ...@@ -392,6 +413,11 @@ header_get_fvalue (header_t header, const char *name, char *buffer,
392 && strcasecmp (header->fhdr[i].fn, name) == 0) 413 && strcasecmp (header->fhdr[i].fn, name) == 0)
393 { 414 {
394 fv_len = header->fhdr[i].fv_end - header->fhdr[i].fv; 415 fv_len = header->fhdr[i].fv_end - header->fhdr[i].fv;
416
417 /* Permanent failure. */
418 if (fv_len == 0)
419 return EINVAL;
420
395 if (buffer && buflen > 0) 421 if (buffer && buflen > 0)
396 { 422 {
397 buflen--; 423 buflen--;
...@@ -422,8 +448,21 @@ header_get_value (header_t header, const char *name, char *buffer, ...@@ -422,8 +448,21 @@ header_get_value (header_t header, const char *name, char *buffer,
422 if (header == NULL || name == NULL) 448 if (header == NULL || name == NULL)
423 return EINVAL; 449 return EINVAL;
424 450
425 /* First Try the Fast header for hits. */ 451 /* First scan our cache headers for hits. */
426 err = header_get_fvalue (header, name, buffer, buflen, pn); 452 err = header_get_fvalue (header, name, buffer, buflen, pn);
453 switch (err)
454 {
455 case EINVAL: /* Permanent failure. */
456 return ENOENT;
457 case 0:
458 return 0;
459 case ENOMEM:
460 return err;
461 }
462
463 /* Try the provided cache. */
464 if (header->_get_fvalue)
465 err = header->_get_fvalue (header, name, buffer, buflen, pn);
427 if (err == 0) 466 if (err == 0)
428 return 0; 467 return 0;
429 468
...@@ -448,6 +487,11 @@ header_get_value (header_t header, const char *name, char *buffer, ...@@ -448,6 +487,11 @@ header_get_value (header_t header, const char *name, char *buffer,
448 if (pn) 487 if (pn)
449 *pn = buflen; 488 *pn = buflen;
450 } 489 }
490 else
491 {
492 /* Cache permanent failure also. */
493 header_set_fvalue (header, name, NULL);
494 }
451 return err; 495 return err;
452 } 496 }
453 497
...@@ -850,6 +894,7 @@ fill_blurb (header_t header) ...@@ -850,6 +894,7 @@ fill_blurb (header_t header)
850 894
851 /* The entire header is now ours(part of header_t), clear all the 895 /* The entire header is now ours(part of header_t), clear all the
852 overloading. */ 896 overloading. */
897 header_free_cache (header);
853 header->_get_fvalue = NULL; 898 header->_get_fvalue = NULL;
854 header->_get_value = NULL; 899 header->_get_value = NULL;
855 header->_set_value = NULL; 900 header->_set_value = NULL;
...@@ -943,7 +988,7 @@ header_read (stream_t is, char *buf, size_t buflen, off_t off, size_t *pnread) ...@@ -943,7 +988,7 @@ header_read (stream_t is, char *buf, size_t buflen, off_t off, size_t *pnread)
943 len = header->blurb_len - off; 988 len = header->blurb_len - off;
944 if (len > 0) 989 if (len > 0)
945 { 990 {
946 len = (buflen < (size_t)len) ? buflen : len; 991 len = (buflen < (size_t)len) ? buflen : (size_t)len;
947 memcpy (buf, header->blurb + off, len); 992 memcpy (buf, header->blurb + off, len);
948 } 993 }
949 else 994 else
...@@ -986,7 +1031,7 @@ header_readline (stream_t is, char *buf, size_t buflen, off_t off, size_t *pn) ...@@ -986,7 +1031,7 @@ header_readline (stream_t is, char *buf, size_t buflen, off_t off, size_t *pn)
986 char *nl = memchr (header->blurb + off, '\n', len); 1031 char *nl = memchr (header->blurb + off, '\n', len);
987 if (nl) 1032 if (nl)
988 len = nl - (header->blurb + off) + 1; 1033 len = nl - (header->blurb + off) + 1;
989 len = (buflen < (size_t)len) ? buflen : len; 1034 len = (buflen < (size_t)len) ? buflen : (size_t)len;
990 memcpy (buf, header->blurb + off, len); 1035 memcpy (buf, header->blurb + off, len);
991 } 1036 }
992 else 1037 else
......
...@@ -187,6 +187,8 @@ struct _msg_imap ...@@ -187,6 +187,8 @@ struct _msg_imap
187 int flags; 187 int flags;
188 size_t uid; 188 size_t uid;
189 189
190 header_t fheader;
191
190 size_t message_size; 192 size_t message_size;
191 size_t message_lines; 193 size_t message_lines;
192 size_t body_size; 194 size_t body_size;
......
...@@ -97,7 +97,7 @@ _mapfile_readline (stream_t stream, char *optr, size_t osize, ...@@ -97,7 +97,7 @@ _mapfile_readline (stream_t stream, char *optr, size_t osize,
97 /* Save space for the null byte. */ 97 /* Save space for the null byte. */
98 osize--; 98 osize--;
99 nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset); 99 nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset);
100 n = (nl) ? nl - (mfs->ptr + offset) + 1 : mfs->size - offset; 100 n = (nl) ? (size_t)(nl - (mfs->ptr + offset) + 1) : mfs->size - offset;
101 n = (n > osize) ? osize : n; 101 n = (n > osize) ? osize : n;
102 memcpy (optr, mfs->ptr + offset, n); 102 memcpy (optr, mfs->ptr + offset, n);
103 optr[n] = '\0'; 103 optr[n] = '\0';
......
...@@ -77,6 +77,7 @@ static int imap_attr_unset_flags __P ((attribute_t, int)); ...@@ -77,6 +77,7 @@ static int imap_attr_unset_flags __P ((attribute_t, int));
77 /* Header. */ 77 /* Header. */
78 static int imap_header_read __P ((header_t, char*, size_t, off_t, size_t *)); 78 static int imap_header_read __P ((header_t, char*, size_t, off_t, size_t *));
79 static int imap_header_get_value __P ((header_t, const char*, char *, size_t, size_t *)); 79 static int imap_header_get_value __P ((header_t, const char*, char *, size_t, size_t *));
80 static int imap_header_get_fvalue __P ((header_t, const char*, char *, size_t, size_t *));
80 81
81 /* Body. */ 82 /* Body. */
82 static int imap_body_read __P ((stream_t, char *, size_t, off_t, size_t *)); 83 static int imap_body_read __P ((stream_t, char *, size_t, off_t, size_t *));
...@@ -163,6 +164,8 @@ free_subparts (msg_imap_t msg_imap) ...@@ -163,6 +164,8 @@ free_subparts (msg_imap_t msg_imap)
163 message_destroy (&(msg_imap->message), msg_imap); 164 message_destroy (&(msg_imap->message), msg_imap);
164 if (msg_imap->parts) 165 if (msg_imap->parts)
165 free (msg_imap->parts); 166 free (msg_imap->parts);
167 if (msg_imap->fheader)
168 header_destroy (&msg_imap->fheader, NULL);
166 free(msg_imap); 169 free(msg_imap);
167 } 170 }
168 171
...@@ -248,6 +251,9 @@ mailbox_imap_close (mailbox_t mailbox) ...@@ -248,6 +251,9 @@ mailbox_imap_close (mailbox_t mailbox)
248 free (m_imap->imessages); 251 free (m_imap->imessages);
249 m_imap->imessages = NULL; 252 m_imap->imessages = NULL;
250 m_imap->imessages_count = 0; 253 m_imap->imessages_count = 0;
254 m_imap->messages_count = 0;
255 m_imap->recent = 0;
256 m_imap->unseen = 0;
251 monitor_unlock (mailbox->monitor); 257 monitor_unlock (mailbox->monitor);
252 } 258 }
253 259
...@@ -293,7 +299,7 @@ imap_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg) ...@@ -293,7 +299,7 @@ imap_get_message (mailbox_t mailbox, size_t msgno, message_t *pmsg)
293 monitor_unlock (mailbox->monitor); 299 monitor_unlock (mailbox->monitor);
294 300
295 /* Allocate a concrete imap message. */ 301 /* Allocate a concrete imap message. */
296 msg_imap = calloc (1, sizeof (*msg_imap)); 302 msg_imap = calloc (1, sizeof *msg_imap);
297 if (msg_imap == NULL) 303 if (msg_imap == NULL)
298 return ENOMEM; 304 return ENOMEM;
299 /* Back pointer. */ 305 /* Back pointer. */
...@@ -365,6 +371,7 @@ imap_get_message0 (msg_imap_t msg_imap, message_t *pmsg) ...@@ -365,6 +371,7 @@ imap_get_message0 (msg_imap_t msg_imap, message_t *pmsg)
365 } 371 }
366 header_set_fill (header, imap_header_read, msg); 372 header_set_fill (header, imap_header_read, msg);
367 header_set_get_value (header, imap_header_get_value, msg); 373 header_set_get_value (header, imap_header_get_value, msg);
374 header_set_get_fvalue (header, imap_header_get_fvalue, msg);
368 message_set_header (msg, header, msg_imap); 375 message_set_header (msg, header, msg_imap);
369 } 376 }
370 377
...@@ -1554,6 +1561,54 @@ imap_header_get_value (header_t header, const char *field, char * buffer, ...@@ -1554,6 +1561,54 @@ imap_header_get_value (header_t header, const char *field, char * buffer,
1554 } 1561 }
1555 1562
1556 static int 1563 static int
1564 imap_header_get_fvalue (header_t header, const char *field, char * buffer,
1565 size_t buflen, size_t *plen)
1566 {
1567 message_t msg = header_get_owner (header);
1568 msg_imap_t msg_imap = message_get_owner (msg);
1569 m_imap_t m_imap = msg_imap->m_imap;
1570 f_imap_t f_imap = m_imap->f_imap;
1571 int status;
1572 size_t len = 0;
1573 char *value;
1574
1575 /* Do we all ready have the headers. */
1576 if (msg_imap->fheader)
1577 return header_get_value (msg_imap->fheader, field, buffer, buflen, plen);
1578
1579 /* We are caching the must use headers. */
1580 if (f_imap->state == IMAP_NO_STATE)
1581 {
1582 /* Select first. */
1583 status = imap_messages_count (m_imap->mailbox, NULL);
1584 if (status != 0)
1585 return status;
1586 #define CACHE_HEADERS "Bcc Cc Content-Language Content-Transfer-Encoding Content-Type Date From In-Reply-To Message-ID Reference Reply-To Sender Subject To X-UIDL"
1587 status = imap_writeline (f_imap,
1588 "g%d FETCH %d BODY.PEEK[HEADER.FIELDS (%s)]\r\n",
1589 f_imap->seq++, msg_imap->num, CACHE_HEADERS);
1590 CHECK_ERROR (f_imap, status);
1591 MAILBOX_DEBUG0 (m_imap->mailbox, MU_DEBUG_PROT, f_imap->buffer);
1592 f_imap->state = IMAP_FETCH;
1593
1594 }
1595
1596 /* Should be enough for our needs. */
1597 len = 2048;
1598 value = calloc (len, sizeof *value);
1599 status = message_operation (f_imap, msg_imap, value, len, &len);
1600 if (status == 0)
1601 {
1602 status = header_create (&msg_imap->fheader, value, len, NULL);
1603 if (status == 0)
1604 status = header_get_value (msg_imap->fheader, field, buffer,
1605 buflen, plen);
1606 }
1607 free (value);
1608 return status;
1609 }
1610
1611 static int
1557 imap_header_read (header_t header, char *buffer, size_t buflen, off_t offset, 1612 imap_header_read (header_t header, char *buffer, size_t buflen, off_t offset,
1558 size_t *plen) 1613 size_t *plen)
1559 { 1614 {
......
...@@ -94,7 +94,7 @@ const char *fhdr_table[] = ...@@ -94,7 +94,7 @@ const char *fhdr_table[] =
94 #define H_MESSAGE_ID 8 94 #define H_MESSAGE_ID 8
95 "Message-ID", 95 "Message-ID",
96 #define H_REFERENCE 9 96 #define H_REFERENCE 9
97 "Reply-To", 97 "Reference",
98 #define H_REPLY_TO 10 98 #define H_REPLY_TO 10
99 "Reply-To", 99 "Reply-To",
100 #define H_SENDER 11 100 #define H_SENDER 11
...@@ -1343,7 +1343,7 @@ mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen, ...@@ -1343,7 +1343,7 @@ mbox_readstream (mbox_message_t mum, char *buffer, size_t buflen,
1343 if (ln > 0) 1343 if (ln > 0)
1344 { 1344 {
1345 /* Position the file pointer and the buffer. */ 1345 /* Position the file pointer and the buffer. */
1346 nread = ((size_t)ln < buflen) ? ln : buflen; 1346 nread = ((size_t)ln < buflen) ? (size_t)ln : buflen;
1347 if (isreadline) 1347 if (isreadline)
1348 status = stream_readline (mum->mud->mailbox->stream, buffer, buflen, 1348 status = stream_readline (mum->mud->mailbox->stream, buffer, buflen,
1349 start + off, &nread); 1349 start + off, &nread);
......
...@@ -70,7 +70,7 @@ _memory_readline (stream_t stream, char *optr, size_t osize, ...@@ -70,7 +70,7 @@ _memory_readline (stream_t stream, char *optr, size_t osize,
70 /* Save space for the null byte. */ 70 /* Save space for the null byte. */
71 osize--; 71 osize--;
72 nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset); 72 nl = memchr (mfs->ptr + offset, '\n', mfs->size - offset);
73 n = (nl) ? nl - (mfs->ptr + offset) + 1 : mfs->size - offset; 73 n = (nl) ? (size_t)(nl - (mfs->ptr + offset) + 1) : mfs->size - offset;
74 n = (n > osize) ? osize : n; 74 n = (n > osize) ? osize : n;
75 memcpy (optr, mfs->ptr + offset, n); 75 memcpy (optr, mfs->ptr + offset, n);
76 optr[n] = '\0'; 76 optr[n] = '\0';
......