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\" /> ",
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\"> <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> </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