1 /*
2 ** Copyright 1998 - 2009 Double Precision, Inc.  See COPYING for
3 ** distribution information.
4 */
5 
6 
7 /*
8 ** $Id: folder.c,v 1.188 2009/11/18 03:38:50 mrsam Exp $
9 */
10 #include	"config.h"
11 #include	<stdio.h>
12 #include	<string.h>
13 #include	<ctype.h>
14 #include	<fcntl.h>
15 #include	<stdlib.h>
16 #include	"sqwebmail.h"
17 #include	"maildir/maildirmisc.h"
18 #include	"maildir/maildircreate.h"
19 #include	"maildir/maildirnewshared.h"
20 #include	"maildir/maildirinfo.h"
21 #include	"maildir/maildiraclt.h"
22 
23 #include	"rfc822/rfc822.h"
24 #include	"rfc822/rfc2047.h"
25 #include	"rfc2045/rfc2045.h"
26 #include	"rfc2045/rfc2646.h"
27 #include	"rfc2646html.h"
28 #include	"md5/md5.h"
29 #include	"gpglib/gpglib.h"
30 #include	"maildir.h"
31 #include	"mailfilter.h"
32 #include	"maildir/maildirquota.h"
33 #include	"maildir/maildirgetquota.h"
34 #include	"maildir/maildirinfo.h"
35 #include	"numlib/numlib.h"
36 #include	"courierauth.h"
37 #include	"folder.h"
38 #include	"addressbook.h"
39 #include	"cgi/cgi.h"
40 #include	"pref.h"
41 #include	"token.h"
42 #include	"filter.h"
43 #include	"buf.h"
44 #include	"pref.h"
45 #include	"newmsg.h"
46 #include	"htmllibdir.h"
47 #include	"gpg.h"
48 #include	"acl.h"
49 #include	"auth.h"
50 
51 #include	"msg2html.h"
52 
53 #if	HAVE_LOCALE_H
54 #if	HAVE_SETLOCALE
55 #include	<locale.h>
56 #endif
57 #endif
58 
59 #if	TIME_WITH_SYS_TIME
60 #include	<sys/time.h>
61 #include	<time.h>
62 #else
63 #if	HAVE_SYS_TIME_H
64 #include	<sys/time.h>
65 #else
66 #include	<time.h>
67 #endif
68 #endif
69 #if	HAVE_SYS_STAT_H
70 #include	<sys/stat.h>
71 #endif
72 #include	<errno.h>
73 
74 #include	"unicode/unicode.h"
75 
76 #include	<unistd.h>
77 #if HAVE_WCHAR_H
78 #include	<wchar.h>
79 #endif
80 
81 #include	"strftime.h"
82 
83 extern FILE *open_langform(const char *lang, const char *formname,
84 			   int print_header);
85 
86 extern const char *sqwebmail_content_language;
87 extern char sqwebmail_folder_rights[];
88 extern const char *sqwebmail_mailboxid;
89 extern char *get_imageurl();
90 extern const char *sqwebmail_content_locale;
91 extern void print_attrencodedlen(const char *, size_t, int, FILE *);
92 
93 extern const char *showsize(unsigned long);
94 extern void maildir_cleanup();
95 extern const char *nonloginscriptptr();
96 extern int pref_flagpagesize;
97 extern int ishttps();
98 extern const char *sqwebmail_content_charset;
99 extern int verify_shared_index_file;
100 
101 extern time_t rfc822_parsedt(const char *);
102 static time_t	current_time;
103 
104 static const char *folder_err_msg=0;
105 
106 extern const char *sqwebmail_folder;
107 
108 extern void output_scriptptrget();
109 extern void output_scriptptr();
110 extern void output_scriptptrpostinfo();
111 extern void output_attrencoded(const char *);
112 extern void output_urlencoded(const char *);
113 extern char *scriptptrget();
114 
print_safe_len(const char * p,size_t n,void (* func)(const char *,size_t))115 void print_safe_len(const char *p, size_t n, void (*func)(const char *, size_t))
116 {
117 char	buf[10];
118 const	char *q=p;
119 
120 	while (n)
121 	{
122 		--n;
123 		if (*p == '<')	strcpy(buf, "&lt;");
124 		else if (*p == '>') strcpy(buf, "&gt;");
125 		else if (*p == '&') strcpy(buf, "&amp;");
126 		else if (*p == ' ') strcpy(buf, "&nbsp;");
127 		else if (*p == '\n') strcpy(buf, "<br />");
128 		else if (ISCTRL(*p))
129 			sprintf(buf, "&#%d;", (int)(unsigned char)*p);
130 		else
131 		{
132 			p++;
133 			continue;
134 		}
135 
136 		(*func)(q, p-q);
137 		(*func)(buf, strlen(buf));
138 		p++;
139 		q=p;
140 	}
141 	(*func)(q, p-q);
142 }
143 
print_safe_to_stdout(const char * p,size_t cnt)144 static void print_safe_to_stdout(const char *p, size_t cnt)
145 {
146 	if (cnt == 0)
147 		return;
148 
149 	if (fwrite(p, cnt, 1, stdout) != 1)
150 		exit(1);
151 }
152 
print_safe(const char * p)153 void print_safe(const char *p)
154 {
155 	print_safe_len(p, strlen(p), print_safe_to_stdout);
156 }
157 
call_print_safe_to_stdout(const char * p,size_t cnt)158 void call_print_safe_to_stdout(const char *p, size_t cnt)
159 {
160 	print_safe_len(p, cnt, print_safe_to_stdout);
161 }
162 
folder_contents_title()163 void folder_contents_title()
164 {
165 const char *lab;
166 const char *f;
167 const char *inbox_lab, *drafts_lab, *trash_lab, *sent_lab;
168 
169 	lab=getarg("FOLDERTITLE");
170 	inbox_lab=getarg("INBOX");
171 	drafts_lab=getarg("DRAFTS");
172 	trash_lab=getarg("TRASH");
173 	sent_lab=getarg("SENT");
174 
175 	f=sqwebmail_folder;
176 	if (strcmp(f, INBOX) == 0)	f=inbox_lab;
177 	else if (strcmp(f, INBOX "." DRAFTS) == 0)	f=drafts_lab;
178 	else if (strcmp(f, INBOX "." SENT) == 0)	f=sent_lab;
179 	else if (strcmp(f, INBOX "." TRASH) == 0)	f=trash_lab;
180 
181 	if (lab)
182 	{
183 		char *ff, *origff;
184 
185 		printf("%s", lab);
186 
187 		origff=ff=folder_fromutf7(f);
188 
189 		if (strcmp(ff, NEWSHAREDSP) == 0 ||
190 		    strncmp(ff, NEWSHAREDSP ".", sizeof(NEWSHAREDSP)) == 0)
191 		{
192 			printf("%s", getarg("PUBLICFOLDERS"));
193 			ff=strchr(ff, '.');
194 			if (!ff)
195 				ff="";
196 		}
197 		output_attrencoded(ff);
198 		free(origff);
199 	}
200 }
201 
group_movedel(const char * folder,int (* func)(const char *,const char *,size_t))202 static int group_movedel(const char *folder,
203 			int (*func)(const char *, const char *, size_t))
204 {
205 struct cgi_arglist *arg;
206 
207 	if (*cgi("SELECTALL"))	/* Everything is selected */
208 	{
209 		for (arg=cgi_arglist; arg; arg=arg->next)
210 		{
211 		const	char *f;
212 
213 			if (strncmp(arg->argname, "MOVEFILE-", 9)) continue;
214 			f=cgi(arg->argname);
215 			CHECKFILENAME(f);
216 			if ((*func)(folder, f, atol(arg->argname+9)))
217 				return (-1);
218 		}
219 		return (0);
220 	}
221 
222 	for (arg=cgi_arglist; arg; arg=arg->next)
223 	{
224 	unsigned long l;
225 	char	movedel[MAXLONGSIZE+10];
226 	const	char *f;
227 
228 		if (strncmp(arg->argname, "MOVE-", 5))	continue;
229 		l=atol(arg->argname+5);
230 		sprintf(movedel, "MOVEFILE-%lu", l);
231 		f=cgi(movedel);
232 		CHECKFILENAME(f);
233 		if ((*func)(folder, f, l))
234 			return (-1);
235 	}
236 	return (0);
237 }
238 
groupdel(const char * folder,const char * file,size_t pos)239 static int groupdel(const char *folder, const char *file, size_t pos)
240 {
241 	maildir_msgdeletefile(folder, file, pos);
242 	return (0);
243 }
244 
groupmove(const char * folder,const char * file,size_t pos)245 static int groupmove(const char *folder, const char *file, size_t pos)
246 {
247 	return (maildir_msgmovefile(folder, file, cgi("moveto"), pos));
248 }
249 
folder_delmsgs(const char * dir,size_t pos)250 void folder_delmsgs(const char *dir, size_t pos)
251 {
252 	int	rc=0;
253 	char	buf[2];
254 	char	*cur;
255 
256 	strcpy(buf, ACL_DELETEMSGS);
257 	acl_computeRightsOnFolder(dir, buf);
258 
259 	if (buf[0] == 0)
260 	{
261 		http_redirect_argu("&error=nodel&form=folder&pos=%s",
262 				   (unsigned long)pos);
263 		return;
264 	}
265 
266 	if (*cgi("cmddel"))
267 	{
268 		rc=group_movedel( dir, &groupdel );
269 		maildir_savefoldermsgs(dir);
270 	}
271 	else if (*cgi("cmdpurgeall"))
272 	{
273 	    char *deldir;
274 	    struct maildir_info minfo;
275 
276 	    if (maildir_info_imap_find(&minfo, dir, login_returnaddr())<0)
277 	    {
278 		http_redirect_argu("&error=othererror&form=folder&pos=%s",
279 			(unsigned long)pos);
280 		return ;
281 	    }
282 
283 	    if ((deldir=maildir_name2dir(minfo.homedir, minfo.maildir)) == NULL)
284 	    {
285 		maildir_info_destroy(&minfo);
286 		http_redirect_argu("&error=othererror&form=folder&pos=%s",
287 			(unsigned long)pos);
288 		return;
289 	    }
290 
291 	    cur = malloc(strlen(deldir)+5);
292 	    strcpy(cur, deldir);
293 	    strcat(cur, "/cur");
294 
295 	    rc=maildir_del_content(cur);
296 	    maildir_quota_recalculate(".");
297 
298 	    maildir_info_destroy(&minfo);
299 	    free(deldir);
300 	    free(cur);
301 
302 	}
303 	else if (*cgi("cmdmove"))
304 	{
305 		const char *p=cgi("moveto");
306 
307 		CHECKFILENAME(p);
308 		strcpy(buf, ACL_INSERT);
309 		acl_computeRightsOnFolder(p, buf);
310 		if (buf[0] == 0)
311 		{
312 			http_redirect_argu("&error=noinsert&form=folder&pos=%s",
313 					   (unsigned long)pos);
314 			return;
315 		}
316 
317 		rc=group_movedel( dir, &groupmove );
318 		maildir_savefoldermsgs(dir);
319 	}
320 
321 	maildir_cleanup();
322 
323 	http_redirect_argu(
324 		(rc ? "&error=quota&form=folder&pos=%s":"&form=folder&pos=%s"),
325 			(unsigned long)pos);
326 }
327 
328 
savepath(const char * path,const char * maildir)329 static void savepath(const char *path, const char *maildir)
330 {
331 	char buf[BUFSIZ];
332 	FILE *ofp;
333 	FILE *fp;
334 	struct maildir_tmpcreate_info createInfo;
335 
336 	maildir_tmpcreate_init(&createInfo);
337 
338 	createInfo.maildir=".";
339 	createInfo.uniq="sharedpath";
340 	createInfo.doordie=1;
341 
342 	ofp=maildir_tmpcreate_fp(&createInfo);
343 
344 	fp=fopen(SHAREDPATHCACHE, "r");
345 
346 	if (fp)
347 	{
348 		int cnt=0;
349 
350 		while (cnt < 1)
351 		{
352 			char *p;
353 
354 			if (fgets(buf, sizeof(buf), fp) == NULL)
355 				break;
356 
357 			if ((p=strchr(buf, '\n')) != NULL) *p=0;
358 
359 			if (strcmp(buf, maildir) == 0)
360 			{
361 				if (fgets(buf, sizeof(buf), fp) == NULL)
362 					break;
363 				continue;
364 			}
365 
366 			fprintf(ofp, "%s\n", buf);
367 
368 			if (fgets(buf, sizeof(buf), fp) == NULL)
369 				strcpy(buf, "");
370 			if ((p=strchr(buf, '\n')) != NULL) *p=0;
371 			fprintf(ofp, "%s\n", buf);
372 
373 			++cnt;
374 		}
375 		fclose(fp);
376 	}
377 
378 	fprintf(ofp, "%s\n%s\n", maildir, path);
379 	fclose(ofp);
380 	rename(createInfo.tmpname, SHAREDPATHCACHE);
381 	maildir_tmpcreate_free(&createInfo);
382 }
383 
384 static void folder_msg_link(const char *, size_t, char);
385 static void folder_msg_unlink(const char *, size_t, char);
386 
387 static char *truncate_at(const char *, const struct unicode_info *, size_t);
388 
389 
folder_contents(const char * dir,size_t pos)390 void folder_contents(const char *dir, size_t pos)
391 {
392 MSGINFO	**contents;
393 int	i, found;
394 int	morebefore, moreafter;
395 const char	*nomsg, *selectalllab;
396 const char	*unselectalllab, *golab;
397 const char	*qerrmsg;
398 long highend;
399 const struct unicode_info *uiptr=unicode_find(sqwebmail_content_charset);
400 
401 	if (!uiptr)
402 		uiptr=&unicode_ISO8859_1;
403 
404 	qerrmsg=getarg("PERMERR");
405 
406 	if (strcmp(cgi("error"), "quota") == 0)
407 		printf("%s", qerrmsg);
408 
409 	if (strcmp(cgi("error"), "nodel") == 0)
410 		printf("%s", getarg("NODELPERM"));
411 
412 	if (strcmp(cgi("error"), "noinsert") == 0)
413 		printf("%s", getarg("NOINSERTPERM"));
414 
415 	if (strcmp(cgi("error"), "othererror") == 0)
416 		printf("%s", getarg("OTHERERROR"));
417 
418 	if (strchr(sqwebmail_folder_rights, ACL_READ[0]) == NULL)
419 	{
420 		printf("%s", getarg("ACL"));
421 		return;
422 	}
423 
424 	maildir_reload(dir);
425 	contents=maildir_read(dir, pref_flagpagesize, &pos,
426 		&morebefore, &moreafter);
427 
428 	time(&current_time);
429 	nomsg=getarg("NOMESSAGES");
430 	golab=getarg("GO");
431 	selectalllab=getarg("SELECTALL");
432 	unselectalllab=getarg("UNSELECTALL");
433 
434 	if (maildir_countof(dir) <= pos + pref_flagpagesize - 1)
435 		highend = (long)(maildir_countof(dir) - 1);
436 	else
437 		highend = (long)(pos + pref_flagpagesize - 1);
438 	if (highend < 0) highend = 0;
439 
440 	if (!qerrmsg)	qerrmsg="";
441 
442 	folder_navigate(dir, pos, highend, morebefore, moreafter);
443 
444 	printf("<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"4\"><tr class=\"folder-index-header\"><th align=\"center\">%s</th><th>&nbsp;</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n",
445 		getarg("NUM"),
446 		getarg("DATE"),
447 		(strncmp(dir, INBOX "." SENT, sizeof(INBOX)+sizeof(SENT)-1) &&
448 		 strncmp(dir, INBOX "." DRAFTS, sizeof(INBOX)+sizeof(DRAFTS)-1))
449 			? getarg("FROM") : getarg("TO"),
450 		getarg("SUBJECT"),
451 		getarg("SIZE"));
452 
453 	found=0;
454 	for (i=0; i<pref_flagpagesize; i++)
455 	{
456 	const char *date, *from, *subj, *size;
457 	char	*froms, *subjs;
458 	const char *p, *q;
459 	size_t l;
460 	char type[8];
461 	char *folder_index_entry_start, *folder_index_entry_end;
462 
463 		if (contents[i] == 0)	continue;
464 		found=1;
465 
466 		date=MSGINFO_DATE(contents[i]);
467 		from=MSGINFO_FROM(contents[i]);
468 		subj=MSGINFO_SUBJECT(contents[i]);
469 		size=MSGINFO_SIZE(contents[i]);
470 
471 		type[0]=maildirfile_type(MSGINFO_FILENAME(contents[i]));
472 		type[1]='\0';
473 		if (type[0] == '\0')	strcpy(type, "&nbsp;");
474 
475 		folder_index_entry_start="<span class=\"read-message\">";
476 		folder_index_entry_end="</span>";
477 
478 		if (type[0] == MSGTYPE_NEW)
479 		{
480 			folder_index_entry_start="<strong class=\"unread-message\">";
481 			folder_index_entry_end="</strong>";
482 		}
483 
484 		p=MSGINFO_FILENAME(contents[i]);
485 
486 		if ((q=strrchr(p, '/')) != 0)
487 			p=q+1;
488 
489 		printf("<tr class=\"folder-index-bg-%d\" id=\"row%d\"><td align=\"right\" class=\"message-number\">%s%ld.%s</td><td class=\"message-status\"><input type=\"checkbox\" name=\"MOVE-%ld\" id=\"MOVE-%ld",
490 		       (i & 1)+1,
491 		       i,
492 		       folder_index_entry_start,
493 		       (long)(i+pos+1),
494 		       folder_index_entry_end,
495 		       (long) (pos+i),
496 		       (long) (pos+i));
497 		printf("\" onchange=\"setsel('MOVE-%ld', 'row%d', 'folder-index-bg-%d');\"%s /><input type=\"hidden\" name=\"MOVEFILE-%ld\" value=\"",
498 		       (long)(pos+i), i, (i & 1)+1,
499 			(type[0] == MSGTYPE_DELETED ? " disabled=\"disabled\"":""),
500 			(long)(pos+i));
501 		output_attrencoded(p);
502 		printf("\" />&nbsp;%s%s%s</td><td class=\"message-date\">%s",
503 		       folder_index_entry_start,
504 		       type,
505 		       folder_index_entry_end,
506 
507 			folder_index_entry_start
508 			);
509 		if (!*date)	date=" ";
510 		folder_msg_link(dir, pos+i, type[0]);
511 		print_safe(date);
512 		folder_msg_unlink(dir, pos+i, type[0]);
513 		printf("%s</td><td class=\"message-from\">%s", folder_index_entry_end,
514 				folder_index_entry_start);
515 		if (!*from)	from=" ";
516 		folder_msg_link(dir, pos+i, type[0]);
517 
518 
519 		froms=truncate_at(from, uiptr, 30);
520 
521 		if (froms == 0)	enomem();
522 
523 		print_safe(froms);
524 		free(froms);
525 		folder_msg_unlink(dir, pos+i, type[0]);
526 		printf("%s<br /></td><td class=\"message-subject\">%s", folder_index_entry_end,
527 				folder_index_entry_start);
528 
529 		folder_msg_link(dir, pos+i, type[0]);
530 
531 
532 		subjs=truncate_at(subj, uiptr, 40);
533 
534 		if (subjs == 0)	enomem();
535 
536 		print_safe(subjs);
537 		l=strlen(subjs);
538 		while (l++ < 8)
539 			printf("&nbsp;");
540 		free(subjs);
541 
542 		folder_msg_unlink(dir, pos+i, type[0]);
543 		printf("%s</td><td align=\"right\" class=\"message-size\">%s%s&nbsp;%s<br /></td></tr>\n", folder_index_entry_end, folder_index_entry_start, size, folder_index_entry_end);
544 	}
545 
546 	if (found)
547 	{
548 		puts("<tr class=\"folder-index-bg-1\"><td colspan=\"6\"><hr /></td></tr>");
549 		puts("<tr class=\"folder-index-bg-2\"><td>&nbsp;</td>");
550 		puts("<td colspan=\"5\">");
551 
552 		puts("<script type=\"text/javascript\">");
553 		puts("/* <![CDATA[ */");
554 		puts("function setAll(input, chk) {");
555 		printf("for (i = %ld; i <= %ld; i++) {\n",
556 			(long)pos, highend);
557 		puts("if (document.getElementById) e = document.getElementById('MOVE-' + i);");
558 		puts("else if (document.all) e = document['MOVE-' + i];");
559 		puts("if (e != null) { e.checked = chk; e.onchange(); }} }");
560 		puts("/* ]]> */");
561 		puts("</script>\n");
562 
563 		puts("<script type=\"text/javascript\">");
564 		puts("/* <![CDATA[ */");
565 		printf("document.write('<button type=\"button\" onclick=\"setAll(this, true); return false;\">%s<\\/button>\\n&nbsp;');\n",
566 			selectalllab);
567 		printf("document.write('<button type=\"button\" onclick=\"setAll(this, false); return false;\">%s<\\/button>\\n');\n",
568 			unselectalllab);
569 		puts("/* ]]> */");
570 		puts("</script>\n");
571 
572 		printf("<noscript><label><input type=\"checkbox\" name=\"SELECTALL\" />&nbsp;%s</label></noscript>\n",
573 			selectalllab);
574 		puts("</td></tr>");
575 
576 		printf("</table>\n");
577 		folder_navigate(dir, pos, highend, morebefore, moreafter);
578 	}
579 	if (!found && nomsg)
580 	{
581 		puts("<tr class=\"folder-index-bg-1\"><td colspan=\"6\" align=\"left\"><p>");
582 		puts(nomsg);
583 		puts("<br /></p></td></tr>");
584 		printf("</table>\n");
585 	}
586 
587 	maildir_free(contents, pref_flagpagesize);
588 }
589 
folder_navigate(const char * dir,size_t pos,long highend,int morebefore,int moreafter)590 void folder_navigate(const char *dir, size_t pos, long highend, int morebefore, int moreafter)
591 {
592 const char	*firstlab, *lastlab;
593 const char	*beforelab, *afterlab;
594 const char	*showncountlab, *jumplab, *golab;
595 
596 	time(&current_time);
597 	showncountlab=getarg("SHOWNCOUNT");
598 	jumplab=getarg("JUMPTO");
599 	golab=getarg("GO");
600 	firstlab=getarg("FIRSTPAGE");
601 	beforelab=getarg("PREVPAGE");
602 	afterlab=getarg("NEXTPAGE");
603 	lastlab=getarg("LASTPAGE");
604 
605 	printf("<table width=\"100%%\" class=\"folder-nextprev-background\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\">");
606 	printf("<tr><td align=\"left\">");
607 	printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"folder-nextprev-buttons\"><tr><td>");
608 	if (morebefore)
609 	{
610 	size_t	beforepos;
611 
612 		if (pos < pref_flagpagesize)	beforepos=0;
613 		else	beforepos=pos-pref_flagpagesize;
614 
615 		printf("<a href=\"");
616 		output_scriptptrget();
617 		printf("&amp;form=folder&amp;pos=0\" style=\"text-decoration: none\">");
618 	}
619 	printf("%s", firstlab);
620 	if (morebefore)
621 		printf("</a>");
622 
623 	puts("&nbsp;");
624 
625 	if (morebefore)
626 	{
627 	size_t	beforepos;
628 
629 		if (pos < pref_flagpagesize)	beforepos=0;
630 		else	beforepos=pos-pref_flagpagesize;
631 
632 		printf("<a href=\"");
633 		output_scriptptrget();
634 		printf("&amp;form=folder&amp;pos=%ld\" style=\"text-decoration: none\">",
635 			(long)beforepos);
636 	}
637 	printf("%s", beforelab);
638 	if (morebefore)
639 		printf("</a>");
640 
641 	printf("</td></tr></table>\n");
642 
643 	if (maildir_countof(dir) > 0) {
644 		puts("</td><td align=\"center\">\n");
645 		puts("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"folder-message-count\"><tr><td>\n");
646 		printf(showncountlab, (long)(pos+1), (long)(highend+1), (long)maildir_countof(dir));
647 		puts("</td></tr></table>");
648 
649 		puts("<script type=\"text/javascript\">");
650 		puts("/* <![CDATA[ */");
651 		puts("document.write('<\\/td><td align=\"center\">' +");
652 
653 		puts("'<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"folder-jumpto-field\"><tr><td>' +");
654 		printf("'%s <input type=\"text\" name=\"jumpto\" size=\"3\" value=\"%ld\" onchange=\"this.form.pos.value = this.value - 1;\" />' +\n",
655 			jumplab, (long)(pos+1));
656 		printf("'<button type=\"button\" onclick=\"this.form.submit();\">%s<\\/button>' +\n",
657 			golab);
658 		puts("'<\\/td><\\/tr><\\/table>');");
659 		puts("/* ]]> */");
660 		puts("</script>");
661 	}
662 
663 	printf("</td><td align=\"right\">\n");
664 
665 	printf("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"folder-nextprev-buttons\"><tr><td>");
666 	if (moreafter)
667 	{
668 		printf("<a href=\"");
669 		output_scriptptrget();
670 		printf("&amp;form=folder&amp;pos=%ld\" style=\"text-decoration: none\">",
671 			(long)(pos+pref_flagpagesize));
672 	}
673 	printf("%s", afterlab);
674 	if (moreafter)
675 		printf("</a>");
676 
677 	puts("&nbsp;");
678 
679 	if (moreafter)
680 	{
681 		printf("<a href=\"");
682 		output_scriptptrget();
683 		printf("&amp;form=folder&amp;pos=%ld\" style=\"text-decoration: none\">",
684 			(long)(maildir_countof(dir)-pref_flagpagesize));
685 	}
686 	printf("%s", lastlab);
687 	if (moreafter)
688 		printf("</a>");
689 
690 	printf("</td></tr></table>\n");
691 
692 	printf("</td></tr></table>");
693 }
694 
folder_msg_link(const char * dir,size_t pos,char t)695 static void folder_msg_link(const char *dir, size_t pos, char t)
696 {
697 #if 0
698 	if (t == MSGTYPE_DELETED)
699 	{
700 		printf("<a href=\"");
701 		output_scriptptrget();
702 		printf("&amp;form=folder&amp;pos=%s\">", cgi("pos"));
703 		return;
704 	}
705 #endif
706 
707 	printf("<a href=\"");
708 	if (strcmp(dir, INBOX "." DRAFTS))
709 	{
710 		output_scriptptrget();
711 		printf("&amp;form=readmsg&amp;pos=%ld\">", (long)pos);
712 	}
713 	else
714 	{
715 	size_t	mpos=pos;
716 	char	*filename=maildir_posfind(dir, &mpos);
717 	char	*basename=filename ? maildir_basename(filename):NULL;
718 
719 		output_scriptptrget();
720 		printf("&amp;form=open-draft&amp;draft=");
721 		output_urlencoded(basename);
722 		printf("\">");
723 		if (basename)	free(basename);
724 		if (filename)	free(filename);
725 	}
726 }
727 
folder_msg_unlink(const char * dir,size_t pos,char t)728 static void folder_msg_unlink(const char *dir, size_t pos, char t)
729 {
730 	printf("</a>");
731 }
732 
733 size_t	msg_pos, msg_count;
734 static char	*msg_posfile;
735 static int	msg_hasprev, msg_hasnext;
736 static const char	*msg_nextlab=0, *msg_prevlab=0, *msg_deletelab=0,
737 		*msg_purgelab=0, *msg_folderlab=0;
738 static char	msg_type;
739 
740 static const char	*msg_replylab=0;
741 static const char	*msg_replyalllab=0;
742 static const char	*msg_replylistlab=0;
743 static const char	*msg_forwardlab=0;
744 static const char	*msg_forwardattlab=0;
745 static const char	*msg_fullheaderlab=0;
746 static const char	*msg_movetolab=0;
747 static const char	*msg_print=0;
748 
749 static const char	*folder_inbox=0;
750 static const char	*folder_drafts=0;
751 static const char	*folder_trash=0;
752 static const char	*folder_sent=0;
753 static const char	*msg_golab=0;
754 
755 static const char *msg_msglab;
756 static const char *msg_add=0;
757 
758 static int	initnextprevcnt;
759 
folder_initnextprev(const char * dir,size_t pos)760 void folder_initnextprev(const char *dir, size_t pos)
761 {
762 	MSGINFO	**info;
763 	const	char *p;
764 	const	char *msg_numlab, *msg_numnewlab;
765 	static char *filename=0;
766 	int fd;
767 
768 	cgi_put(MIMEGPGFILENAME, "");
769 
770 	if (filename)
771 		free(filename);
772 	filename=0;
773 
774 
775 	if (*cgi("mimegpg") && (filename=maildir_posfind(dir, &pos)) != 0)
776 	{
777 		char *tptr;
778 		int nfd;
779 
780 		fd=maildir_semisafeopen(filename, O_RDONLY, 0);
781 
782 		if (fd >= 0)
783 		{
784 			struct maildir_tmpcreate_info createInfo;
785 
786 			maildir_purgemimegpg();
787 
788 			maildir_tmpcreate_init(&createInfo);
789 
790 			createInfo.uniq=":mimegpg:";
791 			createInfo.doordie=1;
792 
793 			if ((nfd=maildir_tmpcreate_fd(&createInfo)) < 0)
794 			{
795 				free(filename);
796 				error("Can't create new file.");
797 			}
798 
799 			tptr=createInfo.tmpname;
800 			createInfo.tmpname=NULL;
801 			maildir_tmpcreate_free(&createInfo);
802 
803 			chmod(tptr, 0600);
804 
805 			/*
806 			** Decrypt/check message into a temporary file
807 			** that's immediately marked as deleted, so that it
808 			** gets purged at the next sweep.
809 			*/
810 
811 			if (gpgdecode(fd, nfd) < 0)
812 			{
813 				close(nfd);
814 				unlink(tptr);
815 				free(tptr);
816 			}
817 			else
818 			{
819 				close(fd);
820 				free(filename);
821 				filename=tptr;
822 				fd=nfd;
823 
824 				cgi_put(MIMEGPGFILENAME,
825 					strrchr(filename, '/')+1);
826 			}
827 			close(fd);
828 		}
829 	}
830 
831 	initnextprevcnt=0;
832 	msg_nextlab=getarg("NEXTLAB");
833 	msg_prevlab=getarg("PREVLAB");
834 	msg_deletelab=getarg("DELETELAB");
835 	msg_purgelab=getarg("PURGELAB");
836 
837 	msg_folderlab=getarg("FOLDERLAB");
838 
839 	msg_replylab=getarg("REPLY");
840 	msg_replyalllab=getarg("REPLYALL");
841 	msg_replylistlab=getarg("REPLYLIST");
842 
843 	msg_forwardlab=getarg("FORWARD");
844 	msg_forwardattlab=getarg("FORWARDATT");
845 
846 	msg_numlab=getarg("MSGNUM");
847 	msg_numnewlab=getarg("MSGNEWNUM");
848 
849 	msg_fullheaderlab=getarg("FULLHDRS");
850 
851 	msg_movetolab=getarg("MOVETO");
852 	msg_print=getarg("PRINT");
853 
854 	folder_inbox=getarg("INBOX");
855 	folder_drafts=getarg("DRAFTS");
856 	folder_trash=getarg("TRASH");
857 	folder_sent=getarg("SENT");
858 
859 	p=getarg("CREATEFAIL");
860 	if (strcmp(cgi("error"),"quota") == 0)
861 		printf("%s", p);
862 
863 	msg_golab=getarg("GOLAB");
864 	msg_add=getarg("QUICKADD");
865 
866 	info=maildir_read(dir, 1, &pos, &msg_hasprev, &msg_hasnext);
867 	msg_pos=pos;
868 
869 	p=strrchr(MSGINFO_FILENAME(info[0]), '/');
870 	if (p)	p++;
871 	else	p=MSGINFO_FILENAME(info[0]);
872 	msg_posfile=strdup(p);
873 	if (!msg_posfile)	enomem();
874 
875 	if ((msg_type=maildirfile_type(MSGINFO_FILENAME(info[0])))
876 		== MSGTYPE_NEW) msg_numlab=msg_numnewlab;
877 
878 	msg_msglab=msg_numlab;
879 	msg_count=maildir_countof(dir);
880 	maildir_free(info, 1);
881 }
882 
get_msgfilename(const char * folder,size_t * pos)883 char *get_msgfilename(const char *folder, size_t *pos)
884 {
885 	char *filename;
886 
887 	if (*cgi(MIMEGPGFILENAME))
888 	{
889 		const char *p=cgi(MIMEGPGFILENAME);
890 
891 		CHECKFILENAME(p);
892 
893 		filename=malloc(sizeof("tmp/")+strlen(p));
894 		if (!filename)
895 			enomem();
896 		strcat(strcpy(filename, "tmp/"), p);
897 	}
898 	else
899 		filename=maildir_posfind(folder, pos);
900 
901 	if (!filename)	error("Message not found.");
902 
903 	return (filename);
904 }
905 
output_mimegpgfilename()906 void output_mimegpgfilename()
907 {
908 	if (*cgi(MIMEGPGFILENAME))
909 	{
910 		printf("&amp;" MIMEGPGFILENAME "=");
911 		output_urlencoded(cgi(MIMEGPGFILENAME));
912 	}
913 }
914 
folder_nextprev()915 void folder_nextprev()
916 {
917 	printf("<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"message-menu-background\"><tr valign=\"middle\">");
918 
919     printf("<td align=\"left\"><table border=\"0\" cellspacing=\"4\" cellpadding=\"4\"><tr valign=\"top\">");
920 
921 	/* PREV */
922 
923 	printf("<td class=\"message-menu-button\">");
924 
925 	if (msg_hasprev)
926 	{
927 		printf("<a href=\"");
928 		output_scriptptrget();
929 		printf("&amp;form=readmsg&amp;pos=%ld\">",
930 			(long)(msg_pos-1));
931 	}
932 
933 	printf("%s", msg_prevlab ? msg_prevlab:"");
934 
935 	if (msg_hasprev)
936 	{
937 		printf("</a>");
938 	}
939 	printf("</td>");
940 
941 	/* NEXT */
942 
943 	printf("<td class=\"message-menu-button\">");
944 
945 	if (msg_hasnext)
946 	{
947 		printf("<a href=\"");
948 		output_scriptptrget();
949 		printf("&amp;form=readmsg&amp;pos=%ld\">",
950 			(long)(msg_pos+1));
951 	}
952 
953 	printf("%s", msg_nextlab ? msg_nextlab:"");
954 
955 	if (msg_hasnext)
956 	{
957 		printf("</a>");
958 	}
959 	printf("</td>");
960 
961 	/* DEL */
962 
963 	printf("<td class=\"message-menu-button\">");
964 	if (msg_type != MSGTYPE_DELETED)
965 	{
966 		printf("<a href=\"");
967 		output_scriptptrget();
968 		tokennewget();
969 		printf("&amp;posfile=");
970 		output_urlencoded(msg_posfile);
971 		printf("&amp;form=delmsg&amp;pos=%ld\">",
972 			(long)msg_pos);
973 	}
974 	printf("%s", strcmp(sqwebmail_folder, INBOX "." TRASH) == 0
975 		? msg_purgelab : msg_deletelab);
976 
977 	if (msg_type != MSGTYPE_DELETED)
978 		printf("</a>");
979 
980 	printf("</td>\n");
981 
982 	/* FOLDER */
983 
984 	printf("<td class=\"message-menu-button\"><a href=\"");
985 	output_scriptptrget();
986 	printf("&amp;pos=%ld&amp;form=folder\">%s</a></td>\n",
987 		(long)( (msg_pos/pref_flagpagesize)*pref_flagpagesize ),
988 		msg_folderlab);
989 
990 	/* REPLY */
991 
992 	printf("<td class=\"message-menu-button\"><a href=\"");
993 	output_scriptptrget();
994 	output_mimegpgfilename();
995 	printf("&amp;pos=%ld&amp;reply=1&amp;form=newmsg\">%s</a></td>\n",
996 		(long)msg_pos,
997 		msg_replylab);
998 
999 	/* REPLY ALL */
1000 
1001 	printf("<td class=\"message-menu-button\"><a href=\"");
1002 	output_scriptptrget();
1003 	output_mimegpgfilename();
1004 	printf("&amp;pos=%ld&amp;replyall=1&amp;form=newmsg\">%s</a></td>\n",
1005 		(long)msg_pos,
1006 		msg_replyalllab);
1007 
1008 	/* REPLY LIST */
1009 
1010 	printf("<td class=\"message-menu-button\"><a href=\"");
1011 	output_scriptptrget();
1012 	output_mimegpgfilename();
1013 	printf("&amp;pos=%ld&amp;replylist=1&amp;form=newmsg\">%s</a></td>\n",
1014 		(long)msg_pos,
1015 		msg_replylistlab);
1016 
1017 	if (auth_getoptionenvint("wbnoimages"))
1018 		printf("<td width=\"100%%\"></td></tr></table><table border=\"0\" cellspacing=\"4\" cellpadding=\"4\"><tr>");
1019 
1020 	/* FORWARD */
1021 
1022 	printf("<td class=\"message-menu-button\"><a href=\"");
1023 	output_scriptptrget();
1024 	output_mimegpgfilename();
1025 	printf("&amp;pos=%ld&amp;forward=1&amp;form=newmsg\">%s</a></td>\n",
1026 		(long)msg_pos,
1027 		msg_forwardlab);
1028 
1029 	/* FORWARD AS ATTACHMENT*/
1030 
1031 	printf("<td class=\"message-menu-button\"><a href=\"");
1032 	output_scriptptrget();
1033 	output_mimegpgfilename();
1034 	printf("&amp;pos=%ld&amp;forwardatt=1&amp;form=newmsg\">%s</a></td>\n",
1035 		(long)msg_pos,
1036 		msg_forwardattlab);
1037 
1038 	/* FULL HEADERS */
1039 
1040 	if (!pref_flagfullheaders && !*cgi("fullheaders"))
1041 	{
1042 		printf("<td class=\"message-menu-button\"><a href=\"");
1043 		output_scriptptrget();
1044 		output_mimegpgfilename();
1045 		printf("&amp;pos=%ld&amp;form=readmsg&amp;fullheaders=1\">%s</a></td>\n",
1046 			(long)msg_pos, msg_fullheaderlab);
1047 	}
1048 
1049 	/* PRINT MESSAGE */
1050 
1051 	printf("<td class=\"message-menu-button\"><a href=\"");
1052 	output_scriptptrget();
1053 	output_mimegpgfilename();
1054 
1055 	printf("&amp;pos=%ld&amp;form=print&amp;setcookie=1%s\" target=\"_blank\">%s</a></td>\n",
1056 		(long)msg_pos,
1057 		((pref_flagfullheaders || *cgi("fullheaders")) ? "&amp;fullheaders=1" : ""),
1058 		msg_print);
1059 
1060 	/* SAVE MESSAGE */
1061 
1062 	printf("<td class=\"message-menu-button\"><a href=\"");
1063 	output_scriptptrget();
1064 	output_mimegpgfilename();
1065 
1066 	printf("&amp;pos=%ld&amp;form=fetch&amp;download=1\">%s</a></td>", (long)msg_pos,
1067 	       getarg("SAVEMESSAGE"));
1068 
1069     printf("<td width=\"100%%\"></td></tr></table></td><td align=\"right\" valign=\"middle\">");
1070 
1071 	printf("<table border=\"0\" cellspacing=\"4\"><tr><td class=\"message-x-of-y\">&nbsp;");
1072 	printf(msg_msglab, (int)msg_pos+1, (int)msg_count);
1073 	printf("&nbsp;</td></tr></table>");
1074     printf("</td></tr></table>\n");
1075 }
1076 
list_folder(const char * p)1077 void list_folder(const char *p)
1078 {
1079 	char *s=folder_fromutf7(p);
1080 	print_safe(s);
1081 	free(s);
1082 }
1083 
list_folder_xlate(const char * p,const char * path,const char * n_inbox,const char * n_drafts,const char * n_sent,const char * n_trash)1084 void list_folder_xlate(const char *p,
1085 		       const char *path,
1086 		       const char *n_inbox,
1087 		       const char *n_drafts,
1088 		       const char *n_sent,
1089 		       const char *n_trash)
1090 {
1091 	if (strcmp(p, INBOX) == 0)
1092 		printf("%s", n_inbox);
1093 	else if (strcmp(p, INBOX "." DRAFTS) == 0)
1094 		printf("%s", n_drafts);
1095 	else if (strcmp(p, INBOX "." TRASH) == 0)
1096 		printf("%s", n_trash);
1097 	else if (strcmp(p, INBOX "." SENT) == 0)
1098 		printf("%s", n_sent);
1099 	else
1100 		list_folder(path);
1101 }
1102 
1103 static void parse_hierarchy(const char *hierarchy,
1104 			    void (*maildir_hier_cb)
1105 			    (const char *pfix, const char *homedir,
1106 			     const char *path, const char *inbox_name),
1107 			    void (*sharehier_cb)
1108 			    (const char *sharedhier,
1109 			     struct maildir_shindex_cache *cache));
1110 
1111 static void show_transfer_dest_real(const char *, const char *,
1112 				    const char *, const char *);
1113 static void show_transfer_dest_fake(const char *,
1114 				    struct maildir_shindex_cache *);
1115 
show_transfer_dest(const char * cur_folder)1116 static void show_transfer_dest(const char *cur_folder)
1117 {
1118 	parse_hierarchy(cur_folder, show_transfer_dest_real,
1119 			show_transfer_dest_fake);
1120 }
1121 
show_transfer_dest_fake(const char * dummy1,struct maildir_shindex_cache * dummy2)1122 static void show_transfer_dest_fake(const char *dummy1,
1123 				    struct maildir_shindex_cache *dummy2)
1124 {
1125 }
1126 
1127 static void show_transfer_dest_real1(const char *inbox_pfix,
1128 				     const char *homedir,
1129 				     const char *cur_folder,
1130 				     const char *inbox_name);
1131 
show_transfer_dest_real(const char * inbox_pfix,const char * homedir,const char * cur_folder,const char * inbox_name)1132 static void show_transfer_dest_real(const char *inbox_pfix,
1133 				    const char *homedir,
1134 				    const char *cur_folder,
1135 				    const char *inbox_name)
1136 {
1137 	FILE *fp;
1138 	char buf1[BUFSIZ];
1139 	char buf2[BUFSIZ];
1140 
1141 	show_transfer_dest_real1(inbox_pfix, homedir, cur_folder, inbox_name);
1142 
1143 	if ((fp=fopen(SHAREDPATHCACHE, "r")) != NULL)
1144 	{
1145 		while (fgets(buf1, sizeof(buf1), fp) &&
1146 		       fgets(buf2, sizeof(buf2), fp))
1147 		{
1148 			char *p;
1149 
1150 			p=strchr(buf1, '\n');
1151 			if (p) *p=0;
1152 			p=strchr(buf2, '\n');
1153 			if (p) *p=0;
1154 
1155 			if (homedir == NULL || strcmp(buf1, homedir))
1156 			{
1157 				show_transfer_dest_real1(buf2, buf1,
1158 							 cur_folder,
1159 							 inbox_name);
1160 			}
1161 		}
1162 		fclose(fp);
1163 	}
1164 }
1165 
show_transfer_dest_real1(const char * inbox_pfix,const char * homedir,const char * cur_folder,const char * inbox_name)1166 static void show_transfer_dest_real1(const char *inbox_pfix,
1167 				     const char *homedir,
1168 				     const char *cur_folder,
1169 				     const char *inbox_name)
1170 {
1171 	char	**folders;
1172 	size_t	i;
1173 	const	char *p;
1174 	int	has_shared=0;
1175 
1176 	maildir_listfolders(inbox_pfix, homedir, &folders);
1177 	for (i=0; folders[i]; i++)
1178 	{
1179 		char acl_buf[2];
1180 
1181 		strcpy(acl_buf, ACL_INSERT);
1182 		acl_computeRightsOnFolder(folders[i], acl_buf);
1183 
1184 		if (acl_buf[0] == 0)
1185 			continue;
1186 
1187 		/* Transferring TO drafts is prohibited */
1188 
1189 		if (cur_folder == NULL || strcmp(cur_folder,
1190 						 INBOX "." DRAFTS))
1191 		{
1192 			if (strcmp(folders[i], INBOX "." DRAFTS) == 0)
1193 				continue;
1194 		}
1195 		else
1196 		{
1197 			if (strncmp(folders[i], SHARED ".",
1198 				    sizeof(SHARED)) &&
1199 			    strcmp(folders[i], INBOX "." TRASH))
1200 				continue;
1201 		}
1202 
1203 		if (cur_folder && strcmp(cur_folder, folders[i]) == 0)
1204 			continue;
1205 
1206 		p=folders[i];
1207 
1208 		if (strcmp(p, INBOX) == 0)
1209 			p=folder_inbox;
1210 		else if (strcmp(p, INBOX "." DRAFTS) == 0)
1211 			p=folder_drafts;
1212 		else if (strcmp(p, INBOX "." TRASH) == 0)
1213 			p=folder_trash;
1214 		else if (strcmp(p, INBOX "." SENT) == 0)
1215 			p=folder_sent;
1216 		if (!p)	p=folders[i];
1217 
1218 		if (strncmp(folders[i], SHARED ".", sizeof(SHARED)) == 0)
1219 		{
1220 		char	*d=maildir_shareddir(".", strchr(folders[i], '.')+1);
1221 		struct	stat	stat_buf;
1222 
1223 			if (!d)
1224 			{
1225 				maildir_freefolders(&folders);
1226 				enomem();
1227 			}
1228 			if (stat(d, &stat_buf))	/* Not subscribed */
1229 			{
1230 				free(d);
1231 				continue;
1232 			}
1233 			free(d);
1234 
1235 			if (!has_shared)
1236 			{
1237 				printf("<option value=\"\"></option>\n");
1238 				has_shared=1;
1239 			}
1240 		}
1241 
1242 		printf("<option value=\"");
1243 		output_attrencoded(folders[i]);
1244 		printf("\">");
1245 
1246 		if (strncmp(folders[i], NEWSHARED, sizeof(NEWSHARED)-1) == 0)
1247 		{
1248 			printf("%s.", getarg("PUBLICFOLDERS"));
1249 		}
1250 
1251 		p=strchr(folders[i], '.');
1252 
1253 		list_folder(p ? p+1:folders[i]);
1254 		printf("</option>\n");
1255 	}
1256 	maildir_freefolders(&folders);
1257 }
1258 
folder_msgmove()1259 void folder_msgmove()
1260 {
1261 	++initnextprevcnt;
1262 	printf("<table border=\"0\" class=\"box-small-outer\"><tr><td>\n");
1263 	printf("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"folder-move-background\">&nbsp;%s&nbsp;<select name=\"list%d\">\n", msg_movetolab, initnextprevcnt);
1264 
1265 	show_transfer_dest(sqwebmail_folder);
1266 
1267 	printf("</select><input type=\"submit\"%s name=\"move%d\" value=\"%s\" /></td></tr></table>\n",
1268 		(msg_type == MSGTYPE_DELETED ? " disabled":""),
1269 		initnextprevcnt,
1270 		msg_golab ? msg_golab:"");
1271 	printf("<input type=\"hidden\" name=\"pos\" value=\"%s\" />", cgi("pos"));
1272 	printf("<input type=\"hidden\" name=\"posfile\" value=\"");
1273 	output_attrencoded(msg_posfile ? msg_posfile:"");
1274 	printf("\" /></td></tr></table>\n");
1275 }
1276 
folder_delmsg(size_t pos)1277 void folder_delmsg(size_t pos)
1278 {
1279 MSGINFO	**info;
1280 int	dummy;
1281 const	char *f=cgi("posfile");
1282 size_t	newpos;
1283 int	rc=0;
1284 
1285 	CHECKFILENAME(f);
1286 
1287 	if (*cgi("move1"))
1288 	{
1289 		rc=maildir_msgmovefile(sqwebmail_folder, f, cgi("list1"), pos);
1290 		maildir_savefoldermsgs(sqwebmail_folder);
1291 	}
1292 	else if (*cgi("move2"))
1293 	{
1294 		rc=maildir_msgmovefile(sqwebmail_folder, f, cgi("list2"), pos);
1295 		maildir_savefoldermsgs(sqwebmail_folder);
1296 	}
1297 	else
1298 	{
1299 		maildir_msgdeletefile(sqwebmail_folder, f, pos);
1300 		maildir_savefoldermsgs(sqwebmail_folder);
1301 	}
1302 
1303 	if (rc)
1304 	{
1305 		http_redirect_argu("&form=readmsg&pos=%s&error=quota",
1306 			(unsigned long)pos);
1307 		return;
1308 	}
1309 
1310 	newpos=pos+1;
1311 	info=maildir_read(sqwebmail_folder, 1, &newpos, &dummy, &dummy);
1312 
1313 	if (info[0] && newpos != pos)
1314 	{
1315 		maildir_free(info, 1);
1316 		http_redirect_argu("&form=readmsg&pos=%s",
1317 						(unsigned long)newpos);
1318 	}
1319 	else
1320 	{
1321 		maildir_free(info, 1);
1322 		http_redirect_argu("&form=folder&pos=%s",
1323 						(unsigned long)pos);
1324 	}
1325 }
1326 
is_preview_mode()1327 static int is_preview_mode()
1328 {
1329 	/* We're in new message window, and we're previewing a draft */
1330 
1331 	return (*cgi("showdraft"));
1332 }
1333 
1334 static void dokeyimport(FILE *, struct rfc2045 *, int);
1335 
charset_warning(const char * mime_charset)1336 void charset_warning(const char *mime_charset)
1337 {
1338 	const struct unicode_info *u1, *u2;
1339 
1340 	if (strcasecmp(mime_charset, "us-ascii") == 0)
1341 		return;
1342 
1343 	u1=unicode_find(mime_charset);
1344 	u2=unicode_find(sqwebmail_content_charset);
1345 
1346 	if (u2 && (u2->flags & UNICODE_UTF))
1347 		return;
1348 
1349 	if (!u1 || !u2 || strcmp(u1->chset, u2->chset))
1350 	{
1351 		printf(getarg("CHSET"), mime_charset,
1352 		       sqwebmail_content_charset);
1353 	}
1354 }
1355 
html_warning()1356 static void html_warning()
1357 {
1358 	printf("%s", getarg("HTML"));
1359 }
1360 
init_smileys(struct msg2html_info * info)1361 static void init_smileys(struct msg2html_info *info)
1362 {
1363 	FILE *fp=open_langform(sqwebmail_content_language, "smileys.txt", 0);
1364 	char buf[1024];
1365 
1366 	char imgbuf[1024];
1367 
1368 	if (!fp)
1369 		return;
1370 
1371 	while (fgets(buf, sizeof(buf), fp) != NULL)
1372 	{
1373 		char *p=strchr(buf, '#');
1374 		char *code;
1375 		char *img;
1376 		char *attr;
1377 
1378 		if (p) *p=0;
1379 
1380 		code=buf;
1381 
1382 		for (p=buf; *p && !isspace(*p); p++)
1383 			;
1384 
1385 		if (*p)
1386 			*p++=0;
1387 
1388 		while (*p && isspace(*p))
1389 			p++;
1390 		img=p;
1391 
1392 		while (*p && !isspace(*p))
1393 			p++;
1394 		if (*p)
1395 			*p++=0;
1396 
1397 		while (*p && isspace(*p))
1398 			p++;
1399 		attr=p;
1400 		p=strchr(p, '\n');
1401 		if (p) *p=0;
1402 
1403 		if (!*code || !*img)
1404 			continue;
1405 
1406 		snprintf(imgbuf, sizeof(imgbuf),
1407 			 "<img src=\"%s/%s\" %s />",
1408 			 get_imageurl(), img, attr);
1409 
1410 		msg2html_add_smiley(info, code, imgbuf);
1411 	}
1412 	fclose(fp);
1413 }
1414 
email_address_start(const char * name,const char * addr)1415 static void email_address_start(const char *name, const char *addr)
1416 {
1417 	if (is_preview_mode())
1418 	    return;
1419 
1420 	printf("<a href=\"");
1421 	output_scriptptrget();
1422 	printf("&amp;form=quickadd&amp;pos=%s&amp;newname=",
1423 	       cgi("pos"));
1424 
1425 	if (name)
1426 		output_urlencoded(name);
1427 
1428 	printf("&amp;newaddr=");
1429 	if (addr)
1430 		output_urlencoded(addr);
1431 
1432 	printf("\" style=\"text-decoration: none\" "
1433 	       "onmouseover=\"window.status='%s'; return true;\" "
1434 	       "onmouseout=\"window.status=''; return true;\" >"
1435 	       "<span class=\"message-rfc822-header-address\">",
1436 	       msg_add ? msg_add:"");
1437 }
1438 
email_address_end()1439 static void email_address_end()
1440 {
1441 	if (is_preview_mode())
1442 		return;
1443 
1444 	printf("</a></span>");
1445 }
1446 
email_header(const char * h,void (* cb_func)(const char *))1447 static void email_header(const char *h,
1448 			 void (*cb_func)(const char *))
1449 {
1450 	char *hdrname;
1451 	char *p;
1452 	const char *hdrvalue;
1453 
1454 	if ((hdrname=malloc(sizeof("DSPHDR_")+strlen(h))) == NULL)
1455 		enomem();
1456 
1457 	strcpy (hdrname, "DSPHDR_");
1458 	strcat (hdrname, h);
1459 
1460 	for (p=hdrname; *p; p++)
1461 		*p=toupper((int)(unsigned char)*p);
1462 
1463 	hdrvalue = getarg (hdrname);
1464 
1465 	(*cb_func)(hdrvalue && *hdrvalue ? hdrvalue:h);
1466 
1467 	free(hdrname);
1468 }
1469 
email_header_date_fmt(const char * def)1470 static const char *email_header_date_fmt(const char *def)
1471 {
1472 	const char *date_fmt = getarg ("DSPFMT_DATE");
1473 
1474 	if (date_fmt && *date_fmt)
1475 		def=date_fmt;
1476 	return def;
1477 }
1478 
1479 extern const char *redirect_hash(const char *timestamp);
1480 
decode_cgiurlencode(const char * s)1481 static char *decode_cgiurlencode(const char *s)
1482 {
1483 char *q=malloc(strlen(s)+1), *r;
1484 const char *t;
1485 
1486 	if (!q)	enomem();
1487 
1488 	for (r=q, t=s; *t; )
1489 	{
1490 		if (strncmp(t, "&amp;", 5) == 0)
1491 		{
1492 			*r++ = '&';
1493 			t += 5;
1494 			continue;
1495 		}
1496 		if (strncmp(t, "&lt;", 4) == 0)
1497 		{
1498 			*r++ = '<';
1499 			t += 4;
1500 			continue;
1501 		}
1502 		if (strncmp(t, "&gt;", 4) == 0)
1503 		{
1504 			*r++ = '>';
1505 			t += 4;
1506 			continue;
1507 		}
1508 		if (strncmp(t, "&quot;", 6) == 0)
1509 		{
1510 			*r++ = '"';
1511 			t += 6;
1512 			continue;
1513 		}
1514 		*r++ = *t++;
1515 	}
1516 	*r=0;
1517 
1518 	r=cgiurlencode(q);
1519 	free(q);
1520 	return (r);
1521 }
1522 
get_textlink(const char * s,void * arg)1523 static char *get_textlink(const char *s, void *arg)
1524 {
1525 char	*t;
1526 struct buf b;
1527 
1528 	buf_init(&b);
1529 
1530 	if (strncmp(s, "mailto:", 7) == 0)
1531 	{
1532 	int	i;
1533 
1534 		buf_cat(&b, "<a href=\"");
1535 		buf_cat(&b, scriptptrget());
1536 		buf_cat(&b, "&amp;form=newmsg&amp;to=");
1537 
1538 		for (i=7; s[i]; i++)
1539 		{
1540 		char	c[2];
1541 
1542 			c[1]=0;
1543 			if ((c[0]=s[i]) == '?')
1544 				c[0]='&';
1545 			buf_cat(&b, c);
1546 		}
1547 		buf_cat(&b, "\">"
1548 			"<span class=\"message-text-plain-mailto-link\">");
1549 		buf_cat(&b, s);
1550 		buf_cat(&b, "</span></a>");
1551 	}
1552 	else if (strncmp(s, "http:", 5) == 0 || strncmp(s, "https:", 6) == 0)
1553 	{
1554 		char buffer[NUMBUFSIZE];
1555 		time_t now;
1556 		char *hash;
1557 		const char *n;
1558 
1559 		time(&now);
1560 		libmail_str_time_t(now, buffer);
1561 
1562 		hash=cgiurlencode(redirect_hash(buffer));
1563 
1564 		t=decode_cgiurlencode(s);
1565 		buf_cat(&b, "<a href=\"");
1566 
1567 		n=getenv("SCRIPT_NAME");
1568 		if (!*n) n="/";
1569 
1570 		buf_cat(&b, n);
1571 		buf_cat(&b, "?redirect=");
1572 		buf_cat(&b, t);
1573 		buf_cat(&b, "&amp;timestamp=");
1574 		buf_cat(&b, buffer);
1575 		buf_cat(&b, "&amp;md5=");
1576 		if (hash)
1577 		{
1578 			buf_cat(&b, hash);
1579 			free(hash);
1580 		}
1581 		buf_cat(&b, "\" target=\"_blank\">"
1582 			"<span class=\"message-text-plain-http-link\">");
1583 		buf_cat(&b, s);
1584 		buf_cat(&b, "</span></a>");
1585 		free(t);
1586 	}
1587 	t=strdup(b.ptr ? b.ptr:"");
1588 	if (!t)	enomem();
1589 	buf_free(&b);
1590 	return (t);
1591 }
1592 
message_rfc822_action(struct rfc2045id * idptr)1593 static void message_rfc822_action(struct rfc2045id *idptr)
1594 {
1595 	if (is_preview_mode())
1596 		return;
1597 
1598 	printf("<tr valign=\"top\"><td>&nbsp;</td><td align=\"left\" valign=\"top\">");
1599 
1600 	printf("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td><a href=\"");
1601 	output_scriptptrget();
1602 	output_mimegpgfilename();
1603 	msg2html_showmimeid(idptr, NULL);
1604 	printf("&amp;pos=%ld&amp;reply=1&amp;form=newmsg\"><font size=\"-1\">%s</font></a>&nbsp;</td><td>&nbsp;<a href=\"",
1605 		(long)msg_pos, msg_replylab);
1606 
1607 	output_scriptptrget();
1608 	output_mimegpgfilename();
1609 	msg2html_showmimeid(idptr, NULL);
1610 	printf("&amp;pos=%ld&amp;replyall=1&amp;form=newmsg\"><font size=\"-1\">%s</font></a>&nbsp;</td><td>&nbsp;<a href=\"",
1611 		(long)msg_pos, msg_replyalllab);
1612 	output_scriptptrget();
1613 	output_mimegpgfilename();
1614 	msg2html_showmimeid(idptr, NULL);
1615 	printf("&amp;pos=%ld&amp;forward=1&amp;form=newmsg\"><font size=\"-1\">%s</font></a>&nbsp;</td><td>&nbsp;<a href=\"",
1616 		(long)msg_pos, msg_forwardlab);
1617 
1618 	output_scriptptrget();
1619 	output_mimegpgfilename();
1620 	msg2html_showmimeid(idptr, NULL);
1621 	printf("&amp;pos=%ld&amp;forwardatt=1&amp;form=newmsg\"><font size=\"-1\">%s</font></a></td></tr></table>\n",
1622 		(long)msg_pos, msg_forwardattlab);
1623 
1624 	printf("</td></tr>\n");
1625 }
1626 
output_mimeurl(struct rfc2045id * id,const char * form)1627 static void output_mimeurl(struct rfc2045id *id, const char *form)
1628 {
1629 	output_scriptptrget();
1630 	printf("&amp;form=%s&amp;pos=%ld", form, (long)msg_pos);
1631 	msg2html_showmimeid(id, NULL);
1632 
1633 	output_mimegpgfilename();
1634 }
1635 
inline_image_action(struct rfc2045id * id,const char * content_type,void * arg)1636 static void inline_image_action(struct rfc2045id *id, const char *content_type,
1637 				void *arg)
1638 {
1639 	if (!is_preview_mode())
1640 	{
1641 		printf("<a href=\"");
1642 		output_mimeurl(id, "fetch");
1643 		printf("\" target=\"_blank\">");
1644 	}
1645 	printf("<img src=\"");
1646 	output_mimeurl(id, "fetch");
1647 	printf("\" alt=\"Inline picture: ");
1648 	output_attrencoded(content_type);
1649 	printf("\" />%s\n",
1650 	       is_preview_mode() ? "":"</a>");
1651 }
1652 
1653 
showattname(const char * fmt,const char * name,const char * content_type)1654 static void showattname(const char *fmt, const char *name,
1655 	const char *content_type)
1656 {
1657 char	*t;
1658 
1659 	if (!name || !*name)	name=content_type;
1660 	if (!name)	name="";
1661 
1662 	t=malloc(strlen(name)+strlen(fmt)+100);
1663 	if (!t)
1664 		return;
1665 
1666 	sprintf(t, fmt, name);
1667 	output_attrencoded(t);
1668 	free(t);
1669 }
1670 
unknown_attachment_action(struct rfc2045id * id,const char * content_type,const char * content_name,off_t size,void * arg)1671 static void unknown_attachment_action(struct rfc2045id *id,
1672 				      const char *content_type,
1673 				      const char *content_name,
1674 				      off_t size,
1675 				      void *arg)
1676 {
1677 	printf("<table border=\"0\" cellpadding=\"1\" cellspacing=\"0\" class=\"box-small-outer\"><tr><td>");
1678 	printf("<table border=\"0\" cellpadding=\"4\" cellspacing=\"0\" class=\"message-download-attachment\"><tr><td>");
1679 
1680 	if (strcmp(cgi("form"), "print") == 0)
1681 	{
1682 		showattname(getarg("ATTSTUB"), content_name, content_type);
1683 
1684 		printf("&nbsp;(");
1685 		output_attrencoded(content_type);
1686 		printf(")");
1687 	}
1688 	else
1689 	{
1690 		printf("<div align=\"center\"><span class=\"message-attachment-header\">");
1691 		showattname(getarg("ATTACHMENT"), content_name, content_type);
1692 
1693 		printf("&nbsp;(");
1694 		output_attrencoded(content_type);
1695 		printf("; %s)</span></div>",
1696 		       showsize(size));
1697 		printf("<br /><div align=\"center\">");
1698 
1699 		if (!is_preview_mode())
1700 		{
1701 			printf("<a href=\"");
1702 			output_mimeurl(id, "fetch");
1703 			printf("\" style=\"text-decoration: none\" target=\"_blank\">");
1704 			printf("%s</a>&nbsp;/&nbsp;", getarg("DISPATT"));
1705 			printf("<a href=\"");
1706 			output_mimeurl(id, "fetch");
1707 			printf("&amp;download=1\" style=\"text-decoration: none\">");
1708 			printf("%s</a>", getarg("DOWNATT"));
1709 		}
1710 
1711 		printf("</div>\n");
1712 	}
1713 
1714 	printf("</td></tr></table>\n");
1715 	printf("</td></tr></table>\n");
1716 }
1717 
is_gpg_enabled()1718 static int is_gpg_enabled()
1719 {
1720 	return *cgi(MIMEGPGFILENAME) && !is_preview_mode();
1721 }
1722 
application_pgp_keys_action(struct rfc2045id * id)1723 static void application_pgp_keys_action(struct rfc2045id *id)
1724 {
1725 	printf("<table border=\"0\" cellpadding=\"8\" cellspacing=\"1\" class=\"box-small-outer\"><tr><td>");
1726 	printf("<table border=\"0\" cellpadding=\"4\" cellspacing=\"4\" class=\"message-application-pgpkeys\"><tr><td>");
1727 
1728 	if (strcmp(cgi("form"), "print") == 0 || is_preview_mode())
1729 	{
1730 		printf("%s", getarg("KEY"));
1731 	}
1732 	else
1733 	{
1734 		printf("<div align=\"center\"><a href=\"");
1735 		output_scriptptrget();
1736 		printf("&amp;form=keyimport&amp;pos=%ld", (long)msg_pos);
1737 		printf("&amp;pubkeyimport=1");
1738 		output_mimegpgfilename();
1739 		msg2html_showmimeid(id, "&amp;keymimeid=");
1740 		printf("\" style=\"text-decoration: none\" class=\"message-application-pgpkeys\">");
1741 		printf("%s", getarg("PUBKEY"));
1742 		printf("</a></div>");
1743 
1744 		printf("<hr width=\"100%%\" />\n");
1745 
1746 		printf("<div align=\"center\"><a href=\"");
1747 		output_scriptptrget();
1748 		printf("&amp;form=keyimport&amp;pos=%ld", (long)msg_pos);
1749 		printf("&amp;privkeyimport=1");
1750 		output_mimegpgfilename();
1751 		msg2html_showmimeid(id, "&amp;keymimeid=");
1752 		printf("\" style=\"text-decoration: none\" class=\"message-application-pgpkeys\">");
1753 		printf("%s", getarg("PRIVKEY"));
1754 		printf("</a></div>");
1755 	}
1756 
1757 	printf("</td></tr></table>\n");
1758 	printf("</td></tr></table>\n<br />\n");
1759 }
1760 
gpg_message_action()1761 static void gpg_message_action()
1762 {
1763 	printf("<form method=\"post\" action=\"");
1764 	output_scriptptr();
1765 	printf("\">");
1766 	output_scriptptrpostinfo();
1767 	printf("<input type=\"hidden\" name=\"form\" value=\"readmsg\" />");
1768 	printf("<input type=\"hidden\" name=\"pos\" value=\"%s\" />",
1769 	       cgi("pos"));
1770 	printf("<input type=\"hidden\" name=\"mimegpg\" value=\"1\" />\n");
1771 
1772 	printf("<table border=\"0\" cellpadding=\"1\""
1773 	       " width=\"100%%\" class=\"box-outer\">"
1774 	       "<tr><td><table width=\"100%%\" border=\"0\" cellspacing=\"0\""
1775 	       " cellpadding=\"0\" class=\"box-white-outer\"><tr><td>");
1776 
1777 	if ( *cgi(MIMEGPGFILENAME))
1778 	{
1779 		printf("%s", getarg("NOTCOMPACTGPG"));
1780 	}
1781 	else
1782 	{
1783 		printf("%s\n", getarg("MIMEGPGNOTICE"));
1784 
1785 		if (ishttps())
1786 			printf("%s\n", getarg("PASSPHRASE"));
1787 
1788 		printf("%s", getarg("DECRYPT"));
1789 	}
1790 	printf("</td><tr></table></td></tr></table></form><br />\n");
1791 }
1792 
redirect_hash(const char * timestamp)1793 const char *redirect_hash(const char *timestamp)
1794 {
1795 	struct stat stat_buf;
1796 
1797 	char buffer[NUMBUFSIZE*2+10];
1798 	const char *p=getenv("SQWEBMAIL_RANDSEED");
1799 
1800 	if (strlen(timestamp) >= NUMBUFSIZE)
1801 		return "";
1802 
1803 	strcat(strcpy(buffer, timestamp), " ");
1804 
1805 	if (p && *p)
1806 		strncat(buffer, p, NUMBUFSIZE);
1807 	else
1808 	{
1809 		if (stat(SENDITSH, &stat_buf) < 0)
1810 			return "";
1811 
1812 		libmail_str_ino_t(stat_buf.st_ino, buffer+strlen(buffer));
1813 	}
1814 
1815 	return md5_hash_courier(buffer);
1816 }
1817 
get_url_to_mime_part(const char * mimeid,void * arg)1818 static char *get_url_to_mime_part(const char *mimeid, void *arg)
1819 {
1820 	const char *mimegpgfilename=cgi(MIMEGPGFILENAME);
1821 	const char *pos;
1822 	char *p, *q;
1823 
1824 	p=scriptptrget();
1825 	pos=cgi("pos");
1826 
1827 	q=malloc(strlen(p)+strlen(pos) +
1828 		 strlen(mimegpgfilename)+
1829 		 strlen(mimeid)+
1830 		 sizeof("&amp;mimeid=&amp;pos=&amp;form=fetch&amp;mimegpgfilename="));
1831 	if (!q)	enomem();
1832 	strcpy(q, p);
1833 	strcat(q, "&amp;form=fetch&amp;pos=");
1834 	strcat(q, pos);
1835 	strcat(q, "&amp;mimeid=");
1836 	strcat(q, mimeid);
1837 
1838 	if (*mimegpgfilename)
1839 		strcat(strcat(q, "&amp;mimegpgfilename="), mimegpgfilename);
1840 
1841 	return (q);
1842 }
1843 
folder_showmsg(const char * dir,size_t pos)1844 void folder_showmsg(const char *dir, size_t pos)
1845 {
1846 	char	*filename;
1847 	FILE	*fp;
1848 	struct	rfc2045 *rfc;
1849 	char	buf[BUFSIZ];
1850 	int	n;
1851 	int	fd;
1852 
1853 	struct msg2html_info *info;
1854 
1855 	const char *script_name=nonloginscriptptr();
1856 
1857 
1858 	if (*cgi("addnick"))
1859 	{
1860 		const char *name=cgi("newname");
1861 		const char *addr=cgi("newaddr");
1862 
1863 		const char *nick1=cgi("newnick1");
1864 		const char *nick2=cgi("newnick2");
1865 
1866 		while (*nick1 && isspace((int)(unsigned char)*nick1))
1867 		       ++nick1;
1868 
1869 		while (*nick2 && isspace((int)(unsigned char)*nick2))
1870 		       ++nick2;
1871 
1872 		if (*nick2)
1873 			nick1=nick2;
1874 
1875 		if (*nick1)
1876 		{
1877 			ab_add(name, addr, nick1);
1878 		}
1879 	}
1880 
1881 	filename=get_msgfilename(dir, &pos);
1882 
1883 	fp=0;
1884 	fd=maildir_semisafeopen(filename, O_RDONLY, 0);
1885 	if (fd >= 0)
1886 	{
1887 		if ((fp=fdopen(fd, "r")) == 0)
1888 			close(fd);
1889 	}
1890 
1891 	if (!fp)
1892 	{
1893 		free(filename);
1894 		return;
1895 	}
1896 
1897 	msg_pos=pos;
1898 	rfc=rfc2045_alloc();
1899 
1900 	while ((n=fread(buf, 1, sizeof(buf), fp)) > 0)
1901 		rfc2045_parse(rfc, buf, n);
1902 	rfc2045_parse_partial(rfc);
1903 
1904 	info=script_name ? msg2html_alloc(sqwebmail_content_charset):NULL;
1905 
1906 	if (info)
1907 	{
1908 		char nowbuffer[NUMBUFSIZE];
1909 		time_t now;
1910 		char *hash;
1911 		char *washpfix;
1912 		char *washpfixmailto;
1913 		char *scriptnameget=scriptptrget();
1914 		static const char formbuf[]="&amp;form=newmsg&amp;to=";
1915 
1916 		info->mimegpgfilename=cgi(MIMEGPGFILENAME);
1917 		if (*info->mimegpgfilename)
1918 			CHECKFILENAME(info->mimegpgfilename);
1919 
1920 		info->gpgdir=GPGDIR;
1921 		info->fullheaders=pref_flagfullheaders || *cgi("fullheaders");
1922 		info->noflowedtext=pref_noflowedtext;
1923 		info->showhtml=pref_showhtml;
1924 
1925 		info->character_set_follows=charset_warning;
1926 		info->html_content_follows=html_warning;
1927 		info->get_url_to_mime_part=get_url_to_mime_part;
1928 
1929 		time(&now);
1930 		libmail_str_time_t(now, nowbuffer);
1931 
1932 		hash=cgiurlencode(redirect_hash(nowbuffer));
1933 
1934 		washpfix=malloc(strlen(script_name)
1935 				+ strlen(hash ? hash:"") + strlen(nowbuffer)
1936 				+ 100);
1937 		if (!washpfix)	enomem();
1938 
1939 		strcat(strcat(strcat(strcat(strcat(strcpy(washpfix,
1940 							  script_name),
1941 						   "?timestamp="),
1942 					    nowbuffer),
1943 				     "&amp;md5="),
1944 			      (hash ? hash:"")),
1945 		       "&amp;redirect=");
1946 
1947 		if (hash)
1948 			free(hash);
1949 
1950 		washpfixmailto=malloc(strlen(scriptnameget)+sizeof(formbuf));
1951 		if (!washpfixmailto)	enomem();
1952 		strcat(strcpy(washpfixmailto, scriptnameget), formbuf);
1953 
1954 		info->wash_http_prefix=washpfix;
1955 		info->wash_mailto_prefix=washpfixmailto;
1956 
1957 		init_smileys(info);
1958 
1959 		info->email_address_start=email_address_start;
1960 		info->email_address_end=email_address_end;
1961 		info->email_header=email_header;
1962 		info->email_header_date_fmt=email_header_date_fmt;
1963 		info->get_textlink=get_textlink;
1964 		info->message_rfc822_action=message_rfc822_action;
1965 		info->inline_image_action=inline_image_action;
1966 		info->unknown_attachment_action=unknown_attachment_action;
1967 		info->application_pgp_keys_action=
1968 			application_pgp_keys_action;
1969 		info->gpg_message_action=gpg_message_action;
1970 
1971 		info->is_gpg_enabled=is_gpg_enabled;
1972 		info->is_preview_mode=is_preview_mode;
1973 		msg2html(fp, rfc, info);
1974 		msg2html_free(info);
1975 
1976 		free(washpfix);
1977 		free(washpfixmailto);
1978 	}
1979 
1980 	rfc2045_free(rfc);
1981 	fclose(fp);
1982 	if (*cgi(MIMEGPGFILENAME) == 0)
1983 		maildir_markread(dir, pos);
1984 	free(filename);
1985 }
1986 
folder_keyimport(const char * dir,size_t pos)1987 void folder_keyimport(const char *dir, size_t pos)
1988 {
1989 	char	*filename;
1990 	FILE	*fp;
1991 	struct	rfc2045 *rfc;
1992 	int	fd;
1993 
1994 	filename=get_msgfilename(dir, &pos);
1995 
1996 	fp=0;
1997 	fd=maildir_semisafeopen(filename, O_RDONLY, 0);
1998 	if (fd >= 0)
1999 	{
2000 		if ((fp=fdopen(fd, "r")) == 0)
2001 			close(fd);
2002 	}
2003 
2004 	if (!fp)
2005 	{
2006 		free(filename);
2007 		return;
2008 	}
2009 
2010 	rfc=rfc2045_fromfp(fp);
2011 
2012 
2013 	if (libmail_gpg_has_gpg(GPGDIR) == 0)
2014 	{
2015 		struct rfc2045 *part;
2016 
2017 		if (*cgi("pubkeyimport")
2018 		    && (part=rfc2045_find(rfc, cgi("keymimeid"))) != 0)
2019 		{
2020 			dokeyimport(fp, part, 0);
2021 		}
2022 		else if (*cgi("privkeyimport")
2023 		    && (part=rfc2045_find(rfc, cgi("keymimeid"))) != 0)
2024 		{
2025 			dokeyimport(fp, part, 1);
2026 		}
2027 	}
2028 	rfc2045_free(rfc);
2029 	fclose(fp);
2030 	free(filename);
2031 
2032 	printf("<p><a href=\"");
2033 	output_scriptptrget();
2034 	printf("&amp;form=readmsg&amp;pos=%s", cgi("pos"));
2035 	printf("\">%s</a>", getarg("KEYIMPORT"));
2036 }
2037 
2038 static int importkey_func(const char *p, size_t cnt, void *voidptr);
2039 static int importkeyin_func(const char *p, size_t cnt, void *voidptr);
2040 
dokeyimport(FILE * fp,struct rfc2045 * rfcp,int issecret)2041 static void dokeyimport(FILE *fp, struct rfc2045 *rfcp, int issecret)
2042 {
2043 	off_t	start_pos, end_pos, start_body, ldummy;
2044 	char buf[BUFSIZ];
2045 	int cnt;
2046 
2047 	static const char start_str[]=
2048 		"<table width=\"100%%\" border=\"0\" class=\"box-outer\"><tr><td>"
2049 		"<table width=\"100%%\" border=\"0\" cellspacing=\"0\" cellpadding=\"4\""
2050 		" class=\"box-white-outer\"><tr><td>%s<pre>\n";
2051 
2052 	static const char end_str[]=
2053 		"</pre></td></tr></table></td></tr></table><br />\n";
2054 
2055 	if (libmail_gpg_import_start(GPGDIR, issecret))
2056 		return;
2057 
2058 	printf(start_str, getarg("IMPORTHDR"));
2059 
2060 	rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body,
2061 		&ldummy, &ldummy);
2062 	if (fseek(fp, start_body, SEEK_SET) < 0)
2063 	{
2064 		error("Seek error.");
2065 		libmail_gpg_import_finish(&importkey_func, NULL);
2066 		printf("%s", end_str);
2067 		return;
2068 	}
2069 
2070 	rfc2045_cdecode_start(rfcp, &importkeyin_func, 0);
2071 
2072 	while (start_body < end_pos)
2073 	{
2074 		cnt=sizeof(buf);
2075 		if (cnt > end_pos-start_body)
2076 			cnt=end_pos-start_body;
2077 		cnt=fread(buf, 1, cnt, fp);
2078 		if (cnt <= 0)	break;
2079 		start_body += cnt;
2080 		if (rfc2045_cdecode(rfcp, buf, cnt))
2081 		{
2082 			rfc2045_cdecode_end(rfcp);
2083 			printf("%s", end_str);
2084 			return;
2085 		}
2086 	}
2087 
2088 	if (rfc2045_cdecode_end(rfcp) == 0)
2089 	{
2090 		libmail_gpg_import_finish(&importkey_func, NULL);
2091 	}
2092 
2093 	printf("%s", end_str);
2094 }
2095 
importkeyin_func(const char * p,size_t cnt,void * voidptr)2096 static int importkeyin_func(const char *p, size_t cnt, void *voidptr)
2097 {
2098 	return (libmail_gpg_import_do(p, cnt, &importkey_func, NULL));
2099 }
2100 
importkey_func(const char * p,size_t cnt,void * voidptr)2101 static int importkey_func(const char *p, size_t cnt, void *voidptr)
2102 {
2103 	print_attrencodedlen(p, cnt, 1, stdout);
2104 	return (0);
2105 }
2106 
2107 
2108 /*
2109 ** If we're currently showing (INBOX|shared|#shared).foo.bar hierarchy, return
2110 ** "x.foo".  If we're currently showing (INBOX|shared|#shared).foo, return
2111 ** an empty string.
2112 */
get_parent_folder(const char * p)2113 static char *get_parent_folder(const char *p)
2114 {
2115 	const char *q;
2116 
2117 	q=strrchr(p, '.');
2118 
2119 	if (q)
2120 	{
2121 		char	*s;
2122 
2123 		s=malloc(q-p+1);
2124 		if (!s)	enomem();
2125 		memcpy(s, p, q-p);
2126 		s[q-p]=0;
2127 		return (s);
2128 	}
2129 	return (strdup(""));
2130 }
2131 
checkrename(const char * origfolder,const char * newfolder)2132 static int checkrename(const char *origfolder,
2133 		       const char *newfolder)
2134 {
2135 	char acl_buf[2];
2136 	char *p, *q;
2137 
2138 	strcpy(acl_buf, ACL_DELETEFOLDER);
2139 	acl_computeRightsOnFolder(origfolder, acl_buf);
2140 	if (acl_buf[0] == 0)
2141 	{
2142 		folder_err_msg=getarg("RENAME");
2143 		return -1;
2144 	}
2145 
2146 	strcpy(acl_buf, ACL_CREATE);
2147 	p=strdup(newfolder);
2148 
2149 	if (!p || !(q=strrchr(p, '.')) ||
2150 	    (*q=0,
2151 	     acl_computeRightsOnFolder(p, acl_buf),
2152 	     acl_buf[0]) == 0)
2153 	{
2154 		if (p)
2155 			free(p);
2156 
2157 		folder_err_msg=getarg("RENAME");
2158 		return -1;
2159 	}
2160 	free(p);
2161 
2162 	return 0;
2163 }
2164 
dorename(const char * origfolder,struct maildir_info * mifrom,struct maildir_info * mito,const char * err_invalid,const char * err_cantdelete,const char * err_exists)2165 static void dorename(const char *origfolder,
2166 		     struct maildir_info *mifrom,
2167 		     struct maildir_info *mito,
2168 		     const char *err_invalid,
2169 		     const char *err_cantdelete,
2170 		     const char *err_exists)
2171 {
2172 	char *s;
2173 	char *t;
2174 	char *u;
2175 
2176 	const char *p;
2177 	struct	stat	stat_buf;
2178 
2179 	if (mifrom->homedir == NULL ||
2180 	    mifrom->maildir == NULL ||
2181 	    mito->homedir == NULL ||
2182 	    mito->maildir == NULL ||
2183 	    strcmp(mifrom->homedir, mito->homedir))
2184 	{
2185 		folder_err_msg=err_invalid;
2186 		return;
2187 	}
2188 
2189 	s=maildir_name2dir(".", mifrom->maildir);
2190 	t=maildir_name2dir(".", mito->maildir);
2191 
2192 	if (!s || !t)
2193 	{
2194 		if (s) free(s);
2195 		if (t) free(t);
2196 		folder_err_msg=err_invalid;
2197 		return;
2198 	}
2199 
2200 	p=s;
2201 	if (strncmp(p, "./", 2) == 0) p += 2;
2202 
2203 	if (strcmp(p, ".") == 0 ||
2204 	    strcmp(p, "." SENT) == 0 ||
2205 	    strcmp(p, "." DRAFTS) == 0 ||
2206 	    strcmp(p, "." TRASH) == 0)
2207 	{
2208 		free(s);
2209 		free(t);
2210 		folder_err_msg=err_invalid;
2211 		return;
2212 	}
2213 
2214 	u=maildir_name2dir(mito->homedir, mito->maildir);
2215 	if (!u)
2216 	{
2217 		free(s);
2218 		free(t);
2219 		folder_err_msg=err_invalid;
2220 		return;
2221 	}
2222 
2223 	if (stat(u, &stat_buf) == 0)
2224 	{
2225 		free(s);
2226 		free(t);
2227 		folder_err_msg=err_exists;
2228 		return;
2229 	}
2230 	free(u);
2231 
2232 	if (mailfilter_folderused(origfolder))
2233 	{
2234 		free(s);
2235 		free(t);
2236 		folder_err_msg=err_cantdelete;
2237 		return;
2238 	}
2239 
2240 	if (maildir_rename(mifrom->homedir,
2241 			   strncmp(s, "./", 2) == 0 ? s+2:s,
2242 			   strncmp(t, "./", 2) == 0 ? t+2:t,
2243 			   MAILDIR_RENAME_FOLDER, NULL))
2244 		folder_err_msg=err_cantdelete;
2245 
2246 	free(s);
2247 	free(t);
2248 }
2249 
2250 struct publicfolderlist_helper {
2251 	char *name;
2252 	char *homedir;
2253 	char *maildir;
2254 };
2255 
freeph(struct publicfolderlist_helper * ph)2256 static void freeph(struct publicfolderlist_helper *ph)
2257 {
2258 	if (ph->name)
2259 		free(ph->name);
2260 	if (ph->homedir)
2261 		free(ph->homedir);
2262 	if (ph->maildir)
2263 		free(ph->maildir);
2264 	memset(ph, 0, sizeof(*ph));
2265 }
2266 
2267 static void do_folderlist(const char *pfix, const char *homedir,
2268 			  const char *path, const char *inbox_name);
2269 static void do_sharedhierlist(const char *sharedhier,
2270 			      struct maildir_shindex_cache *cache);
2271 
checkcreate(const char * f,int isrec)2272 static int checkcreate(const char *f, int isrec)
2273 {
2274 	char *s=strdup(f);
2275 	char *q;
2276 	char buf[2];
2277 	struct maildir_info minfo;
2278 
2279 	if (!s)
2280 	{
2281 		folder_err_msg=getarg("CREATEPERMS");
2282 		return -1;
2283 	}
2284 
2285 	if (isrec)
2286 	{
2287 		if ((q=strrchr(s, '.')) == NULL ||
2288 		    (*q=0, checkcreate(s, 0)) < 0)
2289 		{
2290 			free(s);
2291 			folder_err_msg=getarg("CREATEPERMS");
2292 			return -1;
2293 		}
2294 		*q='.';
2295 	}
2296 
2297 	if (maildir_info_imap_find(&minfo, s, login_returnaddr()) < 0)
2298 	{
2299 		free(s);
2300 		folder_err_msg=getarg("CREATEPERMS");
2301 		return -1;
2302 	}
2303 
2304 	if (strchr(minfo.maildir, '.') == NULL)
2305 	{
2306 		free(s);
2307 		folder_err_msg=getarg("CREATEPERMS");
2308 		return -1;
2309 	}
2310 
2311 	maildir_acl_delete(minfo.homedir,
2312 			   strchr(minfo.maildir, '.'));
2313 
2314 	maildir_info_destroy(&minfo);
2315 
2316 	strcpy(buf, ACL_CREATE);
2317 
2318 	if ((q=strrchr(s, '.')) == NULL ||
2319 	    (*q=0,
2320 	     acl_computeRightsOnFolder(s, buf),
2321 	     buf[0]) == 0)
2322 	{
2323 		free(s);
2324 		folder_err_msg=getarg("CREATEPERMS");
2325 		return -1;
2326 	}
2327 	free(s);
2328 	return 0;
2329 }
2330 
folder_list()2331 void folder_list()
2332 {
2333 	const char	*err_invalid;
2334 	const char	*err_exists;
2335 	const char	*err_cantdelete;
2336 	const char	*msg_hasbeensent;
2337 
2338 	err_invalid=getarg("INVALID");
2339 	err_exists=getarg("EXISTS");
2340 	err_cantdelete=getarg("DELETE");
2341 	msg_hasbeensent=getarg("WASSENT");
2342 
2343 	folder_err_msg=0;
2344 
2345 	if (strcmp(cgi("foldermsg"), "sent") == 0)
2346 		folder_err_msg=msg_hasbeensent;
2347 
2348 	if (*cgi("do.create"))
2349 	{
2350 		const char	*newfoldername=trim_spaces(cgi("foldername"));
2351 		const char	*newdirname=trim_spaces(cgi("dirname"));
2352 		const char	*folderdir=cgi("folderdir");
2353 
2354 		/*
2355 		** New folder names cannot contain .s, and must be considered
2356 		** as valid by maildir_folderpath.
2357 		*/
2358 
2359 		if (!*folderdir)
2360 			folderdir=INBOX;
2361 
2362 		if (!*newfoldername ||
2363 			strchr(newfoldername, '.') ||
2364 			strchr(newdirname, '.'))
2365 			folder_err_msg=err_invalid;
2366 		else
2367 		{
2368 			char	*p;
2369 			char	*futf7;
2370 			char	*dutf7;
2371 			struct maildir_info minfo;
2372 			char *q;
2373 
2374 			futf7=folder_toutf7(newfoldername);
2375 			dutf7=folder_toutf7(newdirname);;
2376 
2377 			p=malloc(strlen(folderdir)+strlen(futf7)
2378 				 +strlen(dutf7)+3);
2379 
2380 			if (!p)	enomem();
2381 			strcpy(p, folderdir);
2382 			if (*dutf7)
2383 			{
2384 				if (*p)	strcat(p, ".");
2385 				strcat(p, dutf7);
2386 			}
2387 			if (*p)	strcat(p, ".");
2388 			strcat(p, futf7);
2389 
2390 			free(futf7);
2391 			free(dutf7);
2392 
2393 			if (maildir_info_imap_find(&minfo, p,
2394 						   login_returnaddr()) < 0)
2395 			{
2396 				folder_err_msg=err_invalid;
2397 			}
2398 			else if (minfo.homedir == NULL ||
2399 				 minfo.maildir == NULL ||
2400 				 (q=maildir_name2dir(minfo.homedir,
2401 						     minfo.maildir)) == NULL)
2402 			{
2403 				maildir_info_destroy(&minfo);
2404 				folder_err_msg=err_invalid;
2405 			}
2406 			else if (access(q, 0) == 0)
2407 			{
2408 				free(q);
2409 				maildir_info_destroy(&minfo);
2410 				folder_err_msg=err_exists;
2411 			}
2412 			else
2413 			{
2414 				if (checkcreate(p, *newdirname != 0) == 0)
2415 				{
2416 					if (maildir_make(q, 0700, 0700, 1))
2417 						folder_err_msg=err_exists;
2418 					else
2419 					{
2420 						char buf[1];
2421 
2422 						buf[0]=0;
2423 						acl_computeRightsOnFolder(p,
2424 									  buf);
2425 						/* Initialize ACLs correctly */
2426 					}
2427 				}
2428 				free(q);
2429 				maildir_info_destroy(&minfo);
2430 			}
2431 		}
2432 	}
2433 
2434 	if (*cgi("do.delete"))
2435 	{
2436 		const char *p=cgi("DELETE");
2437 		char acl_buf[2];
2438 
2439 		strcpy(acl_buf, ACL_DELETEFOLDER);
2440 		acl_computeRightsOnFolder(p, acl_buf);
2441 		if (acl_buf[0] == 0)
2442 			folder_err_msg=getarg("DELETEPERMS");
2443 		else if (mailfilter_folderused(p))
2444 			folder_err_msg=err_cantdelete;
2445 		else if (maildir_delete(p, *cgi("deletecontent")))
2446 			folder_err_msg=err_cantdelete;
2447 		else
2448 			maildir_quota_recalculate(".");
2449 	}
2450 
2451 	if (*cgi("do.subunsub"))
2452 	{
2453 		const char *p=cgi("DELETE");
2454 		char	*pp=strdup(p);
2455 		char *d;
2456 
2457 		if (pp && strncmp(pp, SHARED ".", sizeof(SHARED)) == 0 &&
2458 		    (d=maildir_shareddir(".", pp+sizeof(SHARED))) != 0)
2459 		{
2460 		struct stat	stat_buf;
2461 
2462 			if (stat(d, &stat_buf) == 0)
2463 				maildir_shared_unsubscribe(".",
2464 							   pp+sizeof(SHARED));
2465 			else
2466 				maildir_shared_subscribe(".",
2467 							 pp+sizeof(SHARED));
2468 			free(d);
2469 		}
2470 
2471 		if (pp)
2472 			free(pp);
2473 	}
2474 
2475 	if (*cgi("do.rename"))
2476 	{
2477 		const char *p=cgi("DELETE");
2478 		char	*pp=strdup(p);
2479 		struct maildir_info mifrom, mito;
2480 		const char *qutf7=cgi("renametofolder");
2481 		const char *r=trim_spaces(cgi("renametoname"));
2482 		char	*s;
2483 		char	*rutf7;
2484 
2485 		rutf7=folder_toutf7(r);
2486 
2487 		s=malloc(strlen(qutf7)+strlen(rutf7)+1);
2488 
2489 		if (!s)	enomem();
2490 
2491 		strcat(strcpy(s, qutf7), rutf7);
2492 
2493 		if (strchr(r, '.') == NULL
2494 		    && maildir_info_imap_find(&mifrom, pp,
2495 					      login_returnaddr()) == 0)
2496 		{
2497 			if (maildir_info_imap_find(&mito, s,
2498 						   login_returnaddr()) == 0)
2499 			{
2500 				if (checkrename(pp, s) == 0)
2501 					dorename(pp, &mifrom, &mito,
2502 						 err_invalid,
2503 						 err_cantdelete,
2504 						 err_exists);
2505 				maildir_info_destroy(&mifrom);
2506 			}
2507 			else
2508 			{
2509 				folder_err_msg=err_invalid;
2510 			}
2511 			maildir_info_destroy(&mito);
2512 		}
2513 		else
2514 		{
2515 			folder_err_msg=err_invalid;
2516 		}
2517 		free(rutf7);
2518 		free(pp);
2519 		free(s);
2520 		maildir_quota_recalculate(".");
2521 	}
2522 
2523 	parse_hierarchy(cgi("folderdir"),
2524 			    do_folderlist,
2525 			    do_sharedhierlist);
2526 }
2527 
do_publicfolderlist_cb(struct maildir_newshared_enum_cb * cb)2528 static int do_publicfolderlist_cb(struct maildir_newshared_enum_cb *cb)
2529 {
2530 	struct publicfolderlist_helper *h=
2531 		(struct publicfolderlist_helper *)cb->cb_arg;
2532 
2533 	h->name=strdup(cb->name);
2534 	if (cb->homedir)
2535 		h->homedir=strdup(cb->homedir);
2536 	h->maildir=strdup(cb->maildir);
2537 	return 0;
2538 }
2539 
parse_hierarchy(const char * folderdir,void (* maildir_hier_cb)(const char * pfix,const char * homedir,const char * path,const char * inbox_name),void (* sharedhier_cb)(const char * sharedhier,struct maildir_shindex_cache * cache))2540 static void parse_hierarchy(const char *folderdir,
2541 			    void (*maildir_hier_cb)
2542 			    (const char *pfix, const char *homedir,
2543 			     const char *path, const char *inbox_name),
2544 			    void (*sharedhier_cb)
2545 			    (const char *sharedhier,
2546 			     struct maildir_shindex_cache *cache))
2547 {
2548 	struct maildir_shindex_cache *index;
2549 	const char *indexfile;
2550 	const char *subhierarchy;
2551 	const char *p;
2552 	const char *q;
2553 	size_t l;
2554 	size_t n;
2555 	struct publicfolderlist_helper ph;
2556 	int eof;
2557 
2558 	if (strchr(folderdir, '/'))
2559 		enomem();
2560 
2561 	if (strncmp(folderdir, NEWSHAREDSP, sizeof(NEWSHAREDSP)-1) == 0)
2562 		switch (folderdir[sizeof(NEWSHAREDSP)-1]) {
2563 		case 0:
2564 			verify_shared_index_file=1;
2565 			/* FALLTHRU */
2566 		case '.':
2567 			break;
2568 		default:
2569 			(*maildir_hier_cb)(INBOX, NULL, folderdir, INBOX);
2570 			return;
2571 		}
2572 	else
2573 	{
2574 		(*maildir_hier_cb)(INBOX, NULL, folderdir, INBOX);
2575 		return;
2576 	}
2577 
2578 	index=NULL;
2579 	indexfile=NULL;
2580 	subhierarchy=NULL;
2581 	p=folderdir;
2582 
2583 	memset(&ph, 0, sizeof(ph));
2584 
2585 	while ((index=maildir_shared_cache_read(index, indexfile,
2586 						subhierarchy)) != NULL)
2587 	{
2588 		q=strchr(p, '.');
2589 		if (!q)
2590 			break;
2591 
2592 		p=q+1;
2593 
2594 		if ((q=strchr(p, '.')) != NULL)
2595 			l=q-p;
2596 		else
2597 			l=strlen(p);
2598 
2599 		for (n=0; n<index->nrecords; n++)
2600 		{
2601 			char *m=maildir_info_imapmunge(index->records[n].name);
2602 
2603 			if (!m)
2604 				continue;
2605 
2606 			if (strlen(m) == l &&
2607 			    strncmp(m, p, l) == 0)
2608 			{
2609 				free(m);
2610 				break;
2611 			}
2612 			free(m);
2613 		}
2614 
2615 		if (n >= index->nrecords)
2616 		{
2617 			index=NULL;
2618 			break;
2619 		}
2620 
2621 		index->indexfile.startingpos=index->records[n].offset;
2622 		freeph(&ph);
2623 		if (maildir_newshared_nextAt(&index->indexfile, &eof,
2624 					     &do_publicfolderlist_cb, &ph) ||
2625 		    eof)
2626 		{
2627 			index=NULL;
2628 			break;
2629 		}
2630 
2631 		if (ph.homedir)
2632 		{
2633 			char *loc=maildir_location(ph.homedir,
2634 						   ph.maildir);
2635 			char *m_path;
2636 			char *m_inbox;
2637 
2638 			if (loc)
2639 			{
2640 				while (*p)
2641 				{
2642 					if (*p == '.')
2643 						break;
2644 					++p;
2645 				}
2646 
2647 				m_path=malloc(p-folderdir+1);
2648 				if (!m_path)
2649 					enomem();
2650 				memcpy(m_path, folderdir, p-folderdir);
2651 				m_path[p-folderdir]=0;
2652 
2653 				m_inbox=malloc(strlen(m_path)+1+strlen(p));
2654 				if (!m_inbox)
2655 					enomem();
2656 
2657 				strcat(strcpy(m_inbox, m_path), p);
2658 
2659 				savepath(m_path, loc);
2660 				(*maildir_hier_cb)(m_path, loc, m_inbox,
2661 						  m_path);
2662 				free(loc);
2663 				free(m_path);
2664 				free(m_inbox);
2665 			}
2666 			freeph(&ph);
2667 			return;
2668 		}
2669 
2670 		indexfile=ph.maildir;
2671 		subhierarchy=index->records[n].name;
2672 	}
2673 
2674 	freeph(&ph);
2675 
2676 	(*sharedhier_cb)(folderdir, index);
2677 }
2678 
do_sharedhierlist(const char * folderdir,struct maildir_shindex_cache * index)2679 static void do_sharedhierlist(const char *folderdir,
2680 			      struct maildir_shindex_cache *index)
2681 {
2682 	const char *p;
2683 	const char *q;
2684 	size_t n;
2685 	struct publicfolderlist_helper ph;
2686 	const char *folders_img;
2687 	const char *name_inbox;
2688 	int eof;
2689 	char *url, *url2;
2690 
2691 	p=strrchr(folderdir, '.');
2692 
2693 	if (p)
2694 		++p;
2695 	else p=folderdir;
2696 
2697 	folders_img=getarg("FOLDERSICON");
2698 	name_inbox=getarg("INBOX");
2699 
2700 	memset(&ph, 0, sizeof(ph));
2701 
2702        	printf("<table width=\"100%%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\" class=\"folderlist\">\n"
2703 	       "<tr><td align=\"left\" "
2704 	       "class=\"folderparentdir\">%s&lt;&lt;&lt;&nbsp;",
2705 	       folders_img);
2706 
2707 	if (strcmp(folderdir, NEWSHAREDSP) == 0)
2708 	{
2709 		printf("<a href=\"");
2710 		output_scriptptrget();
2711 		printf("&amp;form=folders&amp;folder=INBOX\">");
2712 		print_safe(name_inbox);
2713 		printf("</a>");
2714 	}
2715 	else
2716 	{
2717 		printf("<a href=\"");
2718 		output_scriptptrget();
2719 		printf("&amp;folderdir=");
2720 		output_urlencoded(NEWSHAREDSP);
2721 		printf("&amp;form=folders&amp;folder=INBOX\">%s</a>",
2722 		       getarg("PUBLICFOLDERS"));
2723 	}
2724 
2725 	for (q=folderdir; q<p; )
2726 	{
2727 		const char *r;
2728 		char *s;
2729 
2730 		if (*q == '.')
2731 		{
2732 			++q;
2733 			continue;
2734 		}
2735 
2736 		for (r=q; *r; r++)
2737 			if (*r == '.')
2738 				break;
2739 
2740 		if (q != folderdir)
2741 		{
2742 			printf(".<a href=\"");
2743 			output_scriptptrget();
2744 			printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir=");
2745 
2746 
2747 			s=malloc(r-folderdir+1);
2748 			if (!s)
2749 				enomem();
2750 
2751 			memcpy(s, folderdir, r-folderdir);
2752 			s[r-folderdir]=0;
2753 
2754 			output_urlencoded(s);
2755 			printf("\">");
2756 			free(s);
2757 
2758 			s=malloc(r-q+1);
2759 			if (!s)
2760 				enomem();
2761 			memcpy(s, q, r-q);
2762 			s[r-q]=0;
2763 			list_folder(s);
2764 			free(s);
2765 			printf("</a>");
2766 		}
2767 		q=r;
2768 	}
2769 
2770 	printf("</td></tr>\n");
2771 
2772 
2773 	while (*q && *q != '.')
2774 		++q;
2775 
2776 	url=malloc(q-folderdir+1);
2777 	if (!url)
2778 		enomem();
2779 	memcpy(url, folderdir, q-folderdir);
2780 	url[q-folderdir]=0;
2781 
2782 	for (n=0; index && n<index->nrecords; n++)
2783 	{
2784 		freeph(&ph);
2785 
2786 		if (n == 0)
2787 			index->indexfile.startingpos=0;
2788 
2789 		if ((n == 0 ? &maildir_newshared_nextAt:
2790 		     &maildir_newshared_next)(&index->indexfile, &eof,
2791 					      &do_publicfolderlist_cb, &ph) ||
2792 		    eof)
2793 		{
2794 			break;
2795 		}
2796 
2797 		if (ph.homedir)
2798 		{
2799 			char *d=maildir_location(ph.homedir, ph.maildir);
2800 
2801 			if (d)
2802 			{
2803 				if (maildir_info_suppress(d))
2804 				{
2805 					free(d);
2806 					continue;
2807 				}
2808 				free(d);
2809 			}
2810 		}
2811 
2812 		printf("<tr class=\"foldersubdir\"><td align=\"left\">%s"
2813 		       "&gt;&gt;&gt;&nbsp;<a href=\"", folders_img);
2814 		output_scriptptrget();
2815 		printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir=");
2816 
2817 		output_urlencoded(url);
2818 
2819 		url2=maildir_info_imapmunge(ph.name);
2820 
2821 		if (!url2)
2822 			enomem();
2823 		printf(".");
2824 		output_urlencoded(url2);
2825 
2826 		printf("\">");
2827 		list_folder(url2);
2828 		free(url2);
2829 		printf("</a></td></tr>\n");
2830 	}
2831 	free(url);
2832 	freeph(&ph);
2833 	printf("</table>\n");
2834 }
2835 
do_folderlist(const char * inbox_pfix,const char * homedir,const char * folderdir,const char * inbox_name)2836 static void do_folderlist(const char *inbox_pfix,
2837 			  const char *homedir,
2838 			  const char *folderdir,
2839 			  const char *inbox_name)
2840 {
2841 	const char	*name_inbox;
2842 	const char	*name_drafts;
2843 	const char	*name_sent;
2844 	const char	*name_trash;
2845 	const char	*folder_img;
2846 	const char	*folders_img;
2847 	const char	*unread_label;
2848 	const char	*acl_img;
2849 	char acl_buf[2];
2850 	char	**folders;
2851 	size_t	i;
2852 	unsigned nnew, nother;
2853 	size_t folderdir_l;
2854 
2855 	name_inbox=getarg("INBOX");
2856 	name_drafts=getarg("DRAFTS");
2857 	name_sent=getarg("SENT");
2858 	name_trash=getarg("TRASH");
2859 	folder_img=getarg("FOLDERICON");
2860 	folders_img=getarg("FOLDERSICON");
2861 	sqwebmail_folder=0;
2862 	unread_label=getarg("UNREAD");
2863 	acl_img=maildir_newshared_disabled ? NULL : getarg("ACLICON");
2864 
2865        	printf("<table width=\"100%%\" border=\"0\" cellpadding=\"2\" cellspacing=\"0\" class=\"folderlist\">\n");
2866 
2867 	maildir_listfolders(inbox_pfix, homedir, &folders);
2868 
2869 	if (*folderdir && strcmp(folderdir, INBOX))
2870 	{
2871 		char *parentfolder;
2872 		size_t	i;
2873 		char *q, *r;
2874 		const char *c;
2875 
2876 		if (strncmp(folderdir, SHARED ".", sizeof(SHARED)) == 0)
2877 		{
2878 			for (c=folderdir; *c; c++)
2879 				if (*c == '.')
2880 					break;
2881 
2882 			r=malloc(strlen(inbox_pfix)+strlen(c)+1);
2883 			if (!r)
2884 				enomem();
2885 			strcat(strcpy(r, inbox_pfix), c);
2886 
2887 			parentfolder=get_parent_folder(r);
2888 			free(r);
2889 		}
2890 		else
2891 			parentfolder=get_parent_folder(folderdir);
2892 
2893 		for (q=parentfolder; *q; q++)
2894 			if (*q == '.')
2895 				break;
2896 
2897 		printf("<tr><td align=\"left\" colspan=\"2\" class=\"folderparentdir\">%s", folders_img);
2898 		printf("&lt;&lt;&lt;&nbsp;");
2899 
2900 #if 0
2901 		printf("<a href=\"");
2902 		output_scriptptrget();
2903 		printf("&amp;folderdir=");
2904 		output_urlencoded(inbox_pfix);
2905 		printf("&amp;form=folders&amp;folder=INBOX\">");
2906 		print_safe(inbox_name);
2907 		printf("</a>");
2908 #endif
2909 
2910 		i=0;
2911 		while (parentfolder[i])
2912 		{
2913 		char	*p=strchr(parentfolder+i, '.');
2914 		int	c;
2915 
2916 			if (!p)	p=parentfolder+strlen(parentfolder);
2917 			c= *p;
2918 			*p=0;
2919 
2920 			if (strchr(parentfolder, '.'))
2921 				printf(".");
2922 			printf("<a href=\"");
2923 			output_scriptptrget();
2924 			printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir=");
2925 			output_urlencoded(parentfolder);
2926 			printf("\">");
2927 			if (strcmp(parentfolder, NEWSHAREDSP) == 0)
2928 				printf("%s", getarg("PUBLICFOLDERS"));
2929 			else
2930 				list_folder_xlate(parentfolder,
2931 						  parentfolder+i,
2932 						  name_inbox,
2933 						  name_drafts,
2934 						  name_sent,
2935 						  name_trash);
2936 			printf("</a>");
2937 			if ( (*p=c) != 0)	++p;
2938 			i=p-parentfolder;
2939 		}
2940 		printf("</td></tr>\n");
2941 		free(parentfolder);
2942 	}
2943 	else if (strcmp(inbox_pfix, INBOX))
2944 	{
2945 		size_t i;
2946 		char *p;
2947 		char *q;
2948 
2949 		printf("<tr><td align=\"left\" colspan=\"2\" class=\"folderparentdir\">%s&lt;&lt;&lt;&nbsp;", folders_img);
2950 
2951 		p=strdup(inbox_pfix);
2952 		if (!p)
2953 			enomem();
2954 
2955 		if ((q=strrchr(p, '.')) != 0)
2956 			*q=0;
2957 
2958 		for (i=0; p[i]; )
2959 		{
2960 			size_t j;
2961 			char save_ch;
2962 
2963 			for (j=i; p[j]; j++)
2964 				if (p[j] == '.')
2965 					break;
2966 
2967 
2968 			save_ch=p[j];
2969 			p[j]=0;
2970 
2971 			if (i)
2972 				printf(".");
2973 
2974 			printf("<a href=\"");
2975 			output_scriptptrget();
2976 			printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir=");
2977 			output_urlencoded(p);
2978 			printf("\">");
2979 
2980 			if (strcmp(p, NEWSHAREDSP) == 0)
2981 				printf("%s", getarg("PUBLICFOLDERS"));
2982 			else
2983 				list_folder(p+i);
2984 			printf("</a>");
2985 
2986 			p[j]=save_ch;
2987 
2988 			if (save_ch)
2989 				++j;
2990 			i=j;
2991 		}
2992 		printf("</td></tr>\n");
2993 		free(p);
2994 	}
2995 
2996 
2997 	if (!folderdir || strchr(folderdir, '.') == 0)
2998 	{
2999 		folderdir=inbox_pfix;
3000 	}
3001 
3002 	folderdir_l=strlen(folderdir);
3003 
3004 	for (i=0; folders[i]; i++)
3005 	{
3006 		const	char *p;
3007 		const	char *shortname=folders[i];
3008 
3009 		size_t	j;
3010 		const char *pfix;
3011 		int isshared=0;
3012 		int isunsubscribed=0;
3013 		const char	*img=folder_img;
3014 
3015 		pfix="&gt;&gt;&gt;";
3016 
3017 		if (strncmp(shortname, SHARED ".",
3018 			    sizeof(SHARED)) == 0)
3019 		{
3020 			char	*dir;
3021 			struct	stat	stat_buf;
3022 
3023 			isshared=1;
3024 			pfix="+++";
3025 
3026 			dir=maildir_shareddir(".",
3027 					      shortname+sizeof(SHARED));
3028 			if (!dir)	continue;
3029 			if (stat(dir, &stat_buf))
3030 				isunsubscribed=1;
3031 			free(dir);
3032 		}
3033 
3034 		if (strcmp(shortname, inbox_name) == 0 &&
3035 		    strcmp(folderdir, inbox_name) == 0)
3036 		{
3037 			/* List INBOX at the top level */
3038 
3039 			strcpy(acl_buf, ACL_LOOKUP);
3040 			acl_computeRightsOnFolder(shortname, acl_buf);
3041 			if (acl_buf[0] == 0)
3042 				continue;
3043 		}
3044 		else
3045 		{
3046 			if (strcmp(folderdir, INBOX) == 0 &&
3047 			    strncmp(shortname, SHARED ".", sizeof(SHARED)) == 0)
3048 			{
3049 				shortname += sizeof(SHARED);
3050 			}
3051 			else
3052 			{
3053 				if (memcmp(shortname, folderdir, folderdir_l) ||
3054 				    shortname[folderdir_l] != '.')
3055 				{
3056 					continue;
3057 				}
3058 
3059 				strcpy(acl_buf, ACL_LOOKUP);
3060 				acl_computeRightsOnFolder(shortname, acl_buf);
3061 				if (acl_buf[0] == 0)
3062 					continue;
3063 
3064 				shortname += folderdir_l;
3065 				++shortname;
3066 			}
3067 
3068 			if ((p=strchr(shortname, '.')) != 0)
3069 			{
3070 				char *s;
3071 				char	*t;
3072 				unsigned tot_nnew, tot_nother;
3073 
3074 				s=malloc(p-folders[i]+1);
3075 				if (!s)
3076 					enomem();
3077 				memcpy(s, folders[i], p-folders[i]);
3078 				s[p-folders[i]]=0;
3079 
3080 				printf("<tr class=\"foldersubdir\"><td align=\"left\">");
3081 				if (acl_img)
3082 				{
3083 					printf("<a href=\"");
3084 					output_scriptptrget();
3085 					printf("&amp;form=acl&amp;folder=");
3086 					output_urlencoded(s);
3087 					printf("\">%s</a>&nbsp;", acl_img);
3088 				}
3089 				printf("%s%s&nbsp;<a href=\"",
3090 				       folders_img, pfix);
3091 
3092 				output_scriptptrget();
3093 				printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir=");
3094 				output_urlencoded(s);
3095 				printf("\">");
3096 				free(s);
3097 
3098 				t=malloc(p-shortname+1);
3099 				if (!t)	enomem();
3100 				memcpy(t, shortname, p-shortname);
3101 				t[p-shortname]=0;
3102 				list_folder_xlate(folders[i],
3103 						  t,
3104 						  name_inbox,
3105 						  name_drafts,
3106 						  name_sent,
3107 						  name_trash);
3108 				free(t);
3109 				printf("</a>");
3110 
3111 				tot_nnew=0;
3112 				tot_nother=0;
3113 
3114 				j=i;
3115 				while (folders[j] && memcmp(folders[j], folders[i],
3116 							    p-folders[i]+1) == 0)
3117 				{
3118 					strcpy(acl_buf, ACL_LOOKUP);
3119 					acl_computeRightsOnFolder(folders[j],
3120 								  acl_buf);
3121 					if (acl_buf[0] == 0)
3122 						continue;
3123 
3124 					maildir_count(folders[j], &nnew, &nother);
3125 					++j;
3126 					tot_nnew += nnew;
3127 					tot_nother += nother;
3128 				}
3129 				i=j-1;
3130 				if (tot_nnew)
3131 				{
3132 					printf(" <span class=\"subfolderlistunread\">");
3133 					printf(unread_label, tot_nnew);
3134 					printf("</span>");
3135 				}
3136 				printf("</td><td align=\"right\" valign=\"top\"><span class=\"subfoldercnt\">%d</span>&nbsp;&nbsp;</td></tr>\n\n",
3137 				       tot_nnew + tot_nother);
3138 				continue;
3139 			}
3140 		}
3141 
3142 		nnew=0;
3143 		nother=0;
3144 
3145 		if (!isunsubscribed)
3146 			maildir_count(folders[i], &nnew, &nother);
3147 
3148 		printf("<tr%s><td align=\"left\" valign=\"top\">",
3149 			isunsubscribed ? " class=\"folderunsubscribed\"":"");
3150 
3151 		if (acl_img)
3152 		{
3153 			printf("<a href=\"");
3154 			output_scriptptrget();
3155 			printf("&amp;form=acl&amp;folder=");
3156 			output_urlencoded(folders[i]);
3157 			printf("\">%s</a>&nbsp", acl_img);
3158 		}
3159 
3160 		printf("%s&nbsp;<input type=\"radio\" name=\"DELETE\" value=\"", img);
3161 		output_attrencoded(folders[i]);
3162 		printf("\" />&nbsp;");
3163 		if (!isunsubscribed)
3164 		{
3165 			printf("<a class=\"folderlink\" href=\"");
3166 			output_scriptptrget();
3167 			printf("&amp;form=folder&amp;folder=");
3168 			output_urlencoded(folders[i]);
3169 			printf("\">");
3170 		}
3171 
3172 		list_folder_xlate(folders[i],
3173 				  strcmp(folders[i], inbox_name) == 0
3174 				  ? INBOX:shortname,
3175 				  name_inbox,
3176 				  name_drafts,
3177 				  name_sent,
3178 				  name_trash);
3179 		if (!isunsubscribed)
3180 			printf("</a>");
3181 		if (nnew)
3182 		{
3183 			printf(" <span class=\"folderlistunread\">");
3184 			printf(unread_label, nnew);
3185 			printf("</span>");
3186 		}
3187 		printf("</td><td align=\"right\" valign=\"top\">");
3188 
3189 		if (!isunsubscribed)
3190 		{
3191 			printf("<span class=\"foldercnt\">%d</span>&nbsp;&nbsp;",
3192 			       nnew + nother);
3193 		}
3194 		else
3195 		printf("&nbsp;\n");
3196 		printf("</td></tr>\n\n");
3197 	}
3198 	maildir_freefolders(&folders);
3199 
3200 	if (strcmp(folderdir, INBOX) == 0 && !maildir_newshared_disabled)
3201 	{
3202 		char *sp=cgiurlencode(NEWSHAREDSP);
3203 
3204 		printf("<tr class=\"foldersubdir\"><td align=\"left\">%s&gt;&gt;&gt;&nbsp;<a href=\"", folders_img);
3205 		output_scriptptrget();
3206 		printf("&amp;form=folders&amp;folder=INBOX&amp;folderdir="
3207 		       "%s\">%s</a>"
3208 		       "</td><td>&nbsp;</td></tr>\n\n",
3209 		       sp,
3210 		       getarg("PUBLICFOLDERS"));
3211 		free(sp);
3212 	}
3213 	printf("</table>\n");
3214 }
3215 
folder_list2()3216 void folder_list2()
3217 {
3218 	if (folder_err_msg)
3219 	{
3220 		printf("%s\n", folder_err_msg);
3221 	}
3222 }
3223 
3224 static void folder_rename_dest_fake(const char *dummy1,
3225 				    struct maildir_shindex_cache *dummy2);
3226 static void folder_rename_dest_real(const char *inbox_pfix,
3227 				    const char *homedir,
3228 				    const char *cur_folder,
3229 				    const char *inbox_name);
3230 
folder_rename_list()3231 void folder_rename_list()
3232 {
3233 	parse_hierarchy(cgi("folderdir"), folder_rename_dest_real,
3234 			folder_rename_dest_fake);
3235 }
3236 
folder_rename_dest_fake(const char * dummy1,struct maildir_shindex_cache * dummy2)3237 static void folder_rename_dest_fake(const char *dummy1,
3238 				    struct maildir_shindex_cache *dummy2)
3239 {
3240 }
3241 
folder_rename_dest_real(const char * inbox_pfix,const char * homedir,const char * cur_folder,const char * inbox_name)3242 static void folder_rename_dest_real(const char *inbox_pfix,
3243 				    const char *homedir,
3244 				    const char *cur_folder,
3245 				    const char *inbox_name)
3246 {
3247 	char	**folders;
3248 	int	i;
3249 	size_t pl=strlen(inbox_pfix);
3250 
3251 	printf("<select name=\"renametofolder\">\n");
3252 	printf("<option value=\"%s.\">", inbox_pfix);
3253 	printf("( ... )");
3254 	printf("</option>\n");
3255 
3256 	maildir_listfolders(inbox_pfix, homedir, &folders);
3257 	for (i=0; folders[i]; i++)
3258 	{
3259 		const char *p=folders[i];
3260 		char	*q;
3261 		size_t	ql;
3262 		char acl_buf[2];
3263 
3264 		if (strncmp(p, inbox_pfix, pl) == 0)
3265 			switch (p[pl]) {
3266 			case '.':
3267 				break;
3268 			default:
3269 				continue;
3270 			}
3271 		else
3272 			continue;
3273 
3274 		p += pl+1;
3275 
3276 		p=strrchr(p, '.');
3277 		if (!p)	continue;
3278 		q=malloc(p-folders[i]+1);
3279 		if (!q)	enomem();
3280 		memcpy(q, folders[i], p-folders[i]);
3281 		q[p-folders[i]]=0;
3282 		strcpy(acl_buf, ACL_CREATE);
3283 		acl_computeRightsOnFolder(q, acl_buf);
3284 		if (acl_buf[0])
3285 		{
3286 			printf("<option value=\"");
3287 			output_attrencoded(q);
3288 			printf(".\"%s>",
3289 			       strcmp(q, cgi("folderdir")) == 0
3290 			       ? " selected='selected'":"");
3291 			list_folder(strchr(q, '.')+1);
3292 			printf(".</option>\n");
3293 		}
3294 		ql=strlen(q);
3295 		while (folders[++i])
3296 		{
3297 			if (memcmp(folders[i], q, ql) ||
3298 				folders[i][ql] != '.' ||
3299 				strchr(folders[i]+ql+1, '.'))	break;
3300 		}
3301 		--i;
3302 		free(q);
3303 	}
3304 	maildir_freefolders(&folders);
3305 	printf("</select>\n");
3306 }
3307 
folder_download(const char * folder,size_t pos,const char * mimeid)3308 void folder_download(const char *folder, size_t pos, const char *mimeid)
3309 {
3310 	char	*filename;
3311 	FILE *fp=NULL;
3312 	int	fd;
3313 
3314 	filename=get_msgfilename(folder, &pos);
3315 
3316 	fd=maildir_semisafeopen(filename, O_RDONLY, 0);
3317 	if (fd >= 0)
3318 	{
3319 		if ((fp=fdopen(fd, "r")) == 0)
3320 			close(fd);
3321 	}
3322 
3323 	if (!fp)
3324 	{
3325 		free(filename);
3326 		error("Message not found.");
3327 		return;
3328 	}
3329 	free(filename);
3330 
3331 	cginocache();
3332 	msg2html_download(fp, mimeid, *cgi("download") == '1');
3333 
3334 	fclose(fp);
3335 }
3336 
folder_showtransfer()3337 void folder_showtransfer()
3338 {
3339 	const char	*deletelab, *purgelab, *movelab, *golab;
3340 
3341 	deletelab=getarg("DELETE");
3342 	purgelab=getarg("PURGE");
3343 	movelab=getarg("ORMOVETO");
3344 	golab=getarg("GO");
3345 	folder_inbox=getarg("INBOX");
3346 	folder_drafts=getarg("DRAFTS");
3347 	folder_trash=getarg("TRASH");
3348 	folder_sent=getarg("SENT");
3349 
3350 	printf("<input type=\"hidden\" name=\"pos\" value=\"%s\" />", cgi("pos"));
3351 	if ((strcmp(sqwebmail_folder, INBOX "." TRASH) == 0) && (strlen(getarg("PURGEALL"))))
3352 	    printf("<input type=\"submit\" name=\"cmdpurgeall\" value=\"%s\" onclick=\"javascript: return deleteAll();\" />",
3353 		getarg("PURGEALL"));
3354 	printf("<input type=\"submit\" name=\"cmddel\" value=\"%s\" />%s<select name=\"moveto\">",
3355 		strcmp(sqwebmail_folder, INBOX "." TRASH) == 0
3356 		? purgelab:deletelab,
3357 		movelab);
3358 
3359 	show_transfer_dest(sqwebmail_folder);
3360 	printf("</select><input type=\"submit\" name=\"cmdmove\" value=\"%s\" />\n",
3361 		golab);
3362 }
3363 
folder_showquota()3364 void folder_showquota()
3365 {
3366 	const char	*quotamsg;
3367 	struct maildirsize quotainfo;
3368 
3369 	quotamsg=getarg("QUOTAUSAGE");
3370 
3371 	if (maildir_openquotafile(&quotainfo, "."))
3372 		return;
3373 
3374 	if (quotainfo.quota.nmessages != 0 ||
3375 	    quotainfo.quota.nbytes != 0)
3376 		printf(quotamsg, maildir_readquota(&quotainfo));
3377 
3378 	maildir_closequotafile(&quotainfo);
3379 }
3380 
3381 void
folder_cleanup()3382 folder_cleanup()
3383 {
3384 	msg_purgelab=0;
3385 	msg_folderlab=0;
3386 	folder_drafts=0;
3387 	folder_inbox=0;
3388 	folder_sent=0;
3389 	folder_trash=0;
3390 	msg_forwardattlab=0;
3391 	msg_forwardlab=0;
3392 	msg_fullheaderlab=0;
3393 	msg_golab=0;
3394 	msg_movetolab=0;
3395 	msg_nextlab=0;
3396 	msg_prevlab=0;
3397 	msg_deletelab=0;
3398 	msg_posfile=0;
3399 	msg_replyalllab=0;
3400 	msg_replylistlab=0;
3401 	msg_replylab=0;
3402 	folder_err_msg=0;
3403 	msg_msglab=0;
3404 	msg_add=0;
3405 
3406 	msg_type=0;
3407 	initnextprevcnt=0;
3408 	msg_hasprev=0;
3409 	msg_hasnext=0;
3410 	msg_pos=0;
3411 	msg_count=0;
3412 }
3413 
3414 
3415 /*
3416 ** Unicode-aware truncation of text at a specified column, if text length
3417 ** exceeds the given # of characters.
3418 */
3419 
truncate_at(const char * str,const struct unicode_info * uiptr,size_t ncols)3420 static char *truncate_at(const char *str,
3421 			 const struct unicode_info *uiptr,
3422 			 size_t ncols)
3423 {
3424 	unicode_char *uc;
3425 	size_t n;
3426 	size_t cols, tp=0;
3427 	char *retbuf;
3428 
3429 	if (!str)
3430 		return NULL;
3431 
3432 	uc= (*unicode_UTF8.c2u)(&unicode_UTF8, str, NULL);
3433 
3434 	if (!uc)
3435 		return NULL;
3436 
3437 	for (cols=0, n=0; uc[n]; n++) {
3438 
3439 		cols += unicode_wcwidth(uc[n]);
3440 
3441 		if (!tp && cols > ncols-3)
3442 			tp = n;
3443 	}
3444 
3445 	if (cols > ncols)
3446 	{
3447 		uc = realloc(uc, sizeof(unicode_char) * (tp+4));
3448 		if (uc == 0) enomem();
3449 		uc[tp]='.';
3450 		uc[tp+1]='.';
3451 		uc[tp+2]='.';
3452 		uc[tp+3]=0;
3453 	}
3454 
3455 	retbuf=(*uiptr->u2c)(uiptr, uc, NULL);
3456 
3457 	free(uc);
3458 	return retbuf;
3459 }
3460