1 /*
2 Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
3 Copyright (c) 2004-2012 NFG Net Facilities Group BV support@nfg.nl
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22 *
23 * Miscelaneous functions */
24
25
26 #include "dbmail.h"
27 #include "dm_mailboxstate.h"
28
29 #define THIS_MODULE "misc"
30
31 const char AcceptedMailboxnameChars[] =
32 "abcdefghijklmnopqrstuvwxyz"
33 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
34 "0123456789-=/ _.&,+@()[]'#";
35
36 /**
37 * abbreviated names of the months
38 */
39 const char *month_desc[] = {
40 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
41 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
42 };
43
44 /* returned by date_sql2imap() */
45 #define IMAP_STANDARD_DATE "03-Nov-1979 00:00:00 +0000"
46
47 /* returned by date_imap2sql() */
48 #define SQL_STANDARD_DATE "1979-11-03 00:00:00"
49
50 const int month_len[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
51
52 extern const char *imap_flag_desc_escaped[];
53
54 extern struct DbmailIconv *ic;
55 extern Mempool_T small_pool;
56
57 #undef max
58 #define max(x,y) ( (x) > (y) ? (x) : (y) )
59
60 /* only locally used */
61 typedef struct {
62 GTree *tree;
63 GList *list;
64 int condition;
65 } tree_merger_t;
66
67 /* only locally used for various operations mostly duplicates*/
68 typedef struct {
69 GTree *treeSource;
70 GTree *treeDestination;
71 } tree_copy_t;
72
73
g_string_maybe_shrink(GString * s)74 void g_string_maybe_shrink(GString *s)
75 {
76 if (s->len+1 < s->allocated_len) {
77 s->str = g_realloc(s->str, s->len+1);
78 s->allocated_len = s->len+1;
79 }
80 }
81
82 /**
83 * Print the trace. The DEBUG level has to be enabled.
84 *
85 */
print_trace(void)86 void print_trace (void)
87 {
88 #ifdef DEBUG
89 /* activate this function only if DEBUG variable is defined */
90 void *array[10];
91 size_t size;
92 char **strings;
93 size_t i;
94
95 size = backtrace (array, 10);
96 strings = backtrace_symbols (array, size);
97
98 TRACE(TRACE_DEBUG, "Obtained %zd stack frames.\n", size);
99
100 for (i = 0; i < size; i++)
101 TRACE(TRACE_DEBUG, "#%d %s\n", i,strings[i]);
102
103 free (strings);
104 #endif
105 }
106
drop_privileges(char * newuser,char * newgroup)107 int drop_privileges(char *newuser, char *newgroup)
108 {
109 /* will drop running program's priviledges to newuser and newgroup */
110 struct passwd pwd;
111 struct passwd *presult;
112 struct group grp;
113 struct group *gresult;
114 char buf[16384];
115
116 memset(buf,0,sizeof(buf));
117
118 if (getgrnam_r(newgroup, &grp, buf, sizeof(buf)-1, &gresult))
119 return -1;
120
121 if (getpwnam_r(newuser, &pwd, buf, sizeof(buf)-1, &presult))
122 return -1;
123
124 if (gresult == NULL || presult == NULL)
125 return -1;
126
127 if (setgid(grp.gr_gid) != 0) {
128 TRACE(TRACE_ERR, "could not set gid to %s\n", newgroup);
129 return -1;
130 }
131
132 if (setuid(pwd.pw_uid) != 0) {
133 TRACE(TRACE_ERR, "could not set uid to %s\n", newuser);
134 return -1;
135 }
136 return 0;
137 }
138
get_opened_fd_count(void)139 int get_opened_fd_count(void)
140 {
141 #if defined(__FreeBSD__) || defined(__APPLE__) || defined(__SUNPRO_C)
142 return 0;
143 #else
144 DIR* dir = NULL;
145 struct dirent* entry = NULL;
146 char buf[32];
147 int fd_count = 0;
148
149 snprintf(buf, 32, "/proc/%i/fd/", getpid());
150
151 dir = opendir(buf);
152 if (dir == NULL)
153 return -1;
154
155 while ((entry = readdir(dir)) != NULL)
156 fd_count++;
157 closedir(dir);
158
159 return fd_count - 2; /* exclude '.' and '..' entries */
160 #endif
161 }
162
create_unique_id(char * target,uint64_t message_idnr)163 void create_unique_id(char *target, uint64_t message_idnr)
164 {
165 char md5_str[FIELDSIZE];
166 if (message_idnr != 0) {
167 snprintf(target, UID_SIZE-1, "%" PRIu64 ":%ld", message_idnr, random());
168 } else {
169 snprintf(target, UID_SIZE-1, "%ld", random());
170 }
171
172 memset(md5_str, 0, sizeof(md5_str));
173 dm_md5(target, md5_str);
174 snprintf(target, UID_SIZE-1, "%s", md5_str);
175
176 TRACE(TRACE_DEBUG, "created: %s", target);
177 }
178
create_current_timestring(TimeString_T * timestring)179 void create_current_timestring(TimeString_T * timestring)
180 {
181 time_t td;
182 struct tm tm;
183
184 if (time(&td) == -1)
185 TRACE(TRACE_EMERG, "error getting time from OS");
186
187 memset(&tm,0,sizeof(tm));
188 localtime_r(&td, &tm); /* get components */
189 strftime((char *) timestring, sizeof(TimeString_T)-1,
190 "%Y-%m-%d %H:%M:%S", &tm);
191 }
192
mailbox_add_namespace(const char * mailbox_name,uint64_t owner_idnr,uint64_t user_idnr)193 char *mailbox_add_namespace(const char *mailbox_name, uint64_t owner_idnr,
194 uint64_t user_idnr)
195 {
196 char *fq;
197 char *owner;
198 GString *t;
199
200 if (mailbox_name == NULL) {
201 TRACE(TRACE_ERR, "error, mailbox_name is NULL.");
202 return NULL;
203 }
204
205 if (user_idnr == owner_idnr)
206 /* mailbox owned by current user */
207 return g_strdup(mailbox_name);
208
209 /* else */
210
211 if ((owner = auth_get_userid(owner_idnr))==NULL)
212 return NULL;
213
214 t = g_string_new("");
215 if (strcmp(owner, PUBLIC_FOLDER_USER) == 0)
216 g_string_printf(t, "%s%s%s", NAMESPACE_PUBLIC, MAILBOX_SEPARATOR, mailbox_name);
217 else
218 g_string_printf(t, "%s%s%s%s%s", NAMESPACE_USER, MAILBOX_SEPARATOR, owner,
219 MAILBOX_SEPARATOR, mailbox_name);
220 g_free(owner);
221
222 fq = t->str;
223 g_string_free(t,FALSE);
224
225 return fq;
226 }
227
228 /* Strips off the #Users or #Public namespace, returning
229 * the simple name, the namespace and username, if present. */
230
mailbox_remove_namespace(char * name,char ** namespace,char ** username)231 char *mailbox_remove_namespace(char *name, char **namespace, char **username)
232 {
233 char *temp = NULL, *user = NULL;
234 size_t ns_user_len = 0;
235 size_t ns_publ_len = 0;
236 size_t fq_name_len;
237 char *mbox = NULL, *fq_name = (char *)name;
238
239 ns_user_len = strlen(NAMESPACE_USER);
240 ns_publ_len = strlen(NAMESPACE_PUBLIC);
241
242 if (username) *username = NULL;
243 if (namespace) *namespace = NULL;
244
245 fq_name_len = strlen(fq_name);
246 while (fq_name_len) {
247 if (! g_str_has_suffix(fq_name,"/"))
248 break;
249 fq_name_len--;
250 fq_name[fq_name_len] = '\0';
251 }
252
253 TRACE(TRACE_DEBUG,"[%s]", fq_name);
254
255 // i.e. '#Users/someuser/foldername'
256 // assume a slash in '#Users/foo*' and '#Users/foo%' like this '#Users/foo/*'
257 if (fq_name_len >= ns_user_len && strncasecmp(fq_name, NAMESPACE_USER, ns_user_len) == 0) {
258 if (namespace) *namespace = NAMESPACE_USER;
259
260 int end = 0, err = 0, slash = 0;
261 // We'll use a simple state machine to parse through this.
262 for (temp = &fq_name[ns_user_len]; !end && !err; temp++) {
263 switch (*temp) {
264 case '/':
265 if (!user) {
266 user = temp + 1;
267 } else if (user && !mbox) {
268 slash = 1;
269 if (strlen(temp+1) && (*(temp+1) != '/'))
270 mbox = temp + 1;
271 } else if (user && mbox) {
272 end = 1;
273 }
274 break;
275 case '*':
276 mbox = temp;
277 break;
278 case '%':
279 mbox = temp;
280 break;
281 case '\0':
282 end = 1;
283 break;
284 }
285 }
286
287 if (err) {
288 TRACE(TRACE_NOTICE, "Illegal mailbox name");
289 return NULL;
290 }
291
292 if (mbox && strlen(mbox) && (!user || (user + slash == mbox))) {
293 TRACE(TRACE_DEBUG, "Username not found, returning mbox [%s]", mbox);
294 return mbox;
295 }
296
297 if (!mbox) {
298 TRACE(TRACE_DEBUG, "Mailbox not found");
299 return NULL;
300 }
301
302 TRACE(TRACE_DEBUG, "Copying out username [%s] of length [%zu]", user, (size_t)(mbox - user - slash));
303 if (username) *username = g_strndup(user, mbox - user - slash);
304
305 TRACE(TRACE_DEBUG, "returning [%s]", mbox);
306 return mbox;
307 }
308
309 // i.e. '#Public/foldername'
310 // accept #Public* and #Public% also
311 if (fq_name_len >= ns_publ_len && strncasecmp(fq_name, NAMESPACE_PUBLIC, ns_publ_len) == 0) {
312 if (namespace) *namespace = NAMESPACE_PUBLIC;
313 if (username) *username = g_strdup(PUBLIC_FOLDER_USER);
314 // Drop the slash between the namespace and the mailbox spec
315 if (fq_name[ns_publ_len] == '/')
316 return &fq_name[ns_publ_len+1];
317 // But if the slash wasn't there, it means we have #Public*, and that's OK.
318 return &fq_name[ns_publ_len]; // FIXME: leakage
319 }
320
321 return fq_name;
322 }
323 /* Finds what lurks between two bounding symbols.
324 * Allocates and fills retchar with the string.
325 *
326 * Return values are:
327 * 0 or greater (size of item found inbounds)
328 * -1 on failure (bounds not found)
329 *
330 * The caller is responsible for free()ing *retchar.
331 * */
find_bounded(const char * const value,char left,char right,char ** retchar,size_t * retsize,size_t * retlast)332 int find_bounded(const char * const value, char left, char right,
333 char **retchar, size_t * retsize, size_t * retlast)
334 {
335 char *tmpleft;
336 char *tmpright;
337 size_t tmplen;
338
339 tmpleft = (char *)value;
340 tmpright = (char *)(value + strlen(value));
341
342 while (tmpleft[0] != left && tmpleft < tmpright)
343 tmpleft++;
344 while (tmpright[0] != right && tmpright > tmpleft)
345 tmpright--;
346
347 if (tmpleft[0] != left || tmpright[0] != right) {
348 TRACE(TRACE_INFO, "Missing part or all of our bounding points");
349 *retchar = NULL;
350 *retsize = 0;
351 *retlast = 0;
352 return -1;
353 }
354 /* else */
355 /* Step left up to skip the actual left bound */
356 if (tmpright != tmpleft)
357 tmpleft++;
358
359 tmplen = tmpright - tmpleft;
360 *retchar = g_new0(char, tmplen + 1);
361 strncpy(*retchar, tmpleft, tmplen);
362 (*retchar)[tmplen] = '\0';
363 *retsize = tmplen;
364 *retlast = tmpright - value;
365 TRACE(TRACE_INFO, "Found [%s] of length [%zu] between '%c' and '%c' so next skip [%zu]", *retchar, *retsize, left,
366 right, *retlast);
367 return *retlast;
368 }
369
zap_between(const char * const instring,signed char left,signed char right,char ** outstring,size_t * outlen,size_t * zaplen)370 int zap_between(const char * const instring, signed char left, signed char right,
371 char **outstring, size_t *outlen, size_t *zaplen)
372 {
373 char *start, *end;
374 char *incopy = g_strdup(instring);
375 int clipleft = 0, clipright = 0;
376
377 if (!incopy)
378 return -2;
379
380 // Should we clip the left char, too?
381 if (left < 0) {
382 left = (signed char)(0 - left);
383 clipleft = 1;
384 }
385
386 // Should we clip the right char, too?
387 if (right < 0) {
388 right = (signed char)(0 - right);
389 clipright = 1;
390 }
391
392 start = strchr(incopy, left);
393 end = strrchr(incopy, right);
394
395 if (!start || !end) {
396 g_free(incopy);
397 return -1;
398 }
399
400 if (!clipleft) start++;
401 if (clipright) end++;
402
403 memmove(start, end, strlen(end)+1);
404
405 if (outstring)
406 *outstring = incopy;
407 if (outlen)
408 *outlen = strlen(incopy);
409 if (zaplen)
410 *zaplen = (end - start);
411
412 return 0;
413 }
414
415
416 /*
417 *
418 *
419 * Some basic string handling utilities
420 *
421 *
422 *
423 */
424
g_string_split(GString * string,const gchar * sep)425 GList * g_string_split(GString * string, const gchar * sep)
426 {
427 GList * list = NULL;
428 char **array;
429 int i = 0;
430
431 if (string->len == 0)
432 return NULL;
433
434 array = (char **)g_strsplit((const gchar *)string->str, sep, 0);
435 while(array[i])
436 list = g_list_append(list,array[i++]);
437
438 g_free(array);
439
440 return list;
441 }
g_strcasestr(const char * haystack,const char * needle)442 char * g_strcasestr(const char *haystack, const char *needle)
443 {
444 // Like strstr, but case insensitive.
445 size_t n = strlen(needle);
446 for (; *haystack; haystack++) {
447 if (g_ascii_strncasecmp(haystack, needle, n) == 0)
448 return (char *)haystack;
449 }
450
451 return NULL;
452 }
453
454 /*
455 * replace all multi-spaces with single spaces
456 */
pack_char(char * in,char c)457 void pack_char(char *in, char c)
458 {
459 char *saved;
460 char *tmp = g_strdup(in);
461 saved = tmp;
462 while(*tmp) {
463 if ((*tmp == c) && (*(tmp+1) == c))
464 tmp++;
465 else
466 *in++=*tmp++;
467 }
468 g_free(saved);
469 *in='\0';
470 }
471
472 /*
473 *
474 * replace tabs with spaces and all multi-spaces with single spaces
475 *
476 */
477
dm_pack_spaces(char * in)478 void dm_pack_spaces(char *in)
479 {
480 /* replace tabs with spaces */
481 g_strdelimit(in,"\t",' ');
482 pack_char(in,' ');
483 }
484 /*
485 * base-subject
486 *
487 */
488
_strip_blob_prefix(char * subject)489 static void _strip_blob_prefix(char *subject)
490 {
491 size_t len;
492 char *tmp = subject;
493 if (*tmp != '[')
494 return;
495
496 tmp++;
497 while (*tmp != '\0' && *tmp != ']' && *tmp != '[')
498 tmp++;
499
500 if (*tmp != ']')
501 return;
502
503 while (isspace(*++tmp))
504 ;
505 len = strlen(tmp);
506
507 if (len > 0)
508 memmove(subject,tmp,len+1);
509
510 return;
511 }
_strip_refwd(char * subject)512 static gboolean _strip_refwd(char *subject)
513 {
514 char *tmp;
515 size_t len;
516 if (! (strncasecmp(subject,"re",2)==0 || strncasecmp(subject,"fw",2)==0))
517 return false;
518
519 tmp = subject;
520
521 if (strncasecmp(tmp,"fwd",3)==0)
522 tmp+=3;
523 else if ((strncasecmp(tmp,"re",2)==0) || (strncasecmp(tmp,"fw",2)==0))
524 tmp+=2;
525
526 g_strstrip(tmp);
527
528 if (strlen(tmp) > 0)
529 _strip_blob_prefix(tmp);
530
531 if (*tmp!=':')
532 return false;
533
534 g_strstrip(++tmp); // skip ':'
535
536 len = strlen(tmp);
537 memmove(subject,tmp,strlen(tmp)+1);
538
539 return len?true:false;
540 }
541
_strip_sub_leader(char * subject)542 static void _strip_sub_leader(char *subject)
543 {
544 unsigned len;
545 /* strip blobs prefixes */
546 while (1) {
547 len = strlen(subject);
548 _strip_blob_prefix(subject);
549 if (strlen(subject)==len)
550 break;
551 }
552 /* strip refwd prefixes */
553 while (1) {
554 if (!_strip_refwd(subject))
555 break;
556 }
557 }
558
dm_base_subject(const char * subject)559 char * dm_base_subject(const char *subject)
560 {
561 unsigned offset, len, olen;
562 char *tmp, *saved;
563
564 // we expect utf-8 or 7-bit data
565 if (subject == NULL) return NULL;
566
567 //(1) Convert any RFC 2047 encoded-words in the subject to [UTF-8]
568 // as described in "Internationalization Considerations".
569 // Convert all tabs and continuations to space. Convert all
570 // multiple spaces to a single space.
571 if (g_mime_utils_text_is_8bit((unsigned char *)subject, strlen(subject)))
572 tmp = g_strdup(subject);
573 else
574 tmp = dbmail_iconv_decode_text(subject);
575 saved = tmp;
576
577 dm_pack_spaces(tmp);
578 while (1) {
579 g_strstrip(tmp);
580 olen = strlen(tmp);
581 // (2) remove subject trailer: "(fwd)" / WSP
582 if (olen > 5) {
583 char *trailer = tmp + (olen - 5);
584 if (strncasecmp(trailer, "(fwd)", 5)==0) {
585 *trailer = '\0';
586 g_strstrip(tmp);
587 continue;
588 }
589 }
590 // (3) remove subject leader: (*subj-blob subj-refwd) / WSP
591 // (4) remove subj-blob prefix if result non-empty
592 while (1==1) {
593 len = strlen(tmp);
594 _strip_sub_leader(tmp);
595 if (strlen(tmp)==len)
596 break;
597 }
598
599 // (6) If the resulting text begins with the subj-fwd-hdr ABNF and
600 // ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and
601 // subj-fwd-trl and repeat from step (2).
602 if (g_str_has_suffix(tmp,"]") && strncasecmp(tmp,"[fwd:",5)==0 ) {
603 offset=strlen(tmp)-1;
604 tmp[offset]='\0';
605 tmp+=5;
606 g_strstrip(tmp);
607 continue;
608 }
609
610 while (g_str_has_prefix(tmp,":") && (strlen(tmp) > 1))
611 g_strstrip(++tmp);
612
613 if (strlen(tmp)==olen)
614 break;
615 }
616
617 tmp = g_utf8_strdown(tmp, strlen(tmp));
618
619 g_free(saved);
620
621 return tmp;
622 }
623
624 /*
625 * \brief listexpression match for imap (rfc2060)
626 * \param p pattern
627 * \param s string to search
628 * \param x separator string ("." or "/"- multichar okay; e.g. "π" would work f
629 * you can find a IMAP client that read rfc2060)
630 * \param flags presently only LISTEX_NOCASE -- if you want case-insensitive
631 * "folders"
632 * \return 1 indicates a match
633 */
634 #define LISTEX_NOCASE 1
listex_match(const char * p,const char * s,const char * x,int flags)635 int listex_match(const char *p, const char *s,
636 const char *x, int flags)
637 {
638 int i, p8;
639 p8=0;
640 while (*p) {
641 if (!p8 && *p == '%') {
642 p++;
643 while (*s) {
644 /* fast-forward until after next separator in string */
645 for (i = 0; x[i] && x[i] == s[i]; i++);
646 if (! x[i]) {
647 s += i;
648 break;
649 }
650 /* %foo. */
651 for (i = 0; s[i] && p[i] && s[i] == p[i] && s[i] != x[0] && p[i] != x[0]; i++);
652 if (i > 0 && ((! p[i] ) || (p[i] == x[0] && s[i] == x[0]))) {
653 p += i; s += i;
654 } else {
655 s++;
656 }
657 }
658 /* %. */
659 for (i = 0; x[i] && x[i] == p[i]; i++);
660 if (! x[i]) p += i;
661 if (*s && *p) return listex_match(p,s,x,flags); // We have more to look at - lets look again.
662 if (*s || *p) return 0;
663 return 1;
664
665 }
666 if (!p8 && *p == '*') {
667 /* use recursive for synchronize */
668 p++;
669 if (!(*p)) return 1;
670 while (*s) {
671 if (listex_match(p,s,x,flags)) return 1;
672 s++;
673 }
674 return 0;
675
676 }
677
678 if (!p8 && *p == *x) {
679 for (i = 0; x[i] && p[i] == x[i] && p[i] == s[i]; i++);
680 if (! x[i]) {
681 p += i; s += i;
682 continue; /* sync'd */
683 }
684 /* fall; try regular search */
685 }
686
687 if ( (*p == *s)||
688 ((flags & LISTEX_NOCASE) && (tolower(*p) == tolower(*s)))) {
689 p8=(((unsigned char)*p) > 0xC0);
690 p++; s++;
691 } else {
692 /* failed */
693 return 0;
694 }
695 }
696 if (*p || *s) return 0;
697 return 1;
698 }
699
dm_getguid(unsigned int serverid)700 uint64_t dm_getguid(unsigned int serverid)
701 {
702 char s[30];
703 struct timeval tv;
704
705 assert((int)serverid >= 0);
706
707 if (gettimeofday(&tv,NULL))
708 return 0;
709
710 snprintf(s,30,"%ld%06ld%02u", tv.tv_sec, tv.tv_usec,serverid);
711 return (uint64_t)strtoll(s,NULL,10);
712 }
713
dm_sock_score(const char * base,const char * test)714 int dm_sock_score(const char *base, const char *test)
715 {
716 Cidr_T basefilter, testfilter;
717 int result = 0;
718 char *t;
719
720 if ((! base) || (! test))
721 return 0;
722
723 t = strstr(base,"unix:");
724 if (t==base) {
725 base = strstr(base,":");
726 test = strstr(test,":");
727 return (fnmatch(base,test,0) ? 0 : 1);
728 }
729
730 t = strstr(base,"inet:");
731 if (t!=base)
732 return 0;
733
734 basefilter = cidr_new(base);
735 testfilter = cidr_new(test);
736
737 if (strlen(test)==0) {
738 result = 32;
739 } else if (basefilter && testfilter) {
740 result = cidr_match(basefilter,testfilter);
741 }
742
743 cidr_free(&basefilter);
744 cidr_free(&testfilter);
745
746 return result;
747 }
748
socket_match(const char * base,const char * test)749 static int socket_match(const char *base, const char *test)
750 {
751 return (dm_sock_score(base,test) ? TRUE : FALSE);
752
753 }
754
dm_sock_compare(const char * clientsock,const char * sock_allow,const char * sock_deny)755 int dm_sock_compare(const char *clientsock, const char *sock_allow, const char *sock_deny)
756 {
757 int result = TRUE;
758 assert(clientsock);
759
760 if ( (strlen(sock_allow) == 0) && (strlen(sock_deny) == 0) )
761 result = TRUE;
762 else if (strlen(sock_deny) && socket_match(sock_deny, clientsock))
763 result = FALSE;
764 else if (strlen(sock_allow))
765 result = socket_match(sock_allow, clientsock);
766
767 TRACE(TRACE_DEBUG, "clientsock [%s] sock_allow[%s], sock_deny [%s] => [%d]", clientsock, sock_allow, sock_deny, result);
768 return result;
769
770 }
771
772
773 /* dm_valid_format
774 * check if str is a valid format string containing a single "%s" for use in
775 * printf style calls
776 * \return 1 format is invalid
777 * \return 0 format is valid
778 */
dm_valid_format(const char * str)779 int dm_valid_format(const char *str)
780 {
781 char *left, *right;
782 left = index(str,'%');
783 right = rindex(str,'%');
784 if (! (left && right && left==right))
785 return DM_EGENERAL;
786 if (*(left+1) != 's')
787 return DM_EGENERAL;
788 return DM_SUCCESS;
789 }
790
791
792
793 /*
794 * checkmailboxname()
795 *
796 * performs a check to see if the mailboxname is valid
797 * returns 0 if invalid, 1 otherwise
798 */
checkmailboxname(const char * s)799 int checkmailboxname(const char *s)
800 {
801 int i;
802
803 if (strlen(s) == 0)
804 return 0; /* empty name is not valid */
805
806 if (strlen(s) >= IMAP_MAX_MAILBOX_NAMELEN)
807 return 0; /* a too large string is not valid */
808
809 /* check for invalid characters */
810 for (i = 0; s[i]; i++) {
811 if (!strchr(AcceptedMailboxnameChars, s[i])) {
812 /* dirty hack to allow namespaces to function */
813 if (i == 0 && s[0] == '#')
814 continue;
815 /* wrong char found */
816 return 0;
817 }
818 }
819
820 /* check for double '/' */
821 for (i = 1; s[i]; i++) {
822 if (s[i] == '/' && s[i - 1] == '/')
823 return 0;
824 }
825
826 /* check if the name consists of a single '/' */
827 if (strlen(s) == 1 && s[0] == '/')
828 return 0;
829
830 return 1;
831 }
832
833
834 /*
835 * check_date()
836 *
837 * checks a date for IMAP-date validity:
838 * dd-MMM-yyyy
839 * 01234567890
840 * month three-letter specifier
841 */
842
843 // Define len if "01-Jan-1970" string
844 #define STRLEN_MINDATA 11
845
check_date(const char * date)846 int check_date(const char *date)
847 {
848 char sub[4];
849 int days, i, j=1;
850 size_t l;
851
852 l = strlen(date);
853
854 if (l != STRLEN_MINDATA && l != STRLEN_MINDATA-1) return 0;
855
856 j = (l==STRLEN_MINDATA) ? 0 : 1;
857
858 if (date[2 - j] != '-' || date[6 - j] != '-') return 0;
859
860 days = strtoul(date, NULL, 10);
861 strncpy(sub, &date[3 - j], 3);
862 sub[3] = 0;
863
864 for (i = 0; i < 12; i++) {
865 if (strcasecmp(month_desc[i], sub) == 0) break;
866 }
867
868 if (i >= 12 || days > month_len[i]) return 0;
869
870 for (i = 7; i < 11; i++)
871 if (!isdigit(date[i - j])) return 0;
872
873 return 1;
874 }
875
876 /*
877 * check_msg_set()
878 *
879 * checks if s represents a valid message set
880 */
check_msg_set(const char * s)881 int check_msg_set(const char *s)
882 {
883 int i, indigit=0, result = 1;
884
885 if (!s || (!isdigit(s[0]) && s[0]!= '*') ) return 0;
886
887 for (i = 0; s[i]; i++) {
888 if (isdigit(s[i]) || s[i]=='*') indigit = 1;
889 else if (s[i] == ',') {
890 if (!indigit) {
891 result = 0;
892 break;
893 }
894
895 indigit = 0;
896 } else if (s[i] == ':') {
897 if (!indigit) {
898 result = 0;
899 break;
900 }
901
902 indigit = 0;
903 } else {
904 result = 0;
905 break;
906 }
907 }
908 TRACE(TRACE_DEBUG, "[%s] [%s]", s, result ? "ok" : "fail" );
909 return result;
910 }
911
912
913 /*
914 * convert a mySQL date (yyyy-mm-dd hh:mm:ss) to a valid IMAP internal date:
915 * dd-mon-yyyy hh:mm:ss with mon characters (i.e. 'Apr' for april)
916 * return value is valid until next function call.
917 * NOTE: if date is not valid, IMAP_STANDARD_DATE is returned
918 */
date_sql2imap(const char * sqldate)919 char *date_sql2imap(const char *sqldate)
920 {
921 struct tm tm_sql_date;
922 char *last;
923 char _imapdate[IMAP_INTERNALDATE_LEN] = IMAP_STANDARD_DATE;
924 char q[IMAP_INTERNALDATE_LEN];
925
926 // bsd needs:
927 memset(&tm_sql_date, 0, sizeof(struct tm));
928
929 last = strptime(sqldate,"%Y-%m-%d %H:%M:%S", &tm_sql_date);
930 if ( (last == NULL) || (*last != '\0') ) {
931 // Could not convert date - something went wrong - using default IMAP_STANDARD_DATE
932 strcpy(_imapdate, IMAP_STANDARD_DATE);
933 return g_strdup(_imapdate);
934 }
935
936 strftime(q, sizeof(q), "%d-%b-%Y %H:%M:%S", &tm_sql_date);
937 snprintf(_imapdate,IMAP_INTERNALDATE_LEN, "%s +0000", q);
938
939 return g_strdup(_imapdate);
940 }
941
942
943 /*
944 * convert TO a mySQL date (yyyy-mm-dd) FROM a valid IMAP internal date:
945 * 0123456789
946 * dd-mon-yyyy with mon characters (i.e. 'Apr' for april)
947 * 01234567890
948 * OR
949 * d-mon-yyyy
950 * return value is valid until next function call.
951 */
date_imap2sql(const char * imapdate,char * sqldate)952 int date_imap2sql(const char *imapdate, char *sqldate)
953 {
954 struct tm tm;
955 char *last_char;
956 size_t l = strlen(imapdate);
957
958 assert(sqldate);
959
960 if ((l < 10) || (l > 11)) {
961 TRACE(TRACE_DEBUG, "invalid size IMAP date [%s]", imapdate);
962 return 1;
963 }
964
965 // bsd needs this:
966 memset(&tm, 0, sizeof(struct tm));
967 last_char = strptime(imapdate, "%d-%b-%Y", &tm);
968 if (last_char == NULL || *last_char != '\0') {
969 TRACE(TRACE_DEBUG, "error parsing IMAP date %s", imapdate);
970 return 1;
971 }
972 (void) strftime(sqldate, SQL_INTERNALDATE_LEN-1, "%Y-%m-%d 00:00:00", &tm);
973
974 return 0;
975 }
976
977
num_from_imapdate(const char * date)978 int num_from_imapdate(const char *date)
979 {
980 int j = 0, i;
981 char datenum[] = "YYYYMMDD";
982 char sub[4];
983
984 if (date[1] == ' ' || date[1] == '-')
985 j = 1;
986
987 strncpy(datenum, &date[7 - j], 4);
988
989 strncpy(sub, &date[3 - j], 3);
990 sub[3] = 0;
991
992 for (i = 0; i < 12; i++) {
993 if (strcasecmp(sub, month_desc[i]) == 0)
994 break;
995 }
996
997 i++;
998 if (i > 12)
999 i = 12;
1000
1001 sprintf(&datenum[4], "%02d", i);
1002
1003 if (j) {
1004 datenum[6] = '0';
1005 datenum[7] = date[0];
1006 } else {
1007 datenum[6] = date[0];
1008 datenum[7] = date[1];
1009 }
1010
1011 return atoi(datenum);
1012 }
traverse_tree_keys(gpointer key,gpointer value UNUSED,GList ** l)1013 static gboolean traverse_tree_keys(gpointer key, gpointer value UNUSED, GList **l)
1014 {
1015 *(GList **)l = g_list_prepend(*(GList **)l, key);
1016 return FALSE;
1017 }
1018
traverse_tree_values(gpointer key UNUSED,gpointer value,GList ** l)1019 static gboolean traverse_tree_values(gpointer key UNUSED, gpointer value, GList **l)
1020 {
1021 *(GList **)l = g_list_prepend(*(GList **)l, value);
1022 return FALSE;
1023 }
1024
g_tree_keys(GTree * tree)1025 GList * g_tree_keys(GTree *tree)
1026 {
1027 GList *l = NULL;
1028 g_tree_foreach(tree, (GTraverseFunc)traverse_tree_keys, &l);
1029 return g_list_reverse(l);
1030 }
g_tree_values(GTree * tree)1031 GList * g_tree_values(GTree *tree)
1032 {
1033 GList *l = NULL;
1034 g_tree_foreach(tree, (GTraverseFunc)traverse_tree_values, &l);
1035 return g_list_reverse(l);
1036 }
1037
1038
traverse_tree_merger(gpointer key,gpointer value UNUSED,tree_merger_t ** merger)1039 static gboolean traverse_tree_merger(gpointer key, gpointer value UNUSED, tree_merger_t **merger)
1040 {
1041 tree_merger_t *t = *(tree_merger_t **)merger;
1042 GTree *tree = t->tree;
1043 int condition = t->condition;
1044
1045 switch(condition) {
1046 case IST_SUBSEARCH_NOT:
1047 break;
1048
1049 default:
1050 case IST_SUBSEARCH_OR:
1051 case IST_SUBSEARCH_AND:
1052 if (! g_tree_lookup(tree,key))
1053 (*(tree_merger_t **)merger)->list = g_list_prepend((*(tree_merger_t **)merger)->list,key);
1054 break;
1055 }
1056
1057 return FALSE;
1058 }
1059
1060
traverse_tree_copy_MessageInfo(gpointer key,gpointer value,tree_copy_t ** copy)1061 static gboolean traverse_tree_copy_MessageInfo(gpointer key, gpointer value, tree_copy_t **copy)
1062 {
1063 int i;
1064 tree_copy_t *t = *(tree_copy_t **)copy;
1065 //GTree *source = t->treeSource;
1066 GTree *destination = t->treeDestination;
1067 uint64_t *uid;
1068 uid = g_new0(uint64_t,1);
1069 *uid = *(uint64_t *) key;
1070
1071 MessageInfo *src=(MessageInfo *) value;
1072 //MessageInfo * src = g_tree_lookup(source, key);
1073 MessageInfo *dst = g_new0(MessageInfo, 1);
1074 //TRACE(TRACE_INFO, "TR MI [%s]", key);
1075 dst->expunge= src->expunge;
1076 dst->expunged= src->expunged;
1077 for(i=0;i<IMAP_NFLAGS;i++){
1078 dst->flags[i]=src->flags[i];
1079 }
1080 dst->mailbox_id=src->mailbox_id;
1081 dst->msn= src->msn;
1082 dst->phys_id= src->phys_id;
1083 dst->rfcsize= src->rfcsize;
1084 dst->seq= src->seq;
1085 dst->status= src->status;
1086 dst->uid= src->uid;
1087
1088 strcpy(dst->internaldate,src->internaldate);
1089 //dst->keywords= g_list_copy(src->keywords);
1090 // copy keywords
1091 GList *tk = g_list_first(src->keywords);
1092 while (tk) {
1093 dst->keywords = g_list_append(dst->keywords, g_strdup((gchar *)tk->data));
1094 if (! g_list_next(tk)) break;
1095 tk = g_list_next(tk);
1096 }
1097 *uid = src->uid;
1098 //TRACE(TRACE_DEBUG,"TRAVERSE MessageInfo add %ld %ld=%d %ld=%d",*uid, source,g_tree_nnodes(source), destination,g_tree_nnodes(destination));
1099
1100
1101 g_tree_insert(destination, uid, dst);
1102 return FALSE;
1103 }
1104
traverse_tree_copy_String(gpointer key,gpointer value UNUSED,tree_copy_t ** copy)1105 static gboolean traverse_tree_copy_String(gpointer key, gpointer value UNUSED, tree_copy_t **copy)
1106 {
1107
1108 tree_copy_t *t = *(tree_copy_t **)copy;
1109 GTree *source = t->treeSource;
1110 GTree *destination = t->treeDestination;
1111 uint64_t *uid;
1112 uid = g_new0(uint64_t,1);
1113 *uid = *(uint64_t *) key;
1114 /* @todo get from value, not do search */
1115 char * src = g_tree_lookup(source, key);
1116 if (src == NULL)
1117 return TRUE;
1118
1119 /*char * dst=g_new0(char,strlen(src)+1);
1120 int i=0;
1121 for(i=0;i<strlen(src);i++){
1122 dst[i]=src[i];
1123 }
1124 g_strdup(src);
1125 dst[i]=0;
1126 g_tree_insert(destination, uid,dst);
1127 */
1128 g_tree_insert(destination, uid,g_strdup(src));
1129 return FALSE;
1130 }
1131
1132 /**
1133 * duplicate a GTree containing MessageInfo structures and keys as uint64_t
1134 * @param a
1135 * @param b
1136 * @return
1137 */
g_tree_copy_MessageInfo(GTree * a,GTree * b)1138 int g_tree_copy_MessageInfo(GTree *a, GTree *b){
1139 g_return_val_if_fail(a && b,1);
1140 tree_copy_t *copier = g_new0(tree_copy_t,1);
1141 copier->treeDestination=a;
1142 copier->treeSource=b;
1143 g_tree_foreach(b,(GTraverseFunc)traverse_tree_copy_MessageInfo, &copier);
1144 return 0;
1145 }
1146
1147 /**
1148 * duplicate a GRee containing char * as structures and keys as uint64_t
1149 * @param a
1150 * @param b
1151 * @return
1152 */
g_tree_copy_String(GTree * a,GTree * b)1153 int g_tree_copy_String(GTree *a, GTree *b){
1154 g_return_val_if_fail(a && b,1);
1155 tree_copy_t *copier = g_new0(tree_copy_t,1);
1156 copier->treeDestination=a;
1157 copier->treeSource=b;
1158 g_tree_foreach(b,(GTraverseFunc)traverse_tree_copy_String, &copier);
1159 return 0;
1160 }
1161 /*
1162 * boolean merge of two GTrees. The result is stored in GTree *a.
1163 * the state of GTree *b is undefined: it may or may not have been changed,
1164 * depending on whether or not key/value pairs were moved from b to a.
1165 * Both trees are safe to destroy afterwards, assuming g_tree_new_full was used
1166 * for their construction.
1167 */
g_tree_merge(GTree * a,GTree * b,int condition)1168 int g_tree_merge(GTree *a, GTree *b, int condition)
1169 {
1170 char *type = NULL;
1171 GList *keys = NULL;
1172 int alen = 0, blen=0, klen=0;
1173
1174 gpointer key;
1175 gpointer value;
1176
1177 g_return_val_if_fail(a && b,1);
1178
1179 tree_merger_t *merger = g_new0(tree_merger_t,1);
1180
1181 alen = g_tree_nnodes(a);
1182 blen = g_tree_nnodes(b);
1183
1184 switch(condition) {
1185 case IST_SUBSEARCH_AND:
1186
1187 type=g_strdup("AND");
1188
1189 if (! (g_tree_nnodes(a) > 0))
1190 break;
1191
1192 /* delete from A all keys not in B */
1193 merger->tree = b;
1194 merger->condition = IST_SUBSEARCH_AND;
1195 g_tree_foreach(a,(GTraverseFunc)traverse_tree_merger, &merger);
1196 keys = g_list_first(merger->list);
1197 if (! (klen = g_list_length(keys)))
1198 break;
1199 if (klen > 1)
1200 keys = g_list_reverse(merger->list);
1201
1202 while (keys->data) {
1203 g_tree_remove(a,keys->data);
1204 if (! g_list_next(keys))
1205 break;
1206 keys = g_list_next(keys);
1207 }
1208 break;
1209
1210 case IST_SUBSEARCH_OR:
1211 type=g_strdup("OR");
1212
1213 if (! (g_tree_nnodes(b) > 0))
1214 break;
1215
1216 merger->tree = a;
1217 merger->condition = IST_SUBSEARCH_OR;
1218 g_tree_foreach(b,(GTraverseFunc)traverse_tree_merger, &merger);
1219 keys = g_list_first(merger->list);
1220 if (! (klen = g_list_length(keys)))
1221 break;
1222 if (klen > 1)
1223 keys = g_list_reverse(keys);
1224
1225 /* add to A all keys in B */
1226 while (keys->data) {
1227 g_tree_lookup_extended(b,keys->data,&key,&value);
1228 g_tree_steal(b,keys->data);
1229 g_tree_insert(a,key,value);
1230
1231 if (! g_list_next(keys))
1232 break;
1233
1234 keys = g_list_next(keys);
1235 }
1236 break;
1237
1238 case IST_SUBSEARCH_NOT:
1239 type=g_strdup("NOT");
1240
1241 if (! (g_tree_nnodes(b) > 0))
1242 break;
1243
1244 keys = g_tree_keys(b);
1245
1246 while (keys->data) {
1247 // remove from A keys also in B
1248 if (g_tree_lookup(a,keys->data)) {
1249 g_tree_remove(a,keys->data);
1250 } else {
1251 // add to A all keys in B not in A
1252 g_tree_lookup_extended(b,keys->data,&key,&value);
1253 g_tree_steal(b,keys->data);
1254 g_tree_insert(a,key,value);
1255 }
1256
1257 if (! g_list_next(keys))
1258 break;
1259
1260 keys = g_list_next(keys);
1261 }
1262
1263 keys = g_list_first(keys);
1264 g_list_free(keys);
1265
1266 break;
1267 }
1268
1269 TRACE(TRACE_DEBUG,"(%p) (%p): a[%d] [%s] b[%d] -> a[%d]",
1270 a, b, alen, type, blen,
1271 g_tree_nnodes(a));
1272
1273 merger->list = g_list_first(merger->list);
1274 g_list_free(merger->list);
1275
1276
1277 g_free(merger);
1278 g_free(type);
1279
1280 return 0;
1281 }
1282
ucmp(const uint64_t * a,const uint64_t * b)1283 gint ucmp(const uint64_t *a, const uint64_t *b)
1284 {
1285 uint64_t x,y;
1286 x = (uint64_t)*a;
1287 y = (uint64_t)*b;
1288
1289 if (x>y)
1290 return 1;
1291 if (x==y)
1292 return 0;
1293 return -1;
1294 }
1295
ucmpdata(const uint64_t * a,const uint64_t * b,gpointer data UNUSED)1296 gint ucmpdata(const uint64_t *a, const uint64_t *b, gpointer data UNUSED)
1297 {
1298 return ucmp(a,b);
1299 }
1300
dm_strcmpdata(gconstpointer a,gconstpointer b,gpointer data UNUSED)1301 gint dm_strcmpdata(gconstpointer a, gconstpointer b, gpointer data UNUSED)
1302 {
1303 return strcmp((const char *)a, (const char *)b);
1304 }
1305
dm_strcasecmpdata(gconstpointer a,gconstpointer b,gpointer data UNUSED)1306 gint dm_strcasecmpdata(gconstpointer a, gconstpointer b, gpointer data UNUSED)
1307 {
1308 return g_ascii_strcasecmp((const char *)a, (const char *)b);
1309 }
1310
1311
1312 /* Read from instream until ".\r\n", discarding what is read. */
discard_client_input(ClientBase_T * ci)1313 int discard_client_input(ClientBase_T *ci)
1314 {
1315 int c = 0, n = 0;
1316
1317 while ((read(ci->rx, (void *)&c, 1)) == 1) {
1318 if (c == '\r') {
1319 if (n == 4) n = 5; /* \r\n.\r */
1320 else n = 1; /* \r */
1321 } else if (c == '\n') {
1322 if (n == 1) n = 2; /* \r\n */
1323 else if (n == 5) /* \r\n.\r\n DONE */
1324 break;
1325 else /* .\n ? */
1326 TRACE(TRACE_ERR, "bare LF.");
1327 } else if (c == '.' && n == 3) /* \r\n. */
1328 n = 4;
1329
1330 }
1331 return 0;
1332 }
1333
1334 /* Following the advice of:
1335 * "Secure Programming for Linux and Unix HOWTO"
1336 * Chapter 8: Carefully Call Out to Other Resources */
dm_shellesc(const char * command)1337 char * dm_shellesc(const char * command)
1338 {
1339 char *safe_command;
1340 int pos, end, len;
1341
1342 // These are the potentially unsafe characters:
1343 // & ; ` ' \ " | * ? ~ < > ^ ( ) [ ] { } $ \n \r
1344 // # ! \t \ (space)
1345
1346 len = strlen(command);
1347 if (! (safe_command = g_new0(char,(len + 1) * 2 + 1)))
1348 return NULL;
1349
1350 for (pos = end = 0; pos < len; pos++) {
1351 switch (command[pos]) {
1352 case '&':
1353 case ';':
1354 case '`':
1355 case '\'':
1356 case '\\':
1357 case '"':
1358 case '|':
1359 case '*':
1360 case '?':
1361 case '~':
1362 case '<':
1363 case '>':
1364 case '^':
1365 case '(':
1366 case ')':
1367 case '[':
1368 case ']':
1369 case '{':
1370 case '}':
1371 case '$':
1372 case '\n':
1373 case '\r':
1374 case '\t':
1375 case ' ':
1376 case '#':
1377 case '!':
1378 // Add an escape before the offending char.
1379 safe_command[end++] = '\\';
1380 // fall-through
1381 default:
1382 // And then put in the character itself.
1383 safe_command[end++] = command[pos];
1384 break;
1385 }
1386 }
1387
1388 /* The string is already initialized,
1389 * but let's be extra double sure. */
1390 safe_command[end] = '\0';
1391
1392 return safe_command;
1393 }
1394
1395 /* some basic imap type utils */
dbmail_imap_plist_collapse(const char * in)1396 char *dbmail_imap_plist_collapse(const char *in)
1397 {
1398 /*
1399 * collapse "(NIL) (NIL)" to "(NIL)(NIL)"
1400 *
1401 * do for bodystructure, and only for addresslists in the envelope
1402 */
1403 char *p;
1404 char **sublists;
1405
1406 g_return_val_if_fail(in,NULL);
1407
1408 sublists = g_strsplit(in,") (",0);
1409 p = g_strjoinv(")(",sublists);
1410 g_strfreev(sublists);
1411 return p;
1412 }
1413
1414 /*
1415 * build a parenthisized list (4.4) from a GList
1416 */
dbmail_imap_plist_as_string(GList * list)1417 char *dbmail_imap_plist_as_string(GList * list)
1418 {
1419 char *p;
1420 size_t l;
1421 GString * tmp1 = g_string_new("");
1422 GString * tmp2 = g_list_join(list, " ");
1423 g_string_printf(tmp1,"(%s)", tmp2->str);
1424
1425 /*
1426 * strip empty outer parenthesis
1427 * "((NIL NIL))" to "(NIL NIL)"
1428 */
1429 p = tmp1->str;
1430 l = tmp1->len;
1431 while (tmp1->len>4 && p[0]=='(' && p[l-1]==')' && p[1]=='(' && p[l-2]==')') {
1432 tmp1 = g_string_truncate(tmp1,l-1);
1433 tmp1 = g_string_erase(tmp1,0,1);
1434 p=tmp1->str;
1435 }
1436
1437 g_string_free(tmp1,FALSE);
1438 g_string_free(tmp2,TRUE);
1439 return p;
1440 }
1441
dbmail_imap_plist_free(GList * l)1442 void dbmail_imap_plist_free(GList *l)
1443 {
1444 g_list_destroy(l);
1445 }
1446
1447 /*
1448 * return a quoted or literal astring
1449 */
1450
dbmail_imap_astring_as_string(const char * s)1451 char *dbmail_imap_astring_as_string(const char *s)
1452 {
1453 int i;
1454 const char *p;
1455 char *r, *t, *l = NULL;
1456 char first, last, penult = '\\';
1457
1458 if (! s)
1459 return g_strdup("\"\"");
1460
1461 l = g_strdup(s);
1462 t = l;
1463 /* strip off dquote */
1464 first = s[0];
1465 last = s[strlen(s)-1];
1466 if (strlen(s) > 2)
1467 penult = s[strlen(s)-2];
1468 if ((first == '"') && (last == '"') && (penult != '\\')) {
1469 l[strlen(l)-1] = '\0';
1470 l++;
1471 }
1472
1473 for (i=0; l[i]; i++) {
1474 if ((l[i] & 0x80) || (l[i] == '\r') || (l[i] == '\n') || (l[i] == '"') || (l[i] == '\\')) {
1475 if ((l[i] == '"') && (i>0) && (l[i-1] != '\\'))
1476 p = s;
1477 else
1478 p = l;
1479 r = g_strdup_printf("{%" PRIu64 "}\r\n%s", (uint64_t) strlen(p), p);
1480 g_free(t);
1481 return r;
1482 }
1483
1484 }
1485 r = g_strdup_printf("\"%s\"", l);
1486 g_free(t);
1487
1488 return r;
1489
1490 }
1491 /* structure and envelope tools */
1492 static void _structure_part_handle_part(GMimeObject *part, gpointer data, gboolean extension);
1493 static void _structure_part_text(GMimeObject *part, gpointer data, gboolean extension);
1494 static void _structure_part_message(GMimeObject *part, gpointer data, gboolean extension);
1495 static void _structure_part_multipart(GMimeObject *part, gpointer data, gboolean extension);
1496
1497
imap_append_hash_as_string(GList * list,const char * type)1498 static GList * imap_append_hash_as_string(GList *list, const char *type)
1499 {
1500 size_t i = 0;
1501 char curr = 0;
1502 char name[512];
1503 char value[1024];
1504 char *head = (char *)type;
1505 GList *l = NULL;
1506
1507 if (! type){
1508 //in case of tye null, it should return NIL, same process is applied at the end of this function
1509 TRACE(TRACE_DEBUG, "hash_as_string: is null (missing): NIL");
1510 list = g_list_append_printf(list, "NIL");
1511 return list;
1512 }
1513
1514 TRACE(TRACE_DEBUG, "analyse [%s]", type);
1515 while (type[i]) {
1516 curr = type[i++];
1517 if (curr == ';') {
1518 break;
1519 }
1520 }
1521
1522 while (type[i]) {
1523 curr = type[i];
1524 if (ISLF(curr) || ISCR(curr) || isblank(curr)) {
1525 i++;
1526 continue;
1527 }
1528 break;
1529 }
1530
1531 head += i;
1532 //implementing a hard protection
1533 int maxSize=strlen(head);
1534 maxSize=strlen(head);
1535 if (maxSize>1536)
1536 maxSize=1536;//hard limit max len of name+len of value
1537 int offset = 0;
1538 int inname = 1;
1539 TRACE(TRACE_DEBUG, "analyse [%s]", head);
1540 while (head && maxSize>=0) {
1541 //hard protection, preventing going maxsize, until the \0
1542 maxSize--;
1543 curr = head[offset];
1544 if ((! curr) && (offset==0))
1545 break;
1546 if (curr == '=' && inname) {
1547 memset(name, 0, sizeof(name));
1548 if (offset>512)
1549 offset=512; //hard limit
1550 strncpy(name, head, offset);
1551 g_strstrip(name);
1552 head += offset+1;
1553 inname = 0;
1554 offset = 0;
1555 TRACE(TRACE_DEBUG, "name: %s", name);
1556 l = g_list_append_printf(l, "\"%s\"", name);
1557 continue;
1558 } else if ((! curr) || (curr == ';')) {
1559 size_t len;
1560 char *clean1, *clean2, *clean3;
1561 memset(value, 0, sizeof(value));
1562 if (offset>1024)
1563 offset=1024; //hard limit
1564 strncpy(value, head, offset);
1565 head += offset+1;
1566 inname = 1;
1567 offset = 0;
1568
1569 clean1 = value;
1570 if (clean1[0] == '"')
1571 clean1++;
1572
1573 len = strlen(clean1);
1574 if (clean1[len-1] == '"')
1575 clean1[len-1] = '\0';
1576
1577 clean2 = g_strcompress(clean1);
1578
1579 if (g_mime_utils_text_is_8bit((const unsigned char *)clean2, strlen(clean2))) {
1580 clean1 = g_mime_utils_header_encode_text(clean2);
1581 g_free(clean2);
1582 clean2 = clean1;
1583 }
1584 clean3 = g_strescape(clean2, NULL);
1585 g_free(clean2);
1586
1587 TRACE(TRACE_DEBUG, "value: %s", value);
1588 TRACE(TRACE_DEBUG, "clean: %s", clean3);
1589 l = g_list_append_printf(l, "\"%s\"", clean3);
1590
1591 g_free(clean3);
1592
1593 if (! curr)
1594 break;
1595 }
1596 offset ++;
1597 }
1598
1599 if (l) {
1600 char *s = dbmail_imap_plist_as_string(l);
1601 TRACE(TRACE_DEBUG, "hash_as_string: from %s => %s", type, s);
1602 list = g_list_append_printf(list, "%s", s);
1603 g_free(s);
1604
1605 g_list_destroy(l);
1606 } else {
1607 TRACE(TRACE_DEBUG, "hash_as_string: from %s => NIL",type);
1608 list = g_list_append_printf(list, "NIL");
1609 }
1610
1611
1612 return list;
1613 }
1614
imap_append_disposition_as_string(GList * list,GMimeObject * part)1615 static GList * imap_append_disposition_as_string(GList *list, GMimeObject *part)
1616 {
1617 GList *t = NULL;
1618 GMimeContentDisposition *disposition;
1619 char *result;
1620 const char *disp = g_mime_object_get_header(part, "Content-Disposition");
1621
1622 if(disp) {
1623 disposition = g_mime_content_disposition_new_from_string(disp);
1624 t = g_list_append_printf(t,"\"%s\"",
1625 g_mime_content_disposition_get_disposition(disposition));
1626
1627 /* paramlist */
1628 t = imap_append_hash_as_string(t, disp);
1629
1630 g_object_unref(disposition);
1631
1632 result = dbmail_imap_plist_as_string(t);
1633 list = g_list_append_printf(list,"%s",result);
1634 g_free(result);
1635
1636 g_list_destroy(t);
1637 } else {
1638 list = g_list_append_printf(list,"NIL");
1639 }
1640 return list;
1641 }
1642
1643 #define imap_append_header_as_string(list, part, header) \
1644 imap_append_header_as_string_default(list, part, header, "NIL")
1645
imap_append_header_as_string_default(GList * list,GMimeObject * part,const char * header,char * def)1646 static GList * imap_append_header_as_string_default(GList *list,
1647 GMimeObject *part, const char *header, char *def)
1648 {
1649 char *result;
1650 char *s;
1651 if((result = (char *)g_mime_object_get_header(part, header))) {
1652 s = dbmail_imap_astring_as_string(result);
1653 list = g_list_append_printf(list, "%s", s);
1654 g_free(s);
1655 } else {
1656 list = g_list_append_printf(list, def);
1657 }
1658 return list;
1659 }
1660
imap_part_get_sizes(GMimeObject * part,size_t * size,size_t * lines)1661 static void imap_part_get_sizes(GMimeObject *part, size_t *size, size_t *lines)
1662 {
1663 char *v = NULL, curr = 0, prev = 0;
1664 int i = 0;
1665 size_t s = 0, l = 1;
1666
1667 /* count body lines */
1668 v = g_mime_object_get_body(part);
1669 if (! v) return;
1670 s = strlen(v);
1671
1672 while (v[i]) {
1673 curr = v[i];
1674 if (ISLF(curr))
1675 l++;
1676 if (ISLF(curr) && (! ISCR(prev))) // rfcsize
1677 s++;
1678 prev = curr;
1679 i++;
1680 }
1681
1682 g_free(v);
1683
1684 *size = s;
1685 *lines = l;
1686 }
1687
1688
_structure_part_handle_part(GMimeObject * part,gpointer data,gboolean extension)1689 void _structure_part_handle_part(GMimeObject *part, gpointer data, gboolean extension)
1690 {
1691 GMimeContentType *type;
1692 GMimeObject *object;
1693
1694 // if (GMIME_IS_MESSAGE(part))
1695 // object = g_mime_message_get_mime_part(GMIME_MESSAGE(part));
1696 // else
1697 // object = part;
1698 object = part;
1699
1700 type = g_mime_object_get_content_type(object);
1701 if (! type) {
1702 TRACE(TRACE_DEBUG, "no type for object!");
1703 return;
1704 }
1705
1706 TRACE(TRACE_DEBUG,"parse [%s/%s]", type->type, type->subtype);
1707
1708 /* multipart composite */
1709 if (g_mime_content_type_is_type(type,"multipart","*"))
1710 _structure_part_multipart(object,data, extension);
1711 /* message included as mimepart */
1712 else if (g_mime_content_type_is_type(type,"message","*"))
1713 _structure_part_message(object,data, extension);
1714 /* simple message */
1715 else
1716 _structure_part_text(object,data, extension);
1717
1718
1719 }
1720
_structure_part_multipart(GMimeObject * part,gpointer data,gboolean extension)1721 void _structure_part_multipart(GMimeObject *part, gpointer data, gboolean extension)
1722 {
1723 GMimeMultipart *multipart;
1724 GMimeObject *subpart, *object;
1725 GList *list = NULL;
1726 GList *alist = NULL;
1727 GString *s;
1728 int i,j;
1729 GMimeContentType *type;
1730
1731 object = part;
1732
1733 type = g_mime_object_get_content_type(object);
1734 if (! type) {
1735 TRACE(TRACE_DEBUG, "no type information");
1736 return;
1737 }
1738 multipart = GMIME_MULTIPART(object);
1739 i = g_mime_multipart_get_count(multipart);
1740
1741 TRACE(TRACE_DEBUG,"parse [%d] parts for [%s/%s] with boundary [%s]",
1742 i, type->type, type->subtype,
1743 g_mime_multipart_get_boundary(multipart));
1744
1745 /* loop over parts for base info */
1746 for (j=0; j<i; j++) {
1747 subpart = g_mime_multipart_get_part(multipart,j);
1748 _structure_part_handle_part(subpart,&alist,extension);
1749 }
1750
1751 /* sub-type */
1752 alist = g_list_append_printf(alist,"\"%s\"", type->subtype);
1753
1754 /* extension data (only for multipart, in case of BODYSTRUCTURE command argument) */
1755 if (extension) {
1756 /* paramlist */
1757 list = imap_append_hash_as_string(list,
1758 g_mime_object_get_header(object, "Content-Type"));
1759
1760 /* disposition */
1761 list = imap_append_disposition_as_string(list, object);
1762 /* language */
1763 list = imap_append_header_as_string(list,object,"Content-Language");
1764 /* location */
1765 list = imap_append_header_as_string(list,object,"Content-Location");
1766 s = g_list_join(list," ");
1767
1768 alist = g_list_append(alist,s->str);
1769
1770 g_list_destroy(list);
1771 g_string_free(s,FALSE);
1772 }
1773
1774 /* done*/
1775 *(GList **)data = (gpointer)g_list_append(*(GList **)data,dbmail_imap_plist_as_string(alist));
1776
1777 g_list_destroy(alist);
1778
1779 }
1780
_structure_basic(GMimeObject * object)1781 static GList * _structure_basic(GMimeObject *object)
1782 {
1783 GList *list = NULL;
1784 char *result;
1785 const GMimeContentType *type;
1786
1787 type = g_mime_object_get_content_type(object);
1788 if (! type) {
1789 TRACE(TRACE_DEBUG, "no type information");
1790 return NULL;
1791 }
1792 TRACE(TRACE_DEBUG, "parse [%s/%s]", type->type, type->subtype);
1793
1794 /* type/subtype */
1795 list = g_list_append_printf(list,"\"%s\"", type->type);
1796 list = g_list_append_printf(list,"\"%s\"", type->subtype);
1797 /* paramlist */
1798 list = imap_append_hash_as_string(list,
1799 g_mime_object_get_header(object, "Content-Type"));
1800 /* body id */
1801 if ((result = (char *)g_mime_object_get_content_id(object)))
1802 list = g_list_append_printf(list,"\"%s\"", result);
1803 else
1804 list = g_list_append_printf(list,"NIL");
1805 /* body description */
1806 list = imap_append_header_as_string(list,object,"Content-Description");
1807 /* body encoding */
1808 list = imap_append_header_as_string_default(list,object,"Content-Transfer-Encoding", "\"7BIT\"");
1809
1810 return list;
1811
1812 }
_structure_part_message(GMimeObject * part,gpointer data,gboolean extension)1813 void _structure_part_message(GMimeObject *part, gpointer data, gboolean extension)
1814 {
1815 char *b;
1816 GList *list = NULL;
1817 size_t s = 0, l = 0;
1818 GMimeObject *object;
1819
1820 object = part;
1821
1822 list = _structure_basic(object);
1823
1824 /* body size */
1825 imap_part_get_sizes(object,&s,&l);
1826
1827 list = g_list_append_printf(list,"%d", s);
1828
1829 /* envelope structure */
1830 b = imap_get_envelope(g_mime_message_part_get_message(GMIME_MESSAGE_PART(part)));
1831 list = g_list_append_printf(list,"%s", b?b:"NIL");
1832 g_free(b);
1833
1834 /* body structure */
1835 b = imap_get_structure(g_mime_message_part_get_message(GMIME_MESSAGE_PART(part)), extension);
1836 list = g_list_append_printf(list,"%s", b?b:"NIL");
1837 g_free(b);
1838
1839 /* lines */
1840 list = g_list_append_printf(list,"%d", l);
1841
1842 /* extension data in case of BODYSTRUCTURE */
1843 if (extension) {
1844 /* body md5 */
1845 list = imap_append_header_as_string(list,object,"Content-MD5");
1846 /* body disposition */
1847 list = imap_append_disposition_as_string(list,object);
1848 /* body language */
1849 list = imap_append_header_as_string(list,object,"Content-Language");
1850 /* body location */
1851 list = imap_append_header_as_string(list,object,"Content-Location");
1852 }
1853
1854 /* done*/
1855 *(GList **)data = (gpointer)g_list_append(*(GList **)data,dbmail_imap_plist_as_string(list));
1856
1857 g_list_destroy(list);
1858
1859 }
1860
_structure_part_text(GMimeObject * part,gpointer data,gboolean extension)1861 void _structure_part_text(GMimeObject *part, gpointer data, gboolean extension)
1862 {
1863 GList *list = NULL;
1864 size_t s = 0, l = 0;
1865 GMimeObject *object;
1866 GMimeContentType *type;
1867
1868 object = part;
1869
1870 list = _structure_basic(object);
1871
1872 /* body size */
1873 imap_part_get_sizes(object,&s,&l);
1874
1875 list = g_list_append_printf(list,"%d", s);
1876
1877 type = g_mime_object_get_content_type(object);
1878
1879 /* body lines */
1880 if (g_mime_content_type_is_type(type,"text","*"))
1881 list = g_list_append_printf(list,"%d", l);
1882
1883 /* extension data in case of BODYSTRUCTURE */
1884 if (extension) {
1885 /* body md5 */
1886 list = imap_append_header_as_string(list,object,"Content-MD5");
1887 /* body disposition */
1888 list = imap_append_disposition_as_string(list,object);
1889 /* body language */
1890 list = imap_append_header_as_string(list,object,"Content-Language");
1891 /* body location */
1892 list = imap_append_header_as_string(list,object,"Content-Location");
1893 }
1894
1895 /* done*/
1896 *(GList **)data = (gpointer)g_list_append(*(GList **)data, dbmail_imap_plist_as_string(list));
1897
1898 g_list_destroy(list);
1899
1900 }
1901
1902
1903
dbmail_imap_append_alist_as_plist(GList * list,InternetAddressList * ialist)1904 GList* dbmail_imap_append_alist_as_plist(GList *list, InternetAddressList *ialist)
1905 {
1906 GList *t = NULL, *p = NULL;
1907 InternetAddress *ia = NULL;
1908 gchar *s = NULL, *st = NULL;
1909 gchar **tokens;
1910 gchar *mailbox;
1911 int i,j=0;
1912
1913 if (ialist==NULL)
1914 return g_list_append_printf(list, "NIL");
1915
1916 i = internet_address_list_length(ialist);
1917 for (j=0; j<i; j++) {
1918 ia = internet_address_list_get_address(ialist,j);
1919
1920 g_return_val_if_fail(ia!=NULL, list);
1921
1922 if (internet_address_group_get_members((InternetAddressGroup *)ia)) {
1923 TRACE(TRACE_DEBUG, "recursing into address group [%s].", internet_address_get_name(ia));
1924
1925 /* Address list beginning. */
1926 p = g_list_append_printf(p, "(NIL NIL \"%s\" NIL)", internet_address_get_name(ia));
1927
1928 /* Dive into the address list.
1929 * Careful, this builds up the stack; it's not a tail call.
1930 */
1931 t = dbmail_imap_append_alist_as_plist(t, internet_address_group_get_members((InternetAddressGroup *)ia));
1932
1933 s = dbmail_imap_plist_as_string(t);
1934 /* Only use the results if they're interesting --
1935 * (NIL) is the special case of nothing inside the group.
1936 */
1937 if (strcmp(s, "(NIL)") != 0) {
1938 /* Lop off the extra parens at each end.
1939 * Really do the pointer math carefully.
1940 */
1941 size_t slen = strlen(s);
1942 if (slen) slen--;
1943 s[slen] = '\0';
1944 p = g_list_append_printf(p, "%s", (slen ? s+1 : s));
1945 }
1946 g_free(s);
1947
1948 g_list_destroy(t);
1949 t = NULL;
1950
1951 /* Address list ending. */
1952 p = g_list_append_printf(p, "(NIL NIL NIL NIL)");
1953
1954 }
1955
1956 if (internet_address_mailbox_get_addr((InternetAddressMailbox *)ia)) {
1957 const char *name = internet_address_get_name(ia);
1958 const char *addr = internet_address_mailbox_get_addr((InternetAddressMailbox *)ia);
1959 TRACE(TRACE_DEBUG, "handling a standard address [%s] [%s].", name, addr);
1960
1961 /* personal name */
1962 if (name) {
1963 char * encname = g_mime_utils_header_encode_phrase(name);
1964 g_strdelimit(encname,"\"\\",' ');
1965 g_strstrip(encname);
1966 s = dbmail_imap_astring_as_string(encname);
1967 t = g_list_append_printf(t, "%s", s);
1968 g_free(encname);
1969 g_free(s);
1970 } else {
1971 t = g_list_append_printf(t, "NIL");
1972 }
1973
1974 /* source route */
1975 t = g_list_append_printf(t, "NIL");
1976
1977 /* mailbox name and host name */
1978 if ((mailbox = addr ? (char *)addr : NULL) != NULL) {
1979 /* defensive mode for 'To: "foo@bar.org"' addresses */
1980 g_strstrip(g_strdelimit(mailbox,"\"",' '));
1981
1982 tokens = g_strsplit(mailbox,"@",2);
1983
1984 /* mailbox name */
1985 if (tokens[0])
1986 t = g_list_append_printf(t, "\"%s\"", tokens[0]);
1987 else
1988 t = g_list_append_printf(t, "NIL");
1989 /* host name */
1990 /* Note that if tokens[0] was null, we must
1991 * short-circuit because tokens[1] is out of bounds! */
1992 if (tokens[0] && tokens[1])
1993 t = g_list_append_printf(t, "\"%s\"", tokens[1]);
1994 else
1995 t = g_list_append_printf(t, "NIL");
1996
1997 g_strfreev(tokens);
1998 } else {
1999 t = g_list_append_printf(t, "NIL NIL");
2000 }
2001
2002 s = dbmail_imap_plist_as_string(t);
2003 p = g_list_append_printf(p, "%s", s);
2004 g_free(s);
2005
2006 g_list_destroy(t);
2007 t = NULL;
2008 }
2009
2010 /* Bottom of the while loop.
2011 * Advance the address list.
2012 */
2013 }
2014
2015 /* Tack it onto the outer list. */
2016 if (p) {
2017 s = dbmail_imap_plist_as_string(p);
2018 st = dbmail_imap_plist_collapse(s);
2019 list = g_list_append_printf(list, "(%s)", st);
2020 g_free(s);
2021 g_free(st);
2022
2023 g_list_destroy(p);
2024 } else {
2025 list = g_list_append_printf(list, "NIL");
2026 }
2027
2028 return list;
2029 }
2030
2031 /* structure access point */
imap_get_structure(GMimeMessage * message,gboolean extension)2032 char * imap_get_structure(GMimeMessage *message, gboolean extension)
2033 {
2034 GList *structure = NULL;
2035 GMimeContentType *type;
2036 GMimeObject *part;
2037 char *s, *t;
2038
2039 if (! message)
2040 return NULL;
2041
2042 if (! GMIME_IS_MESSAGE(message))
2043 return NULL;
2044
2045 part = g_mime_message_get_mime_part(message);
2046 type = (GMimeContentType *)g_mime_object_get_content_type(part);
2047 if (! type) {
2048 TRACE(TRACE_DEBUG,"error getting content_type");
2049 return NULL;
2050 }
2051
2052 TRACE(TRACE_DEBUG,"message type: [%s/%s]", type->type, type->subtype);
2053
2054 /* multipart composite */
2055 if (g_mime_content_type_is_type(type,"multipart","*"))
2056 _structure_part_multipart(part,(gpointer)&structure, extension);
2057 /* message included as mimepart */
2058 else if (g_mime_content_type_is_type(type,"message","*"))
2059 _structure_part_message(part,(gpointer)&structure, extension);
2060 /* as simple message */
2061 else
2062 _structure_part_text(part,(gpointer)&structure, extension);
2063
2064 s = dbmail_imap_plist_as_string(structure);
2065 t = dbmail_imap_plist_collapse(s);
2066 g_free(s);
2067
2068 g_list_destroy(structure);
2069
2070 return t;
2071 }
2072
envelope_address_part(GList * list,GMimeMessage * message,const char * header)2073 static GList * envelope_address_part(GList *list, GMimeMessage *message, const char *header)
2074 {
2075 const char *result;
2076 char *t;
2077 InternetAddressList *alist;
2078 char *result_enc;
2079 const char *charset;
2080
2081 charset = message_get_charset(message);
2082
2083 result = g_mime_object_get_header(GMIME_OBJECT(message),header);
2084
2085 if (result) {
2086 result_enc = dbmail_iconv_str_to_utf8(result, charset);
2087 t = imap_cleanup_address(result_enc);
2088 g_free(result_enc);
2089 alist = internet_address_list_parse_string(t);
2090 g_free(t);
2091 list = dbmail_imap_append_alist_as_plist(list, (InternetAddressList *)alist);
2092 g_object_unref(alist);
2093 alist = NULL;
2094 } else {
2095 list = g_list_append_printf(list,"NIL");
2096 }
2097
2098 return list;
2099 }
2100
2101
get_msg_charset_frompart(GMimeObject UNUSED * parent,GMimeObject * part,gpointer data)2102 static void get_msg_charset_frompart(GMimeObject UNUSED *parent, GMimeObject *part, gpointer data)
2103 {
2104 const char *charset=NULL;
2105 if (*((char **)data)==NULL && (charset=g_mime_object_get_content_type_parameter(part,"charset"))) {
2106 *((char **)data)=(char *)charset;
2107 }
2108 return;
2109 }
2110
message_get_charset(GMimeMessage * message)2111 const char * message_get_charset(GMimeMessage *message)
2112 {
2113 GMimeObject *mime_part=NULL;
2114 const char *mess_charset=NULL;
2115
2116 if (message)
2117 mime_part=g_mime_message_get_mime_part(message);
2118
2119 if (mime_part) {
2120 const char * charset = NULL;
2121 if ((charset=g_mime_object_get_content_type_parameter(mime_part,"charset")))
2122 mess_charset = charset;
2123 }
2124
2125 if (mess_charset == NULL)
2126 g_mime_message_foreach(message,get_msg_charset_frompart,&mess_charset);
2127
2128 return mess_charset;
2129 }
2130
2131 /* envelope access point */
imap_get_envelope(GMimeMessage * message)2132 char * imap_get_envelope(GMimeMessage *message)
2133 {
2134 GMimeObject *part;
2135 GList *list = NULL;
2136 char *result;
2137 char *s = NULL, *t = NULL;
2138 const char *h;
2139
2140 if (! message)
2141 return NULL;
2142
2143 if (! GMIME_IS_MESSAGE(message))
2144 return NULL;
2145
2146 part = GMIME_OBJECT(message);
2147 /* date */
2148 result = g_mime_message_get_date_as_string(message);
2149 if (result) {
2150 t = dbmail_imap_astring_as_string(result);
2151 list = g_list_append_printf(list,"%s", t);
2152 g_free(result);
2153 g_free(t);
2154 result = NULL;
2155 } else {
2156 list = g_list_append_printf(list,"NIL");
2157 }
2158
2159 /* subject */
2160 result = (char *)g_mime_object_get_header(GMIME_OBJECT(message),"Subject");
2161
2162 if (result) {
2163 const char *charset = message_get_charset(message);
2164 char * subj = dbmail_iconv_str_to_utf8(result, charset);
2165 TRACE(TRACE_DEBUG, "[%s] [%s] -> [%s]", charset, result, subj);
2166 if (g_mime_utils_text_is_8bit((unsigned char *)subj, strlen(subj))) {
2167 s = g_mime_utils_header_encode_text((const char *)subj);
2168 TRACE(TRACE_DEBUG, "[%s] -> [%s]", subj, s);
2169 g_free(subj);
2170 subj = s;
2171 }
2172 t = dbmail_imap_astring_as_string(subj);
2173 TRACE(TRACE_DEBUG, "[%s] -> [%s]", subj, t);
2174 g_free(subj);
2175 list = g_list_append_printf(list,"%s", t);
2176 g_free(t);
2177 } else {
2178 list = g_list_append_printf(list,"NIL");
2179 }
2180
2181 /* from */
2182 list = envelope_address_part(list, message, "From");
2183 /* sender */
2184 h = g_mime_object_get_header(GMIME_OBJECT(message),"Sender");
2185 if (h && (strlen(h) > 0))
2186 list = envelope_address_part(list, message, "Sender");
2187 else
2188 list = envelope_address_part(list, message, "From");
2189
2190 /* reply-to */
2191 h = g_mime_object_get_header(GMIME_OBJECT(message),"Reply-to");
2192 if (h && (strlen(h) > 0))
2193 list = envelope_address_part(list, message, "Reply-to");
2194 else
2195 list = envelope_address_part(list, message, "From");
2196
2197 /* to */
2198 list = envelope_address_part(list, message, "To");
2199 /* cc */
2200 list = envelope_address_part(list, message, "Cc");
2201 /* bcc */
2202 list = envelope_address_part(list, message, "Bcc");
2203
2204 /* in-reply-to */
2205 list = imap_append_header_as_string(list,part,"In-Reply-to");
2206 /* message-id */
2207 result = (char *)g_mime_message_get_message_id(message);
2208 if (result && (! g_strrstr(result,"=")) && (! g_strrstr(result,"@(none)"))) {
2209 t = g_strdup_printf("<%s>", result);
2210 s = dbmail_imap_astring_as_string(t);
2211 list = g_list_append_printf(list,"%s", s);
2212 g_free(s);
2213 g_free(t);
2214 } else {
2215 list = g_list_append_printf(list,"NIL");
2216 }
2217
2218 s = dbmail_imap_plist_as_string(list);
2219
2220 g_list_destroy(list);
2221
2222 return s;
2223 }
2224
2225
imap_get_logical_part(const GMimeObject * object,const char * specifier)2226 char * imap_get_logical_part(const GMimeObject *object, const char * specifier)
2227 {
2228 GMimeContentType *type;
2229 gchar *s = NULL, *t=NULL;
2230 gboolean rfc822 = false;
2231
2232 assert(object);
2233
2234 type = (GMimeContentType *)g_mime_object_get_content_type(
2235 (GMimeObject *)object);
2236
2237 rfc822 = g_mime_content_type_is_type(type,"message","rfc822");
2238
2239 if (specifier == NULL || MATCH(specifier, "HEADER") || MATCH(specifier, "TEXT")) {
2240 if (rfc822)
2241 object = (GMimeObject *)g_mime_message_part_get_message(
2242 (GMimeMessagePart *)object);
2243 if (! object)
2244 return g_strdup("");
2245 }
2246
2247 if (MATCH(specifier,"HEADER") || MATCH(specifier,"MIME")) {
2248 t = g_mime_object_get_headers(GMIME_OBJECT(object));
2249 s = get_crlf_encoded(t);
2250 g_free(t);
2251 s = g_realloc(s, strlen(s) + 3);
2252 strcat(s, "\r\n");
2253 } else {
2254 if (rfc822)
2255 t = g_mime_object_to_string(GMIME_OBJECT(object));
2256 else
2257 t = g_mime_object_get_body(GMIME_OBJECT(object));
2258 s = get_crlf_encoded(t);
2259 g_free(t);
2260 }
2261
2262 return s;
2263 }
2264
2265
2266
imap_get_partspec(const GMimeObject * message,const char * partspec)2267 GMimeObject * imap_get_partspec(const GMimeObject *message, const char *partspec)
2268 {
2269 GMimeObject *object;
2270 GMimeContentType *type;
2271 char *part;
2272 guint index, maxindex;
2273 guint i;
2274
2275 assert(message);
2276 assert(partspec);
2277
2278 object = (GMimeObject *)message;
2279 GString *t = g_string_new(partspec);
2280 GList *specs = g_string_split(t,".");
2281 g_string_free(t,TRUE);
2282
2283 maxindex = g_list_length(specs);
2284
2285 for (i=0; i< maxindex; i++) {
2286 part = g_list_nth_data(specs,i);
2287 if (! (index = strtol((const char *)part, NULL, 0)))
2288 break;
2289 if (! object)
2290 break;
2291
2292 if (GMIME_IS_MESSAGE(object))
2293 object = g_mime_message_get_mime_part((GMimeMessage *)object);
2294
2295 type = (GMimeContentType *)g_mime_object_get_content_type(object);
2296
2297 if (g_mime_content_type_is_type(type,"multipart","*")) {
2298 object = g_mime_multipart_get_part(
2299 (GMimeMultipart *)object, (int)index-1);
2300 type = (GMimeContentType *)g_mime_object_get_content_type(
2301 object);
2302 }
2303
2304 if (g_mime_content_type_is_type(type, "message", "rfc822")) {
2305 if (i+1 < maxindex) {
2306 object = (GMimeObject *)g_mime_message_part_get_message(
2307 (GMimeMessagePart *)object);
2308 }
2309 }
2310
2311 }
2312
2313 g_list_destroy(specs);
2314
2315 return object;
2316 }
2317
2318 /* Ugly hacks because sometimes GMime is too strict. */
imap_cleanup_address(const char * a)2319 char * imap_cleanup_address(const char *a)
2320 {
2321 char *r, *t;
2322 char *inptr;
2323 char prev, next=0;
2324 unsigned incode=0, inquote=0;
2325 size_t i, l;
2326 GString *s;
2327
2328 if (!a || !a[0])
2329 return g_strdup("");
2330
2331 s = g_string_new("");
2332 t = g_strdup(a);
2333
2334 // un-fold and collapse tabs and spaces
2335 g_strdelimit(t,"\n",' ');
2336 dm_pack_spaces(t);
2337 inptr = t;
2338 inptr = g_strstrip(inptr);
2339 prev = inptr[0];
2340
2341 l = strlen(inptr);
2342
2343 TRACE(TRACE_DEBUG, "[%s]", inptr);
2344 for (i = 0; i < l - 1; i++) {
2345
2346 next = inptr[i+1];
2347
2348 if (incode && (inptr[i] == '"' || inptr[i] == ' '))
2349 continue; // skip illegal chars inquote
2350
2351 if ((! inquote) && inptr[i]=='"')
2352 inquote = 1;
2353 else if (inquote && inptr[i] == '"')
2354 inquote = 0;
2355
2356 // quote encoded string
2357 if (inptr[i] == '=' && next == '?' && (! incode)) {
2358 incode=1;
2359 if (prev != '"' && (! inquote)) {
2360 g_string_append_c(s,'"');
2361 inquote = 1;
2362 }
2363 }
2364
2365 g_string_append_c(s,inptr[i]);
2366
2367 if (inquote && incode && prev == '?' && inptr[i] == '=' && (next == '"' || next == ' ' || next == '<')) {
2368 if ((next != '"' ) && ((i < l-2) && (inptr[i+2] != '='))) {
2369 g_string_append_c(s, '"');
2370 inquote = 0;
2371 }
2372 if (next == '<')
2373 g_string_append_c(s,' ');
2374 incode=0;
2375 }
2376
2377 prev = inptr[i];
2378 }
2379
2380 inptr+=i;
2381
2382 if (*inptr)
2383 g_string_append(s,inptr);
2384
2385 if (incode && inquote)
2386 g_string_append_c(s,'"');
2387
2388 g_free(t);
2389
2390 if (g_str_has_suffix(s->str,";"))
2391 s = g_string_truncate(s,s->len-1);
2392
2393 /* This second hack changes semicolons into commas when not preceded by a colon.
2394 * The purpose is to fix broken syntax like this: "one@dom; two@dom"
2395 * But to allow correct syntax like this: "Group: one@dom, two@dom;"
2396 */
2397 int colon = 0;
2398
2399 for (i = 0; i < s->len; i++) {
2400 switch (s->str[i]) {
2401 case ':':
2402 colon = 1;
2403 break;
2404 case ';':
2405 s->str[i] = ',';
2406 break;
2407 }
2408 if (colon)
2409 break;
2410 }
2411
2412 r = s->str;
2413 g_string_free(s,FALSE);
2414 TRACE(TRACE_DEBUG,"[%s]", r);
2415 return r;
2416 }
2417
dm_strtoull(const char * nptr,char ** endptr,int base)2418 uint64_t dm_strtoull(const char *nptr, char **endptr, int base)
2419 {
2420 errno = 0;
2421 long long int r = strtoll(nptr, endptr, base);
2422 if (errno)
2423 return (long long unsigned)0;
2424
2425 if (r < 0) {
2426 errno = EINVAL;
2427 return (long long unsigned)0;
2428 }
2429 return (long long unsigned)r;
2430 }
2431
2432 /* A frontend to the base64_decode_internal() that deals with embedded strings. */
base64_decodev(char * str)2433 char **base64_decodev(char *str)
2434 {
2435 size_t i, j, n;
2436 int numstrings = 0;
2437 size_t decodelen = 0;
2438 char *decoded;
2439 char **ret = NULL;
2440
2441 /* Base64 always decodes to a shorter string. */
2442 decoded = (char *)g_base64_decode((const gchar *)str, &decodelen);
2443
2444 /* Count up the number of embedded strings... */
2445 for (i = 0; i <= decodelen; i++) {
2446 if (decoded[i] == '\0') {
2447 numstrings++;
2448 }
2449 }
2450
2451 /* Allocate an array large enough
2452 * for the strings and a final NULL. */
2453 ret = g_new0(char *, (numstrings + 1));
2454
2455 /* Copy each nul terminated string to the array. */
2456 for (i = j = n = 0; i <= decodelen; i++) {
2457 if (decoded[i] == '\0') {
2458 ret[n] = g_strdup(decoded + j);
2459 j = i + 1;
2460 n++;
2461 }
2462 }
2463
2464 /* Put the final NULL on the end of the array. */
2465 ret[n] = NULL;
2466
2467 g_free(decoded);
2468
2469 return ret;
2470 }
2471
dm_get_hash_for_string(const char * buf,char * digest)2472 int dm_get_hash_for_string(const char *buf, char *digest)
2473 {
2474 Field_T hash_algorithm;
2475 static hashid type;
2476 static int initialized=0;
2477 int result=0;
2478
2479 if (! initialized) {
2480 if (config_get_value("hash_algorithm", "DBMAIL", hash_algorithm) < 0)
2481 g_strlcpy(hash_algorithm, "sha1", FIELDSIZE-1);
2482
2483 if (SMATCH(hash_algorithm,"md5"))
2484 type=MHASH_MD5;
2485 else if (SMATCH(hash_algorithm,"sha1"))
2486 type=MHASH_SHA1;
2487 else if (SMATCH(hash_algorithm,"sha256"))
2488 type=MHASH_SHA256;
2489 else if (SMATCH(hash_algorithm,"sha512"))
2490 type=MHASH_SHA512;
2491 else if (SMATCH(hash_algorithm,"whirlpool"))
2492 type=MHASH_WHIRLPOOL;
2493 else if (SMATCH(hash_algorithm,"tiger"))
2494 type=MHASH_TIGER;
2495 else {
2496 TRACE(TRACE_INFO,"hash algorithm not supported. Using SHA1.");
2497 type=MHASH_SHA1;
2498 }
2499 initialized=1;
2500 }
2501
2502 switch(type) {
2503 case MHASH_MD5:
2504 result=dm_md5(buf,digest);
2505 break;
2506 case MHASH_SHA1:
2507 result=dm_sha1(buf,digest);
2508 break;
2509 case MHASH_SHA256:
2510 result=dm_sha256(buf,digest);
2511 break;
2512 case MHASH_SHA512:
2513 result=dm_sha512(buf,digest);
2514 break;
2515 case MHASH_WHIRLPOOL:
2516 result=dm_whirlpool(buf,digest);
2517 break;
2518 case MHASH_TIGER:
2519 result=dm_tiger(buf,digest);
2520 break;
2521 default:
2522 result=1;
2523 TRACE(TRACE_EMERG,"unhandled hash algorithm");
2524 break;
2525 }
2526
2527 return result;
2528 }
2529
get_crlf_encoded_opt(const char * in,int dots)2530 gchar *get_crlf_encoded_opt(const char *in, int dots)
2531 {
2532 char prev = 0, curr = 0, *t, *out;
2533 const char *p = in;
2534 int i=0, nl = 0;
2535 assert(in);
2536
2537 while (p[i]) {
2538 curr = p[i];
2539 if ISLF(curr) nl++;
2540 prev = curr;
2541 i++;
2542 }
2543
2544 out = g_new0(char,i+(2*nl)+1);
2545 t = out;
2546 p = in;
2547 i = 0;
2548 while (p[i]) {
2549 curr = p[i];
2550 if (ISLF(curr) && (! ISCR(prev)))
2551 *t++ = '\r';
2552 if (dots && ISDOT(curr) && ISLF(prev))
2553 *t++ = '.';
2554 *t++=curr;
2555 prev = curr;
2556 i++;
2557 }
2558 return out;
2559 }
2560
2561
strip_crlf(char * buffer)2562 void strip_crlf(char *buffer)
2563 {
2564 if (! (buffer && buffer[0])) return;
2565 size_t l = strlen(buffer);
2566 while (--l > 0) {
2567 if (buffer[l] == '\r' || buffer[l] == '\n')
2568 buffer[l] = '\0';
2569 else
2570 break;
2571 }
2572 }
2573
2574 // work around glib allocation bug
dm_base64_decode(const gchar * s,uint64_t * len)2575 char * dm_base64_decode(const gchar *s, uint64_t *len)
2576 {
2577 char *r = NULL, *p = (char *)g_base64_decode((const gchar *)s, (gsize *)len);
2578 r = g_strndup(p, (gsize)*len);
2579 g_free(p);
2580 TRACE(TRACE_DEBUG,"[%" PRIu64 ":%s]->[%s]", *len, s, r);
2581 return r;
2582 }
2583
2584
stridx(const char * s,char c)2585 uint64_t stridx(const char *s, char c)
2586 {
2587 uint64_t i;
2588 for (i = 0; s[i] && s[i] != c; i++);
2589 return i;
2590 }
2591
uint64_free(void * data)2592 void uint64_free(void *data)
2593 {
2594 mempool_push(small_pool, data, sizeof(uint64_t));
2595 }
2596
2597 /*
2598 * calculate the difference between two timeval values
2599 * as number of seconds, using default rounding
2600 */
diff_time(struct timeval before,struct timeval after)2601 int diff_time(struct timeval before, struct timeval after)
2602 {
2603 int tbefore = before.tv_sec * 1000000 + before.tv_usec;
2604 int tafter = after.tv_sec * 1000000 + after.tv_usec;
2605 int tdiff = tafter - tbefore;
2606 return (int)rint((double)tdiff/1000000);
2607 }
2608
2609
2610