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-2016 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
[10];
68 memset(&menu_attach
, 0, 10*sizeof(EXTRAKEYS
));
69 menu_attach
[n
= 0].name
= "^T";
70 menu_attach
[n
].label
= N_("To Files");
71 menu_attach
[n
].key
= (CTRL
|'T');
74 menu_attach
[++n
].name
= "TAB";
75 menu_attach
[n
].label
= N_("Complete");
76 menu_attach
[n
].key
= (CTRL
|'I');
79 #if !defined(DOS) && !defined(MAC)
80 if(Pmaster
&& Pmaster
->upload
){
82 * The Plan: ^R prompts for uploaded file's name which
83 * is passed to the defined upload command when the user
84 * hits Return to confirm the name.
85 * NOTE: this is different than upload into message
86 * text in which case the uploaded name isn't useful so
87 * a temp file is ok (be sure to fix the file mode).
89 menu_attach
[++n
].name
= "^Y";
90 menu_attach
[n
].key
= (CTRL
|'Y');
91 /* TRANSLATORS: Read File is a prompt for the name of
92 a file to be read into the composer. */
93 menu_attach
[n
].label
= upload
? N_("Read File") : N_("RcvUpload");
97 menu_attach
[++n
].name
= NULL
;
98 KS_OSDATASET(&menu_attach
[0], KS_NONE
);
99 status
= mlreply_utf8(upload
? _("Name to give uploaded attachment: ")
100 /* TRANSLATORS: User is being asked for the name
101 of the file they want to attach to a message. */
102 : _("File to attach: "),
103 fn
, sizeof(fn
), QNORML
, menu_attach
);
106 /* TRANSLATORS: This is a prompt for a comment about the file
107 they have attached. */
108 status
= mlreply_utf8(_("Attachment comment: "), cmnt
, cmntlen
, QNODQT
, NULL
);
113 VARS_TO_SAVE
*saved_state
;
115 saved_state
= save_pico_state();
116 (*Pmaster
->helper
)(Pmaster
->attach_help
, _("Attach Help"), 1);
118 restore_pico_state(saved_state
);
119 free_pico_state(saved_state
);
122 pico_refresh(FALSE
, 1);
127 eml
.s
= (i
== 2) ? "file" : "comment";
128 emlwrite("No Attachment %s help yet!", &eml
);
140 if(*fn
&& (p
= strrchr(fn
, C_FILESEP
))){
144 strncpy(bfn
, S_FILESEP
, sizeof(bfn
));
145 bfn
[sizeof(bfn
)-1] = '\0';
148 else if(fn
[0] == C_FILESEP
149 || (isalpha((unsigned char)fn
[0])
151 if(fn
[1] == ':' && p
== fn
+2)
154 if(dirlen
< sizeof(bfn
)){
155 strncpy(bfn
, fn
, dirlen
);
160 else if (fn
[0] == C_FILESEP
|| fn
[0] == '~'){
161 if(dirlen
< sizeof(bfn
)){
162 strncpy(bfn
, fn
, dirlen
);
168 snprintf(bfn
, sizeof(bfn
), "%s%c%.*s",
171 : ((gmode
& MDTREE
) || opertree
[0])
172 ? opertree
: gethomedir(NULL
),
173 C_FILESEP
, (int) (p
- fn
), fn
);
177 strncpy(bfn
, (gmode
& MDCURDIR
)
179 : ((gmode
& MDTREE
) || opertree
[0])
180 ? opertree
: gethomedir(NULL
), sizeof(bfn
));
181 bfn
[sizeof(bfn
)-1] = '\0';
184 if(!pico_fncomplete(bfn
, fname
, sizeof(fn
)-(fname
-fn
)))
199 if(*fn
== '\0' || !isdir(fn
, NULL
, NULL
)){
200 strncpy(fn
, (gmode
& MDCURDIR
)
201 ? (browse_dir
[0] ? browse_dir
: ".")
202 : ((gmode
& MDTREE
) || opertree
[0])
204 : (browse_dir
[0] ? browse_dir
205 : gethomedir(NULL
)), sizeof(fn
));
206 fn
[sizeof(fn
)-1] = '\0';
209 if((fbrv
= FileBrowse(fn
, sizeof(fn
), bfn
, sizeof(bfn
), sz
, sizeof(sz
),
211 : fb_flags
|FB_LMODEPOS
,
214 if (upload
&& (strlen(fn
)+strlen(S_FILESEP
)+strlen(bfn
)) < sizeof(fn
)){
219 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
220 || (new->fname
=malloc((len1
+1) * sizeof(char))) == NULL
221 || (new->dir
=malloc((len2
+1) * sizeof(char))) == NULL
){
222 emlwrite("\007Can't malloc space for filename", NULL
);
226 strncpy(new->fname
, bfn
, len1
);
227 new->fname
[len1
] = '\0';
228 strncpy(new->dir
, fn
, len2
);
229 new->dir
[len2
] = '\0';
230 strncpy(new->size
, sz
, sizeof(new->size
)-1);
231 new->size
[sizeof(new->size
)-1] = '\0';
235 strncat(fn
, S_FILESEP
, sizeof(fn
)-strlen(fn
)-1);
236 fn
[sizeof(fn
)-1] = '\0';
237 strncat(fn
, bfn
, sizeof(fn
)-strlen(fn
)-1);
238 fn
[sizeof(fn
)-1] = '\0';
239 if(!AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
))){
240 i
= 2; /* keep prompting for file */
241 sleep(3); /* problem, show error! */
244 i
--; /* go prompt for comment */
248 if(lm
&& *lm
&& !(*lm
)->next
) /* get comment */
250 else{ /* no comments if multiple files */
258 pico_refresh(FALSE
,1);
260 emlwrite("\007File name too BIG, cannot select!", NULL
);
269 pico_refresh(FALSE
, 1);
271 emlwrite("\007File name too big, cannot select!", NULL
);
275 /* fall thru to clean up the screen */
278 pico_refresh(FALSE
, 1);
282 #if !defined(DOS) && !defined(MAC)
283 case (CTRL
|'Y'): /* upload? */
285 upload
^= 1; /* flip mode */
293 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
295 case TRUE
: /* some comment */
296 case FALSE
: /* No comment */
299 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
300 status
= AttachUpload(fn
, sizeof(fn
), sz
, sizeof(sz
));
301 pico_refresh(FALSE
, 1);
304 i
= 2; /* keep prompting for file */
305 sleep(3); /* problem, show error! */
309 if(*fn
== '\"' && fn
[strlen(fn
)-1] == '\"'){
312 for(j
= 0; (fn
[j
] = fn
[j
+1]); j
++)
320 && !compresspath(opertree
, fn
, sizeof(fn
))){
321 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
323 /* TRANSLATORS: the %s is replaced with the name of a directory */
324 _("Restricted mode allows attachments from %s only: too many ..'s"),
329 fixpath(fn
, sizeof(fn
)); /* names relative to ~ */
330 if((gmode
&MDTREE
) && !in_oper_tree(fn
)){
331 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
333 _("\007Restricted mode allows attachments from %s only"), &eml
);
338 if((status
= fexist(fn
, "r", &attsz
)) != FIOSUC
){
339 fioperr(status
, fn
); /* file DOESN'T exist! */
344 if((new=(LMLIST
*)malloc(sizeof(*new))) == NULL
345 || (new->fname
=malloc((len
+1)*sizeof(char))) == NULL
){
346 emlwrite("\007Can't malloc space for filename", NULL
);
351 strncpy(new->fname
, fn
, len
);
352 new->fname
[len
] = '\0';
353 strncpy(new->size
, prettysz(attsz
), sizeof(new->size
));
354 new->size
[sizeof(new->size
)-1] = '\0';
359 return(AttachCancel((upload
&& i
== 1) ? fn
: NULL
));
364 return(1); /* mission accomplished! */
378 * AttachUpload - Use call back to run the external upload command.
381 AttachUpload(char *fn
, size_t fnlen
, char *sz
, size_t szlen
)
386 emlwrite("\007Restricted mode disallows uploaded command", NULL
);
390 if(Pmaster
&& Pmaster
->upload
&& (*Pmaster
->upload
)(fn
, fnlen
, &l
)){
391 strncpy(sz
, prettysz((off_t
)l
), szlen
);
404 AttachCancel(char *fn
)
406 emlwrite(_("Attach cancelled"), NULL
);
408 our_unlink(fn
); /* blast uploaded file */
414 extern struct headerentry
*headents
;
417 * SyncAttach - given a pointer to a linked list of attachment structures,
418 * return with that structure sync'd with what's displayed.
419 * delete any attachments in list of structs that's not on
420 * the display, and add any that aren't in list but on display.
425 int offset
= 0, /* the offset to begin */
427 ki
= 0, /* number of known attmnts */
428 bi
= 0, /* build array index */
429 nbld
= 0, /* size of build array */
430 na
, /* old number of attachmnt */
433 char file
[NLINE
], /* buffers to hold it all */
436 struct hdr_line
*lp
; /* current line in header */
437 struct headerentry
*entry
;
438 PATMT
*tp
, **knwn
= NULL
, **bld
;
444 for(entry
= headents
; entry
->name
!= NULL
; entry
++) {
449 for(tp
= Pmaster
->attachments
; tp
; tp
= tp
->next
)
450 ki
++; /* Count known attachments */
453 if((knwn
= (PATMT
**)malloc((ki
+1) * (sizeof(PATMT
*)))) == NULL
){
454 eml
.s
= comatose(ki
+ 1);
455 emlwrite("\007Can't allocate space for %s known attachment array entries",
460 for(i
=0, tp
= Pmaster
->attachments
; i
< ki
; i
++, tp
= tp
->next
){
461 knwn
[i
] = tp
; /* fill table of */
462 /* known attachments */
468 * As a quick hack to avoid too many reallocs, check to see if
469 * there are more header lines than known attachments.
471 for(lp
= entry
->hd_text
; lp
; lp
= lp
->next
)
472 nbld
++; /* count header lines */
474 nbld
= nbld
> ki
? nbld
: ki
+ 1;
476 if((bld
= (PATMT
**)malloc(nbld
* (sizeof(PATMT
*)))) == NULL
){
477 eml
.s
= comatose(nbld
);
478 emlwrite("\007Can't allocate space for %s build array entries", &eml
);
488 if(bi
== nbld
){ /* need to grow build array? */
489 if((bld
= (PATMT
**)realloc(bld
, ++nbld
* sizeof(PATMT
*))) == NULL
){
490 eml
.s
= comatose(nbld
);
491 emlwrite("\007Can't resize build array to %s entries ", &eml
);
497 if((status
= ParseAttach(&lp
, &offset
, file
, sizeof(file
), size
, sizeof(size
),
498 comment
, sizeof(comment
), &na
)) != 0)
499 rv
= (rv
< 0) ? rv
: status
; /* remember worst case */
502 if(n
!= na
&& na
> 0 && na
<= ki
&& (knwn
[na
-1]->flags
& A_FLIT
)){
503 bld
[bi
++] = knwn
[na
-1];
511 && (!in_oper_tree(file
)
512 || !compresspath(file
, fn
, sizeof(fn
))))
513 /* no attachments outside ~ in secure mode! */
517 for(i
= 0; i
< ki
; i
++){ /* already know about it? */
519 * this is kind of gruesome. what we want to do is keep track
520 * of literal attachment entries because they may not be
521 * actual files we can access or that the user can readily
525 && ((!(knwn
[i
]->flags
&A_FLIT
)
526 && !strcmp(file
, knwn
[i
]->filename
))
527 || ((knwn
[i
]->flags
&A_FLIT
) && i
+1 == na
))){
529 knwn
[i
] = NULL
; /* forget we know about it */
531 if(status
== -1) /* ignore garbage! */
534 if((tp
->flags
&A_FLIT
) && strcmp(file
, tp
->filename
)){
536 if((j
=strlen(file
)) > strlen(tp
->filename
)){
537 if((tp
->filename
= (char *)realloc(tp
->filename
,
538 sizeof(char)*(j
+1))) == NULL
){
539 emlwrite("\007Can't realloc filename space",NULL
);
545 strncpy(tp
->filename
, file
, j
);
546 tp
->filename
[j
] = '\0';
548 else if(tp
->size
&& strcmp(tp
->size
, size
)){
550 if((j
=strlen(size
)) > strlen(tp
->size
)){
551 if((tp
->size
=(char *)realloc(tp
->size
,
552 sizeof(char)*(j
+1))) == NULL
){
553 emlwrite("\007Can't realloc space for size", NULL
);
559 strncpy(tp
->size
, size
, j
);
563 if(strcmp(tp
->description
, comment
)){ /* new comment */
565 if((j
=strlen(comment
)) > strlen(tp
->description
)){
566 if((tp
->description
=(char *)realloc(tp
->description
,
567 sizeof(char)*(j
+1))) == NULL
){
568 emlwrite("\007Can't realloc description", NULL
);
574 strncpy(tp
->description
, comment
, j
);
575 tp
->description
[j
] = '\0';
586 if((tp
= NewAttach(file
, atol(size
), comment
)) == NULL
){
596 tp
->flags
|= A_ERR
; /* turn ON error bit */
598 tp
->flags
&= ~(A_ERR
); /* turn OFF error bit */
602 for(i
=0; i
< bi
-1; i
++) /* link together newly built list */
603 bld
[i
]->next
= bld
[i
+1];
605 bld
[i
]->next
= NULL
; /* tie it off */
606 Pmaster
->attachments
= bld
[0];
609 Pmaster
->attachments
= NULL
;
613 for(i
= 0; i
< ki
; i
++){ /* kill old/unused references */
616 free((char *) knwn
[i
]);
630 * ParseAttach - given a header line and an offset into it, return with
631 * the three given fields filled in. Size of fn and cmnt
632 * buffers should be passed in fnlen and cmntlen.
633 * Always updates header fields that have changed or are
634 * fixed. An error advances offset to next attachment.
636 * returns: 1 if a field changed
641 ParseAttach(struct hdr_line
**lp
, /* current header line */
642 int *off
, /* offset into that line */
643 char *fn
, /* return file name field */
644 size_t fnlen
, /* fn buffer size */
647 char *cmnt
, /* places to return fields */
649 int *no
) /* attachment number */
651 int j
, status
, bod
, eod
= -1,
652 rv
= 0, /* return value */
654 lbln
= 0, /* label'd attachment */
659 off_t attsz
; /* attachment length */
662 UCS tmp
[1024], *p
, *u
, quotechar
[2];
665 char *lblsz
= NULL
, /* label'd attchmnt's size */
667 struct hdr_line
*lprev
= NULL
;
668 enum { /* parse levels */
669 LWS
, /* leading white space */
670 NUMB
, /* attachment number */
671 WSN
, /* white space after number */
672 TAG
, /* attachments tag (fname) */
673 WST
, /* white space after tag */
674 ASIZE
, /* attachments size */
675 SWS
, /* white space after size */
676 CMMNT
, /* attachment comment */
677 TG
} level
; /* trailing garbage */
679 *fn
= *sz
= *cmnt
= '\0'; /* initialize return strings */
681 orig_offset
= bod
= *off
;
685 level
= LWS
; /* start at beginning */
688 if((c
=(*lp
)->text
[*off
]) == '\0'){ /* end of display line */
689 if(level
== LWS
&& bod
!= *off
){
690 (*lp
)->text
[bod
] = '\0';
694 if((*lp
= (*lp
)->next
) != NULL
)
695 c
= (*lp
)->text
[*off
= bod
= 0]; /* reset offset */
699 c_lookahead
= (*lp
)->text
[*off
+ 1];
700 if(c_lookahead
== '\0'){ /* end of display line */
701 if((*lp
)->next
!= NULL
)
702 c_lookahead
= (*lp
)->next
->text
[0];
707 case LWS
: /* skip leading white space */
708 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== ',')){
714 if(bod
> 0 && *off
>= bod
&& lprev
){
715 lprev
->text
[bod
- 1] = '\0';
719 else if(*off
> bod
&& *lp
){ /* wipe out whitespace */
720 memcpy(&(*lp
)->text
[bod
], &(*lp
)->text
[*off
],
721 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
722 *off
= bod
; /* reset pointer */
729 if(c
> 0xff || !isdigit((unsigned char)c
)){ /* add a number */
730 snprintf(number
, sizeof(number
), "%d. ", *no
);
731 *no
= 0; /* no previous number! */
732 u
= utf8_to_ucs4_cpystr(number
);
734 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
735 : &(*lp
)->text
[*off
],
736 0, u
, j
=ucs4_strlen(u
));
738 fs_give((void **) &u
);
743 level
= TAG
; /* interpret the name */
747 case NUMB
: /* attachment number */
748 if(c
== '\0' || c
== ','){ /* got to end, no number yet */
750 snprintf(number
, sizeof(number
), "%d. ", *no
);
751 *no
= 0; /* no previous number! */
753 *lp
= lprev
; /* go back and look at prev */
755 c
= (*lp
)->text
[*off
= orig_offset
]; /* reset offset */
756 u
= utf8_to_ucs4_cpystr(number
);
758 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
759 : &(*lp
)->text
[*off
],
760 0, u
, j
=ucs4_strlen(u
));
762 fs_give((void **) &u
);
767 level
= WSN
; /* what's next... */
770 else if(c
== '.' && c_lookahead
<= 0xff && isspace((unsigned char)c_lookahead
)){
771 /* finished grabbing number */
772 /* if not space is not number */
774 * replace number if it's not right
777 snprintf(number
, sizeof(number
), "%d", *no
); /* record the current... */
778 utf8
= ucs4_to_utf8_cpystr(tmp
);
779 *no
= atoi(utf8
); /* and the old place in list */
780 if(strcmp(number
, utf8
)){
781 if(p
-tmp
> *off
){ /* where to begin replacemnt */
786 sinserts((*lp
)->text
, *off
, uu
, 0);
787 u
= utf8_to_ucs4_cpystr(number
);
789 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
792 fs_give((void **) &u
);
798 j
= (*off
) - (p
-tmp
);
799 u
= utf8_to_ucs4_cpystr(number
);
801 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
803 p
-tmp
, u
, ucs4_strlen(u
));
805 fs_give((void **) &u
);
808 *off
+= strlen(number
) - (p
-tmp
);
814 fs_give((void **) &utf8
);
817 level
= WSN
; /* what's next... */
819 else if(c
< '0' || c
> '9'){ /* Must be part of tag */
820 snprintf(number
, sizeof(number
), "%d. ", *no
);
821 u
= utf8_to_ucs4_cpystr(number
);
823 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
- tmp
)]
824 : &(*lp
)->text
[*off
- (p
- tmp
)],
825 0, u
, j
=ucs4_strlen(u
));
827 fs_give((void **) &u
);
831 level
= TAG
; /* interpret the name */
832 goto process_tag
; /* in case already past end of tag */
839 case WSN
: /* blast whitespace */
840 if(c
<= 0xff && (isspace((unsigned char)c
) || c
== '\0')){
843 else if(c
== '['){ /* labeled attachment */
846 else if(c
== ',' || c
== ' '){
847 /* TRANSLATORS: Attchmnt is an abbreviation for Attachment and
848 the %s is replaced with the character that is not
849 allowed in the name. */
850 eml
.s
= (c
== ',') ? "," : "space";
851 emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml
);
853 level
= TG
; /* eat rest of garbage */
858 case TAG
: /* get and check filename */
859 /* or labeled attachment */
860 process_tag
: /* enclosed in [] */
862 || (lbln
&& c
== ']')
863 || (quoted
&& p
!= tmp
&& c
== '\"')
864 || (!(lbln
|| quoted
)
865 && (c
<= 0xff && ((isspace((unsigned char) c
) && c_lookahead
== (UCS
) '(') || strchr(",(\"", c
))))){
871 *p
= '\0'; /* got something */
873 utf8
= ucs4_to_utf8_cpystr(tmp
);
875 if(strlen(utf8
) > fnlen
)
876 emlwrite("File name too big!",NULL
);
878 strncpy(fn
, utf8
, fnlen
); /* store file name */
880 fs_give((void **) &utf8
);
883 if(!lbln
){ /* normal file attachment */
885 && !compresspath(opertree
, fn
, fnlen
)){
886 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
888 _("Attachments allowed only from %s: too many ..'s"),
896 if((status
=fexist(fn
, "r", &attsz
)) != FIOSUC
){
899 level
= TG
; /* munch rest of garbage */
903 if((gmode
& MDTREE
) && !in_oper_tree(fn
)){
904 eml
.s
= (gmode
&MDSCUR
) ? _("home directory") : opertree
;
905 emlwrite(_("\007Attachments allowed only from %s"), &eml
);
912 utf8
= ucs4_to_utf8_cpystr(tmp
);
914 if(utf8
&& strcmp(fn
, utf8
)){ /* fn changed: display it */
915 if(*off
>= p
- tmp
){ /* room for it? */
916 u
= utf8_to_ucs4_cpystr(fn
);
919 * This whole parsing of the attachment line
920 * thing is ad hoc and susceptible to problems,
921 * and this particular part is no exception.
922 * Quote the filename if it contains spaces.
925 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
926 : &(*lp
)->text
[*off
- (p
-tmp
)],
931 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
932 : &(*lp
)->text
[*off
- (p
-tmp
)],
933 p
-tmp
, u
, j
=ucs4_strlen(u
));
935 *off
+= j
- (p
- tmp
); /* advance offset */
938 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
939 : &(*lp
)->text
[*off
],
945 fs_give((void **) &u
);
951 emlwrite("\007Attchmnt: Problem displaying real file path", NULL
);
956 fs_give((void **) &utf8
);
958 else{ /* labelled attachment! */
960 * should explain about labelled attachments:
961 * these are attachments that came into the composer
962 * with meaningless file names (up to caller of
963 * composer to decide), for example, attachments
964 * being forwarded from another message. here, we
965 * just make sure the size stays what was passed
966 * to us. The user is SOL if they change the label
967 * since, as it is now, after changed, it will
968 * just get dropped from the list of what gets
969 * passed back to the caller.
973 if(c
!= ']'){ /* legit label? */
975 emlwrite(_("\007Attchmnt: Expected ']' after \"%s\""),
982 strncat(fn
, "]", fnlen
-strlen(fn
)-1);
986 * This is kind of cheating since otherwise
987 * ParseAttach doesn't know about the attachment
988 * struct. OK if filename's not found as it will
989 * get taken care of later...
991 tp
= Pmaster
->attachments
; /* caller check Pmaster! */
1004 emlwrite("\007Attchmnt: Unknown reference: %s", &eml
);
1010 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
- (p
-tmp
)]
1011 : &(*lp
)->text
[*off
- (p
-tmp
)],
1014 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1015 : &(*lp
)->text
[*off
],
1021 p
= tmp
; /* reset p in tmp */
1025 if(!lbln
&& c
== '(') /* no space 'tween file, size*/
1028 || (!(lbln
|| quoted
) && (c
== ',' || c
== '\"'))){
1029 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1032 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1033 u
= utf8_to_ucs4_cpystr(ctmp
);
1035 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1036 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1037 fs_give((void **) &u
);
1040 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1041 : &(*lp
)->text
[*off
],
1042 0, tmp
, j
=ucs4_strlen(tmp
));
1045 level
= (c
== '\"') ? CMMNT
: TG
;/* cmnt or eat trash */
1048 else if(!(lbln
|| quoted
)
1049 && (c
== ',' || /** c == ' ' || **/ c
== '[' || c
== ']')){
1050 eml
.s
= c
== ',' ? ","
1051 : c
== ' ' ? "space"
1052 : c
== '[' ? "[" : "]";
1053 emlwrite(_("\007Attchmnt: '%s' not allowed in file name"), &eml
);
1054 rv
= -1; /* bad char in file name */
1055 level
= TG
; /* gobble garbage */
1057 else if(!(lbln
|| quoted
) && (c
<= 0xff && isspace((unsigned char) c
))){
1059 *p
++ = c
; /* add char to name */
1062 *p
++ = c
; /* add char to name */
1066 case WST
: /* skip white space */
1067 if(c
> 0xff || !isspace((unsigned char)c
)){
1069 * whole attachment, comment or done!
1071 if(c
== ',' || c
== '\0' || c
== '\"'){
1072 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1075 snprintf(ctmp
, sizeof(ctmp
), " (%s) %s", sz
, (c
== '\"') ? "" : "\"\"");
1076 u
= utf8_to_ucs4_cpystr(ctmp
);
1078 ucs4_strncpy(tmp
, u
, sizeof(tmp
)/sizeof(tmp
[0]));
1079 tmp
[sizeof(tmp
)/sizeof(tmp
[0]) - 1] = '\0';
1080 fs_give((void **) &u
);
1083 sinserts((*lp
== NULL
) ? &lprev
->text
[*off
]
1084 : &(*lp
)->text
[*off
],
1085 0, tmp
, j
=ucs4_strlen(tmp
));
1088 level
= (c
== '\"') ? CMMNT
: TG
;
1089 lbln
= 0; /* reset flag */
1091 else if(c
== '('){ /* get the size */
1096 emlwrite(_("\007Attchmnt: Expected '(' or '\"' after %s"), &eml
);
1097 rv
= -1; /* bag it all */
1103 case ASIZE
: /* check size */
1104 if(c
== ')'){ /* finished grabbing size */
1107 * replace sizes if they don't match!
1109 utf8
= ucs4_to_utf8_cpystr(tmp
);
1111 strncpy(sz
, utf8
, szlen
);
1113 fs_give((void **) &utf8
);
1116 if(strcmp(sz
, (lblsz
) ? lblsz
: prettysz(attsz
))){
1117 strncpy(sz
, (lblsz
) ? lblsz
: prettysz(attsz
), szlen
);
1119 if(p
-tmp
> *off
){ /* where to begin replacemnt */
1124 sinserts((*lp
)->text
, *off
, uu
, 0);
1125 u
= utf8_to_ucs4_cpystr(sz
);
1127 sinserts(&lprev
->text
[ucs4_strlen(lprev
->text
)-j
], j
,
1129 fs_give((void **) &u
);
1135 j
= (*off
) - (p
-tmp
);
1136 u
= utf8_to_ucs4_cpystr(sz
);
1138 sinserts((*lp
== NULL
) ? &lprev
->text
[j
]
1140 p
-tmp
, u
, ucs4_strlen(u
));
1141 *off
+= ucs4_strlen(u
) - (p
-tmp
);
1142 fs_give((void **) &u
);
1149 level
= SWS
; /* what's next... */
1151 else if(c
== '\0' || c
== ','){
1153 utf8
= ucs4_to_utf8_cpystr(tmp
);
1155 emlwrite(_("\007Attchmnt: Size field missing ')': \"%s\""), &eml
);
1157 fs_give((void **) &utf8
);
1167 case SWS
: /* skip white space */
1168 if(c
> 0xff || !isspace((unsigned char)c
)){
1169 if(c
== ','){ /* no description */
1170 level
= TG
; /* munch rest of garbage */
1171 lbln
= 0; /* reset flag */
1173 else if(c
!= '\"' && c
!= '\0'){
1174 emlwrite(_("\007Attchmnt: Malformed comment, quotes required"), NULL
);
1183 case CMMNT
: /* slurp up comment */
1184 if((c
== '\"' && !escaped
) || c
== '\0'){
1185 *p
= '\0'; /* cap it off */
1186 p
= tmp
; /* reset p */
1187 utf8
= ucs4_to_utf8_cpystr(tmp
);
1189 if(strlen(utf8
) > cmntlen
)
1190 emlwrite("Comment too long!",NULL
);
1192 strncpy(cmnt
,utf8
,cmntlen
-1); /* copy the comment */
1193 cmnt
[cmntlen
-1] = '\0';
1194 fs_give((void **) &utf8
);
1196 emlwrite(_("\007Attchmnt: Closing quote required at end of comment"), NULL
);
1201 level
= TG
; /* prepare for next one */
1202 lbln
= 0; /* reset flag */
1204 else if(c
== '\\' && !escaped
){ /* something escaped? */
1209 if(c
!= '\"') /* we only quote escapes */
1215 if(((*p
++ = c
) & 0x80) && (gmode
& MDHBTIGN
) && !hibit
++)
1216 emlwrite(HIBIT_WARN
, NULL
);
1221 case TG
: /* get comma or final EOL */
1224 if(c
> 0xff || !isspace((unsigned char)c
)){
1228 lprev
->text
[*off
= eod
] = '\0';
1232 memcpy(&(*lp
)->text
[eod
], &(*lp
)->text
[*off
],
1233 ucs4_strlen(&(*lp
)->text
[*off
]) + 1);
1240 emlwrite(_("\007Attchmnt: Comma must separate attachments"), NULL
);
1246 default: /* something's very wrong */
1247 emlwrite("\007Attchmnt: Weirdness in ParseAttach", NULL
);
1248 return(-1); /* just give up */
1251 if(c
== '\0') /* we're done */
1257 * not in comment or label name? done.
1259 if(c
== ',' && (level
!= TAG
&& level
!= CMMNT
&& !lbln
))
1260 break; /* put offset past ',' */
1268 * NewAttach - given a filename (assumed to accessible) and comment, creat
1271 NewAttach(char *f
, long l
, char *c
)
1276 if((tp
=(PATMT
*)malloc(sizeof(PATMT
))) == NULL
){
1277 emlwrite("No memory to add attachment", NULL
);
1281 memset(tp
, 0, sizeof(PATMT
));
1283 /* file and size malloc */
1285 if((tp
->filename
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1286 emlwrite("Can't malloc name for attachment", NULL
);
1291 strncpy(tp
->filename
, f
, len
);
1292 tp
->filename
[len
] = '\0';
1295 len
= strlen(prettysz((off_t
) l
));
1296 tp
->size
= (char *) malloc((len
+1) * sizeof(char));
1297 if(tp
->size
== NULL
){
1298 emlwrite("Can't malloc size for attachment", NULL
);
1299 free((char *) tp
->filename
);
1304 strncpy(tp
->size
, prettysz((off_t
) l
), len
);
1305 tp
->size
[len
] = '\0';
1309 /* description malloc */
1311 if((tp
->description
= (char *) malloc((len
+1) * sizeof(char))) == NULL
){
1312 emlwrite("Can't malloc description for attachment", NULL
);
1313 free((char *) tp
->size
);
1314 free((char *) tp
->filename
);
1319 strncpy(tp
->description
, c
, len
);
1320 tp
->description
[len
] = '\0';
1322 /* callback to show user the mime type that will be used for attachment */
1323 if(Pmaster
->mimetype
&& (*Pmaster
->mimetype
)(f
) > 0){
1328 rv
= (*Pmaster
->showmsg
)('x');
1331 if(rv
) /* Did showmsg corrupt the screen? */
1332 PaintBody(0); /* Yes, repaint it */
1342 * AttachError - Sniff list of attachments, returning TRUE if there's
1343 * any sign of trouble...
1353 ap
= Pmaster
->attachments
;
1355 if((ap
->flags
) & A_ERR
)
1366 QuoteAttach(char *fn
, size_t fnlen
)
1370 if(*fn
&& strpbrk(fn
, " \t,(\"")){ /* Quote it? */
1371 p
= &fn
[strlen(fn
)];
1394 free((char *)p
->description
);
1397 if(p
->flags
& A_TMP
)
1398 our_unlink(p
->filename
);
1400 free((char *)p
->filename
);
1404 free((char *)p
->size
);
1407 free((char *)p
->id
);
1411 #endif /* ATTACHMENTS */
1415 * intag - return TRUE if i is in a column that makes up an
1416 * attachment line number
1419 intag(UCS
*s
, int i
)
1424 while(*p
!= '\0' && (p
-s
) < 5){ /* is there a tag? it */
1425 if(n
&& *p
== '.') /* can't be more than 4 */
1426 return(i
<= p
-s
); /* chars long! */
1428 if(*p
< '0' || *p
> '9')
1431 n
= (n
* 10) + (*p
- '0');
1441 * prettysz - return pointer to string containing nice size description
1447 long sz
, left
, right
;
1453 snprintf(b
, sizeof(b
), "%ld B", sz
); /* xxx B */
1455 else if(sz
< 9950L){
1456 left
= (sz
+ 50L) / 1000L;
1457 right
= ((sz
+ 50L) - left
* 1000L) / 100L;
1458 snprintf(b
, sizeof(b
), "%ld.%ld KB", left
, right
); /* x.x KB */
1460 else if(sz
< 999500L){
1461 snprintf(b
, sizeof(b
), "%ld KB", (sz
+ 500L) / 1000L); /* xxx KB */
1463 else if(sz
< 9950000L){
1464 left
= (sz
+ 50000L) / 1000000L;
1465 right
= ((sz
+ 50000L) - left
* 1000000L) / 100000L;
1466 snprintf(b
, sizeof(b
), "%ld.%ld MB", left
, right
); /* x.x MB */
1469 snprintf(b
, sizeof(b
), "%ld MB", (sz
+ 500000L) / 1000000L); /* xxx MB */
1477 * sinserts - s insert into another string
1480 sinserts(UCS
*ds
, /* dest string */
1481 int dl
, /* where to begin insert */
1482 UCS
*ss
, /* source string */
1483 int sl
) /* length of ss */
1485 UCS
*dp
, *edp
; /* pointers into dest. */
1486 size_t j
; /* jump difference */
1488 if(sl
>= dl
){ /* source bigger than dest. */
1489 dp
= ds
+ dl
; /* shift dest. to make room */
1490 if((edp
= ucs4_strchr(dp
, '\0')) != NULL
){
1493 for( ;edp
>= dp
; edp
--)
1500 emlwrite("\007No end of line???", NULL
); /* can this happen? */
1502 else{ /* dest is longer, shrink it */
1503 j
= dl
- sl
; /* difference in lengths */
1505 while(sl
--) /* copy u onto ds */
1508 if(ucs4_strlen(ds
) > j
){ /* shuffle the rest left */
1511 while(*ds
++ != '\0');