1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: attach.c 1082 2008-06-12 18:39:50Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2015 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 * Program: Routines to support attachments in the Pine composer
22 #include "../pith/charconv/filesys.h"
23 #include "../pith/string.h"
30 int ParseAttach(struct hdr_line
**, int *, char *,
31 size_t, char *, size_t, char *, size_t, int *);
32 PATMT
*NewAttach(char *, long, char *);
33 void ZotAttach(struct pico_atmt
*);
34 void sinserts(UCS
*, int, UCS
*, int);
35 int AttachUpload(char *, size_t, char *, size_t);
36 int AttachCancel(char *);
39 #define HIBIT_WARN "Only ASCII characters allowed in attachment comments"
43 * AskAttach - ask for attachment fields and build resulting structure
44 * return pointer to that struct if OK, NULL otherwise
47 AskAttach(char *cmnt
, size_t cmntlen
, LMLIST
**lm
)
49 int i
, status
, fbrv
, upload
= 0;
50 int fb_flags
= FB_READ
| FB_ATTACH
;
54 char fn
[NLINE
], sz
[32];
58 i
= 2; /* 2 is prompt for file, 1 is prompt for comment */
65 EXTRAKEYS menu_attach
[4];
68 menu_attach
[n
= 0].name
= "^T";
69 menu_attach
[n
].label
= N_("To Files");
70 menu_attach
[n
].key
= (CTRL
|'T');
73 menu_attach
[++n
].name
= "TAB";
74 menu_attach
[n
].label
= N_("Complete");
75 menu_attach
[n
].key
= (CTRL
|'I');
78 #if !defined(DOS) && !defined(MAC)
79 if(Pmaster
&& Pmaster
->upload
){
81 * The Plan: ^R prompts for uploaded file's name which
82 * is passed to the defined upload command when the user
83 * hits Return to confirm the name.
84 * NOTE: this is different than upload into message
85 * text in which case the uploaded name isn't useful so
86 * a temp file is ok (be sure to fix the file mode).
88 menu_attach
[++n
].name
= "^Y";
89 menu_attach
[n
].key
= (CTRL
|'Y');
90 /* TRANSLATORS: Read File is a prompt for the name of
91 a file to be read into the composer. */
92 menu_attach
[n
].label
= upload
? N_("Read File") : N_("RcvUpload");
96 menu_attach
[++n
].name
= NULL
;
97 KS_OSDATASET(&menu_attach
[0], KS_NONE
);
98 status
= mlreply_utf8(upload
? _("Name to give uploaded attachment: ")
99 /* TRANSLATORS: User is being asked for the name
100 of the file they want to attach to a message. */
101 : _("File to attach: "),
102 fn
, sizeof(fn
), QNORML
, menu_attach
);
105 /* TRANSLATORS: This is a prompt for a comment about the file
106 they have attached. */
107 status
= mlreply_utf8(_("Attachment comment: "), cmnt
, cmntlen
, QNODQT
, NULL
);
112 VARS_TO_SAVE
*saved_state
;
114 saved_state
= save_pico_state();
115 (*Pmaster
->helper
)(Pmaster
->attach_help
, _("Attach Help"), 1);
117 restore_pico_state(saved_state
);
118 free_pico_state(saved_state
);
121 pico_refresh(FALSE
, 1);
126 eml
.s
= (i
== 2) ? "file" : "comment";
127 emlwrite("No Attachment %s help yet!", &eml
);
139 if(*fn
&& (p
= strrchr(fn
, C_FILESEP
))){
143 strncpy(bfn
, S_FILESEP
, sizeof(bfn
));
144 bfn
[sizeof(bfn
)-1] = '\0';
147 else if(fn
[0] == C_FILESEP
148 || (isalpha((unsigned char)fn
[0])
150 if(fn
[1] == ':' && p
== fn
+2)
153 if(dirlen
< sizeof(bfn
)){
154 strncpy(bfn
, fn
, dirlen
);
159 else if (fn
[0] == C_FILESEP
|| fn
[0] == '~'){
160 if(dirlen
< sizeof(bfn
)){
161 strncpy(bfn
, fn
, dirlen
);
167 snprintf(bfn
, sizeof(bfn
), "%s%c%.*s",
170 : ((gmode
& MDTREE
) || opertree
[0])
171 ? opertree
: gethomedir(NULL
),
172 C_FILESEP
, p
- fn
, fn
);
176 strncpy(bfn
, (gmode
& MDCURDIR
)
178 : ((gmode
& MDTREE
) || opertree
[0])
179 ? opertree
: gethomedir(NULL
), sizeof(bfn
));
180 bfn
[sizeof(bfn
)-1] = '\0';
183 if(!pico_fncomplete(bfn
, fname
, sizeof(fn
)-(fname
-fn
)))
198 if(*fn
== '\0' || !isdir(fn
, NULL
, NULL
)){
199 strncpy(fn
, (gmode
& MDCURDIR
)
200 ? (browse_dir
[0] ? browse_dir
: ".")
201 : ((gmode
& MDTREE
) || opertree
[0])
203 : (browse_dir
[0] ? browse_dir
204 : gethomedir(NULL
)), sizeof(fn
));
205 fn
[sizeof(fn
)-1] = '\0';
208 if((fbrv
= FileBrowse(fn
, sizeof(fn
), bfn
, sizeof(bfn
), sz
, sizeof(sz
),
210 : fb_flags
|FB_LMODEPOS
,
213 if (upload
&& (strlen(fn
)+strlen(S_FILESEP
)+strlen(bfn
)) < sizeof(fn
)){
218 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
219 || (new->fname
=malloc((len1
+1) * sizeof(char))) == NULL
220 || (new->dir
=malloc((len2
+1) * sizeof(char))) == NULL
){
221 emlwrite("\007Can't malloc space for filename", NULL
);
225 strncpy(new->fname
, bfn
, len1
);
226 new->fname
[len1
] = '\0';
227 strncpy(new->dir
, fn
, len2
);
228 new->dir
[len2
] = '\0';
229 strncpy(new->size
, sz
, sizeof(new->size
)-1);
230 new->size
[sizeof(new->size
)-1] = '\0';
234 strncat(fn
, S_FILESEP
, sizeof(fn
)-strlen(fn
)-1);
235 fn
[sizeof(fn
)-1] = '\0';
236 strncat(fn
, bfn
, sizeof(fn
)-strlen(fn
)-1);
237 fn
[sizeof(fn
)-1] = '\0';
238 if(!AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
))){
239 i
= 2; /* keep prompting for file */
240 sleep(3); /* problem, show error! */
243 i
--; /* go prompt for comment */
247 if(lm
&& *lm
&& !(*lm
)->next
) /* get comment */
249 else{ /* no comments if multiple files */
257 pico_refresh(FALSE
,1);
259 emlwrite("\007File name too BIG, cannot select!", NULL
);
268 pico_refresh(FALSE
, 1);
270 emlwrite("\007File name too big, cannot select!", NULL
);
274 /* fall thru to clean up the screen */
277 pico_refresh(FALSE
, 1);
281 #if !defined(DOS) && !defined(MAC)
282 case (CTRL
|'Y'): /* upload? */
284 upload
^= 1; /* flip mode */
292 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
294 case TRUE
: /* some comment */
295 case FALSE
: /* No comment */
298 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
299 status
= AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
));
300 pico_refresh(FALSE
, 1);
303 i
= 2; /* keep prompting for file */
304 sleep(3); /* problem, show error! */
308 if(*fn
== '\"' && fn
[strlen(fn
)-1] == '\"'){
311 for(j
= 0; (fn
[j
] = fn
[j
+1]); j
++)
319 && !compresspath(opertree
, fn
, sizeof(fn
))){
320 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
322 /* TRANSLATORS: the %s is replaced with the name of a directory */
323 _("Restricted mode allows attachments from %s only: too many ..'s"),
328 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
329 if((gmode
&MDTREE
) && !in_oper_tree(fn
)){
330 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
332 _("\007Restricted mode allows attachments from %s only"), &eml
);
337 if((status
= fexist(fn
, "r", &attsz
)) != FIOSUC
){
338 fioperr(status
, fn
); /* file DOESN'T exist! */
343 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
344 || (new->fname
=malloc((len
+1)*sizeof(char))) == NULL
){
345 emlwrite("\007Can't malloc space for filename", NULL
);
350 strncpy(new->fname
, fn
, len
);
351 new->fname
[len
] = '\0';
352 strncpy(new->size
, prettysz(attsz
), sizeof(new->size
));
353 new->size
[sizeof(new->size
)-1] = '\0';
358 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
363 return(1); /* mission accomplished! */
377 * AttachUpload - Use call back to run the external upload command.
380 AttachUpload(char *fn
, size_t fnlen
, char *sz
, size_t szlen
)
385 emlwrite("\007Restricted mode disallows uploaded command", NULL
);
389 if(Pmaster
&& Pmaster
->upload
&& (*Pmaster
->upload
)(fn
, fnlen
, &l
)){
390 strncpy(sz
, prettysz((off_t
)l
), szlen
);
403 AttachCancel(char *fn
)
405 emlwrite(_("Attach cancelled"), NULL
);
407 our_unlink(fn
); /* blast uploaded file */
413 extern struct headerentry
*headents
;
416 * SyncAttach - given a pointer to a linked list of attachment structures,
417 * return with that structure sync'd with what's displayed.
418 * delete any attachments in list of structs that's not on
419 * the display, and add any that aren't in list but on display.
424 int offset
= 0, /* the offset to begin */
426 ki
= 0, /* number of known attmnts */
427 bi
= 0, /* build array index */
428 nbld
= 0, /* size of build array */
429 na
, /* old number of attachmnt */
432 char file
[NLINE
], /* buffers to hold it all */
435 struct hdr_line
*lp
; /* current line in header */
436 struct headerentry
*entry
;
437 PATMT
*tp
, **knwn
= NULL
, **bld
;
443 for(entry
= headents
; entry
->name
!= NULL
; entry
++) {
448 for(tp
= Pmaster
->attachments
; tp
; tp
= tp
->next
)
449 ki
++; /* Count known attachments */
452 if((knwn
= (PATMT
**)malloc((ki
+1) * (sizeof(PATMT
*)))) == NULL
){
453 eml
.s
= comatose(ki
+ 1);
454 emlwrite("\007Can't allocate space for %s known attachment array entries",
459 for(i
=0, tp
= Pmaster
->attachments
; i
< ki
; i
++, tp
= tp
->next
){
460 knwn
[i
] = tp
; /* fill table of */
461 /* known attachments */
467 * As a quick hack to avoid too many reallocs, check to see if
468 * there are more header lines than known attachments.
470 for(lp
= entry
->hd_text
; lp
; lp
= lp
->next
)
471 nbld
++; /* count header lines */
473 nbld
= nbld
> ki
? nbld
: ki
+ 1;
475 if((bld
= (PATMT
**)malloc(nbld
* (sizeof(PATMT
*)))) == NULL
){
476 eml
.s
= comatose(nbld
);
477 emlwrite("\007Can't allocate space for %s build array entries", &eml
);
487 if(bi
== nbld
){ /* need to grow build array? */
488 if((bld
= (PATMT
**)realloc(bld
, ++nbld
* sizeof(PATMT
*))) == NULL
){
489 eml
.s
= comatose(nbld
);
490 emlwrite("\007Can't resize build array to %s entries ", &eml
);
496 if((status
= ParseAttach(&lp
, &offset
, file
, sizeof(file
), size
, sizeof(size
),
497 comment
, sizeof(comment
), &na
)) != 0)
498 rv
= (rv
< 0) ? rv
: status
; /* remember worst case */
501 if(n
!= na
&& na
> 0 && na
<= ki
&& (knwn
[na
-1]->flags
& A_FLIT
)){
502 bld
[bi
++] = knwn
[na
-1];
510 && (!in_oper_tree(file
)
511 || !compresspath(file
, fn
, sizeof(fn
))))
512 /* no attachments outside ~ in secure mode! */
516 for(i
= 0; i
< ki
; i
++){ /* already know about it? */
518 * this is kind of gruesome. what we want to do is keep track
519 * of literal attachment entries because they may not be
520 * actual files we can access or that the user can readily
524 && ((!(knwn
[i
]->flags
&A_FLIT
)
525 && !strcmp(file
, knwn
[i
]->filename
))
526 || ((knwn
[i
]->flags
&A_FLIT
) && i
+1 == na
))){
528 knwn
[i
] = NULL
; /* forget we know about it */
530 if(status
== -1) /* ignore garbage! */
533 if((tp
->flags
&A_FLIT
) && strcmp(file
, tp
->filename
)){
535 if((j
=strlen(file
)) > strlen(tp
->filename
)){
536 if((tp
->filename
= (char *)realloc(tp
->filename
,
537 sizeof(char)*(j
+1))) == NULL
){
538 emlwrite("\007Can't realloc filename space",NULL
);
544 strncpy(tp
->filename
, file
, j
);
545 tp
->filename
[j
] = '\0';
547 else if(tp
->size
&& strcmp(tp
->size
, size
)){
549 if((j
=strlen(size
)) > strlen(tp
->size
)){
550 if((tp
->size
=(char *)realloc(tp
->size
,
551 sizeof(char)*(j
+1))) == NULL
){
552 emlwrite("\007Can't realloc space for size", NULL
);
558 strncpy(tp
->size
, size
, j
);
562 if(strcmp(tp
->description
, comment
)){ /* new comment */
564 if((j
=strlen(comment
)) > strlen(tp
->description
)){
565 if((tp
->description
=(char *)realloc(tp
->description
,
566 sizeof(char)*(j
+1))) == NULL
){
567 emlwrite("\007Can't realloc description", NULL
);
573 strncpy(tp
->description
, comment
, j
);
574 tp
->description
[j
] = '\0';
585 if((tp
= NewAttach(file
, atol(size
), comment
)) == NULL
){
595 tp
->flags
|= A_ERR
; /* turn ON error bit */
597 tp
->flags
&= ~(A_ERR
); /* turn OFF error bit */
601 for(i
=0; i
< bi
-1; i
++) /* link together newly built list */
602 bld
[i
]->next
= bld
[i
+1];
604 bld
[i
]->next
= NULL
; /* tie it off */
605 Pmaster
->attachments
= bld
[0];
608 Pmaster
->attachments
= NULL
;
612 for(i
= 0; i
< ki
; i
++){ /* kill old/unused references */
615 free((char *) knwn
[i
]);
629 * ParseAttach - given a header line and an offset into it, return with
630 * the three given fields filled in. Size of fn and cmnt
631 * buffers should be passed in fnlen and cmntlen.
632 * Always updates header fields that have changed or are
633 * fixed. An error advances offset to next attachment.
635 * returns: 1 if a field changed
640 ParseAttach(struct hdr_line
**lp
, /* current header line */
641 int *off
, /* offset into that line */
642 char *fn
, /* return file name field */
643 size_t fnlen
, /* fn buffer size */
646 char *cmnt
, /* places to return fields */
648 int *no
) /* attachment number */
650 int j
, status
, bod
, eod
= -1,
651 rv
= 0, /* return value */
653 lbln
= 0, /* label'd attachment */
658 off_t attsz
; /* attachment length */
661 UCS tmp
[1024], *p
, *u
, quotechar
[2];
664 char *lblsz
= NULL
, /* label'd attchmnt's size */
666 struct hdr_line
*lprev
= NULL
;
667 enum { /* parse levels */
668 LWS
, /* leading white space */
669 NUMB
, /* attachment number */
670 WSN
, /* white space after number */
671 TAG
, /* attachments tag (fname) */
672 WST
, /* white space after tag */
673 ASIZE
, /* attachments size */
674 SWS
, /* white space after size */
675 CMMNT
, /* attachment comment */
676 TG
} level
; /* trailing garbage */
678 *fn
= *sz
= *cmnt
= '\0'; /* initialize return strings */
680 orig_offset
= bod
= *off
;
684 level
= LWS
; /* start at beginning */
687 if((c
=(*lp
)->text
[*off
]) == '\0'){ /* end of display line */
688 if(level
== LWS
&& bod
!= *off
){
689 (*lp
)->text
[bod
] = '\0';
693 if((*lp
= (*lp
)->next
) != NULL
)
694 c
= (*lp
)->text
[*off
= bod
= 0]; /* reset offset */
698 c_lookahead
= (*lp
)->text
[*off
+ 1];
699 if(c_lookahead
== '\0'){ /* end of display line */
700 if((*lp
)->next
!= NULL
)
701 c_lookahead
= (*lp
)->next
->text
[0];
706 case LWS
: /* skip leading white space */
707 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== ',')){
713 if(bod
> 0 && *off
>= bod
&& lprev
){
714 lprev
->text
[bod
- 1] = '\0';
718 else if(*off
> bod
&& *lp
){ /* wipe out whitespace */
719 memcpy(&(*lp
)->text
[bod
], &(*lp
)->text
[*off
],
720 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
721 *off
= bod
; /* reset pointer */
728 if(c
> 0xff || !isdigit((unsigned char)c
)){ /* add a number */
729 snprintf(number
, sizeof(number
), "%d. ", *no
);
730 *no
= 0; /* no previous number! */
731 u
= utf8_to_ucs4_cpystr(number
);
733 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
734 : &(*lp
)->text
[*off
],
735 0, u
, j
=ucs4_strlen(u
));
737 fs_give((void **) &u
);
742 level
= TAG
; /* interpret the name */
746 case NUMB
: /* attachment number */
747 if(c
== '\0' || c
== ','){ /* got to end, no number yet */
749 snprintf(number
, sizeof(number
), "%d. ", *no
);
750 *no
= 0; /* no previous number! */
752 *lp
= lprev
; /* go back and look at prev */
754 c
= (*lp
)->text
[*off
= orig_offset
]; /* reset offset */
755 u
= utf8_to_ucs4_cpystr(number
);
757 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
758 : &(*lp
)->text
[*off
],
759 0, u
, j
=ucs4_strlen(u
));
761 fs_give((void **) &u
);
766 level
= WSN
; /* what's next... */
769 else if(c
== '.' && c_lookahead
<= 0xff && isspace((unsigned char)c_lookahead
)){
770 /* finished grabbing number */
771 /* if not space is not number */
773 * replace number if it's not right
776 snprintf(number
, sizeof(number
), "%d", *no
); /* record the current... */
777 utf8
= ucs4_to_utf8_cpystr(tmp
);
778 *no
= atoi(utf8
); /* and the old place in list */
779 if(strcmp(number
, utf8
)){
780 if(p
-tmp
> *off
){ /* where to begin replacemnt */
785 sinserts((*lp
)->text
, *off
, uu
, 0);
786 u
= utf8_to_ucs4_cpystr(number
);
788 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
791 fs_give((void **) &u
);
797 j
= (*off
) - (p
-tmp
);
798 u
= utf8_to_ucs4_cpystr(number
);
800 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
802 p
-tmp
, u
, ucs4_strlen(u
));
804 fs_give((void **) &u
);
807 *off
+= strlen(number
) - (p
-tmp
);
813 fs_give((void **) &utf8
);
816 level
= WSN
; /* what's next... */
818 else if(c
< '0' || c
> '9'){ /* Must be part of tag */
819 snprintf(number
, sizeof(number
), "%d. ", *no
);
820 u
= utf8_to_ucs4_cpystr(number
);
822 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
- tmp
)]
823 : &(*lp
)->text
[*off
- (p
- tmp
)],
824 0, u
, j
=ucs4_strlen(u
));
826 fs_give((void **) &u
);
830 level
= TAG
; /* interpret the name */
831 goto process_tag
; /* in case already past end of tag */
838 case WSN
: /* blast whitespace */
839 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== '\0')){
842 else if(c
== '['){ /* labeled attachment */
845 else if(c
== ',' || c
== ' '){
846 /* TRANSLATORS: Attchmnt is an abbreviation for Attachment and
847 the %s is replaced with the character that is not
848 allowed in the name. */
849 eml
.s
= (c
== ',') ? "," : "space";
850 emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml
);
852 level
= TG
; /* eat rest of garbage */
857 case TAG
: /* get and check filename */
858 /* or labeled attachment */
859 process_tag
: /* enclosed in [] */
861 || (lbln
&& c
== ']')
862 || (quoted
&& p
!= tmp
&& c
== '\"')
863 || (!(lbln
|| quoted
)
864 && (c
<= 0xff && ((isspace((unsigned char) c
) && c_lookahead
== (UCS
) '(') || strchr(",(\"", c
))))){
870 *p
= '\0'; /* got something */
872 utf8
= ucs4_to_utf8_cpystr(tmp
);
874 if(strlen(utf8
) > fnlen
)
875 emlwrite("File name too big!",NULL
);
877 strncpy(fn
, utf8
, fnlen
); /* store file name */
879 fs_give((void **) &utf8
);
882 if(!lbln
){ /* normal file attachment */
884 && !compresspath(opertree
, fn
, fnlen
)){
885 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
887 _("Attachments allowed only from %s: too many ..'s"),
895 if((status
=fexist(fn
, "r", &attsz
)) != FIOSUC
){
898 level
= TG
; /* munch rest of garbage */
902 if((gmode
& MDTREE
) && !in_oper_tree(fn
)){
903 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
904 emlwrite(_("\007Attachments allowed only from %s"), &eml
);
911 utf8
= ucs4_to_utf8_cpystr(tmp
);
913 if(utf8
&& strcmp(fn
, utf8
)){ /* fn changed: display it */
914 if(*off
>= p
- tmp
){ /* room for it? */
915 u
= utf8_to_ucs4_cpystr(fn
);
918 * This whole parsing of the attachment line
919 * thing is ad hoc and susceptible to problems,
920 * and this particular part is no exception.
921 * Quote the filename if it contains spaces.
924 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
925 : &(*lp
)->text
[*off
- (p
-tmp
)],
930 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
931 : &(*lp
)->text
[*off
- (p
-tmp
)],
932 p
-tmp
, u
, j
=ucs4_strlen(u
));
934 *off
+= j
- (p
- tmp
); /* advance offset */
937 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
938 : &(*lp
)->text
[*off
],
944 fs_give((void **) &u
);
950 emlwrite("\007Attchmnt: Problem displaying real file path", NULL
);
955 fs_give((void **) &utf8
);
957 else{ /* labelled attachment! */
959 * should explain about labelled attachments:
960 * these are attachments that came into the composer
961 * with meaningless file names (up to caller of
962 * composer to decide), for example, attachments
963 * being forwarded from another message. here, we
964 * just make sure the size stays what was passed
965 * to us. The user is SOL if they change the label
966 * since, as it is now, after changed, it will
967 * just get dropped from the list of what gets
968 * passed back to the caller.
972 if(c
!= ']'){ /* legit label? */
974 emlwrite(_("\007Attchmnt: Expected ']' after \"%s\""),
981 strncat(fn
, "]", fnlen
-strlen(fn
)-1);
985 * This is kind of cheating since otherwise
986 * ParseAttach doesn't know about the attachment
987 * struct. OK if filename's not found as it will
988 * get taken care of later...
990 tp
= Pmaster
->attachments
; /* caller check Pmaster! */
1003 emlwrite("\007Attchmnt: Unknown reference: %s", &eml
);
1009 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
1010 : &(*lp
)->text
[*off
- (p
-tmp
)],
1013 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1014 : &(*lp
)->text
[*off
],
1020 p
= tmp
; /* reset p in tmp */
1024 if(!lbln
&& c
== '(') /* no space 'tween file, size*/
1027 || (!(lbln
|| quoted
) && (c
== ',' || c
== '\"'))){
1028 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1031 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1032 u
= utf8_to_ucs4_cpystr(ctmp
);
1034 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1035 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1036 fs_give((void **) &u
);
1039 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1040 : &(*lp
)->text
[*off
],
1041 0, tmp
, j
=ucs4_strlen(tmp
));
1044 level
= (c
== '\"') ? CMMNT
: TG
;/* cmnt or eat trash */
1047 else if(!(lbln
|| quoted
)
1048 && (c
== ',' || /** c == ' ' || **/ c
== '[' || c
== ']')){
1049 eml
.s
= c
== ',' ? ","
1050 : c
== ' ' ? "space"
1051 : c
== '[' ? "[" : "]";
1052 emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml
);
1053 rv
= -1; /* bad char in file name */
1054 level
= TG
; /* gobble garbage */
1056 else if(!(lbln
|| quoted
) && (c
<= 0xff && isspace((unsigned char) c
))){
1058 *p
++ = c
; /* add char to name */
1061 *p
++ = c
; /* add char to name */
1065 case WST
: /* skip white space */
1066 if(c
> 0xff || !isspace((unsigned char)c
)){
1068 * whole attachment, comment or done!
1070 if(c
== ',' || c
== '\0' || c
== '\"'){
1071 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), sizeof(sz
));
1072 sz
[sizeof(sz
)-1] = '\0';
1074 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1075 u
= utf8_to_ucs4_cpystr(ctmp
);
1077 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1078 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1079 fs_give((void **) &u
);
1082 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1083 : &(*lp
)->text
[*off
],
1084 0, tmp
, j
=ucs4_strlen(tmp
));
1087 level
= (c
== '\"') ? CMMNT
: TG
;
1088 lbln
= 0; /* reset flag */
1090 else if(c
== '('){ /* get the size */
1095 emlwrite(_("\007Attchmnt: Expected '(' or '\"' after %s"), &eml
);
1096 rv
= -1; /* bag it all */
1102 case ASIZE
: /* check size */
1103 if(c
== ')'){ /* finished grabbing size */
1106 * replace sizes if they don't match!
1108 utf8
= ucs4_to_utf8_cpystr(tmp
);
1110 strncpy(sz
, utf8
, szlen
);
1112 fs_give((void **) &utf8
);
1115 if(strcmp(sz
, (lblsz
) ? lblsz
: prettysz(attsz
))){
1116 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1118 if(p
-tmp
> *off
){ /* where to begin replacemnt */
1123 sinserts((*lp
)->text
, *off
, uu
, 0);
1124 u
= utf8_to_ucs4_cpystr(sz
);
1126 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
1128 fs_give((void **) &u
);
1134 j
= (*off
) - (p
-tmp
);
1135 u
= utf8_to_ucs4_cpystr(sz
);
1137 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
1139 p
-tmp
, u
, ucs4_strlen(u
));
1140 *off
+= ucs4_strlen(u
) - (p
-tmp
);
1141 fs_give((void **) &u
);
1148 level
= SWS
; /* what's next... */
1150 else if(c
== '\0' || c
== ','){
1152 utf8
= ucs4_to_utf8_cpystr(tmp
);
1154 emlwrite(_("\007Attchmnt: Size field missing ')': \"%s\""), &eml
);
1156 fs_give((void **) &utf8
);
1166 case SWS
: /* skip white space */
1167 if(c
> 0xff || !isspace((unsigned char)c
)){
1168 if(c
== ','){ /* no description */
1169 level
= TG
; /* munch rest of garbage */
1170 lbln
= 0; /* reset flag */
1172 else if(c
!= '\"' && c
!= '\0'){
1173 emlwrite(_("\007Attchmnt: Malformed comment, quotes required"), NULL
);
1182 case CMMNT
: /* slurp up comment */
1183 if((c
== '\"' && !escaped
) || c
== '\0'){
1184 *p
= '\0'; /* cap it off */
1185 p
= tmp
; /* reset p */
1186 utf8
= ucs4_to_utf8_cpystr(tmp
);
1188 if(strlen(utf8
) > cmntlen
)
1189 emlwrite("Comment too long!",NULL
);
1191 strncpy(cmnt
,utf8
,cmntlen
-1); /* copy the comment */
1192 cmnt
[cmntlen
-1] = '\0';
1193 fs_give((void **) &utf8
);
1195 emlwrite(_("\007Attchmnt: Closing quote required at end of comment"), NULL
);
1200 level
= TG
; /* prepare for next one */
1201 lbln
= 0; /* reset flag */
1203 else if(c
== '\\' && !escaped
){ /* something escaped? */
1208 if(c
!= '\"') /* we only quote escapes */
1214 if(((*p
++ = c
) & 0x80) && (gmode
& MDHBTIGN
) && !hibit
++)
1215 emlwrite(HIBIT_WARN
, NULL
);
1220 case TG
: /* get comma or final EOL */
1223 if(c
> 0xff || !isspace((unsigned char)c
)){
1227 lprev
->text
[*off
= eod
] = '\0';
1231 memcpy(&(*lp
)->text
[eod
], &(*lp
)->text
[*off
],
1232 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
1239 emlwrite(_("\007Attchmnt: Comma must separate attachments"), NULL
);
1245 default: /* something's very wrong */
1246 emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL
);
1247 return(-1); /* just give up */
1250 if(c
== '\0') /* we're done */
1256 * not in comment or label name? done.
1258 if(c
== ',' && (level
!= TAG
&& level
!= CMMNT
&& !lbln
))
1259 break; /* put offset past ',' */
1267 * NewAttach - given a filename (assumed to accessible) and comment, creat
1270 NewAttach(char *f
, long l
, char *c
)
1275 if((tp
=(PATMT
*)malloc(sizeof(PATMT
))) == NULL
){
1276 emlwrite("No memory to add attachment", NULL
);
1280 memset(tp
, 0, sizeof(PATMT
));
1282 /* file and size malloc */
1284 if((tp
->filename
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1285 emlwrite("Can't malloc name for attachment", NULL
);
1290 strncpy(tp
->filename
, f
, len
);
1291 tp
->filename
[len
] = '\0';
1294 len
= strlen(prettysz((off_t
) l
));
1295 tp
->size
= (char *) malloc((len
+1) * sizeof(char));
1296 if(tp
->size
== NULL
){
1297 emlwrite("Can't malloc size for attachment", NULL
);
1298 free((char *) tp
->filename
);
1303 strncpy(tp
->size
, prettysz((off_t
) l
), len
);
1304 tp
->size
[len
] = '\0';
1308 /* description malloc */
1310 if((tp
->description
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1311 emlwrite("Can't malloc description for attachment", NULL
);
1312 free((char *) tp
->size
);
1313 free((char *) tp
->filename
);
1318 strncpy(tp
->description
, c
, len
);
1319 tp
->description
[len
] = '\0';
1321 /* callback to show user the mime type that will be used for attachment */
1322 if(Pmaster
->mimetype
&& (*Pmaster
->mimetype
)(f
) > 0){
1327 rv
= (*Pmaster
->showmsg
)('x');
1330 if(rv
) /* Did showmsg corrupt the screen? */
1331 PaintBody(0); /* Yes, repaint it */
1341 * AttachError - Sniff list of attachments, returning TRUE if there's
1342 * any sign of trouble...
1352 ap
= Pmaster
->attachments
;
1354 if((ap
->flags
) & A_ERR
)
1365 QuoteAttach(char *fn
, size_t fnlen
)
1369 if(*fn
&& strpbrk(fn
, " \t,(\"")){ /* Quote it? */
1370 p
= &fn
[strlen(fn
)];
1393 free((char *)p
->description
);
1396 if(p
->flags
& A_TMP
)
1397 our_unlink(p
->filename
);
1399 free((char *)p
->filename
);
1403 free((char *)p
->size
);
1406 free((char *)p
->id
);
1410 #endif /* ATTACHMENTS */
1414 * intag - return TRUE if i is in a column that makes up an
1415 * attachment line number
1418 intag(UCS
*s
, int i
)
1423 while(*p
!= '\0' && (p
-s
) < 5){ /* is there a tag? it */
1424 if(n
&& *p
== '.') /* can't be more than 4 */
1425 return(i
<= p
-s
); /* chars long! */
1427 if(*p
< '0' || *p
> '9')
1430 n
= (n
* 10) + (*p
- '0');
1440 * prettysz - return pointer to string containing nice size description
1446 long sz
, left
, right
;
1452 snprintf(b
, sizeof(b
), "%ld B", sz
); /* xxx B */
1454 else if(sz
< 9950L){
1455 left
= (sz
+ 50L) / 1000L;
1456 right
= ((sz
+ 50L) - left
* 1000L) / 100L;
1457 snprintf(b
, sizeof(b
), "%ld.%ld KB", left
, right
); /* x.x KB */
1459 else if(sz
< 999500L){
1460 snprintf(b
, sizeof(b
), "%ld KB", (sz
+ 500L) / 1000L); /* xxx KB */
1462 else if(sz
< 9950000L){
1463 left
= (sz
+ 50000L) / 1000000L;
1464 right
= ((sz
+ 50000L) - left
* 1000000L) / 100000L;
1465 snprintf(b
, sizeof(b
), "%ld.%ld MB", left
, right
); /* x.x MB */
1468 snprintf(b
, sizeof(b
), "%ld MB", (sz
+ 500000L) / 1000000L); /* xxx MB */
1476 * sinserts - s insert into another string
1479 sinserts(UCS
*ds
, /* dest string */
1480 int dl
, /* where to begin insert */
1481 UCS
*ss
, /* source string */
1482 int sl
) /* length of ss */
1484 UCS
*dp
, *edp
; /* pointers into dest. */
1485 size_t j
; /* jump difference */
1487 if(sl
>= dl
){ /* source bigger than dest. */
1488 dp
= ds
+ dl
; /* shift dest. to make room */
1489 if((edp
= ucs4_strchr(dp
, '\0')) != NULL
){
1492 for( ;edp
>= dp
; edp
--)
1499 emlwrite("\007No end of line???", NULL
); /* can this happen? */
1501 else{ /* dest is longer, shrink it */
1502 j
= dl
- sl
; /* difference in lengths */
1504 while(sl
--) /* copy u onto ds */
1507 if(ucs4_strlen(ds
) > j
){ /* shuffle the rest left */
1510 while(*ds
++ != '\0');