1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2005 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 /*
33  * $Id: generic_cache.c,v 1.34 2008/02/20 22:15:51 hoa Exp $
34  */
35 
36 #ifdef HAVE_CONFIG_H
37 #	include <config.h>
38 #endif
39 
40 #include "generic_cache.h"
41 
42 #include "libetpan-config.h"
43 
44 #ifdef HAVE_UNISTD_H
45 #	include <unistd.h>
46 #endif
47 #ifdef HAVE_SYS_MMAN_H
48 #	include <sys/mman.h>
49 #endif
50 #ifdef WIN32
51 #	include "win_etpan.h"
52 #endif
53 #include <string.h>
54 #include <stdio.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58 #include <stdlib.h>
59 
60 #include "maildriver_types.h"
61 #include "imfcache.h"
62 #include "chash.h"
63 #include "mailmessage.h"
64 #include "mail_cache_db.h"
65 
generic_cache_create_dir(char * dirname)66 int generic_cache_create_dir(char * dirname)
67 {
68   struct stat buf;
69   int r;
70 
71   r = stat(dirname, &buf);
72   if (r != 0) {
73 
74 #ifdef WIN32
75 	r = mkdir(dirname);
76 #else
77     r = mkdir(dirname, 0700);
78 #endif
79 
80     if (r < 0)
81       return MAIL_ERROR_FILE;
82   }
83   else {
84     if (!S_ISDIR(buf.st_mode))
85       return MAIL_ERROR_FILE;
86   }
87 
88   return MAIL_NO_ERROR;
89 }
90 
generic_cache_store(char * filename,char * content,size_t length)91 int generic_cache_store(char * filename, char * content, size_t length)
92 {
93   int fd;
94   char * str;
95 
96   fd = open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
97   if (fd == -1)
98     return MAIL_ERROR_FILE;
99 
100   if (ftruncate(fd, length) < 0) {
101 	  close(fd);
102     return MAIL_ERROR_FILE;
103 	}
104 
105   str = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
106   if (str == (char *)MAP_FAILED) {
107 	  close(fd);
108     return MAIL_ERROR_FILE;
109 	}
110 
111   memcpy(str, content, length);
112   msync(str, length, MS_SYNC);
113   munmap(str, length);
114 
115   close(fd);
116 
117   return MAIL_NO_ERROR;
118 }
119 
generic_cache_read(char * filename,char ** result,size_t * result_len)120 int generic_cache_read(char * filename, char ** result, size_t * result_len)
121 {
122   int fd;
123   char * str;
124   struct stat buf;
125   MMAPString * mmapstr;
126   char * content;
127   int res;
128 
129   if (stat(filename, &buf) < 0) {
130     res = MAIL_ERROR_CACHE_MISS;
131     goto err;
132   }
133 
134   fd = open(filename, O_RDONLY);
135   if (fd == -1) {
136     res = MAIL_ERROR_CACHE_MISS;
137     goto err;
138   }
139 
140   str = mmap(NULL, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
141   if (str == (char *)MAP_FAILED) {
142     res = MAIL_ERROR_FILE;
143     goto close;
144   }
145 
146   mmapstr = mmap_string_new_len(str, buf.st_size);
147   if (mmapstr == NULL) {
148     res = MAIL_ERROR_MEMORY;
149     goto unmap;
150   }
151 
152   if (mmap_string_ref(mmapstr) < 0) {
153     res = MAIL_ERROR_MEMORY;
154     goto free;
155   }
156 
157   content = mmapstr->str;
158 
159   munmap(str, buf.st_size);
160   close(fd);
161 
162   * result = content;
163   * result_len = buf.st_size;
164 
165   return MAIL_NO_ERROR;
166 
167  free:
168   mmap_string_free(mmapstr);
169  unmap:
170   munmap(str, buf.st_size);
171  close:
172   close(fd);
173  err:
174   return res;
175 }
176 
flags_extension_read(MMAPString * mmapstr,size_t * indx,clist ** result)177 static int flags_extension_read(MMAPString * mmapstr, size_t * indx,
178 				clist ** result)
179 {
180   clist * list;
181   int r;
182   uint32_t count;
183   uint32_t i;
184   int res;
185 
186   r = mailimf_cache_int_read(mmapstr, indx, &count);
187   if (r != MAIL_NO_ERROR) {
188     res = r;
189     goto err;
190   }
191 
192   list = clist_new();
193   if (list == NULL) {
194     res = MAIL_ERROR_MEMORY;
195     goto err;
196   }
197 
198   for(i = 0 ; i < count ; i++) {
199     char * str;
200 
201     r = mailimf_cache_string_read(mmapstr, indx, &str);
202     if (r != MAIL_NO_ERROR) {
203       res = r;
204       goto free_list;
205     }
206 
207     r = clist_append(list, str);
208     if (r < 0) {
209       free(str);
210       res = MAIL_ERROR_MEMORY;
211       goto free_list;
212     }
213   }
214 
215   * result = list;
216 
217   return MAIL_NO_ERROR;
218 
219  free_list:
220   clist_foreach(list, (clist_func) free, NULL);
221   clist_free(list);
222  err:
223   return res;
224 }
225 
generic_flags_read(MMAPString * mmapstr,size_t * indx,struct mail_flags ** result)226 static int generic_flags_read(MMAPString * mmapstr, size_t * indx,
227 			      struct mail_flags ** result)
228 {
229   clist * ext;
230   int r;
231   struct mail_flags * flags;
232   uint32_t value;
233   int res;
234 
235   r = mailimf_cache_int_read(mmapstr, indx, &value);
236   if (r != MAIL_NO_ERROR) {
237     res = r;
238     goto err;
239   }
240 
241   ext = NULL;
242   r = flags_extension_read(mmapstr, indx, &ext);
243   if (r != MAIL_NO_ERROR) {
244     res = r;
245     goto err;
246   }
247 
248   flags = mail_flags_new(value, ext);
249   if (flags == NULL) {
250     res = r;
251     goto free;
252   }
253 
254   * result = flags;
255 
256   return MAIL_NO_ERROR;
257 
258  free:
259   clist_foreach(ext, (clist_func) free, NULL);
260   clist_free(ext);
261  err:
262   return res;
263 }
264 
flags_extension_write(MMAPString * mmapstr,size_t * indx,clist * ext)265 static int flags_extension_write(MMAPString * mmapstr, size_t * indx,
266 				 clist * ext)
267 {
268   int r;
269   clistiter * cur;
270 
271   r = mailimf_cache_int_write(mmapstr, indx, clist_count(ext));
272   if (r != MAIL_NO_ERROR)
273     return r;
274 
275   for(cur = clist_begin(ext) ; cur != NULL ; cur = clist_next(cur)) {
276     char * ext_flag;
277 
278     ext_flag = clist_content(cur);
279     r = mailimf_cache_string_write(mmapstr, indx,
280         ext_flag, strlen(ext_flag));
281     if (r != MAIL_NO_ERROR)
282       return r;
283   }
284 
285   return MAIL_NO_ERROR;
286 }
287 
generic_flags_write(MMAPString * mmapstr,size_t * indx,struct mail_flags * flags)288 static int generic_flags_write(MMAPString * mmapstr, size_t * indx,
289 			       struct mail_flags * flags)
290 {
291   int r;
292 
293   r = mailimf_cache_int_write(mmapstr, indx,
294       flags->fl_flags & ~MAIL_FLAG_NEW);
295   if (r != MAIL_NO_ERROR)
296     return r;
297 
298   r = flags_extension_write(mmapstr, indx,
299       flags->fl_extension);
300   if (r != MAIL_NO_ERROR)
301     return r;
302 
303   return MAIL_NO_ERROR;
304 }
305 
306 
307 
308 
mail_flags_dup(struct mail_flags * flags)309 static struct mail_flags * mail_flags_dup(struct mail_flags * flags)
310 {
311   clist * list;
312   struct mail_flags * new_flags;
313   int r;
314   clistiter * cur;
315 
316   list = clist_new();
317   if (list == NULL) {
318     goto err;
319   }
320 
321   for(cur = clist_begin(flags->fl_extension) ; cur != NULL ;
322       cur = clist_next(cur)) {
323     char * ext;
324     char * original_ext;
325 
326     original_ext = clist_content(cur);
327     ext = strdup(original_ext);
328     if (ext == NULL) {
329       goto free;
330     }
331 
332     r = clist_append(list, ext);
333     if (r < 0) {
334       free(ext);
335       goto free;
336     }
337   }
338 
339   new_flags = mail_flags_new(flags->fl_flags, list);
340   if (new_flags == NULL) {
341     goto free;
342   }
343 
344   return new_flags;
345 
346  free:
347   clist_foreach(list, (clist_func) free, NULL);
348   clist_free(list);
349  err:
350   return NULL;
351 }
352 
mailmessage_build(mailmessage * msg)353 static mailmessage * mailmessage_build(mailmessage * msg)
354 {
355   mailmessage * new_msg;
356 
357   new_msg = malloc(sizeof(* new_msg));
358   if (new_msg == NULL)
359     goto err;
360 
361   new_msg->msg_session = msg->msg_session;
362   new_msg->msg_driver = msg->msg_driver;
363   new_msg->msg_index = msg->msg_index;
364   if (msg->msg_uid == NULL)
365     new_msg->msg_uid = NULL;
366   else {
367     new_msg->msg_uid = strdup(msg->msg_uid);
368     if (new_msg->msg_uid == NULL)
369       goto free;
370   }
371 
372   new_msg->msg_cached = msg->msg_cached;
373   new_msg->msg_size = msg->msg_size;
374   new_msg->msg_fields = NULL;
375   new_msg->msg_flags = mail_flags_dup(msg->msg_flags);
376   if (new_msg->msg_flags == NULL) {
377     free(new_msg->msg_uid);
378     goto free;
379   }
380 
381   new_msg->msg_mime = NULL;
382   new_msg->msg_data = NULL;
383 
384   return new_msg;
385 
386  free:
387   free(new_msg);
388  err:
389   return NULL;
390 }
391 
mail_flags_store_new(void)392 struct mail_flags_store * mail_flags_store_new(void)
393 {
394   struct mail_flags_store * flags_store;
395 
396   flags_store = malloc(sizeof(struct mail_flags_store));
397   if (flags_store == NULL)
398     goto err;
399 
400   flags_store->fls_tab = carray_new(128);
401   if (flags_store->fls_tab == NULL)
402     goto free;
403 
404   flags_store->fls_hash = chash_new(128, CHASH_COPYALL);
405   if (flags_store->fls_hash == NULL)
406     goto free_tab;
407 
408   return flags_store;
409 
410  free_tab:
411   carray_free(flags_store->fls_tab);
412  free:
413   free(flags_store);
414  err:
415   return NULL;
416 }
417 
mail_flags_store_clear(struct mail_flags_store * flags_store)418 void mail_flags_store_clear(struct mail_flags_store * flags_store)
419 {
420   unsigned int i;
421 
422   for(i = 0 ; i < carray_count(flags_store->fls_tab) ; i ++) {
423     chashdatum key;
424     mailmessage * msg;
425 
426     msg = carray_get(flags_store->fls_tab, i);
427 
428     key.data = &msg->msg_index;
429     key.len = sizeof(msg->msg_index);
430     chash_delete(flags_store->fls_hash, &key, NULL);
431 
432     mailmessage_free(msg);
433   }
434   carray_set_size(flags_store->fls_tab, 0);
435 }
436 
mail_flags_store_free(struct mail_flags_store * flags_store)437 void mail_flags_store_free(struct mail_flags_store * flags_store)
438 {
439   mail_flags_store_clear(flags_store);
440   chash_free(flags_store->fls_hash);
441   carray_free(flags_store->fls_tab);
442   free(flags_store);
443 }
444 
mail_flags_store_set(struct mail_flags_store * flags_store,mailmessage * msg)445 int mail_flags_store_set(struct mail_flags_store * flags_store,
446 			 mailmessage * msg)
447 {
448   chashdatum key;
449   chashdatum value;
450   unsigned int indx;
451   int res;
452   int r;
453   mailmessage * new_msg;
454 
455   if (msg->msg_flags == NULL) {
456     res = MAIL_NO_ERROR;
457     goto err;
458   }
459 
460   /* duplicate needed message info */
461   new_msg = mailmessage_build(msg);
462   if (new_msg == NULL) {
463     res = MAIL_ERROR_MEMORY;
464     goto err;
465   }
466 
467   key.data = &new_msg->msg_index;
468   key.len = sizeof(new_msg->msg_index);
469 
470   r = chash_get(flags_store->fls_hash, &key, &value);
471   if (r == 0) {
472     mailmessage * old_msg;
473 
474     indx = * (unsigned int *) value.data;
475     old_msg = carray_get(flags_store->fls_tab, indx);
476     mailmessage_free(old_msg);
477   }
478   else {
479     r = carray_set_size(flags_store->fls_tab,
480         carray_count(flags_store->fls_tab) + 1);
481     if (r != 0) {
482       res = MAIL_ERROR_MEMORY;
483       goto err;
484     }
485     indx = carray_count(flags_store->fls_tab) - 1;
486   }
487 
488   carray_set(flags_store->fls_tab, indx, new_msg);
489 
490   value.data = &indx;
491   value.len = sizeof(indx);
492 
493   r = chash_set(flags_store->fls_hash, &key, &value, NULL);
494   if (r < 0) {
495     carray_delete(flags_store->fls_tab, indx);
496     res = MAIL_ERROR_MEMORY;
497     goto free;
498   }
499 
500   return MAIL_NO_ERROR;
501 
502  free:
503   mailmessage_free(new_msg);
504  err:
505   return res;
506 }
507 
msg_index_compare(mailmessage ** msg1,mailmessage ** msg2)508 static int msg_index_compare(mailmessage ** msg1, mailmessage ** msg2)
509 {
510   return (* msg1)->msg_index - (* msg2)->msg_index;
511 }
512 
mail_flags_store_sort(struct mail_flags_store * flags_store)513 void mail_flags_store_sort(struct mail_flags_store * flags_store)
514 {
515   qsort(carray_data(flags_store->fls_tab),
516       carray_count(flags_store->fls_tab), sizeof(mailmessage *),
517       (int (*)(const void *, const void *)) msg_index_compare);
518 }
519 
520 struct mail_flags *
mail_flags_store_get(struct mail_flags_store * flags_store,uint32_t indx)521 mail_flags_store_get(struct mail_flags_store * flags_store, uint32_t indx)
522 {
523   struct mail_flags * flags;
524   chashdatum key;
525   chashdatum value;
526   int r;
527   unsigned int tab_index;
528   mailmessage * msg;
529 
530   key.data = &indx;
531   key.len = sizeof(indx);
532 
533   r = chash_get(flags_store->fls_hash, &key, &value);
534 
535   if (r < 0)
536     return NULL;
537 
538 #if 0
539   flags = mail_flags_dup((struct mail_flags *) value.data);
540 #endif
541   tab_index = * (unsigned int *) value.data;
542   msg = carray_get(flags_store->fls_tab, tab_index);
543   if (msg->msg_flags == NULL)
544     return NULL;
545 
546   flags = mail_flags_dup(msg->msg_flags);
547 
548   return flags;
549 }
550 
mail_flags_compare(struct mail_flags * flags1,struct mail_flags * flags2)551 int mail_flags_compare(struct mail_flags * flags1, struct mail_flags * flags2)
552 {
553   clistiter * cur1;
554 
555   if (clist_count(flags1->fl_extension) != clist_count(flags2->fl_extension))
556     return -1;
557 
558   for(cur1 = clist_begin(flags1->fl_extension) ; cur1 != NULL ;
559       cur1 = clist_next(cur1)) {
560     char * flag1;
561     clistiter * cur2;
562     int found;
563 
564     flag1 = clist_content(cur1);
565 
566     found = 0;
567     for(cur2 = clist_begin(flags2->fl_extension) ; cur2 != NULL ;
568         cur2 = clist_next(cur2)) {
569       char * flag2;
570 
571       flag2 = clist_content(cur2);
572 
573       if (strcasecmp(flag1, flag2) == 0) {
574         found = 1;
575         break;
576       }
577     }
578 
579     if (!found)
580       return -1;
581   }
582 
583   return flags1->fl_flags - flags2->fl_flags;
584 }
585 
generic_cache_fields_read(struct mail_cache_db * cache_db,MMAPString * mmapstr,char * keyname,struct mailimf_fields ** result)586 int generic_cache_fields_read(struct mail_cache_db * cache_db,
587     MMAPString * mmapstr,
588     char * keyname, struct mailimf_fields ** result)
589 {
590   int r;
591   int res;
592   size_t cur_token;
593   struct mailimf_fields * fields;
594   void * data;
595   size_t data_len;
596 
597   r = mail_cache_db_get(cache_db, keyname, strlen(keyname), &data, &data_len);
598   if (r != 0) {
599     res = MAIL_ERROR_CACHE_MISS;
600     goto err;
601   }
602 
603   r = mail_serialize_clear(mmapstr, &cur_token);
604   if (r != MAIL_NO_ERROR) {
605     res = r;
606     goto err;
607   }
608 
609   if (mmap_string_append_len(mmapstr, data, data_len) == NULL) {
610     res = MAIL_ERROR_MEMORY;
611     goto err;
612   }
613 
614   r = mailimf_cache_fields_read(mmapstr, &cur_token, &fields);
615   if (r != MAIL_NO_ERROR) {
616     res = r;
617     goto err;
618   }
619 
620   * result = fields;
621 
622   return MAIL_NO_ERROR;
623 
624  err:
625   return res;
626 }
627 
generic_cache_fields_write(struct mail_cache_db * cache_db,MMAPString * mmapstr,char * keyname,struct mailimf_fields * fields)628 int generic_cache_fields_write(struct mail_cache_db * cache_db,
629     MMAPString * mmapstr,
630     char * keyname, struct mailimf_fields * fields)
631 {
632   int r;
633   int res;
634   size_t cur_token;
635 
636   r = mail_serialize_clear(mmapstr, &cur_token);
637   if (r != MAIL_NO_ERROR) {
638     res = r;
639     goto err;
640   }
641 
642   r = mailimf_cache_fields_write(mmapstr, &cur_token, fields);
643   if (r != MAIL_NO_ERROR) {
644     res = r;
645     goto err;
646   }
647 
648   r = mail_cache_db_put(cache_db, keyname, strlen(keyname),
649       mmapstr->str, mmapstr->len);
650   if (r != 0) {
651     res = MAIL_ERROR_FILE;
652     goto err;
653   }
654 
655   return MAIL_NO_ERROR;
656 
657  err:
658   return res;
659 }
660 
generic_cache_flags_read(struct mail_cache_db * cache_db,MMAPString * mmapstr,char * keyname,struct mail_flags ** result)661 int generic_cache_flags_read(struct mail_cache_db * cache_db,
662     MMAPString * mmapstr,
663     char * keyname, struct mail_flags ** result)
664 {
665   int r;
666   int res;
667   size_t cur_token;
668   struct mail_flags * flags;
669   void * data;
670   size_t data_len;
671 
672   data = NULL;
673   data_len = 0;
674   r = mail_cache_db_get(cache_db, keyname, strlen(keyname), &data, &data_len);
675   if (r != 0) {
676     res = MAIL_ERROR_CACHE_MISS;
677     goto err;
678   }
679 
680   r = mail_serialize_clear(mmapstr, &cur_token);
681   if (r != MAIL_NO_ERROR) {
682     res = r;
683     goto err;
684   }
685 
686   if (mmap_string_append_len(mmapstr, data, data_len) == NULL) {
687     res = MAIL_ERROR_MEMORY;
688     goto err;
689   }
690 
691   flags = NULL;
692   r = generic_flags_read(mmapstr, &cur_token, &flags);
693   if (r != MAIL_NO_ERROR) {
694     res = r;
695     goto err;
696   }
697 
698   * result = flags;
699 
700   return MAIL_NO_ERROR;
701 
702  err:
703   return res;
704 }
705 
generic_cache_flags_write(struct mail_cache_db * cache_db,MMAPString * mmapstr,char * keyname,struct mail_flags * flags)706 int generic_cache_flags_write(struct mail_cache_db * cache_db,
707     MMAPString * mmapstr,
708     char * keyname, struct mail_flags * flags)
709 {
710   int r;
711   int res;
712   size_t cur_token;
713 
714   r = mail_serialize_clear(mmapstr, &cur_token);
715   if (r != MAIL_NO_ERROR) {
716     res = r;
717     goto err;
718   }
719 
720   r = generic_flags_write(mmapstr, &cur_token, flags);
721   if (r != MAIL_NO_ERROR) {
722     res = r;
723     goto err;
724   }
725 
726   r = mail_cache_db_put(cache_db, keyname, strlen(keyname),
727       mmapstr->str, mmapstr->len);
728   if (r != 0) {
729     res = MAIL_ERROR_FILE;
730     goto err;
731   }
732 
733   return MAIL_NO_ERROR;
734 
735  err:
736   return res;
737 }
738 
739 
generic_cache_delete(struct mail_cache_db * cache_db,char * keyname)740 int generic_cache_delete(struct mail_cache_db * cache_db,
741     char * keyname)
742 {
743   int r;
744   int res;
745 
746   r = mail_cache_db_del(cache_db, keyname, strlen(keyname));
747   if (r != 0) {
748     res = MAIL_ERROR_FILE;
749     goto err;
750   }
751 
752   return MAIL_NO_ERROR;
753 
754  err:
755   return res;
756 }
757 
758 
759