Commit 525ec621 525ec6215f6085d262c849468cb2c1ca36992a6a by Sergey Poznyakoff

Rewrite header caching

1 parent e359804b
1 /* GNU Mailutils -- a suite of utilities for electronic mail 1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2004, 2005 Free Software Foundation, Inc. 2 Copyright (C) 1999, 2000, 2001, 2004, 2005,
3 2007 Free Software Foundation, Inc.
3 4
4 This library is free software; you can redistribute it and/or 5 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public 6 modify it under the terms of the GNU Lesser General Public
...@@ -66,21 +67,7 @@ mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner) ...@@ -66,21 +67,7 @@ mu_header_create (mu_header_t *ph, const char *blurb, size_t len, void *owner)
66 static void 67 static void
67 header_free_cache (mu_header_t header) 68 header_free_cache (mu_header_t header)
68 { 69 {
69 /* Clean up our fast header cache. */ 70 mu_assoc_clear (header->cache);
70 if (header->fhdr)
71 {
72 size_t i;
73 for (i = 0; i < header->fhdr_count; i++)
74 {
75 if (header->fhdr[i].fn)
76 free (header->fhdr[i].fn);
77 if (header->fhdr[i].fv)
78 free (header->fhdr[i].fv);
79 }
80 free (header->fhdr);
81 header->fhdr = NULL;
82 header->fhdr_count = 0;
83 }
84 } 71 }
85 72
86 void 73 void
...@@ -103,6 +90,8 @@ mu_header_destroy (mu_header_t *ph, void *owner) ...@@ -103,6 +90,8 @@ mu_header_destroy (mu_header_t *ph, void *owner)
103 90
104 header_free_cache (header); 91 header_free_cache (header);
105 92
93 mu_assoc_destroy (&header->cache);
94
106 if (header->mstream) 95 if (header->mstream)
107 mu_stream_destroy (&(header->mstream), NULL); 96 mu_stream_destroy (&(header->mstream), NULL);
108 97
...@@ -145,10 +134,12 @@ header_parse (mu_header_t header, const char *blurb, int len) ...@@ -145,10 +134,12 @@ header_parse (mu_header_t header, const char *blurb, int len)
145 char *header_start2; 134 char *header_start2;
146 struct _hdr *hdr; 135 struct _hdr *hdr;
147 136
137 header_free_cache (header);
138
148 /* Nothing to parse. */ 139 /* Nothing to parse. */
149 if (blurb == NULL) 140 if (blurb == NULL)
150 return 0; 141 return 0;
151 142
152 header->blurb_len = len; 143 header->blurb_len = len;
153 /* Why "+ 1", if for a terminating NULL, where is written? */ 144 /* Why "+ 1", if for a terminating NULL, where is written? */
154 header->blurb = calloc (1, header->blurb_len + 1); 145 header->blurb = calloc (1, header->blurb_len + 1);
...@@ -257,8 +248,8 @@ header_parse (mu_header_t header, const char *blurb, int len) ...@@ -257,8 +248,8 @@ header_parse (mu_header_t header, const char *blurb, int len)
257 return 0; 248 return 0;
258 } 249 }
259 250
260 /* FIXME: grossly inneficient, to many copies and reallocating. 251 /* FIXME: grossly inneficient, too many copies and reallocating.
261 This all header business need a good rewrite. */ 252 This all header business needs a good rewrite. */
262 int 253 int
263 mu_header_set_value (mu_header_t header, const char *fn, const char *fv, 254 mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
264 int replace) 255 int replace)
...@@ -395,100 +386,94 @@ mu_header_set_value (mu_header_t header, const char *fn, const char *fv, ...@@ -395,100 +386,94 @@ mu_header_set_value (mu_header_t header, const char *fn, const char *fv,
395 return 0; 386 return 0;
396 } 387 }
397 388
398 /* We try to cache the headers here to reduce networking access 389 struct _hdr_cache {
399 especially for IMAP. When the buffer is NULL it means that 390 size_t len;
400 the field does not exist on the server and we should not 391 char *ptr;
401 attempt to contact the server again for this field. */ 392 };
402 static int
403 header_set_fvalue (mu_header_t header, const char *name, char *buffer)
404 {
405 struct _hdr *thdr;
406 thdr = realloc (header->fhdr, (header->fhdr_count + 1) * sizeof(*thdr));
407 if (thdr)
408 {
409 size_t len = strlen (name);
410 char *field = malloc (len + 1);
411 if (field == NULL)
412 return ENOMEM;
413 memcpy (field, name, len);
414 field[len] = '\0';
415 thdr[header->fhdr_count].fn = field;
416 thdr[header->fhdr_count].fn_end = field + len;
417 393
418 if (buffer) 394 static void
419 { 395 _hdr_cache_destroy (void *data)
420 len = strlen (buffer); 396 {
421 field = malloc (len + 1); 397 struct _hdr_cache *hc = data;
422 if (field == NULL) 398 free (hc->ptr);
423 return ENOMEM;
424 memcpy (field, buffer, len);
425 field[len] = '\0';
426 thdr[header->fhdr_count].fv = field;
427 thdr[header->fhdr_count].fv_end = field + len;
428 }
429 else
430 {
431 thdr[header->fhdr_count].fv = NULL;
432 thdr[header->fhdr_count].fv_end = NULL;
433 }
434 header->fhdr_count++;
435 header->fhdr = thdr;
436 return 0;
437 }
438 return ENOMEM;
439 } 399 }
440 400
441 /* For the cache header if the field exist but with no corresponding
442 value, it is a permanent failure i.e. the field does not exist
443 in the header return EINVAL to notify mu_header_get_value(). */
444 static int 401 static int
445 header_get_fvalue (mu_header_t header, const char *name, char *buffer, 402 header_get_fvalue (mu_header_t header, const char *name, char *buffer,
446 size_t buflen, size_t *pn) 403 size_t buflen, size_t *pn)
447 { 404 {
448 size_t i, fn_len, fv_len = 0; 405 struct _hdr_cache *hdr = mu_assoc_ref (header->cache, name);
449 size_t name_len; 406 if (hdr)
450 int err = MU_ERR_NOENT;
451
452 for (i = 0, name_len = strlen (name); i < header->fhdr_count; i++)
453 { 407 {
454 fn_len = header->fhdr[i].fn_end - header->fhdr[i].fn; 408 if (hdr->len == 0)
455 if (fn_len == name_len 409 return MU_ERR_NOENT;
456 && strcasecmp (header->fhdr[i].fn, name) == 0) 410 else
457 { 411 {
458 fv_len = header->fhdr[i].fv_end - header->fhdr[i].fv; 412 size_t fv_len = hdr->len;
459
460 /* Permanent failure. */
461 if (fv_len == 0)
462 {
463 err = EINVAL;
464 break;
465 }
466 413
467 if (buffer && buflen > 0) 414 if (buffer && buflen > 0)
468 { 415 {
469 buflen--; 416 buflen--;
470 fv_len = (fv_len < buflen) ? fv_len : buflen; 417 fv_len = (fv_len < buflen) ? fv_len : buflen;
471 memcpy (buffer, header->fhdr[i].fv, fv_len); 418 memcpy (buffer, hdr->ptr, fv_len);
472 buffer[fv_len] = '\0'; 419 buffer[fv_len] = '\0';
473 } 420 }
474 err = 0; 421 if (pn)
475 break; 422 *pn = fv_len;
423 return 0;
476 } 424 }
477 } 425 }
478 if (pn) 426 return MU_ERR_NOENT;
479 *pn = fv_len;
480 return err;
481
482 } 427 }
483 428
429 /* We try to cache the headers here to reduce networking access
430 especially for IMAP. When the buffer is NULL it means that
431 the field does not exist on the server and we should not
432 attempt to contact the server again for this field. */
433 static int
434 header_set_fvalue (mu_header_t header, const char *name, char *buffer,
435 size_t len)
436 {
437 int rc;
438 struct _hdr_cache hcache;
439
440 if (!header->cache)
441 {
442 rc = mu_assoc_create (&header->cache, sizeof(struct _hdr_cache),
443 MU_ASSOC_ICASE);
444 if (rc)
445 return rc;
446 mu_assoc_set_free (header->cache, _hdr_cache_destroy);
447 }
448 if (buffer == NULL)
449 {
450 hcache.ptr = NULL;
451 hcache.len = 0;
452 }
453 else
454 {
455 if (!len)
456 len = strlen (buffer);
457 hcache.ptr = malloc (len + 1);
458 if (!hcache.ptr)
459 return ENOMEM;
460 memcpy (hcache.ptr, buffer, len);
461 hcache.ptr[len] = 0;
462 hcache.len = len;
463 }
464 rc = mu_assoc_install (header->cache, name, &hcache);
465 if (rc)
466 free (hcache.ptr);
467 return rc;
468 }
469
484 int 470 int
485 mu_header_get_value (mu_header_t header, const char *name, char *buffer, 471 mu_header_get_value (mu_header_t header, const char *name, char *buffer,
486 size_t buflen, size_t *pn) 472 size_t buflen, size_t *pn)
487 { 473 {
488 size_t i = 0; 474 size_t i = 0;
489 size_t name_len; 475 size_t name_len;
490 size_t total = 0, fn_len = 0, fv_len = 0; 476 size_t fn_len = 0, fv_len = 0;
491 size_t threshold;
492 int err = 0; 477 int err = 0;
493 478
494 if (header == NULL || name == NULL) 479 if (header == NULL || name == NULL)
...@@ -507,12 +492,6 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, ...@@ -507,12 +492,6 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer,
507 return err; 492 return err;
508 } 493 }
509 494
510 /* Try the provided cache. */
511 if (header->_get_fvalue)
512 err = header->_get_fvalue (header, name, buffer, buflen, pn);
513 if (err == 0)
514 return 0;
515
516 if (header->_get_value) 495 if (header->_get_value)
517 { 496 {
518 char buf[1024]; /* should suffice for field-value. */ 497 char buf[1024]; /* should suffice for field-value. */
...@@ -521,7 +500,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, ...@@ -521,7 +500,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer,
521 if (err == 0) 500 if (err == 0)
522 { 501 {
523 /* Save in the fast header buffer. */ 502 /* Save in the fast header buffer. */
524 header_set_fvalue (header, name, buf); 503 header_set_fvalue (header, name, buf, 0);
525 if (buffer && buflen > 0) 504 if (buffer && buflen > 0)
526 { 505 {
527 buflen--; 506 buflen--;
...@@ -537,7 +516,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, ...@@ -537,7 +516,7 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer,
537 else 516 else
538 { 517 {
539 /* Cache permanent failure also. */ 518 /* Cache permanent failure also. */
540 header_set_fvalue (header, name, NULL); 519 header_set_fvalue (header, name, NULL, 0);
541 } 520 }
542 return err; 521 return err;
543 } 522 }
...@@ -551,15 +530,8 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, ...@@ -551,15 +530,8 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer,
551 } 530 }
552 531
553 /* We set the threshold to be 1 less for the null. */ 532 /* We set the threshold to be 1 less for the null. */
554 threshold = --buflen; 533 --buflen;
555 534
556 /* Caution: We may have more then one value for a field name, for example
557 a "Received" field-name is added by each passing MTA. The way that the
558 parsing (_parse()) is done it's not take to account. So we just stuff
559 in the buffer all the field-values to a corresponding field-name.
560 FIXME: Should we kosher the output ? meaning replace occurences of
561 " \t\r\n" for spaces ? for now we don't.
562 */
563 for (name_len = strlen (name), i = 0; i < header->hdr_count; i++) 535 for (name_len = strlen (name), i = 0; i < header->hdr_count; i++)
564 { 536 {
565 fn_len = header->hdr[i].fn_end - header->hdr[i].fn; 537 fn_len = header->hdr[i].fn_end - header->hdr[i].fn;
...@@ -567,39 +539,26 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer, ...@@ -567,39 +539,26 @@ mu_header_get_value (mu_header_t header, const char *name, char *buffer,
567 strncasecmp (header->hdr[i].fn, name, fn_len) == 0) 539 strncasecmp (header->hdr[i].fn, name, fn_len) == 0)
568 { 540 {
569 fv_len = (header->hdr[i].fv_end - header->hdr[i].fv); 541 fv_len = (header->hdr[i].fv_end - header->hdr[i].fv);
570 /* FIXME:FIXME:PLEASE: hack, add a space/nl separator */ 542 header_set_fvalue (header, name, header->hdr[i].fv, fv_len);
571 /*
572 if (total && (threshold - 2) > 0)
573 {
574 if (buffer)
575 {
576 *buffer++ = '\n';
577 *buffer++ = ' ';
578 }
579 threshold -= 2;
580 total += 2;
581 }
582 */
583 total += fv_len;
584 /* Can everything fit in the buffer. */ 543 /* Can everything fit in the buffer. */
585 if (buffer && threshold > 0) 544 if (buffer && buflen > 0)
586 { 545 {
587 buflen = (fv_len < threshold) ? fv_len : threshold; 546 if (fv_len > buflen)
588 memcpy (buffer, header->hdr[i].fv, buflen); 547 fv_len = buflen;
589 buffer += buflen; 548 memcpy (buffer, header->hdr[i].fv, fv_len);
590 threshold -= buflen; 549 buffer[buflen] = 0;
591 } 550 }
592 551
593 /* Jump out after the first header we found. -sr */ 552 if (pn)
594 break; 553 *pn = fv_len;
554 return 0;
595 } 555 }
596 } 556 }
597 if (buffer)
598 *buffer = '\0'; /* Null terminated. */
599 if (pn)
600 *pn = total;
601 557
602 return (total == 0) ? MU_ERR_NOENT : 0; 558 if (buffer)
559 *buffer = 0;
560
561 return MU_ERR_NOENT;
603 } 562 }
604 563
605 int 564 int
...@@ -888,19 +847,6 @@ mu_header_size (mu_header_t header, size_t *psize) ...@@ -888,19 +847,6 @@ mu_header_size (mu_header_t header, size_t *psize)
888 } 847 }
889 848
890 int 849 int
891 mu_header_set_get_fvalue (mu_header_t header,
892 int (*_get_fvalue) (mu_header_t, const char *, char *, size_t, size_t *),
893 void *owner)
894 {
895 if (header == NULL)
896 return EINVAL;
897 if (header->owner != owner)
898 return EACCES;
899 header->_get_fvalue = _get_fvalue;
900 return 0;
901 }
902
903 int
904 mu_header_set_get_value (mu_header_t header, int (*_get_value) 850 mu_header_set_get_value (mu_header_t header, int (*_get_value)
905 (mu_header_t, const char *, char *, size_t, size_t *), 851 (mu_header_t, const char *, char *, size_t, size_t *),
906 void *owner) 852 void *owner)
...@@ -939,8 +885,8 @@ mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner) ...@@ -939,8 +885,8 @@ mu_header_set_stream (mu_header_t header, mu_stream_t stream, void *owner)
939 885
940 int 886 int
941 mu_header_set_fill (mu_header_t header, int 887 mu_header_set_fill (mu_header_t header, int
942 (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *), 888 (*_fill) (mu_header_t, char *, size_t, mu_off_t, size_t *),
943 void *owner) 889 void *owner)
944 { 890 {
945 if (header == NULL) 891 if (header == NULL)
946 return EINVAL; 892 return EINVAL;
...@@ -963,7 +909,6 @@ fill_blurb (mu_header_t header) ...@@ -963,7 +909,6 @@ fill_blurb (mu_header_t header)
963 /* The entire header is now ours(part of mu_header_t), clear all the 909 /* The entire header is now ours(part of mu_header_t), clear all the
964 overloading. */ 910 overloading. */
965 header_free_cache (header); 911 header_free_cache (header);
966 header->_get_fvalue = NULL;
967 header->_get_value = NULL; 912 header->_get_value = NULL;
968 header->_set_value = NULL; 913 header->_set_value = NULL;
969 header->_size = NULL; 914 header->_size = NULL;
......