1 /*
2 ** Copyright 1998 - 2010 Double Precision, Inc.  See COPYING for
3 ** distribution information.
4 */
5 
6 
7 /*
8 ** $Id: attachments.c,v 1.50 2010/10/04 23:08:38 mrsam Exp $
9 */
10 #include	"config.h"
11 #include	"sqwebmail.h"
12 #include	"cgi/cgi.h"
13 #include	"sqconfig.h"
14 #include	"maildir.h"
15 #include	"folder.h"
16 #include	"pref.h"
17 #include	"rfc822/rfc822.h"
18 #include	"rfc822/rfc2047.h"
19 #include	"rfc2045/rfc2045.h"
20 #include	"token.h"
21 #include	"newmsg.h"
22 #include	"gpg.h"
23 #include	"gpglib/gpglib.h"
24 #include	"courierauth.h"
25 #include	<stdio.h>
26 #include	<stdlib.h>
27 #include	<ctype.h>
28 #include	<signal.h>
29 #include	<errno.h>
30 #include	<fcntl.h>
31 #if	HAVE_UNISTD_H
32 #include        <unistd.h>
33 #endif
34 #include	<sys/types.h>
35 #include	<sys/stat.h>
36 #if HAVE_SYS_WAIT_H
37 #include	<sys/wait.h>
38 #endif
39 #ifndef WEXITSTATUS
40 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
41 #endif
42 #ifndef WIFEXITED
43 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
44 #endif
45 
46 #include	"maildir/maildirmisc.h"
47 
48 #include	"htmllibdir.h"
49 #include	"unicode/unicode.h"
50 
51 extern char *newmsg_alladdrs(FILE *);
52 extern void newmsg_copy_content_headers(FILE *fp);
53 
54 extern char *alloc_filename(const char *, const char *, const char *);
55 extern const char *showsize(unsigned long);
56 extern void output_attrencoded(const char *);
57 extern int newdraftfd;
58 extern void newmsg_hiddenheader(const char *, const char *);
59 extern void output_scriptptrget();
60 extern void output_urlencoded(const char *);
61 extern void sendmsg_done();
62 
63 extern char *multipart_boundary_create();
64 extern int multipart_boundary_checkf(const char *, FILE *);
65 extern int ishttps();
66 
67 extern void newmsg_create_multipart(int, const char *, const char *);
68 extern void newmsg_copy_nonmime_headers(FILE *);
69 
70 extern const char *sqwebmail_content_charset;
71 extern const char *sqwebmail_content_language;
72 
73 static void attachment_showname(const char *);
74 
75 #define HASTEXTPLAIN(q) (rfc2045_searchcontenttype((q), "text/plain") != NULL)
76 /* Also in newmsg_create.c */
77 
78 
max_attach()79 static off_t max_attach()
80 {
81 	off_t n=0;
82 	const char *p=getenv("SQWEBMAIL_MAXMSGSIZE");
83 
84 	if (p)
85 		n=atol(p);
86 
87 	if (n < MAXMSGSIZE)
88 		n=MAXMSGSIZE;
89 	return n;
90 }
91 
92 
attachments_head(const char * folder,const char * pos,const char * draft)93 void attachments_head(const char *folder, const char *pos, const char *draft)
94 {
95 char *filename;
96 FILE	*fp;
97 struct	rfc2045 *rfcp;
98 int	cnt=0;
99 struct	rfc2045 *q;
100 int	foundtextplain=0;
101 const char	*noattach_lab=getarg("NOATTACH");
102 const char	*quotaerr=getarg("QUOTAERR");
103 const char	*limiterr=getarg("LIMITERR");
104 off_t	dummy;
105 int	fd2;
106 const struct unicode_info *uiptr=unicode_find(sqwebmail_content_charset);
107 
108 	CHECKFILENAME(draft);
109 	filename=maildir_find(INBOX "." DRAFTS, draft);
110 	if (!filename)	return;
111 
112 	fd2=maildir_safeopen(filename, O_RDONLY, 0);
113 
114 	fp=0;
115 	if (fd2 >= 0)
116 	{
117 		fp=fdopen(fd2, "r");
118 		if (fp == NULL)
119 			close(fd2);
120 	}
121 
122 	if (fp == NULL)
123 	{
124 		free(filename);
125 		return;
126 	}
127 
128 	rfcp=rfc2045_fromfp(fp);
129 	fclose(fp);
130 	free(filename);
131 
132 	if (strcmp(cgi("error"), "quota") == 0)
133 	{
134 		printf("%s", quotaerr);
135 	}
136 
137 	if (strcmp(cgi("error"), "limits") == 0)
138 	{
139 		printf(limiterr, (unsigned long)(max_attach() / (1024 * 1024)));
140 	}
141 
142 	if (strcmp(cgi("error"), "makemime") == 0)
143 	{
144 		printf(getarg("MAKEMIMEERR"), MAKEMIME);
145 	}
146 	newmsg_hiddenheader("pos", pos);
147 	newmsg_hiddenheader("draft", draft);
148 	tokennew();
149 	printf("<table width=\"100%%\" border=\"0\">");
150 
151 	if (rfcp)
152 	{
153 		const char *content_type;
154 		const char *content_transfer_encoding;
155 		const char *charset;
156 
157 		rfc2045_mimeinfo(rfcp, &content_type,
158 			&content_transfer_encoding, &charset);
159 
160 		if (content_type &&
161 		    strcmp(content_type, "multipart/alternative") == 0)
162 			rfcp=NULL;
163 
164 		/* No attachments here */
165 	}
166 
167 	for (q=rfcp ? rfcp->firstpart:0; q; q=q->next)
168 	{
169 	const char *content_type;
170 	const char *content_transfer_encoding;
171 	const char *charset;
172 	const char *name;
173 	const char *cn;
174 	char *content_name;
175 
176 	off_t start_pos, end_pos, start_body;
177 
178 		if (q->isdummy)	continue;
179 
180 		rfc2045_mimeinfo(q, &content_type,
181 			&content_transfer_encoding, &charset);
182 		if (!foundtextplain && HASTEXTPLAIN(q))
183 		{
184 			foundtextplain=1;
185 			continue;
186 		}
187 		rfc2045_mimepos(q, &start_pos, &end_pos, &start_body,
188 			&dummy, &dummy);
189 
190 		++cnt;
191 		printf("<tr><td align=\"left\"><input type=\"checkbox\" name=\"del%d\" id=\"del%d\" />&nbsp;",
192 			cnt, cnt);
193 
194 		if (rfc2231_udecodeType(q, "name", uiptr, &content_name) < 0 ||
195 		    rfc2231_udecodeDisposition(q, "filename", uiptr,
196 					       &content_name) < 0)
197 			content_name=NULL;
198 
199 		if (!content_name &&
200 		    ((cn=rfc2045_getattr(q->content_type_attr, "name")) ||
201 		     (cn=rfc2045_getattr(q->content_disposition_attr,
202 					 "filename"))))
203 		{
204 			content_name =
205 				rfc822_display_hdrvalue_tobuf("subject",
206 							      cn,
207 							      uiptr->chset,
208 							      NULL,
209 							      NULL);
210 		}
211 
212 		if ((!content_name || !*content_name) &&
213 		    strcmp(content_type, "application/pgp-keys") == 0)
214 			name=getarg("KEYDESCR");
215 		else
216 		{
217 			name=content_name;
218 		}
219 
220 		attachment_showname(name);
221 		if (content_name)
222 			free(content_name);
223 		printf("</td><td align=\"left\">&nbsp;&nbsp;<label for=\"del%d\">", cnt);
224 		output_attrencoded( content_type );
225 		printf("</label></td><td align=\"right\">%s<br /></td></tr>",
226 			showsize(end_pos - start_body));
227 	}
228 
229 	if (cnt == 0)
230 		printf("<tr><td align=\"center\">%s<br /></td></tr>\n",
231 			noattach_lab);
232 	printf("</table>\n");
233 }
234 
attachments_opts(const char * draft)235 void attachments_opts(const char *draft)
236 {
237 	char *filename;
238 	FILE *fp;
239 
240 	CHECKFILENAME(draft);
241 
242 	filename=maildir_find(INBOX "." DRAFTS, draft);
243 	if (!filename)
244 		return;
245 	fp=fopen(filename, "r");
246 	free(filename);
247 	if (!fp)
248 		return;
249 
250 	printf("<label><input type=\"checkbox\" name=\"fcc\"%s />%s</label><br />",
251 	       pref_noarchive ? "":" checked=\"checked\"",
252 	       getarg("PRESERVELAB"));
253 	if (auth_getoptionenvint("wbnodsn") == 0)
254 		printf("<label><input type=\"checkbox\" name=\"dsn\" />%s</label><br />",
255 		       getarg("DSN"));
256 
257 	if (libmail_gpg_has_gpg(GPGDIR) == 0)
258 	{
259 		char *all_addr;
260 
261 		printf("<label><input type=\"checkbox\" "
262 		       "name=\"sign\" />%s</label><select name=\"signkey\">",
263 		       getarg("SIGNLAB"));
264 		gpgselectkey();
265 		printf("</select><br />\n");
266 
267 		all_addr=newmsg_alladdrs(fp);
268 
269 		printf("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
270 		       "<tr valign=\"middle\"><td><input type=\"checkbox\""
271 		       " name=\"encrypt\" id=\"encrypt\" /></td><td><label for=\"encrypt\">%s</label></td>"
272 		       "<td><select size=\"4\" multiple=\"multiple\" name=\"encryptkey\">",
273 		       getarg("ENCRYPTLAB"));
274 		gpgencryptkeys(all_addr);
275 		printf("</select></td></tr>\n");
276 
277 		if (ishttps())
278 			printf("<tr valign=\"middle\"><td>&nbsp;</td><td>%s</td><td><input type=\"password\" name=\"passphrase\" /></td></tr>\n",
279 			       getarg("PASSPHRASE"));
280 
281 		printf("</table><br />\n");
282 		if (all_addr)
283 			free(all_addr);
284 	}
285 	fclose(fp);
286 }
287 
attachment_showname(const char * name)288 static void attachment_showname(const char *name)
289 {
290 	if (!name || !*name)	name="[attachment]";	/* Eh??? */
291 	output_attrencoded(name);
292 }
293 
attachment_open(const char * draft,FILE ** fp,int * fd2,struct rfc2045 ** rfcp)294 static void attachment_open(const char *draft,
295 	FILE **fp,
296 	int	*fd2,
297 	struct rfc2045 **rfcp)
298 {
299 char	*oldname=maildir_find(INBOX "." DRAFTS, draft);
300 
301 	if (!oldname)	enomem();
302 
303 	*fd2=maildir_safeopen(oldname, O_RDONLY, 0);
304 
305 	*fp=0;
306 	if (*fd2 >= 0)
307 	{
308 		*fp=fdopen(*fd2, "r");
309 		if (*fp == NULL)
310 			close(*fd2);
311 	}
312 
313 	if (*fp == NULL)	enomem();
314 	*rfcp=rfc2045_fromfp( *fp );
315 	if (!*rfcp)	enomem();
316 }
317 
messagecopy(FILE * fp,off_t start,off_t end)318 static int messagecopy(FILE *fp, off_t start, off_t end)
319 {
320 char	buf[512];
321 int	n;
322 
323 	if (fseek(fp, start, SEEK_SET) == -1)	return (-1);
324 	while (start < end)
325 	{
326 		n=sizeof(buf);
327 		if (n > end - start)
328 			n=end - start;
329 		n=fread(buf, 1, n, fp);
330 		if (n <= 0)	enomem();
331 		maildir_writemsg(newdraftfd, buf, n);
332 		start += n;
333 	}
334 	return (0);
335 }
336 
337 /* Return non-zero if user selected all attachments for deletion */
338 
deleting_all_attachments(struct rfc2045 * p)339 static int deleting_all_attachments(struct rfc2045 *p)
340 {
341 struct	rfc2045 *q;
342 const char *content_type;
343 const char *content_transfer_encoding;
344 const char *charset;
345 int	foundtextplain, cnt;
346 char	buf[MAXLONGSIZE+4];
347 
348 	foundtextplain=0;
349 	cnt=0;
350 	for (q=p->firstpart; q; q=q->next)
351 	{
352 		rfc2045_mimeinfo(q, &content_type,
353 			&content_transfer_encoding, &charset);
354 		if (q->isdummy)	continue;
355 
356 		if (!foundtextplain && HASTEXTPLAIN(q))
357 		{
358 			foundtextplain=1;
359 			continue;
360 		}
361 
362 		sprintf(buf, "del%d", ++cnt);
363 		if (*cgi(buf) == '\0')	return (0);
364 	}
365 	return (1);
366 }
367 
del_final_attachment(FILE * fp,struct rfc2045 * rfcp)368 static int del_final_attachment(FILE *fp, struct rfc2045 *rfcp)
369 {
370 struct	rfc2045 *q;
371 const char *content_type;
372 const char *content_transfer_encoding;
373 const char *charset;
374 off_t start_pos, end_pos, start_body;
375 off_t dummy;
376 
377 	for (q=rfcp->firstpart; q; q=q->next)
378 	{
379 		if (q->isdummy)	continue;
380 		rfc2045_mimeinfo(q, &content_type,
381 			&content_transfer_encoding, &charset);
382 		if (HASTEXTPLAIN(q))
383 			break;
384 	}
385 	if (!q)	return (-1);
386 
387 	if (fseek(fp, 0L, SEEK_SET) == -1)	return (-1);
388 	newmsg_copy_nonmime_headers(fp);
389 	maildir_writemsgstr(newdraftfd, "mime-version: 1.0\n");
390 
391 	rfc2045_mimepos(q, &start_pos, &end_pos, &start_body, &dummy, &dummy);
392 	return (messagecopy(fp, start_pos, end_pos));
393 }
394 
del_some_attachments(FILE * fp,struct rfc2045 * rfcp)395 static int del_some_attachments(FILE *fp, struct rfc2045 *rfcp)
396 {
397 struct	rfc2045 *q;
398 const char *content_type;
399 const char *content_transfer_encoding;
400 const char *charset;
401 int	foundtextplain;
402 int	cnt;
403 const char *boundary=rfc2045_boundary(rfcp);
404 off_t	start_pos, end_pos, start_body;
405 off_t	dummy;
406 
407 	rfc2045_mimepos(rfcp, &start_pos, &end_pos, &start_body, &dummy,
408 		&dummy);
409 	if (messagecopy(fp, 0, start_body))	return (-1);
410 
411 	foundtextplain=0;
412 	cnt=0;
413 	for (q=rfcp->firstpart; q; q=q->next)
414 	{
415 		rfc2045_mimeinfo(q, &content_type,
416 			&content_transfer_encoding, &charset);
417 		if (q->isdummy)
418 			;
419 		else if (!foundtextplain && HASTEXTPLAIN(q))
420 			foundtextplain=1;
421 		else
422 		{
423 		char	buf[MAXLONGSIZE+4];
424 
425 			sprintf(buf, "del%d", ++cnt);
426 			if (*cgi(buf))	continue;	/* This one's gone */
427 		}
428 
429 		if (!q->isdummy)
430 		{
431 			maildir_writemsgstr(newdraftfd, "\n--");
432 			maildir_writemsgstr(newdraftfd, boundary);
433 			maildir_writemsgstr(newdraftfd, "\n");
434 		}
435 		rfc2045_mimepos(q, &start_pos, &end_pos, &start_body, &dummy,
436 			&dummy);
437 		if (messagecopy(fp, start_pos, end_pos))
438 			return (-1);
439 	}
440 	maildir_writemsgstr(newdraftfd, "\n--");
441 	maildir_writemsgstr(newdraftfd, boundary);
442 	maildir_writemsgstr(newdraftfd, "--\n");
443 	return (0);
444 }
445 
attach_delete(const char * draft)446 void attach_delete(const char *draft)
447 {
448 FILE	*fp;
449 int	fd2;
450 struct	rfc2045 *rfcp;
451 char	*draftfilename;
452 int	isok=1;
453 struct	stat	stat_buf;
454 
455 	attachment_open(draft, &fp, &fd2, &rfcp);
456 	if (!rfcp->firstpart)
457 	{
458 		rfc2045_free(rfcp);
459 		fclose(fp);
460 		return;	/* No attachments to delete */
461 	}
462 
463 	if (fstat(fileno(fp), &stat_buf))
464 	{
465 		fclose(fp);
466 		enomem();
467 	}
468 
469 	newdraftfd=maildir_recreatemsg(INBOX "." DRAFTS, draft, &draftfilename);
470 	if (newdraftfd < 0)
471 	{
472 		fclose(fp);
473 		enomem();
474 	}
475 
476 	if (deleting_all_attachments(rfcp))
477 	{
478 		/* Deleting all attachments */
479 
480 		if (del_final_attachment(fp, rfcp))	isok=0;
481 	}
482 	else
483 	{
484 		if (del_some_attachments(fp, rfcp))	isok=0;
485 	}
486 	fclose(fp);
487 	rfc2045_free(rfcp);
488 
489 	if ( maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, isok,
490 		stat_buf.st_size))
491 	{
492 		free(draftfilename);
493 		enomem();
494 	}
495 	free(draftfilename);
496 	maildir_remcache(INBOX "." DRAFTS);	/* Cache file invalid now */
497 }
498 
499 /* ---------------------------------------------------------------------- */
500 /* Upload an attachment */
501 
502 static int isbinary;
503 static int attachfd;
504 static const char *cgi_attachname, *cgi_attachfilename;
505 
upload_start(const char * name,const char * filename,void * dummy)506 static int upload_start(const char *name, const char *filename, void *dummy)
507 {
508 const	char *p;
509 
510 	p=strrchr(filename, '/');
511 	if (p)	filename=p+1;
512 
513 	p=strrchr(filename, '\\');
514 	if (p)	filename=p+1;
515 
516 	cgi_attachname=name;
517 	cgi_attachfilename=filename;
518 	isbinary=0;
519 	return (0);
520 }
521 
upload_file(const char * ptr,size_t cnt,void * voidptr)522 static int upload_file(const char *ptr, size_t cnt, void *voidptr)
523 {
524 size_t	i;
525 
526 	for (i=0; i<cnt; i++)
527 		if ( (ptr[i] < ' ' || ptr[i] >= 127) && ptr[i] != '\n' &&
528 			ptr[i] != '\r')
529 			isbinary=1;
530 	maildir_writemsg(attachfd, ptr, cnt);
531 	return (0);
532 }
533 
upload_end(void * dummy)534 static void upload_end(void *dummy)
535 {
536 }
537 
538 #if 0
539 static void writebase64encode(const char *p, size_t n)
540 {
541 	maildir_writemsg(newdraftfd, p, n);
542 }
543 #endif
544 
search_mime_type(const char * mimetype,const char * filename)545 static const char *search_mime_type(const char *mimetype, const char *filename)
546 {
547 FILE	*fp;
548 char	*p, *q;
549 
550 	if (!filename || !(filename=strrchr(filename, '.')))	return (0);
551 	++filename;
552 
553 	if ((fp=fopen(mimetype, "r")) == NULL)	return(0);
554 	while ((p=maildir_readline(fp)) != NULL)
555 	{
556 		if ((q=strchr(p, '#')) != NULL)	*q='\0';
557 		if ((p=strtok(p, " \t")) == NULL)	continue;
558 		while ((q=strtok(NULL, " \t")) != NULL)
559 			if (strcasecmp(q, filename) == 0)
560 			{
561 				fclose(fp);
562 				return (p);
563 			}
564 	}
565 	fclose(fp);
566 	return (NULL);
567 }
568 
calc_mime_type(const char * filename)569 const char *calc_mime_type(const char *filename)
570 {
571 static const char mimetypes[]=MIMETYPES;
572 const char	*p;
573 char *q;
574 const char *r;
575 char *s;
576 
577 	p=mimetypes;
578 	if (!p)	enomem();
579 	while (*p)
580 	{
581 		if (*p == ':')
582 		{
583 			++p;
584 			continue;
585 		}
586 		q=strdup(p);
587 		if (!q)	enomem();
588 		if ((s=strchr(q, ':')) != NULL)	*s='\0';
589 		if ((r=search_mime_type(q, filename)) != 0)
590 		{
591 			free(q);
592 			return (r);
593 		}
594 		free(q);
595 		while (*p && *p != ':')
596 			p++;
597 	}
598 	return ("auto");
599 }
600 
getkey(const char * keyname,int issecret)601 static int getkey(const char *keyname, int issecret)
602 {
603 	int rc;
604 
605 	if (!*keyname)
606 		return (1);
607 	upload_start("", "", NULL);
608 
609 	rc=gpgexportkey(keyname, issecret, &upload_file, NULL);
610 	upload_end(NULL);
611 	return (rc);
612 }
613 
614 #if 0
615 static void write_disposition_param(const char *label, const char *value)
616 {
617 char	*p, *q;
618 const char *r;
619 
620         while (value && ((r=strchr(value, ':')) || (r=strchr(value, '/'))
621                 || (r=strchr(value, '\\'))))
622                 value=r+1;
623 
624 	if (!value || !*value)	return;
625 	maildir_writemsgstr(newdraftfd, "; ");
626 	maildir_writemsgstr(newdraftfd, label);
627 	maildir_writemsgstr(newdraftfd, "=\"");
628 	p=strdup(value);
629 	if (!p)	enomem();
630 	while ((q=strchr(p, '\\')) || (q=strchr(p, '"')))
631 		*q='_';
632 	maildir_writemsgstr(newdraftfd, p);
633 	maildir_writemsgstr(newdraftfd, "\"");
634 	free(p);
635 }
636 #endif
637 
cnt_filename(const char * param,const char * value,void * void_arg)638 static int cnt_filename(const char *param,
639 			const char *value,
640 			void *void_arg)
641 {
642 	*(int *)void_arg += strlen(param)+strlen(value)+5;
643 	return 0;
644 }
645 
save_filename(const char * param,const char * value,void * void_arg)646 static int save_filename(const char *param,
647 			 const char *value,
648 			 void *void_arg)
649 {
650 	strcat(strcat(strcat(strcat((char *)void_arg, ";\n  "), param),
651 		      "="), value);
652 	return 0;
653 }
654 
attach_upload(const char * draft,const char * attpubkey,const char * attprivkey)655 int attach_upload(const char *draft,
656 		  const char *attpubkey,
657 		  const char *attprivkey)
658 {
659 	char	*attachfilename;
660 	char	*draftfilename;
661 	FILE	*draftfp;
662 	char	*boundary;
663 	FILE	*tempfp;
664 	struct	rfc2045 *rfcp, *q;
665 	const char *content_type;
666 	const char *content_transfer_encoding;
667 	const char *charset;
668 	off_t	start_pos, end_pos, start_body;
669 	int	n;
670 	char	buf[BUFSIZ];
671 	int	pipefd[2];
672 	struct	stat	stat_buf, attach_stat_buf;
673 	off_t	dummy;
674 	int	fd2;
675 	char	*filenamemime;
676 	char *argvec[20];
677 	char	*filenamebuf;
678 	pid_t pid1, pid2;
679 	int waitstat;
680 
681 	/* Open the file containing the draft message */
682 
683 	draftfilename=maildir_find(INBOX "." DRAFTS, draft);
684 	if (!draftfilename)	return (0);
685 
686 	fd2=maildir_safeopen(draftfilename, O_RDONLY, 0);
687 
688 	draftfp=0;
689 	if (fd2 >= 0)
690 	{
691 		draftfp=fdopen(fd2, "r");
692 		if (draftfp == NULL)
693 			close(fd2);
694 	}
695 
696 	if (draftfp == 0)
697 		enomem();
698 
699 	free(draftfilename);
700 	if (fstat(fileno(draftfp), &stat_buf))
701 	{
702 		fclose(draftfp);
703 		enomem();
704 	}
705 
706 	/* Create a temporary file in tmp where we'll temporarily store the
707 	** attachment
708 	*/
709 
710 	attachfd=maildir_createmsg(INBOX "." DRAFTS, "temp", &attachfilename);
711 	if (attachfd < 0)
712 	{
713 		fclose(draftfp);
714 		enomem();
715 	}
716 
717 	if ((
718 	     attpubkey ? getkey(attpubkey, 0):
719 	     attprivkey ? getkey(attprivkey, 1):
720 	     cgi_getfiles( &upload_start, &upload_file, &upload_end, 1, NULL))
721 		|| maildir_writemsg_flush(attachfd))
722 	{
723 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
724 		free(attachfilename);
725 		fclose(draftfp);
726 		close(attachfd);
727 		return (0);
728 	}
729 
730 	if (fstat(attachfd, &attach_stat_buf) ||
731 	    attach_stat_buf.st_size + stat_buf.st_size > max_attach())
732 	{
733 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
734 		maildir_deletenewmsg(attachfd, INBOX "." DRAFTS, attachfilename);
735 		free(attachfilename);
736 		fclose(draftfp);
737 		close(attachfd);
738 		return (-2);
739 	}
740 
741 
742 	/* Calculate new MIME content boundary */
743 
744 	boundary=0;
745 	tempfp=0;
746 
747 	n=dup(attachfd);
748 
749 	if (n < 0)
750 	{
751 		fclose(draftfp);
752 		enomem();
753 	}
754 	tempfp=fdopen(n, "r");
755 	if (tempfp == 0)
756 	{
757 		fclose(draftfp);
758 		enomem();
759 	}
760 
761 	do
762 	{
763 		if (boundary)	free(boundary);
764 		boundary=multipart_boundary_create();
765 	} while ( multipart_boundary_checkf(boundary, draftfp) ||
766 		  multipart_boundary_checkf(boundary, tempfp));
767 
768 	if (tempfp)	fclose(tempfp);
769 
770 	/* Parse existing draft for its MIME structure */
771 
772 	rfcp=rfc2045_fromfp(draftfp);
773 
774 	rfc2045_mimeinfo(rfcp, &content_type,
775 		&content_transfer_encoding, &charset);
776 
777 	/* Create a new version of the draft message */
778 
779 	newdraftfd=maildir_recreatemsg(INBOX "." DRAFTS, draft, &draftfilename);
780 	if (newdraftfd < 0)
781 	{
782 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
783 		fclose(draftfp);
784 		close(attachfd);
785 		enomem();
786 	}
787 
788 	if (fseek(draftfp, 0L, SEEK_SET) < 0)
789 	{
790 		maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 0, 0);
791 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
792 		fclose(draftfp);
793 		close(attachfd);
794 		enomem();
795 	}
796 
797 	newmsg_copy_nonmime_headers(draftfp);
798 
799 	/* Create a multipart message, 1st attachment is the existing
800 	** contents.
801 	*/
802 
803 	newmsg_create_multipart(newdraftfd, charset, boundary);
804 	maildir_writemsgstr(newdraftfd, "--");
805 	maildir_writemsgstr(newdraftfd, boundary);
806 	maildir_writemsgstr(newdraftfd, "\n");
807 
808 	if (rfcp == NULL || strcmp(content_type, "multipart/mixed"))
809 	{
810 		int rc;
811 
812 		/*
813 		** The current draft does not have attachments.  Take its
814 		** sole contents, and write it as a text/plain attachment.
815 		*/
816 
817 		if (fseek(draftfp, 0L, SEEK_SET) < 0)
818 			rc = -1;
819 		else
820 		{
821 			newmsg_copy_content_headers(draftfp);
822 			maildir_writemsgstr(newdraftfd, "\n");
823 			rfc2045_mimepos(rfcp, &start_pos, &end_pos,
824 					&start_body,
825 					&dummy, &dummy);
826 			rc=messagecopy(draftfp, start_body, end_pos);
827 		}
828 
829 		if (rc)
830 		{
831 			maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename,
832 				0, 0);
833 			maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename,
834 				0, 0);
835 			fclose(draftfp);
836 			close(newdraftfd);
837 			close(attachfd);
838 			enomem();
839 		}
840 
841 		maildir_writemsgstr(newdraftfd, "\n--");
842 		maildir_writemsgstr(newdraftfd, boundary);
843 		maildir_writemsgstr(newdraftfd, "\n");
844 	}
845 	else
846 	{
847 		/* If the current draft already has MIME attachments,
848 		** just copy them over to the new draft message.
849 		*/
850 
851 		for (q=rfcp->firstpart; q; q=q->next)
852 		{
853 			if (q->isdummy)	continue;
854 			rfc2045_mimepos(q, &start_pos, &end_pos, &start_body,
855 					&dummy, &dummy);
856 			if (messagecopy(draftfp, start_pos, end_pos))
857 			{
858 				maildir_closemsg(newdraftfd, INBOX "." DRAFTS,
859 					draftfilename, 0, 0);
860 				maildir_closemsg(attachfd, INBOX "." DRAFTS,
861 					attachfilename, 0, 0);
862 				fclose(draftfp);
863 				close(newdraftfd);
864 				close(attachfd);
865 				enomem();
866 			}
867 			maildir_writemsgstr(newdraftfd, "\n--");
868 			maildir_writemsgstr(newdraftfd, boundary);
869 			maildir_writemsgstr(newdraftfd, "\n");
870 		}
871 	}
872 
873 	{
874 		const char *cp=strrchr(cgi_attachfilename, '/');
875 		int len;
876 		static const char fnStr[]="filename";
877 
878 		if (cp)
879 			++cp;
880 		else
881 			cp=cgi_attachfilename;
882 
883 		len=1;
884 		rfc2231_attrCreate(fnStr, cp,
885 				   sqwebmail_content_charset,
886 				   sqwebmail_content_language,
887 				   &cnt_filename, &len);
888 
889 		filenamemime=malloc(len);
890 
891 		if (filenamemime)
892 		{
893 			*filenamemime=0;
894 			rfc2231_attrCreate(fnStr, cp,
895 					   sqwebmail_content_charset,
896 					   sqwebmail_content_language,
897 					   save_filename, filenamemime);
898 		}
899 	}
900 
901 	argvec[0]="makemime";
902 	argvec[1]="-c";
903 
904 	if (attpubkey || attprivkey)
905 	{
906 		argvec[2]="application/pgp-keys";
907 		n=3;
908 		filenamebuf=0;
909 	}
910 	else
911 	{
912 		const char *pp;
913 
914 		argvec[2]=(char *)calc_mime_type(cgi_attachfilename);
915 		argvec[3]="-N";
916 		argvec[4]=cgi_attachfilename ?
917 			(char *)cgi_attachfilename:"filename.dat";
918 		n=5;
919 
920 		pp=*cgi("attach_inline") ?
921 			"Content-Disposition: inline":
922 			"Content-Disposition: attachment";
923 
924 		filenamebuf=malloc(strlen(pp)+strlen(filenamemime ?
925 						     filenamemime:"") + 15);
926 
927 		if (filenamebuf)
928 		{
929 			strcpy(filenamebuf, pp);
930 			strcat(filenamebuf, filenamemime ? filenamemime:"");
931 
932 			argvec[n++]="-a";
933 			argvec[n++]=filenamebuf;
934 		}
935 	}
936 
937 	argvec[n++]="-C";
938 	argvec[n++]=(char *)sqwebmail_content_charset;
939 
940 	signal(SIGCHLD, SIG_DFL);
941 
942 	argvec[n++]="-";
943 	argvec[n++]=0;
944 
945 	if (pipe(pipefd) < 0)
946 	{
947 		if (filenamemime)
948 			free(filenamemime);
949 		if (filenamebuf)
950 			free(filenamebuf);
951 		maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 0, 0);
952 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
953 		fclose(draftfp);
954 		close(newdraftfd);
955 		close(attachfd);
956 		enomem();
957 	}
958 
959 	if (lseek(attachfd, 0L, SEEK_SET) < 0 || (pid1=fork()) < 0)
960 	{
961 		close(pipefd[0]);
962 		close(pipefd[1]);
963 		if (filenamemime)
964 			free(filenamemime);
965 		if (filenamebuf)
966 			free(filenamebuf);
967 		maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 0, 0);
968 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
969 		fclose(draftfp);
970 		close(newdraftfd);
971 		close(attachfd);
972 		enomem();
973 		return (0);
974 	}
975 
976 	if (pid1 == 0)
977 	{
978 		dup2(attachfd, 0);
979 		dup2(pipefd[1], 1);
980 		close(attachfd);
981 		close(newdraftfd);
982 		close(pipefd[0]);
983 		close(pipefd[1]);
984 		execv(MAKEMIME, argvec);
985 		fprintf(stderr,
986 		       "CRIT: exec %s: %s\n", MAKEMIME, strerror(errno));
987 		exit(1);
988 	}
989 
990 	if (filenamemime)
991 		free(filenamemime);
992 	if (filenamebuf)
993 		free(filenamebuf);
994 
995 	close (pipefd[1]);
996 
997 
998 	while ((n=read(pipefd[0], buf, sizeof(buf))) > 0)
999 	{
1000 		maildir_writemsg(newdraftfd, buf, n);
1001 	}
1002 	close(pipefd[0]);
1003 
1004 	for (;;)
1005 	{
1006 		pid2=wait(&waitstat);
1007 
1008 		if (pid2 == pid1)
1009 		{
1010 			waitstat= WIFEXITED(waitstat) ? WEXITSTATUS(waitstat)
1011 				: 1;
1012 			break;
1013 		}
1014 
1015 		if (pid2 == -1)
1016 		{
1017 			waitstat=1;
1018 			break;
1019 		}
1020 	}
1021 
1022 	if (waitstat > 0 || n < 0)
1023 	{
1024 		maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 0, 0);
1025 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
1026 		fclose(draftfp);
1027 		close(newdraftfd);
1028 		maildir_deletenewmsg(attachfd, INBOX "." DRAFTS, attachfilename);
1029 		close(attachfd);
1030 		return (-3);
1031 	}
1032 
1033 	maildir_writemsgstr(newdraftfd, "\n--");
1034 	maildir_writemsgstr(newdraftfd, boundary);
1035 	maildir_writemsgstr(newdraftfd, "--\n");
1036 
1037 	/* Finish new draft message, let it replace the current one */
1038 
1039 	if (maildir_closemsg(newdraftfd, INBOX "." DRAFTS, draftfilename, 1,
1040 		stat_buf.st_size))
1041 	{
1042 		maildir_closemsg(attachfd, INBOX "." DRAFTS, attachfilename, 0, 0);
1043 		free(draftfilename);
1044 		maildir_deletenewmsg(attachfd, INBOX "." DRAFTS, attachfilename);
1045 		free(attachfilename);
1046 		rfc2045_free(rfcp);
1047 		fclose(draftfp);
1048 		close(attachfd);
1049 		return (-1);
1050 	}
1051 	free(draftfilename);
1052 
1053 	fclose(draftfp);
1054 
1055 	/* Remove and delete temp attachment file */
1056 
1057 	maildir_deletenewmsg(attachfd, INBOX "." DRAFTS, attachfilename);
1058 	free(attachfilename);
1059 	rfc2045_free(rfcp);
1060 	return (0);
1061 }
1062 
doattach(const char * folder,const char * draft)1063 void doattach(const char *folder, const char *draft)
1064 {
1065 int	quotaflag=0;
1066 
1067 	CHECKFILENAME(draft);
1068 	if (*cgi("dodelete"))
1069 	{
1070 		if (!tokencheck())
1071 		{
1072 			attach_delete(draft);
1073 			tokensave();
1074 		}
1075 	}
1076 	else if (*cgi("upload"))
1077 	{
1078 		if (!tokencheck())
1079 		{
1080 			quotaflag=attach_upload(draft, NULL, NULL);
1081 			tokensave();
1082 		}
1083 	}
1084 	else if (*cgi("uppubkey") && libmail_gpg_has_gpg(GPGDIR) == 0)
1085 	{
1086 		if (!tokencheck())
1087 		{
1088 			quotaflag=attach_upload(draft, cgi("pubkey"), NULL);
1089 			tokensave();
1090 		}
1091 	}
1092 	else if (*cgi("upprivkey") && *cgi("really") &&
1093 		 libmail_gpg_has_gpg(GPGDIR) == 0)
1094 	{
1095 		if (!tokencheck())
1096 		{
1097 			quotaflag=attach_upload(draft, NULL, cgi("privkey"));
1098 			tokensave();
1099 		}
1100 	}
1101 	else if (*cgi("previewmsg"))
1102 	{
1103 		cgi_put("draft", draft);
1104 		newmsg_do(folder);
1105 		return;
1106 	}
1107 	else if (*cgi("sendmsg"))
1108 	{
1109 		cgi_put("draftmessage", draft);
1110 		newmsg_do(folder);
1111 		return;
1112 	}
1113 	else if (*cgi("savedraft"))
1114 	{
1115 		sendmsg_done();
1116 		return;
1117 	}
1118 
1119 	if (quotaflag == -2)
1120         {
1121                 http_redirect_argss(
1122                   "&form=attachments&pos=%s&draft=%s&error=limits",
1123                   cgi("pos"), draft);
1124         }
1125 	else if (quotaflag == -3)
1126 	{
1127                 http_redirect_argss(
1128                   "&form=attachments&pos=%s&draft=%s&error=makemime",
1129                   cgi("pos"), draft);
1130 	}
1131         else
1132         {
1133                 http_redirect_argss(
1134                   (quotaflag ? "&form=attachments&pos=%s&draft=%s&error=quota":
1135                   "&form=attachments&pos=%s&draft=%s"), cgi("pos"),
1136                   draft);
1137         }
1138 }
1139