2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2021 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: Routines to support attachments in the Pine composer
18 #include "../pith/charconv/filesys.h"
19 #include "../pith/string.h"
26 int ParseAttach(struct hdr_line
**, int *, char *,
27 size_t, char *, size_t, char *, size_t, int *);
28 PATMT
*NewAttach(char *, long, char *);
29 void ZotAttach(struct pico_atmt
*);
30 void sinserts(UCS
*, int, UCS
*, int);
31 int AttachUpload(char *, size_t, char *, size_t);
32 int AttachCancel(char *);
35 #define HIBIT_WARN "Only ASCII characters allowed in attachment comments"
39 * AskAttach - ask for attachment fields and build resulting structure
40 * return pointer to that struct if OK, NULL otherwise
43 AskAttach(char *cmnt
, size_t cmntlen
, LMLIST
**lm
)
45 int i
, status
, fbrv
, upload
= 0;
46 int fb_flags
= FB_READ
| FB_ATTACH
;
50 char fn
[NLINE
], sz
[32];
54 i
= 2; /* 2 is prompt for file, 1 is prompt for comment */
61 EXTRAKEYS menu_attach
[10];
64 memset(&menu_attach
, 0, 10*sizeof(EXTRAKEYS
));
65 menu_attach
[n
= 0].name
= "^T";
66 menu_attach
[n
].label
= N_("To Files");
67 menu_attach
[n
].key
= (CTRL
|'T');
70 menu_attach
[++n
].name
= "TAB";
71 menu_attach
[n
].label
= N_("Complete");
72 menu_attach
[n
].key
= (CTRL
|'I');
75 #if !defined(DOS) && !defined(MAC)
76 if(Pmaster
&& Pmaster
->upload
){
78 * The Plan: ^R prompts for uploaded file's name which
79 * is passed to the defined upload command when the user
80 * hits Return to confirm the name.
81 * NOTE: this is different than upload into message
82 * text in which case the uploaded name isn't useful so
83 * a temp file is ok (be sure to fix the file mode).
85 menu_attach
[++n
].name
= "^Y";
86 menu_attach
[n
].key
= (CTRL
|'Y');
87 /* TRANSLATORS: Read File is a prompt for the name of
88 a file to be read into the composer. */
89 menu_attach
[n
].label
= upload
? N_("Read File") : N_("RcvUpload");
93 menu_attach
[++n
].name
= NULL
;
94 KS_OSDATASET(&menu_attach
[0], KS_NONE
);
95 status
= mlreply_utf8(upload
? _("Name to give uploaded attachment: ")
96 /* TRANSLATORS: User is being asked for the name
97 of the file they want to attach to a message. */
98 : _("File to attach: "),
99 fn
, sizeof(fn
), QNORML
, menu_attach
);
102 /* TRANSLATORS: This is a prompt for a comment about the file
103 they have attached. */
104 status
= mlreply_utf8(_("Attachment comment: "), cmnt
, cmntlen
, QNODQT
, NULL
);
109 VARS_TO_SAVE
*saved_state
;
111 saved_state
= save_pico_state();
112 (*Pmaster
->helper
)(Pmaster
->attach_help
, _("Attach Help"), 1);
114 restore_pico_state(saved_state
);
115 free_pico_state(saved_state
);
118 pico_refresh(FALSE
, 1);
123 eml
.s
= (i
== 2) ? "file" : "comment";
124 emlwrite("No Attachment %s help yet!", &eml
);
136 if(*fn
&& (p
= strrchr(fn
, C_FILESEP
))){
140 strncpy(bfn
, S_FILESEP
, sizeof(bfn
));
141 bfn
[sizeof(bfn
)-1] = '\0';
144 else if(fn
[0] == C_FILESEP
145 || (isalpha((unsigned char)fn
[0])
147 if(fn
[1] == ':' && p
== fn
+2)
150 if(dirlen
< sizeof(bfn
)){
151 strncpy(bfn
, fn
, dirlen
);
156 else if (fn
[0] == C_FILESEP
|| fn
[0] == '~'){
157 if(dirlen
< sizeof(bfn
)){
158 strncpy(bfn
, fn
, dirlen
);
164 snprintf(bfn
, sizeof(bfn
), "%s%c%.*s",
167 : ((gmode
& MDTREE
) || opertree
[0])
168 ? opertree
: gethomedir(NULL
),
169 C_FILESEP
, (int) (p
- fn
), fn
);
173 strncpy(bfn
, (gmode
& MDCURDIR
)
175 : ((gmode
& MDTREE
) || opertree
[0])
176 ? opertree
: gethomedir(NULL
), sizeof(bfn
));
177 bfn
[sizeof(bfn
)-1] = '\0';
180 if(!pico_fncomplete(bfn
, fname
, sizeof(fn
)-(fname
-fn
)))
195 if(*fn
== '\0' || !isdir(fn
, NULL
, NULL
)){
196 strncpy(fn
, (gmode
& MDCURDIR
)
197 ? (browse_dir
[0] ? browse_dir
: ".")
198 : ((gmode
& MDTREE
) || opertree
[0])
200 : (browse_dir
[0] ? browse_dir
201 : gethomedir(NULL
)), sizeof(fn
));
202 fn
[sizeof(fn
)-1] = '\0';
205 if((fbrv
= FileBrowse(fn
, sizeof(fn
), bfn
, sizeof(bfn
), sz
, sizeof(sz
),
207 : fb_flags
|FB_LMODEPOS
,
210 if (upload
&& (strlen(fn
)+strlen(S_FILESEP
)+strlen(bfn
)) < sizeof(fn
)){
215 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
216 || (new->fname
=malloc((len1
+1) * sizeof(char))) == NULL
217 || (new->dir
=malloc((len2
+1) * sizeof(char))) == NULL
){
218 emlwwrite(_("Can't malloc space for filename"), NULL
);
222 strncpy(new->fname
, bfn
, len1
);
223 new->fname
[len1
] = '\0';
224 strncpy(new->dir
, fn
, len2
);
225 new->dir
[len2
] = '\0';
226 strncpy(new->size
, sz
, sizeof(new->size
)-1);
227 new->size
[sizeof(new->size
)-1] = '\0';
231 strncat(fn
, S_FILESEP
, sizeof(fn
)-strlen(fn
)-1);
232 fn
[sizeof(fn
)-1] = '\0';
233 strncat(fn
, bfn
, sizeof(fn
)-strlen(fn
)-1);
234 fn
[sizeof(fn
)-1] = '\0';
235 if(!AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
))){
236 i
= 2; /* keep prompting for file */
237 sleep(3); /* problem, show error! */
240 i
--; /* go prompt for comment */
244 if(lm
&& *lm
&& !(*lm
)->next
) /* get comment */
246 else{ /* no comments if multiple files */
254 pico_refresh(FALSE
,1);
256 emlwwrite(_("File name too BIG, cannot select!"), NULL
);
265 pico_refresh(FALSE
, 1);
267 emlwwrite(_("File name too big, cannot select!"), NULL
);
271 /* fall thru to clean up the screen */
274 pico_refresh(FALSE
, 1);
278 #if !defined(DOS) && !defined(MAC)
279 case (CTRL
|'Y'): /* upload? */
281 upload
^= 1; /* flip mode */
289 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
291 case TRUE
: /* some comment */
292 case FALSE
: /* No comment */
295 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
296 status
= AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
));
297 pico_refresh(FALSE
, 1);
300 i
= 2; /* keep prompting for file */
301 sleep(3); /* problem, show error! */
305 if(*fn
== '\"' && fn
[strlen(fn
)-1] == '\"'){
308 for(j
= 0; (fn
[j
] = fn
[j
+1]); j
++)
316 && !compresspath(opertree
, fn
, sizeof(fn
))){
317 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
319 /* TRANSLATORS: the %s is replaced with the name of a directory */
320 _("Restricted mode allows attachments from %s only: too many ..'s"),
325 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
326 if((gmode
&MDTREE
) && !in_oper_tree(fn
)){
327 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
329 _("Restricted mode allows attachments from %s only"), &eml
);
334 if((status
= fexist(fn
, "r", &attsz
)) != FIOSUC
){
335 fioperr(status
, fn
); /* file DOESN'T exist! */
340 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
341 || (new->fname
=malloc((len
+1)*sizeof(char))) == NULL
){
342 emlwwrite(_("Can't malloc space for filename"), NULL
);
347 strncpy(new->fname
, fn
, len
);
348 new->fname
[len
] = '\0';
349 strncpy(new->size
, prettysz(attsz
), sizeof(new->size
));
350 new->size
[sizeof(new->size
)-1] = '\0';
355 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
360 return(1); /* mission accomplished! */
374 * AttachUpload - Use call back to run the external upload command.
377 AttachUpload(char *fn
, size_t fnlen
, char *sz
, size_t szlen
)
382 emlwwrite(_("Restricted mode disallows uploaded command"), NULL
);
386 if(Pmaster
&& Pmaster
->upload
&& (*Pmaster
->upload
)(fn
, fnlen
, &l
)){
387 strncpy(sz
, prettysz((off_t
)l
), szlen
);
400 AttachCancel(char *fn
)
402 emlwrite(_("Attach cancelled"), NULL
);
404 our_unlink(fn
); /* blast uploaded file */
410 extern struct headerentry
*headents
;
413 * SyncAttach - given a pointer to a linked list of attachment structures,
414 * return with that structure sync'd with what's displayed.
415 * delete any attachments in list of structs that's not on
416 * the display, and add any that aren't in list but on display.
421 int offset
= 0, /* the offset to begin */
423 ki
= 0, /* number of known attmnts */
424 bi
= 0, /* build array index */
425 nbld
= 0, /* size of build array */
426 na
, /* old number of attachmnt */
429 char file
[NLINE
], /* buffers to hold it all */
432 struct hdr_line
*lp
; /* current line in header */
433 struct headerentry
*entry
;
434 PATMT
*tp
, **knwn
= NULL
, **bld
= NULL
;
440 for(entry
= headents
; entry
->name
!= NULL
; entry
++) {
445 for(tp
= Pmaster
->attachments
; tp
; tp
= tp
->next
)
446 ki
++; /* Count known attachments */
449 if((knwn
= (PATMT
**)malloc((ki
+1) * (sizeof(PATMT
*)))) == NULL
){
450 eml
.s
= comatose(ki
+ 1);
451 emlwwrite(_("Can't allocate space for %s known attachment array entries"),
456 for(i
=0, tp
= Pmaster
->attachments
; i
< ki
; i
++, tp
= tp
->next
){
457 knwn
[i
] = tp
; /* fill table of */
458 /* known attachments */
464 * As a quick hack to avoid too many reallocs, check to see if
465 * there are more header lines than known attachments.
467 for(lp
= entry
->hd_text
; lp
; lp
= lp
->next
)
468 nbld
++; /* count header lines */
470 nbld
= nbld
> ki
? nbld
: ki
+ 1;
472 if((bld
= (PATMT
**)malloc(nbld
* (sizeof(PATMT
*)))) == NULL
){
473 eml
.s
= comatose(nbld
);
474 emlwwrite(_("Can't allocate space for %s build array entries"), &eml
);
484 if(bi
== nbld
){ /* need to grow build array? */
485 if((bld
= (PATMT
**)realloc(bld
, ++nbld
* sizeof(PATMT
*))) == NULL
){
486 eml
.s
= comatose(nbld
);
487 emlwwrite(_("Can't resize build array to %s entries"), &eml
);
493 if((status
= ParseAttach(&lp
, &offset
, file
, sizeof(file
), size
, sizeof(size
),
494 comment
, sizeof(comment
), &na
)) != 0)
495 rv
= (rv
< 0) ? rv
: status
; /* remember worst case */
498 if(n
!= na
&& na
> 0 && na
<= ki
&& (knwn
[na
-1]->flags
& A_FLIT
)){
499 bld
[bi
++] = knwn
[na
-1];
507 && (!in_oper_tree(file
)
508 || !compresspath(file
, fn
, sizeof(fn
))))
509 /* no attachments outside ~ in secure mode! */
513 for(i
= 0; i
< ki
; i
++){ /* already know about it? */
515 * this is kind of gruesome. what we want to do is keep track
516 * of literal attachment entries because they may not be
517 * actual files we can access or that the user can readily
521 && ((!(knwn
[i
]->flags
&A_FLIT
)
522 && !strcmp(file
, knwn
[i
]->filename
))
523 || ((knwn
[i
]->flags
&A_FLIT
) && i
+1 == na
))){
525 knwn
[i
] = NULL
; /* forget we know about it */
527 if(status
== -1) /* ignore garbage! */
530 if((tp
->flags
&A_FLIT
) && strcmp(file
, tp
->filename
)){
532 if((j
=strlen(file
)) > strlen(tp
->filename
)){
533 if((tp
->filename
= (char *)realloc(tp
->filename
,
534 sizeof(char)*(j
+1))) == NULL
){
535 emlwwrite(_("Can't realloc filename space"),NULL
);
541 strncpy(tp
->filename
, file
, j
);
542 tp
->filename
[j
] = '\0';
544 if(tp
->id
) fs_give((void **) &tp
->id
);
546 else if(tp
->size
&& strcmp(tp
->size
, size
)){
548 if((j
=strlen(size
)) > strlen(tp
->size
)){
549 if((tp
->size
=(char *)realloc(tp
->size
,
550 sizeof(char)*(j
+1))) == NULL
){
551 emlwwrite(_("Can't realloc space for size"), NULL
);
557 strncpy(tp
->size
, size
, j
);
561 if(strcmp(tp
->description
, comment
)){ /* new comment */
563 if((j
=strlen(comment
)) > strlen(tp
->description
)){
564 if((tp
->description
=(char *)realloc(tp
->description
,
565 sizeof(char)*(j
+1))) == NULL
){
566 emlwwrite(_("Can't realloc description"), NULL
);
572 strncpy(tp
->description
, comment
, j
);
573 tp
->description
[j
] = '\0';
584 if((tp
= NewAttach(file
, atol(size
), comment
)) == NULL
){
594 tp
->flags
|= A_ERR
; /* turn ON error bit */
596 tp
->flags
&= ~(A_ERR
); /* turn OFF error bit */
600 for(i
=0; i
< bi
-1; i
++) /* link together newly built list */
601 bld
[i
]->next
= bld
[i
+1];
603 bld
[i
]->next
= NULL
; /* tie it off */
604 Pmaster
->attachments
= bld
[0];
607 Pmaster
->attachments
= NULL
;
611 for(i
= 0; i
< ki
; i
++){ /* kill old/unused references */
614 free((char *) knwn
[i
]);
628 * ParseAttach - given a header line and an offset into it, return with
629 * the three given fields filled in. Size of fn and cmnt
630 * buffers should be passed in fnlen and cmntlen.
631 * Always updates header fields that have changed or are
632 * fixed. An error advances offset to next attachment.
634 * returns: 1 if a field changed
639 ParseAttach(struct hdr_line
**lp
, /* current header line */
640 int *off
, /* offset into that line */
641 char *fn
, /* return file name field */
642 size_t fnlen
, /* fn buffer size */
645 char *cmnt
, /* places to return fields */
647 int *no
) /* attachment number */
649 int j
= 0, status
, bod
, eod
= -1,
650 rv
= 0, /* return value */
652 lbln
= 0, /* label'd attachment */
657 off_t attsz
; /* attachment length */
660 UCS tmp
[1024], *p
, *u
, quotechar
[2];
663 char *lblsz
= NULL
, /* label'd attchmnt's size */
665 struct hdr_line
*lprev
= NULL
;
666 enum { /* parse levels */
667 LWS
, /* leading white space */
668 NUMB
, /* attachment number */
669 WSN
, /* white space after number */
670 TAG
, /* attachments tag (fname) */
671 WST
, /* white space after tag */
672 ASIZE
, /* attachments size */
673 SWS
, /* white space after size */
674 CMMNT
, /* attachment comment */
675 TG
} level
; /* trailing garbage */
677 *fn
= *sz
= *cmnt
= '\0'; /* initialize return strings */
679 orig_offset
= bod
= *off
;
683 level
= LWS
; /* start at beginning */
686 if((c
=(*lp
)->text
[*off
]) == '\0'){ /* end of display line */
687 if(level
== LWS
&& bod
!= *off
){
688 (*lp
)->text
[bod
] = '\0';
692 if((*lp
= (*lp
)->next
) != NULL
)
693 c
= (*lp
)->text
[*off
= bod
= 0]; /* reset offset */
697 c_lookahead
= (*lp
)->text
[*off
+ 1];
698 if(c_lookahead
== '\0'){ /* end of display line */
699 if((*lp
)->next
!= NULL
)
700 c_lookahead
= (*lp
)->next
->text
[0];
705 case LWS
: /* skip leading white space */
706 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== ',')){
712 if(bod
> 0 && *off
>= bod
&& lprev
){
713 lprev
->text
[bod
- 1] = '\0';
717 else if(*off
> bod
&& *lp
){ /* wipe out whitespace */
718 memcpy(&(*lp
)->text
[bod
], &(*lp
)->text
[*off
],
719 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
720 *off
= bod
; /* reset pointer */
727 if(c
> 0xff || !isdigit((unsigned char)c
)){ /* add a number */
728 snprintf(number
, sizeof(number
), "%d. ", *no
);
729 *no
= 0; /* no previous number! */
730 u
= utf8_to_ucs4_cpystr(number
);
732 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
733 : &(*lp
)->text
[*off
],
734 0, u
, j
=ucs4_strlen(u
));
736 fs_give((void **) &u
);
741 level
= TAG
; /* interpret the name */
745 case NUMB
: /* attachment number */
746 if(c
== '\0' || c
== ','){ /* got to end, no number yet */
748 snprintf(number
, sizeof(number
), "%d. ", *no
);
749 *no
= 0; /* no previous number! */
751 *lp
= lprev
; /* go back and look at prev */
753 c
= (*lp
)->text
[*off
= orig_offset
]; /* reset offset */
754 u
= utf8_to_ucs4_cpystr(number
);
756 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
757 : &(*lp
)->text
[*off
],
758 0, u
, j
=ucs4_strlen(u
));
760 fs_give((void **) &u
);
765 level
= WSN
; /* what's next... */
768 else if(c
== '.' && c_lookahead
<= 0xff && isspace((unsigned char)c_lookahead
)){
769 /* finished grabbing number */
770 /* if not space is not number */
772 * replace number if it's not right
775 snprintf(number
, sizeof(number
), "%d", *no
); /* record the current... */
776 utf8
= ucs4_to_utf8_cpystr(tmp
);
777 *no
= atoi(utf8
); /* and the old place in list */
778 if(strcmp(number
, utf8
)){
779 if(p
-tmp
> *off
){ /* where to begin replacement */
784 sinserts((*lp
)->text
, *off
, uu
, 0);
785 u
= utf8_to_ucs4_cpystr(number
);
787 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
790 fs_give((void **) &u
);
796 j
= (*off
) - (p
-tmp
);
797 u
= utf8_to_ucs4_cpystr(number
);
799 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
801 p
-tmp
, u
, ucs4_strlen(u
));
803 fs_give((void **) &u
);
806 *off
+= strlen(number
) - (p
-tmp
);
812 fs_give((void **) &utf8
);
815 level
= WSN
; /* what's next... */
817 else if(c
< '0' || c
> '9'){ /* Must be part of tag */
818 snprintf(number
, sizeof(number
), "%d. ", *no
);
819 u
= utf8_to_ucs4_cpystr(number
);
821 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
- tmp
)]
822 : &(*lp
)->text
[*off
- (p
- tmp
)],
823 0, u
, j
=ucs4_strlen(u
));
825 fs_give((void **) &u
);
829 level
= TAG
; /* interpret the name */
830 goto process_tag
; /* in case already past end of tag */
837 case WSN
: /* blast whitespace */
838 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== '\0')){
841 else if(c
== '['){ /* labeled attachment */
844 else if(c
== ',' || c
== ' '){
845 /* TRANSLATORS: Attchmnt is an abbreviation for Attachment and
846 the %s is replaced with the character that is not
847 allowed in the name. */
848 eml
.s
= (c
== ',') ? "," : "space";
849 emlwwrite(_("Attchmnt: '%s' not allowed in file name"), &eml
);
851 level
= TG
; /* eat rest of garbage */
856 case TAG
: /* get and check filename */
857 /* or labeled attachment */
858 process_tag
: /* enclosed in [] */
860 || (lbln
&& c
== ']')
861 || (quoted
&& p
!= tmp
&& c
== '\"')
862 || (!(lbln
|| quoted
)
863 && (c
<= 0xff && ((isspace((unsigned char) c
) && c_lookahead
== (UCS
) '(') || strchr(",(\"", c
))))){
869 *p
= '\0'; /* got something */
871 utf8
= ucs4_to_utf8_cpystr(tmp
);
873 if(strlen(utf8
) > fnlen
)
874 emlwrite("File name too big!",NULL
);
876 strncpy(fn
, utf8
, fnlen
); /* store file name */
878 fs_give((void **) &utf8
);
881 if(!lbln
){ /* normal file attachment */
883 && !compresspath(opertree
, fn
, fnlen
)){
884 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
886 _("Attachments allowed only from %s: too many ..'s"),
894 if((status
=fexist(fn
, "r", &attsz
)) != FIOSUC
){
897 level
= TG
; /* munch rest of garbage */
901 if((gmode
& MDTREE
) && !in_oper_tree(fn
)){
902 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
903 emlwwrite(_("Attachments allowed only from %s"), &eml
);
910 utf8
= ucs4_to_utf8_cpystr(tmp
);
912 if(utf8
&& strcmp(fn
, utf8
)){ /* fn changed: display it */
913 if(*off
>= p
- tmp
){ /* room for it? */
914 u
= utf8_to_ucs4_cpystr(fn
);
917 * This whole parsing of the attachment line
918 * thing is ad hoc and susceptible to problems,
919 * and this particular part is no exception.
920 * Quote the filename if it contains spaces.
923 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
924 : &(*lp
)->text
[*off
- (p
-tmp
)],
929 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
930 : &(*lp
)->text
[*off
- (p
-tmp
)],
931 p
-tmp
, u
, j
=ucs4_strlen(u
));
933 *off
+= j
- (p
- tmp
); /* advance offset */
936 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
937 : &(*lp
)->text
[*off
],
943 fs_give((void **) &u
);
949 emlwwrite(_("Attchmnt: Problem displaying real file path"), NULL
);
954 fs_give((void **) &utf8
);
956 else{ /* labelled attachment! */
958 * should explain about labelled attachments:
959 * these are attachments that came into the composer
960 * with meaningless file names (up to caller of
961 * composer to decide), for example, attachments
962 * being forwarded from another message. here, we
963 * just make sure the size stays what was passed
964 * to us. The user is SOL if they change the label
965 * since, as it is now, after changed, it will
966 * just get dropped from the list of what gets
967 * passed back to the caller.
971 if(c
!= ']'){ /* legit label? */
973 emlwwrite(_("Attchmnt: Expected ']' after \"%s\""),
980 strncat(fn
, "]", fnlen
-strlen(fn
)-1);
984 * This is kind of cheating since otherwise
985 * ParseAttach doesn't know about the attachment
986 * struct. OK if filename's not found as it will
987 * get taken care of later...
989 tp
= Pmaster
->attachments
; /* caller check Pmaster! */
1002 emlwwrite(_("Attchmnt: Unknown reference: %s"), &eml
);
1008 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
1009 : &(*lp
)->text
[*off
- (p
-tmp
)],
1012 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1013 : &(*lp
)->text
[*off
],
1019 p
= tmp
; /* reset p in tmp */
1023 if(!lbln
&& c
== '(') /* no space 'tween file, size*/
1026 || (!(lbln
|| quoted
) && (c
== ',' || c
== '\"'))){
1027 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1030 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1031 u
= utf8_to_ucs4_cpystr(ctmp
);
1033 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1034 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1035 fs_give((void **) &u
);
1038 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1039 : &(*lp
)->text
[*off
],
1040 0, tmp
, j
=ucs4_strlen(tmp
));
1043 level
= (c
== '\"') ? CMMNT
: TG
;/* cmnt or eat trash */
1046 else if(!(lbln
|| quoted
)
1047 && (c
== ',' || /** c == ' ' || **/ c
== '[' || c
== ']')){
1048 eml
.s
= c
== ',' ? ","
1049 : c
== ' ' ? "space"
1050 : c
== '[' ? "[" : "]";
1051 emlwwrite(_("Attchmnt: '%s' not allowed in file name"), &eml
);
1052 rv
= -1; /* bad char in file name */
1053 level
= TG
; /* gobble garbage */
1055 else if(!(lbln
|| quoted
) && (c
<= 0xff && isspace((unsigned char) c
))){
1057 *p
++ = c
; /* add char to name */
1060 *p
++ = c
; /* add char to name */
1064 case WST
: /* skip white space */
1065 if(c
> 0xff || !isspace((unsigned char)c
)){
1067 * whole attachment, comment or done!
1069 if(c
== ',' || c
== '\0' || c
== '\"'){
1070 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1073 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1074 u
= utf8_to_ucs4_cpystr(ctmp
);
1076 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1077 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1078 fs_give((void **) &u
);
1081 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1082 : &(*lp
)->text
[*off
],
1083 0, tmp
, j
=ucs4_strlen(tmp
));
1086 level
= (c
== '\"') ? CMMNT
: TG
;
1087 lbln
= 0; /* reset flag */
1089 else if(c
== '('){ /* get the size */
1094 emlwwrite(_("Attchmnt: Expected '(' or '\"' after %s"), &eml
);
1095 rv
= -1; /* bag it all */
1101 case ASIZE
: /* check size */
1102 if(c
== ')'){ /* finished grabbing size */
1105 * replace sizes if they don't match!
1107 utf8
= ucs4_to_utf8_cpystr(tmp
);
1109 strncpy(sz
, utf8
, szlen
);
1111 fs_give((void **) &utf8
);
1114 if(strcmp(sz
, (lblsz
) ? lblsz
: prettysz(attsz
))){
1115 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1117 if(p
-tmp
> *off
){ /* where to begin replacement */
1122 sinserts((*lp
)->text
, *off
, uu
, 0);
1123 u
= utf8_to_ucs4_cpystr(sz
);
1125 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
1127 fs_give((void **) &u
);
1133 j
= (*off
) - (p
-tmp
);
1134 u
= utf8_to_ucs4_cpystr(sz
);
1136 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
1138 p
-tmp
, u
, ucs4_strlen(u
));
1139 *off
+= ucs4_strlen(u
) - (p
-tmp
);
1140 fs_give((void **) &u
);
1147 level
= SWS
; /* what's next... */
1149 else if(c
== '\0' || c
== ','){
1151 utf8
= ucs4_to_utf8_cpystr(tmp
);
1153 emlwwrite(_("Attchmnt: Size field missing ')': \"%s\""), &eml
);
1155 fs_give((void **) &utf8
);
1165 case SWS
: /* skip white space */
1166 if(c
> 0xff || !isspace((unsigned char)c
)){
1167 if(c
== ','){ /* no description */
1168 level
= TG
; /* munch rest of garbage */
1169 lbln
= 0; /* reset flag */
1171 else if(c
!= '\"' && c
!= '\0'){
1172 emlwwrite(_("Attchmnt: Malformed comment, quotes required"), NULL
);
1181 case CMMNT
: /* slurp up comment */
1182 if((c
== '\"' && !escaped
) || c
== '\0'){
1183 *p
= '\0'; /* cap it off */
1184 p
= tmp
; /* reset p */
1185 utf8
= ucs4_to_utf8_cpystr(tmp
);
1187 if(strlen(utf8
) > cmntlen
)
1188 emlwrite("Comment too long!",NULL
);
1190 strncpy(cmnt
,utf8
,cmntlen
-1); /* copy the comment */
1191 cmnt
[cmntlen
-1] = '\0';
1192 fs_give((void **) &utf8
);
1194 emlwwrite(_("Attchmnt: Closing quote required at end of comment"), NULL
);
1199 level
= TG
; /* prepare for next one */
1200 lbln
= 0; /* reset flag */
1202 else if(c
== '\\' && !escaped
){ /* something escaped? */
1207 if(c
!= '\"') /* we only quote escapes */
1213 if(((*p
++ = c
) & 0x80) && (gmode
& MDHBTIGN
) && !hibit
++)
1214 emlwrite(HIBIT_WARN
, NULL
);
1219 case TG
: /* get comma or final EOL */
1222 if(c
> 0xff || !isspace((unsigned char)c
)){
1226 lprev
->text
[*off
= eod
] = '\0';
1230 memcpy(&(*lp
)->text
[eod
], &(*lp
)->text
[*off
],
1231 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
1238 emlwwrite(_("Attchmnt: Comma must separate attachments"), NULL
);
1244 default: /* something's very wrong */
1245 emlwwrite(_("Attchmnt: Weirdness in ParseAttach"), NULL
);
1246 return(-1); /* just give up */
1249 if(c
== '\0') /* we're done */
1255 * not in comment or label name? done.
1257 if(c
== ',' && (level
!= TAG
&& level
!= CMMNT
&& !lbln
))
1258 break; /* put offset past ',' */
1266 * NewAttach - given a filename (assumed to accessible) and comment, creat
1269 NewAttach(char *f
, long l
, char *c
)
1274 if((tp
=(PATMT
*)malloc(sizeof(PATMT
))) == NULL
){
1275 emlwrite("No memory to add attachment", NULL
);
1279 memset(tp
, 0, sizeof(PATMT
));
1281 /* file and size malloc */
1283 if((tp
->filename
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1284 emlwrite("Can't malloc name for attachment", NULL
);
1289 strncpy(tp
->filename
, f
, len
);
1290 tp
->filename
[len
] = '\0';
1293 len
= strlen(prettysz((off_t
) l
));
1294 tp
->size
= (char *) malloc((len
+1) * sizeof(char));
1295 if(tp
->size
== NULL
){
1296 emlwrite("Can't malloc size for attachment", NULL
);
1297 free((char *) tp
->filename
);
1302 strncpy(tp
->size
, prettysz((off_t
) l
), len
);
1303 tp
->size
[len
] = '\0';
1307 /* description malloc */
1309 if((tp
->description
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1310 emlwrite("Can't malloc description for attachment", NULL
);
1311 free((char *) tp
->size
);
1312 free((char *) tp
->filename
);
1317 strncpy(tp
->description
, c
, len
);
1318 tp
->description
[len
] = '\0';
1320 /* callback to show user the mime type that will be used for attachment */
1321 if(Pmaster
->mimetype
&& (*Pmaster
->mimetype
)(f
) > 0){
1326 rv
= (*Pmaster
->showmsg
)('x');
1329 if(rv
) /* Did showmsg corrupt the screen? */
1330 PaintBody(0); /* Yes, repaint it */
1340 * AttachError - Sniff list of attachments, returning TRUE if there's
1341 * any sign of trouble...
1351 ap
= Pmaster
->attachments
;
1353 if((ap
->flags
) & A_ERR
)
1364 QuoteAttach(char *fn
, size_t fnlen
)
1368 if(*fn
&& strpbrk(fn
, " \t,(\"")){ /* Quote it? */
1369 p
= &fn
[strlen(fn
)];
1392 free((char *)p
->description
);
1395 if(p
->flags
& A_TMP
)
1396 our_unlink(p
->filename
);
1398 free((char *)p
->filename
);
1402 free((char *)p
->size
);
1405 free((char *)p
->id
);
1409 #endif /* ATTACHMENTS */
1413 * intag - return TRUE if i is in a column that makes up an
1414 * attachment line number
1417 intag(UCS
*s
, int i
)
1422 while(*p
!= '\0' && (p
-s
) < 5){ /* is there a tag? it */
1423 if(n
&& *p
== '.') /* can't be more than 4 */
1424 return(i
<= p
-s
); /* chars long! */
1426 if(*p
< '0' || *p
> '9')
1429 n
= (n
* 10) + (*p
- '0');
1439 * prettysz - return pointer to string containing nice size description
1445 long sz
, left
, right
;
1451 snprintf(b
, sizeof(b
), "%ld B", sz
); /* xxx B */
1453 else if(sz
< 9950L){
1454 left
= (sz
+ 50L) / 1000L;
1455 right
= ((sz
+ 50L) - left
* 1000L) / 100L;
1456 snprintf(b
, sizeof(b
), "%ld.%ld KB", left
, right
); /* x.x KB */
1458 else if(sz
< 999500L){
1459 snprintf(b
, sizeof(b
), "%ld KB", (sz
+ 500L) / 1000L); /* xxx KB */
1461 else if(sz
< 9950000L){
1462 left
= (sz
+ 50000L) / 1000000L;
1463 right
= ((sz
+ 50000L) - left
* 1000000L) / 100000L;
1464 snprintf(b
, sizeof(b
), "%ld.%ld MB", left
, right
); /* x.x MB */
1467 snprintf(b
, sizeof(b
), "%ld MB", (sz
+ 500000L) / 1000000L); /* xxx MB */
1475 * sinserts - s insert into another string
1478 sinserts(UCS
*ds
, /* dest string */
1479 int dl
, /* where to begin insert */
1480 UCS
*ss
, /* source string */
1481 int sl
) /* length of ss */
1483 UCS
*dp
, *edp
; /* pointers into dest. */
1484 size_t j
; /* jump difference */
1486 if(sl
>= dl
){ /* source bigger than dest. */
1487 dp
= ds
+ dl
; /* shift dest. to make room */
1488 if((edp
= ucs4_strchr(dp
, '\0')) != NULL
){
1491 for( ;edp
>= dp
; edp
--)
1498 emlwwrite(_("No end of line???"), NULL
); /* can this happen? */
1500 else{ /* dest is longer, shrink it */
1501 j
= dl
- sl
; /* difference in lengths */
1503 while(sl
--) /* copy u onto ds */
1506 if(ucs4_strlen(ds
) > j
){ /* shuffle the rest left */
1509 while(*ds
++ != '\0');