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