2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
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 * ========================================================================
15 #include "../pith/headers.h"
16 #include "../pith/send.h"
17 #include "../pith/state.h"
18 #include "../pith/conf.h"
19 #include "../pith/store.h"
20 #include "../pith/mimedesc.h"
21 #include "../pith/context.h"
22 #include "../pith/status.h"
23 #include "../pith/folder.h"
24 #include "../pith/bldaddr.h"
25 #include "../pith/pipe.h"
26 #include "../pith/mailview.h"
27 #include "../pith/mailindx.h"
28 #include "../pith/list.h"
29 #include "../pith/filter.h"
30 #include "../pith/reply.h"
31 #include "../pith/addrstring.h"
32 #include "../pith/rfc2231.h"
33 #include "../pith/stream.h"
34 #include "../pith/util.h"
35 #include "../pith/adrbklib.h"
36 #include "../pith/options.h"
37 #include "../pith/busy.h"
38 #include "../pith/text.h"
39 #include "../pith/imap.h"
40 #include "../pith/ablookup.h"
41 #include "../pith/sort.h"
42 #include "../pith/smime.h"
44 #include "../c-client/smtp.h"
45 #include "../c-client/nntp.h"
48 /* this is used in pine_send and pine_simple_send */
49 /* name::type::canedit::writehdr::localcopy::rcptto */
50 PINEFIELD pf_template
[] = {
51 {"X-Auth-Received", FreeText
, 0, 1, 1, 0}, /* N_AUTHRCVD */
52 {"From", Address
, 0, 1, 1, 0},
53 {"Reply-To", Address
, 0, 1, 1, 0},
54 {TONAME
, Address
, 1, 1, 1, 1},
55 {CCNAME
, Address
, 1, 1, 1, 1},
56 {"bcc", Address
, 1, 0, 1, 1},
57 {"Newsgroups", FreeText
, 1, 1, 1, 0},
58 {"Fcc", Fcc
, 1, 0, 0, 0},
59 {"Lcc", Address
, 1, 0, 1, 1},
60 {"Attchmnt", Attachment
, 1, 1, 1, 0},
61 {SUBJNAME
, Subject
, 1, 1, 1, 0},
62 {"References", FreeText
, 0, 1, 1, 0},
63 {"Date", FreeText
, 0, 1, 1, 0},
64 {"In-Reply-To", FreeText
, 0, 1, 1, 0},
65 {"Message-ID", FreeText
, 0, 1, 1, 0},
66 {PRIORITYNAME
, FreeText
, 0, 1, 1, 0},
67 {"User-Agent", FreeText
, 0, 1, 1, 0},
68 {"To", Address
, 0, 0, 0, 0}, /* N_NOBODY */
69 {"X-Post-Error",FreeText
, 0, 0, 0, 0}, /* N_POSTERR */
70 {"X-Reply-UID", FreeText
, 0, 0, 0, 0}, /* N_RPLUID */
71 {"X-Reply-Mbox",FreeText
, 0, 0, 0, 0}, /* N_RPLMBOX */
72 {"X-SMTP-Server",FreeText
, 0, 0, 0, 0}, /* N_SMTP */
73 {"X-NNTP-Server",FreeText
, 0, 0, 0, 0}, /* N_NNTP */
74 {"X-Cursor-Pos",FreeText
, 0, 0, 0, 0}, /* N_CURPOS */
75 {"X-Our-ReplyTo",FreeText
, 0, 0, 0, 0}, /* N_OURREPLYTO */
76 {OUR_HDRS_LIST
, FreeText
, 0, 0, 0, 0}, /* N_OURHDRS */
77 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
78 {"X-X-Sender", Address
, 0, 1, 1, 0},
84 PRIORITY_S priorities
[] = {
94 #define ctrl(c) ((c) & 0x1f)
96 /* which message part to test for xliteration */
97 typedef enum {MsgBody
, HdrText
} MsgPart
;
101 * Internal prototypes
103 long post_rfc822_output(char *, ENVELOPE
*, BODY
*, soutr_t
, TCPSTREAM
*, long);
104 int l_flush_net(int);
106 int pine_write_header_line(char *, char *, STORE_S
*);
107 int pine_write_params(PARAMETER
*, STORE_S
*);
108 char *tidy_smtp_mess(char *, char *, char *, size_t);
109 int lmc_body_header_line(char *, int);
110 int lmc_body_header_finish(void);
111 int pwbh_finish(int, STORE_S
*);
112 int sent_percent(void);
113 unsigned short *setup_avoid_table(void);
115 int mta_handoff(METAENV
*, BODY
*, char *, size_t, char **, void (*)(char *, int),
116 void (*)(PIPE_S
*, int, void *));
117 char *post_handoff(METAENV
*, BODY
*, char *, size_t, void (*)(char *, int),
118 void (*)(PIPE_S
*, int, void *));
119 char *mta_parse_post(METAENV
*, BODY
*, char *, char *, size_t, void (*)(char *, int),
120 void (*)(PIPE_S
*, int, void *));
121 long pine_pipe_soutr_nl(void *, char *);
123 char *smtp_command(char *, size_t);
124 void *piped_smtp_open(char *, char *, unsigned long);
125 void *piped_aopen(NETMBX
*, char *, char *);
126 long piped_soutr(void *, char *);
127 long piped_sout(void *, char *, unsigned long);
128 char *piped_getline(void *);
129 void piped_close(void *);
130 void piped_abort(void *);
131 char *piped_host(void *);
132 unsigned long piped_port(void *);
133 char *posting_characterset(void *, char *, MsgPart
);
134 int body_is_translatable(void *, char *);
135 int text_is_translatable(void *, char *);
137 unsigned long *init_charsetchecker(char *);
138 int representable_in_charset(unsigned long, char *);
139 char *most_preferred_charset(unsigned long);
142 * Storage object where the FCC (or postponed msg) is to be written.
143 * This is amazingly bogus. Much work was done to put messages
144 * together and encode them as they went to the tmp file for sendmail
145 * or into the SMTP slot (especially for for DOS, to prevent a temporary
146 * file (and needlessly copying the message).
148 * HOWEVER, since there's no piping into c-client routines
149 * (particularly mail_append() which copies the fcc), the fcc will have
150 * to be copied to disk. This global tells pine's copy of the rfc822
151 * output functions where to also write the message bytes for the fcc.
152 * With piping in the c-client we could just have two pipes to shove
153 * down rather than messing with damn copies. FIX THIS!
155 * The function open_fcc, locates the actual folder and creates it if
156 * requested before any mailing or posting is done.
158 struct local_message_copy lmc
;
162 * Locally global pointer to stream used for sending/posting.
163 * It's also used to indicate when/if we write the Bcc: field in
166 static SENDSTREAM
*sending_stream
= NULL
;
169 static struct hooks
{
170 void *rfc822_out
; /* Message outputter */
174 static FILE *verbose_send_output
= NULL
;
175 static long send_bytes_sent
, send_bytes_to_send
;
176 static METAENV
*send_header
= NULL
;
179 * Hooks for prompts and confirmations
181 int (*pith_opt_daemon_confirm
)(void);
184 static NETDRIVER piped_io
= {
185 piped_smtp_open
, /* open a connection */
186 piped_aopen
, /* open an authenticated connection */
187 piped_getline
, /* get a line */
188 NULL
, /* get a buffer */
189 piped_soutr
, /* output pushed data */
190 piped_sout
, /* output string */
191 piped_close
, /* close connection */
192 piped_host
, /* return host name */
193 piped_host
, /* remotehost */
194 piped_port
, /* return port number */
195 piped_host
/* return local host (NOTE: same as host!) */
200 * Since c-client preallocates, it's necessary here to define a limit
201 * such that we don't blow up in c-client (see rfc822_address_line()).
203 #define MAX_SINGLE_ADDR MAILTMPLEN
205 #define AVOID_2022_JP_FOR_PUNC "AVOID_2022_JP_FOR_PUNC"
209 * postponed_stream - return stream associated with postponed messages
213 postponed_stream(MAILSTREAM
**streamp
, char *mbox
, char *type
, int checknmsgs
)
215 MAILSTREAM
*stream
= NULL
;
216 CONTEXT_S
*p_cntxt
= NULL
;
217 char *p
, *q
, tmp
[MAILTMPLEN
], *fullname
= NULL
;
220 if(!(streamp
&& mbox
))
226 * find default context to look for folder...
228 * The "mbox" is assumed to be local if we're given what looks
229 * like an absolute path. This is different from Goto/Save
230 * where we do a lot of work to interpret paths relative to the
231 * server. This reason is to support all the pre-4.00 pinerc'
232 * that specified a path and because there's yet to be a way
233 * in c-client to specify otherwise in the face of a remote
236 if(!is_absolute_path(mbox
)
237 && !(p_cntxt
= default_save_context(ps_global
->context_list
)))
238 p_cntxt
= ps_global
->context_list
;
240 /* check to see if the folder exists, the user wants to continue
241 * and that we can actually read something in...
243 exists
= folder_name_exists(p_cntxt
, mbox
, &fullname
);
247 if(exists
& FEX_ISFILE
){
248 context_apply(tmp
, p_cntxt
, mbox
, sizeof(tmp
));
249 if(!(IS_REMOTE(tmp
) || is_absolute_path(tmp
))){
251 * The mbox is relative to the home directory.
252 * Make it absolute so we can compare it to
255 build_path(tmp_20k_buf
, ps_global
->ui
.homedir
, tmp
,
257 strncpy(tmp
, tmp_20k_buf
, sizeof(tmp
));
258 tmp
[sizeof(tmp
)-1] = '\0';
261 if((stream
= ps_global
->mail_stream
)
263 && ((*tmp
!= '{' && !strcmp(tmp
, stream
->mailbox
))
265 && same_stream(tmp
, stream
)
266 && (p
= strchr(tmp
, '}'))
267 && (q
= strchr(stream
->mailbox
,'}'))
268 && !strcmp(p
+ 1, q
+ 1)))))
272 stream
= context_open(p_cntxt
, NULL
, mbox
,
273 SP_USEPOOL
|SP_TEMPUSE
, NULL
);
274 if(stream
&& !stream
->halfopen
){
275 if(stream
->nmsgs
> 0)
276 refresh_sort(stream
, sp_msgmap(stream
), SRT_NON
);
278 if(checknmsgs
&& stream
->nmsgs
< 1){
279 pine_mail_close(stream
);
285 q_status_message2(SM_ORDER
| SM_DING
, 3, 3,
286 _("Can't open %s mailbox: %s"), type
, mbox
);
288 pine_mail_close(stream
);
296 if(F_ON(F_ALT_COMPOSE_MENU
, ps_global
)){
297 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
298 _("%s message folder doesn't exist!"), type
);
303 fs_give((void **) &fullname
);
312 redraft_work(MAILSTREAM
**streamp
, long int cont_msg
, ENVELOPE
**outgoing
,
313 struct mail_bodystruct
**body
, char **fcc
, char **lcc
,
314 REPLY_S
**reply
, REDRAFT_POS_S
**redraft_pos
, PINEFIELD
**custom
,
315 ACTION_S
**role
, int flags
, STORE_S
*so
)
323 char *extras
, **fields
, **values
, *p
;
324 char *hdrs
[2], *h
, *charset
;
325 char **smtp_servers
= NULL
, **nntp_servers
= NULL
;
326 int i
, pine_generated
= 0, our_replyto
= 0;
327 int added_to_role
= 0;
328 unsigned gbpt_flags
= GBPT_NONE
;
331 if(!(streamp
&& *streamp
))
332 return(redraft_cleanup(streamp
, TRUE
, flags
));
336 if(flags
& REDRAFT_HTML
)
337 gbpt_flags
|= GBPT_HTML_OK
;
339 /* grok any user-defined or non-c-client headers */
340 if((e
= pine_mail_fetchstructure(stream
, cont_msg
, &b
)) != NULL
){
343 * The custom headers to look for in the suspended message should
344 * have been stored in the X-Our-Headers header. So first we get
345 * that list. If we can't find it (version that stored the
346 * message < 4.30) then we use the global custom list.
348 hdrs
[0] = OUR_HDRS_LIST
;
350 if((h
= pine_fetchheader_lines(stream
, cont_msg
, NULL
, hdrs
)) != NULL
){
355 if((hdrval
= strindex(h
, ':')) != NULL
){
356 for(hdrval
++; *hdrval
&& isspace((unsigned char)*hdrval
);
361 /* count elements in list */
362 for(p
= hdrval
; p
&& *p
; p
++)
366 if(hdrval
&& (list
= parse_list(hdrval
,commas
+1,0,NULL
)) != NULL
){
368 *custom
= parse_custom_hdrs(list
, Replace
);
369 add_defaults_from_list(*custom
,
370 ps_global
->VAR_CUSTOM_HDRS
);
371 free_list_array(&list
);
374 if(*custom
&& !(*custom
)->name
){
375 free_customs(*custom
);
379 fs_give((void **)&h
);
383 *custom
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
386 #define INDEX_POSTERR 1
387 #define INDEX_REPLYUID 2
388 #define INDEX_REPLYMBOX 3
391 #define INDEX_CURSORPOS 6
392 #define INDEX_OUR_REPLYTO 7
393 #define INDEX_LCC 8 /* MUST REMAIN LAST FIELD DECLARED */
394 #define FIELD_COUNT 9
396 i
= count_custom_hdrs_pf(*custom
,1) + FIELD_COUNT
+ 1;
399 * Having these two fields separated isn't the slickest, but
400 * converting the pointer array for fetchheader_lines() to
401 * a list of structures or some such for simple_header_parse()
402 * is too goonie. We could do something like re-use c-client's
403 * PARAMETER struct which is a simple char * pairing, but that
404 * doesn't make sense to pass to fetchheader_lines()...
406 fields
= (char **) fs_get((size_t) i
* sizeof(char *));
407 values
= (char **) fs_get((size_t) i
* sizeof(char *));
408 memset(fields
, 0, (size_t) i
* sizeof(char *));
409 memset(values
, 0, (size_t) i
* sizeof(char *));
411 fields
[i
= INDEX_FCC
] = "Fcc"; /* Fcc: special case */
412 fields
[++i
] = "X-Post-Error"; /* posting errors too */
413 fields
[++i
] = "X-Reply-UID"; /* Reply'd to msg's UID */
414 fields
[++i
] = "X-Reply-Mbox"; /* Reply'd to msg's Mailbox */
415 fields
[++i
] = "X-SMTP-Server";/* SMTP server to use */
416 fields
[++i
] = "X-NNTP-Server";/* NNTP server to use */
417 fields
[++i
] = "X-Cursor-Pos"; /* Cursor position */
418 fields
[++i
] = "X-Our-ReplyTo"; /* ReplyTo is real */
419 fields
[++i
] = "Lcc"; /* Lcc: too... */
420 if(++i
!= FIELD_COUNT
)
421 alpine_panic("Fix FIELD_COUNT");
423 for(pf
= *custom
; pf
&& pf
->name
; pf
= pf
->next
)
425 fields
[i
++] = pf
->name
; /* assign custom fields */
427 if((extras
= pine_fetchheader_lines(stream
, cont_msg
, NULL
,fields
)) != NULL
){
428 simple_header_parse(extras
, fields
, values
);
429 fs_give((void **) &extras
);
432 * translate RFC 1522 strings,
433 * starting with "Lcc" field
435 for(i
= INDEX_LCC
; fields
[i
]; i
++)
438 char *bufp
, *biggerbuf
= NULL
;
440 if((len
=4*strlen(values
[i
])) > SIZEOF_20KBUF
-1){
442 biggerbuf
= (char *)fs_get(len
* sizeof(char));
450 p
= (char *)rfc1522_decode_to_utf8((unsigned char*)bufp
, len
, values
[i
]);
452 if(p
== tmp_20k_buf
){
453 fs_give((void **)&values
[i
]);
454 values
[i
] = cpystr(p
);
458 fs_give((void **)&biggerbuf
);
461 for(pf
= *custom
, i
= FIELD_COUNT
;
466 * Because the value is already in the envelope.
468 pf
->cstmtype
= NoMatch
;
472 if(values
[i
]){ /* use this instead of default */
474 fs_give((void **)&pf
->textbuf
);
476 pf
->textbuf
= values
[i
]; /* freed in pine_send! */
478 else if(pf
->textbuf
) /* was erased before postpone */
479 fs_give((void **)&pf
->textbuf
);
484 if(values
[INDEX_FCC
]) /* If "Fcc:" was there... */
485 pine_generated
= 1; /* we put it there? */
488 * Since c-client fills in the reply_to field in the envelope
489 * even if there isn't a Reply-To header in the message we
490 * have to work around that. When we postpone we add
491 * a second header that has value "Empty" if there really
492 * was a Reply-To and it was empty. It has the
493 * value "Full" if we put the Reply-To contents there
494 * intentionally (and it isn't empty).
496 if(values
[INDEX_OUR_REPLYTO
]){
497 if(values
[INDEX_OUR_REPLYTO
][0] == 'E')
498 our_replyto
= 'E'; /* we put an empty one there */
499 else if(values
[INDEX_OUR_REPLYTO
][0] == 'F')
500 our_replyto
= 'F'; /* we put it there */
502 fs_give((void **) &values
[INDEX_OUR_REPLYTO
]);
505 if(fcc
) /* fcc: special case... */
506 *fcc
= values
[INDEX_FCC
] ? values
[INDEX_FCC
] : cpystr("");
507 else if(values
[INDEX_FCC
])
508 fs_give((void **) &values
[INDEX_FCC
]);
510 if(values
[INDEX_POSTERR
]){ /* x-post-error?!?1 */
511 q_status_message(SM_ORDER
|SM_DING
, 4, 4,
512 values
[INDEX_POSTERR
]);
513 fs_give((void **) &values
[INDEX_POSTERR
]);
516 if(values
[INDEX_REPLYUID
]){
518 *reply
= build_reply_uid(values
[INDEX_REPLYUID
]);
520 fs_give((void **) &values
[INDEX_REPLYUID
]);
522 if(values
[INDEX_REPLYMBOX
] && reply
&& *reply
)
523 (*reply
)->origmbox
= cpystr(values
[INDEX_REPLYMBOX
]);
525 if(reply
&& *reply
&& !(*reply
)->origmbox
&& (*reply
)->mailbox
)
526 (*reply
)->origmbox
= cpystr((*reply
)->mailbox
);
529 if(values
[INDEX_REPLYMBOX
])
530 fs_give((void **) &values
[INDEX_REPLYMBOX
]);
532 if(values
[INDEX_SMTP
]){
537 * Turn the space delimited list of smtp servers into
540 p
= values
[INDEX_SMTP
];
542 if(!*p
|| isspace((unsigned char) *p
))
546 smtp_servers
= (char **) fs_get((cnt
+1) * sizeof(char *));
547 memset(smtp_servers
, 0, (cnt
+1) * sizeof(char *));
550 q
= p
= values
[INDEX_SMTP
];
552 if(!*p
|| isspace((unsigned char) *p
)){
555 smtp_servers
[cnt
++] = cpystr(q
);
560 smtp_servers
[cnt
++] = cpystr(q
);
564 fs_give((void **) &values
[INDEX_SMTP
]);
567 if(values
[INDEX_NNTP
]){
572 * Turn the space delimited list of smtp nntp into
575 p
= values
[INDEX_NNTP
];
577 if(!*p
|| isspace((unsigned char) *p
))
581 nntp_servers
= (char **) fs_get((cnt
+1) * sizeof(char *));
582 memset(nntp_servers
, 0, (cnt
+1) * sizeof(char *));
585 q
= p
= values
[INDEX_NNTP
];
587 if(!*p
|| isspace((unsigned char) *p
)){
590 nntp_servers
[cnt
++] = cpystr(q
);
595 nntp_servers
[cnt
++] = cpystr(q
);
599 fs_give((void **) &values
[INDEX_NNTP
]);
602 if(values
[INDEX_CURSORPOS
]){
604 * The redraft cursor position is written as two fields
605 * separated by a space. First comes the name of the
606 * header field we're in, or just a ":" if we're in the
607 * body. Then comes the offset into that header or into
614 = (REDRAFT_POS_S
*)fs_get(sizeof(REDRAFT_POS_S
));
615 (*redraft_pos
)->offset
= 0L;
617 q1
= skip_white_space(values
[INDEX_CURSORPOS
]);
618 if(*q1
&& (q2
= strindex(q1
, SPACE
))){
620 (*redraft_pos
)->hdrname
= cpystr(q1
);
621 q1
= skip_white_space(q2
+1);
623 (*redraft_pos
)->offset
= atol(q1
);
626 (*redraft_pos
)->hdrname
= cpystr(":");
629 fs_give((void **) &values
[INDEX_CURSORPOS
]);
633 *lcc
= values
[INDEX_LCC
];
635 fs_give((void **) &values
[INDEX_LCC
]);
638 fs_give((void **)&fields
);
639 fs_give((void **)&values
);
641 *outgoing
= copy_envelope(e
);
644 * If the postponed message has a From which is different from
645 * the default, it is either because allow-changing-from is on
646 * or because there was a role with a from that allowed it to happen.
647 * If allow-changing-from is not on, put this back in a role
648 * so that it will be allowed again in pine_send.
650 if(role
&& *role
== NULL
&&
651 !ps_global
->never_allow_changing_from
&&
654 * Now check to see if the from is different from default from.
658 deffrom
= generate_from();
659 if(!((*outgoing
)->from
&&
660 address_is_same(deffrom
, (*outgoing
)->from
) &&
661 ((!(deffrom
->personal
&& deffrom
->personal
[0]) &&
662 !((*outgoing
)->from
->personal
&&
663 (*outgoing
)->from
->personal
[0])) ||
664 (deffrom
->personal
&& (*outgoing
)->from
->personal
&&
665 !strcmp(deffrom
->personal
, (*outgoing
)->from
->personal
))))){
667 *role
= (ACTION_S
*)fs_get(sizeof(**role
));
668 memset((void *)*role
, 0, sizeof(**role
));
669 if(!(*outgoing
)->from
)
670 (*outgoing
)->from
= mail_newaddr();
672 (*role
)->from
= (*outgoing
)->from
;
673 (*outgoing
)->from
= NULL
;
677 mail_free_address(&deffrom
);
681 * Look at each empty address and see if the user has specified
682 * a default for that field or not. If they have, that means
683 * they have erased it before postponing, so they won't want
684 * the default to come back. If they haven't specified a default,
685 * then the default should be generated in pine_send. We prevent
686 * the default from being assigned by assigning an empty address
687 * to the variable here.
689 * BUG: We should do this for custom Address headers, too, but
690 * there isn't such a thing yet.
692 if(!(*outgoing
)->to
&& hdr_is_in_list("to", *custom
))
693 (*outgoing
)->to
= mail_newaddr();
694 if(!(*outgoing
)->cc
&& hdr_is_in_list("cc", *custom
))
695 (*outgoing
)->cc
= mail_newaddr();
696 if(!(*outgoing
)->bcc
&& hdr_is_in_list("bcc", *custom
))
697 (*outgoing
)->bcc
= mail_newaddr();
699 if(our_replyto
== 'E'){
700 /* user erased reply-to before postponing */
701 if((*outgoing
)->reply_to
)
702 mail_free_address(&(*outgoing
)->reply_to
);
705 * If empty is not the normal default, make the outgoing
706 * reply_to be an empty address. If it is default, leave it
707 * as NULL and the default will be used.
709 if(hdr_is_in_list("reply-to", *custom
)){
712 pf
.name
= "reply-to";
713 set_default_hdrval(&pf
, *custom
);
715 if(pf
.textbuf
[0]) /* empty is not default */
716 (*outgoing
)->reply_to
= mail_newaddr();
718 fs_give((void **)&pf
.textbuf
);
722 else if(our_replyto
== 'F'){
726 * The reply-to is real. If it is different from the default
727 * reply-to, put it in the role so that it will show up when
730 if(hdr_is_in_list("reply-to", *custom
)){
734 pf
.name
= "reply-to";
735 set_default_hdrval(&pf
, *custom
);
736 if(pf
.textbuf
&& pf
.textbuf
[0]){
737 if((str
= addr_list_string((*outgoing
)->reply_to
,NULL
,1)) != NULL
){
738 if(!strcmp(str
, pf
.textbuf
)){
739 /* standard value, leave it alone */
742 else /* not standard, put in role */
745 fs_give((void **)&str
);
748 else /* not standard, put in role */
752 fs_give((void **)&pf
.textbuf
);
754 else /* not standard, put in role */
757 if(add_to_role
&& role
&& (*role
== NULL
|| added_to_role
)){
760 *role
= (ACTION_S
*)fs_get(sizeof(**role
));
761 memset((void *)*role
, 0, sizeof(**role
));
764 (*role
)->replyto
= (*outgoing
)->reply_to
;
765 (*outgoing
)->reply_to
= NULL
;
769 /* this is a bogus c-client generated replyto */
770 if((*outgoing
)->reply_to
)
771 mail_free_address(&(*outgoing
)->reply_to
);
774 if((smtp_servers
|| nntp_servers
)
775 && role
&& (*role
== NULL
|| added_to_role
)){
777 *role
= (ACTION_S
*)fs_get(sizeof(**role
));
778 memset((void *)*role
, 0, sizeof(**role
));
782 (*role
)->smtp
= smtp_servers
;
784 (*role
)->nntp
= nntp_servers
;
787 if(!(*outgoing
)->subject
&& hdr_is_in_list("subject", *custom
))
788 (*outgoing
)->subject
= cpystr("");
792 * Now, this is interesting. We should have found
793 * the "fcc:" field if pine wrote the message being
794 * redrafted. Hence, we probably can't trust the
795 * "originator" type fields, so we'll blast them and let
796 * them get set later in pine_send. This should allow
797 * folks with custom or edited From's and such to still
798 * use redraft reasonably, without inadvertently sending
799 * messages that appear to be "From" others...
801 if((*outgoing
)->from
)
802 mail_free_address(&(*outgoing
)->from
);
805 * Ditto for Reply-To and Sender...
807 if((*outgoing
)->reply_to
)
808 mail_free_address(&(*outgoing
)->reply_to
);
810 if((*outgoing
)->sender
)
811 mail_free_address(&(*outgoing
)->sender
);
814 if(!pine_generated
|| !(flags
& REDRAFT_DEL
)){
817 * Generate a fresh message id for pretty much the same
818 * reason From and such got wacked...
819 * Also, if we're coming from a form letter, we need to
820 * generate a different id each time.
822 if((*outgoing
)->message_id
)
823 fs_give((void **)&(*outgoing
)->message_id
);
825 (*outgoing
)->message_id
= generate_message_id(role
? *role
: NULL
);
828 if(b
&& b
->type
!= TYPETEXT
){
829 if(b
->type
== TYPEMULTIPART
){
830 if(strucmp(b
->subtype
, "mixed")){
831 q_status_message1(SM_INFO
, 3, 4,
832 "Converting Multipart/%s to Multipart/Mixed",
834 fs_give((void **)&b
->subtype
);
835 b
->subtype
= cpystr("mixed");
839 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
840 "Unable to resume type %s/%s message",
841 body_types
[b
->type
], b
->subtype
);
842 return(redraft_cleanup(streamp
, TRUE
, flags
));
846 gf_set_so_writec(&pc
, so
);
848 if(b
&& b
->type
!= TYPETEXT
){ /* already TYPEMULTIPART */
849 *body
= copy_body(NULL
, b
);
850 part
= (*body
)->nested
.part
;
851 part
->body
.contents
.text
.data
= (void *)so
;
852 set_mime_type_by_grope(&part
->body
);
853 if(part
->body
.type
!= TYPETEXT
){
854 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
855 "Unable to resume; first part is non-text: %s/%s",
856 body_types
[part
->body
.type
],
858 return(redraft_cleanup(streamp
, TRUE
, flags
));
861 if((charset
= parameter_val(part
->body
.parameter
,"charset")) != NULL
){
862 /* let outgoing routines decide on charset */
863 if(!strucmp(charset
, "US-ASCII") || !strucmp(charset
, "UTF-8"))
864 set_parameter(&part
->body
.parameter
, "charset", NULL
);
866 fs_give((void **) &charset
);
869 ps_global
->postpone_no_flow
= 1;
871 get_body_part_text(stream
, &b
->nested
.part
->body
,
872 cont_msg
, "1", 0L, pc
, NULL
, NULL
, gbpt_flags
);
873 ps_global
->postpone_no_flow
= 0;
875 if(!fetch_contents(stream
, cont_msg
, NULL
, *body
))
876 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
877 _("Error including all message parts"));
880 *body
= mail_newbody();
881 (*body
)->type
= TYPETEXT
;
882 if(b
->subtype
/* these types are transformed to text/plain */
883 && strucmp(b
->subtype
, "richtext")
884 && strucmp(b
->subtype
, "enriched")
885 && strucmp(b
->subtype
, "html"))
886 (*body
)->subtype
= cpystr(b
->subtype
);
888 if((charset
= parameter_val(b
->parameter
,"charset")) != NULL
){
889 /* let outgoing routines decide on charset */
890 if(!strucmp(charset
, "US-ASCII") || !strucmp(charset
, "UTF-8"))
891 fs_give((void **) &charset
);
893 (*body
)->parameter
= mail_newbody_parameter();
894 (*body
)->parameter
->attribute
= cpystr("charset");
895 if(utf8_charset(charset
)){
896 fs_give((void **) &charset
);
897 (*body
)->parameter
->value
= cpystr("UTF-8");
900 (*body
)->parameter
->value
= charset
;
904 (*body
)->contents
.text
.data
= (void *)so
;
905 ps_global
->postpone_no_flow
= 1;
906 get_body_part_text(stream
, b
, cont_msg
, "1", 0L, pc
,
907 NULL
, NULL
, gbpt_flags
);
908 ps_global
->postpone_no_flow
= 0;
911 gf_clear_so_writec(so
);
913 /* We have what we want, blast this message... */
914 if((flags
& REDRAFT_DEL
)
915 && cont_msg
> 0L && stream
&& cont_msg
<= stream
->nmsgs
916 && (mc
= mail_elt(stream
, cont_msg
)) && !mc
->deleted
)
917 mail_flag(stream
, long2string(cont_msg
), "\\DELETED", ST_SET
);
920 return(redraft_cleanup(streamp
, TRUE
, flags
));
922 return(redraft_cleanup(streamp
, FALSE
, flags
));
926 /*----------------------------------------------------------------------
927 Clear deleted messages from given stream and expunge if necessary
934 redraft_cleanup(MAILSTREAM
**streamp
, int problem
, int flags
)
938 if(!(streamp
&& *streamp
))
941 if(!problem
&& streamp
&& (stream
= *streamp
)){
943 ps_global
->expunge_in_progress
= 1;
944 mail_expunge(stream
); /* clean out deleted */
945 ps_global
->expunge_in_progress
= 0;
948 if(!stream
->nmsgs
){ /* close and delete folder */
949 int do_the_broach
= 0;
953 if(stream
->original_mailbox
&& stream
->original_mailbox
[0])
954 mbox
= cpystr(stream
->original_mailbox
);
955 else if(stream
->mailbox
&& stream
->mailbox
[0])
956 mbox
= cpystr(stream
->mailbox
);
959 /* if it is current, we have to change folders */
960 if(stream
== ps_global
->mail_stream
)
964 * This is the stream to the empty postponed-msgs folder.
965 * We are going to delete the folder in a second. It is
966 * probably preferable to unselect the mailbox and leave
967 * this stream open for re-use instead of actually closing it,
968 * so we do that if possible.
970 if(is_imap_stream(stream
) && LEVELUNSELECT(stream
)){
972 * This does the UNSELECT on the stream. A NULL
973 * return should mean that something went wrong and
974 * a mail_close already happened, so that should have
975 * cleaned things up in the callback.
977 if((stream
=mail_open(stream
, stream
->mailbox
,
978 OP_HALFOPEN
| (stream
->debug
? OP_DEBUG
: NIL
))) != NULL
){
979 /* now close it so it is put into the stream cache */
980 sp_set_flags(stream
, sp_flags(stream
) | SP_TEMPUSE
);
981 pine_mail_close(stream
);
985 pine_mail_actually_close(stream
);
990 ps_global
->mail_stream
= NULL
; /* already closed above */
993 if(mbox
&& !pine_mail_delete(NULL
, mbox
))
994 q_status_message1(SM_ORDER
|SM_DING
, 3, 3,
995 /* TRANSLATORS: Arg is a mailbox name */
996 _("Can't delete %s"), mbox
);
999 fs_give((void **) &mbox
);
1007 /*----------------------------------------------------------------------
1008 Parse the given header text for any given fields
1010 Args: text -- Text to parse for fcc and attachments refs
1011 fields -- array of field names to look for
1012 values -- array of pointer to save values to, returned NULL if
1013 fields isn't in text.
1015 This function simply looks for the given fields in the given header
1017 NOTE: newlines are expected CRLF, and we'll ignore continuations
1020 simple_header_parse(char *text
, char **fields
, char **values
)
1025 for(i
= 0; fields
[i
]; i
++)
1026 values
[i
] = NULL
; /* clear values array */
1028 /*---- Loop until the end of the header ----*/
1029 for(p
= text
; *p
; ){
1030 for(i
= 0; fields
[i
]; i
++) /* find matching field? */
1031 if(!struncmp(p
, fields
[i
], (n
=strlen(fields
[i
]))) && p
[n
] == ':'){
1032 for(p
+= n
+ 1; *p
; p
++){ /* find start of value */
1033 if(*p
== '\015' && *(p
+1) == '\012'
1034 && !isspace((unsigned char) *(p
+2)))
1037 if(!isspace((unsigned char) *p
))
1038 break; /* order here is key... */
1041 if(!values
[i
]){ /* if we haven't already */
1042 values
[i
] = fs_get(strlen(text
) + 1);
1043 values
[i
][0] = '\0'; /* alloc space for it */
1046 if(*p
&& *p
!= '\015'){ /* non-blank value. */
1047 t
= values
[i
] + (values
[i
][0] ? strlen(values
[i
]) : 0);
1048 while(*p
){ /* check for cont'n lines */
1049 if(*p
== '\015' && *(p
+1) == '\012'){
1050 if(isspace((unsigned char) *(p
+2))){
1067 /* Skip to end of line, what ever it was */
1069 if(*p
== '\015' && *(p
+1) == '\012'){
1077 /*----------------------------------------------------------------------
1078 build a fresh REPLY_S from the given string (see pine_send for format)
1080 Args: s -- "X-Reply-UID" header value
1082 Returns: filled in REPLY_S or NULL on parse error
1085 build_reply_uid(char *s
)
1087 char *p
, *prefix
= NULL
, *val
, *seq
, *mbox
;
1088 int i
, nseq
, forwarded
= 0;
1089 REPLY_S
*reply
= NULL
;
1091 /* FORMAT: (n prefix)(n validity uidlist)mailbox */
1092 /* if 'n prefix' is empty, uid list represents forwarded msgs */
1094 if(*(p
= s
+ 1) == ')'){
1098 for(; isdigit(*p
); p
++)
1104 if((i
= atoi(s
+1)) && i
< strlen(p
)){
1113 if(*++p
== '(' && *++p
){
1114 for(seq
= p
; isdigit(*p
); p
++)
1119 for(val
= p
; isdigit(*p
); p
++)
1125 if((nseq
= atoi(seq
)) && isdigit(*(seq
= p
))
1126 && (p
= strchr(p
, ')')) && *(mbox
= ++p
)){
1129 uidl
= (imapuid_t
*) fs_get ((nseq
+1)*sizeof(imapuid_t
));
1130 for(i
= 0; i
< nseq
; i
++)
1131 if((p
= strchr(seq
,',')) != NULL
){
1133 if((uidl
[i
]= strtoul(seq
,NULL
,10)) != 0)
1138 else if((p
= strchr(seq
, ')')) != NULL
){
1139 if((uidl
[i
] = strtoul(seq
,NULL
,10)) != 0)
1146 reply
= (REPLY_S
*)fs_get(sizeof(REPLY_S
));
1147 memset(reply
, 0, sizeof(REPLY_S
));
1149 reply
->data
.uid
.validity
= strtoul(val
, NULL
, 10);
1151 reply
->forwarded
= 1;
1153 reply
->prefix
= cpystr(prefix
);
1155 reply
->mailbox
= cpystr(mbox
);
1157 reply
->data
.uid
.msgs
= uidl
;
1160 fs_give((void **) &uidl
);
1172 * pine_new_env - allocate a new METAENV and fill it in.
1175 pine_new_env(ENVELOPE
*outgoing
, char **fccp
, char ***tobufpp
, PINEFIELD
*custom
)
1179 PINEFIELD
*pfields
, *pf
, **sending_order
;
1182 header
= (METAENV
*) fs_get(sizeof(METAENV
));
1184 /* how many fields are there? */
1185 for(cnt
= 0; pf_template
&& pf_template
[cnt
].name
; cnt
++)
1190 for(pf
= custom
; pf
; pf
= pf
->next
)
1193 /* temporary PINEFIELD array */
1194 i
= (cnt
+ 1) * sizeof(PINEFIELD
);
1195 pfields
= (PINEFIELD
*)fs_get((size_t) i
);
1196 memset(pfields
, 0, (size_t) i
);
1198 i
= (cnt
+ 1) * sizeof(PINEFIELD
*);
1199 sending_order
= (PINEFIELD
**)fs_get((size_t) i
);
1200 memset(sending_order
, 0, (size_t) i
);
1202 header
->env
= outgoing
;
1203 header
->local
= pfields
;
1204 header
->custom
= custom
;
1205 header
->sending_order
= sending_order
;
1207 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
1213 /* initialize pfield */
1215 for(i
=0; i
< stdcnt
; i
++, pf
++){
1217 pf
->name
= cpystr(pf_template
[i
].name
);
1218 if(i
== N_SENDER
&& F_ON(F_USE_SENDER_NOT_X
, ps_global
)){
1219 /* slide string over so it is Sender instead of X-X-Sender */
1220 for(p
=pf
->name
+4; *p
!= '\0'; p
++)
1225 pf
->type
= pf_template
[i
].type
;
1226 pf
->canedit
= pf_template
[i
].canedit
;
1227 pf
->rcptto
= pf_template
[i
].rcptto
;
1228 pf
->writehdr
= pf_template
[i
].writehdr
;
1229 pf
->localcopy
= pf_template
[i
].localcopy
;
1230 pf
->extdata
= NULL
; /* unused */
1237 sending_order
[0] = pf
;
1241 pf
->text
= &outgoing
->newsgroups
;
1242 sending_order
[1] = pf
;
1246 pf
->text
= (char **) &outgoing
->date
;
1247 sending_order
[2] = pf
;
1251 pf
->text
= &outgoing
->in_reply_to
;
1252 sending_order
[NN
+9] = pf
;
1256 pf
->text
= &outgoing
->message_id
;
1257 sending_order
[NN
+10] = pf
;
1260 case N_REF
: /* won't be used here */
1261 sending_order
[NN
+11] = pf
;
1265 sending_order
[NN
+12] = pf
;
1269 pf
->text
= &pf
->textbuf
;
1270 pf
->textbuf
= generate_user_agent();
1271 sending_order
[NN
+13] = pf
;
1274 case N_POSTERR
: /* won't be used here */
1275 sending_order
[NN
+14] = pf
;
1278 case N_RPLUID
: /* won't be used here */
1279 sending_order
[NN
+15] = pf
;
1282 case N_RPLMBOX
: /* won't be used here */
1283 sending_order
[NN
+16] = pf
;
1286 case N_SMTP
: /* won't be used here */
1287 sending_order
[NN
+17] = pf
;
1290 case N_NNTP
: /* won't be used here */
1291 sending_order
[NN
+18] = pf
;
1294 case N_CURPOS
: /* won't be used here */
1295 sending_order
[NN
+19] = pf
;
1298 case N_OURREPLYTO
: /* won't be used here */
1299 sending_order
[NN
+20] = pf
;
1302 case N_OURHDRS
: /* won't be used here */
1303 sending_order
[NN
+21] = pf
;
1307 q_status_message1(SM_ORDER
,3,3,
1308 "Internal error: 1)FreeText header %s", comatose(i
));
1320 sending_order
[3] = pf
;
1321 pf
->addr
= &outgoing
->from
;
1325 sending_order
[NN
+2] = pf
;
1326 pf
->addr
= &outgoing
->to
;
1328 (*tobufpp
) = &pf
->scratch
;
1333 sending_order
[NN
+3] = pf
;
1334 pf
->addr
= &outgoing
->cc
;
1338 sending_order
[NN
+4] = pf
;
1339 pf
->addr
= &outgoing
->bcc
;
1343 sending_order
[NN
+1] = pf
;
1344 pf
->addr
= &outgoing
->reply_to
;
1347 case N_LCC
: /* won't be used here */
1348 sending_order
[NN
+7] = pf
;
1351 #if !(defined(DOS) || defined(OS2)) || defined(NOAUTH)
1353 sending_order
[4] = pf
;
1354 pf
->addr
= &outgoing
->sender
;
1358 case N_NOBODY
: /* won't be used here */
1359 sending_order
[NN
+5] = pf
;
1363 q_status_message1(SM_ORDER
,3,3,
1364 "Internal error: Address header %s", comatose(i
));
1370 sending_order
[NN
+8] = pf
;
1375 sending_order
[NN
+6] = pf
;
1376 pf
->text
= &outgoing
->subject
;
1380 q_status_message1(SM_ORDER
,3,3,
1381 "Unknown header type %d in pine_new_send", (void *)pf
->type
);
1386 if(((--pf
)->next
= custom
) != NULL
){
1390 * NOTE: "i" is assumed to now index first custom field in sending
1393 for(pf
= pf
->next
; pf
&& pf
->name
; pf
= pf
->next
){
1404 if(pf
->addr
){ /* better be set */
1409 bldto
.arg
.str
= pf
->textbuf
;
1411 sending_order
[i
++] = pf
;
1412 /* change default text into an ADDRESS */
1413 /* strip quotes around whole default */
1414 removing_trailing_white_space(pf
->textbuf
);
1415 (void)removing_double_quotes(pf
->textbuf
);
1416 build_address_internal(bldto
, &addr
, NULL
, NULL
, NULL
, NULL
, NULL
, 0, NULL
);
1417 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
1418 fs_give((void **)&addr
);
1420 fs_give((void **)&pf
->textbuf
);
1426 sending_order
[i
++] = pf
;
1427 pf
->text
= &pf
->textbuf
;
1431 q_status_message1(SM_ORDER
,0,7,"Unknown custom header type %d",
1444 pine_free_env(METAENV
**menv
)
1450 for(cnt
= 0; pf_template
&& pf_template
[cnt
].name
; cnt
++)
1453 for(; cnt
>= 0; cnt
--){
1454 if((*menv
)->local
[cnt
].textbuf
)
1455 fs_give((void **) &(*menv
)->local
[cnt
].textbuf
);
1457 fs_give((void **) &(*menv
)->local
[cnt
].name
);
1460 fs_give((void **) &(*menv
)->local
);
1463 if((*menv
)->sending_order
)
1464 fs_give((void **) &(*menv
)->sending_order
);
1466 fs_give((void **) menv
);
1470 /*----------------------------------------------------------------------
1471 Check for addresses the user is not permitted to send to, or probably
1472 doesn't want to send to
1475 1 if there are only empty groups
1476 -1 if the message shouldn't be sent
1478 Queues a message indicating what happened
1481 check_addresses(METAENV
*header
)
1485 int send_daemon
= 0, rv
= CA_EMPTY
;
1487 /*---- Is he/she trying to send mail to the mailer-daemon ----*/
1488 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1489 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
)
1490 for(a
= *pf
->addr
; a
!= NULL
; a
= a
->next
){
1491 if(a
->host
&& (a
->host
[0] == '.'
1492 || (F_ON(F_COMPOSE_REJECTS_UNQUAL
, ps_global
)
1493 && a
->host
[0] == '@'))){
1494 q_status_message2(SM_ORDER
, 4, 7,
1495 /* TRANSLATORS: First arg is the address we can't
1496 send to, second arg is "not in addressbook". */
1497 _("Can't send to address %s: %s"),
1501 : _("not in addressbook"));
1504 else if(ps_global
->restricted
1505 && !address_is_us(*pf
->addr
, ps_global
)){
1506 q_status_message(SM_ORDER
, 3, 3,
1507 "Restricted demo version of Alpine. You may only send mail to yourself");
1510 else if(a
->mailbox
&& strucmp(a
->mailbox
, "mailer-daemon") == 0 && !send_daemon
){
1512 rv
= (pith_opt_daemon_confirm
&& (*pith_opt_daemon_confirm
)()) ? CA_OK
: CA_BAD
;
1514 else if(a
->mailbox
&& a
->host
){
1524 * If this isn't general enough we can modify it. The value passed in
1525 * is expected to be one of the desc settings from the priorities array,
1526 * like "High". The header value is X-Priority: 2 (High)
1527 * or something similar. If value doesn't match any of the values then
1528 * the actual value is used instead.
1531 set_priority_header(METAENV
*header
, char *value
)
1535 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1536 if(pf
->type
== FreeText
&& !strcmp(pf
->name
, PRIORITYNAME
))
1541 fs_give((void **) &pf
->textbuf
);
1546 for(p
= priorities
; p
&& p
->desc
; p
++)
1547 if(!strcmp(p
->desc
, value
))
1553 snprintf(buf
, sizeof(buf
), "%d (%s)", p
->val
, p
->desc
);
1554 pf
->textbuf
= cpystr(buf
);
1557 pf
->textbuf
= cpystr(value
);
1564 /*----------------------------------------------------------------------
1565 Set answered flags for messages specified by reply structure
1569 Returns: with appropriate flags set and index cache entries suitably tweeked
1572 update_answered_flags(REPLY_S
*reply
)
1574 char *seq
= NULL
, *p
;
1575 long i
, ourstream
= 0, we_cancel
= 0;
1576 MAILSTREAM
*stream
= NULL
;
1578 /* nothing to flip in a pseudo reply */
1579 if(reply
&& (reply
->msgno
|| reply
->uid
)){
1584 * If an established stream will do, use it, else
1585 * build one unless we have an array of msgno's...
1587 * I was just mimicking what was already here. I don't really
1588 * understand why we use strcmp instead of same_stream_and_mailbox().
1589 * Or sp_stream_get(reply->mailbox, SP_MATCH).
1592 for(j
= 0; !stream
&& j
< ps_global
->s_pool
.nstream
; j
++){
1593 m
= ps_global
->s_pool
.streams
[j
];
1594 if(m
&& reply
->mailbox
&& m
->mailbox
1595 && !strcmp(reply
->mailbox
, m
->mailbox
))
1599 if(!stream
&& reply
->msgno
)
1602 /* TRANSLATORS: program is busy updating the Answered flags so warns user */
1603 we_cancel
= reply
->forwarded
1604 ? busy_cue(_("Setting \"Forwarded\" Keyword"), NULL
, 0)
1605 : busy_cue(_("Updating \"Answered\" Flags"), NULL
, 0);
1607 if((stream
= pine_mail_open(NULL
,
1608 reply
->origmbox
? reply
->origmbox
1610 OP_SILENT
| SP_USEPOOL
| SP_TEMPUSE
,
1622 if(stream
->uid_validity
== reply
->data
.uid
.validity
){
1623 for(i
= 0L, p
= tmp_20k_buf
; reply
->data
.uid
.msgs
[i
]; i
++){
1625 sstrncpy(&p
, ",", SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
1626 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1629 sstrncpy(&p
, ulong2string(reply
->data
.uid
.msgs
[i
]),
1630 SIZEOF_20KBUF
-(p
-tmp_20k_buf
));
1631 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1634 if(reply
->forwarded
){
1636 * $Forwarded is a regular keyword so we only try to
1637 * set it if the stream allows keywords.
1638 * We could mess up if the stream has keywords but just
1639 * isn't allowing anymore and $Forwarded already exists,
1640 * but what are the odds?
1642 if(stream
&& stream
->kwd_create
)
1643 mail_flag(stream
, seq
= cpystr(tmp_20k_buf
),
1645 ST_SET
| ((reply
->uid
) ? ST_UID
: 0L));
1648 mail_flag(stream
, seq
= cpystr(tmp_20k_buf
),
1650 ST_SET
| ((reply
->uid
) ? ST_UID
: 0L));
1653 fs_give((void **)&seq
);
1657 pine_mail_close(stream
); /* clean up dangling stream */
1665 /*----------------------------------------------------------------------
1666 Call the mailer, SMTP, sendmail or whatever
1668 Args: header -- full header (envelope and local parts) of message to send
1669 body -- The full body of the message including text
1671 verbosefile -- non-null means caller wants verbose interaction and the resulting
1672 output file name to be returned
1674 Returns: -1 if failed, 1 if succeeded
1677 call_mailer(METAENV
*header
, struct mail_bodystruct
*body
, char **alt_smtp_servers
,
1678 int flags
, void (*bigresult_f
)(char *, int),
1679 void (*pipecb_f
)(PIPE_S
*, int, void *))
1681 char error_buf
[200], *error_mess
= NULL
, *postcmd
;
1683 ENVELOPE
*fake_env
= NULL
;
1684 int addr_error_count
, we_cancel
= 0;
1685 long smtp_opts
= 0L;
1686 char *verbose_file
= NULL
;
1689 BODY
*origBody
= body
;
1691 dprint((4, "Sending mail...\n"));
1693 /* Check for any recipients */
1694 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1695 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
)
1699 q_status_message(SM_ORDER
,3,3,
1700 _("Can't send message. No recipients specified!"));
1705 if(ps_global
->smime
&& (ps_global
->smime
->do_encrypt
|| ps_global
->smime
->do_sign
)){
1708 STORE_S
*so
= lmc
.so
;
1713 if(ps_global
->smime
->do_sign
){
1714 bp
= F_ON(F_ENABLE_8BIT
, ps_global
) ? first_text_8bit(body
) : NULL
;
1715 result
= sign_outgoing_message(header
, &body
, 0, &bp
);
1718 /* need to free new body from encrypt if sign fails? */
1719 if(result
&& ps_global
->smime
->do_encrypt
)
1720 result
= encrypt_outgoing_message(header
, &body
);
1729 /* set up counts and such to keep track sent percentage */
1730 send_bytes_sent
= 0;
1731 gf_filter_init(); /* zero piped byte count, 'n */
1732 send_bytes_to_send
= send_body_size(body
); /* count body bytes */
1733 ps_global
->c_client_error
[0] = error_buf
[0] = '\0';
1734 we_cancel
= busy_cue(_("Sending mail"),
1735 send_bytes_to_send
? sent_percent
: NULL
, 0);
1739 /* try posting via local "<mta> <-t>" if specified */
1740 if(mta_handoff(header
, body
, error_buf
, sizeof(error_buf
), alt_smtp_servers
, bigresult_f
, pipecb_f
)){
1742 error_mess
= error_buf
;
1750 * If the user's asked for it, and we find that the first text
1751 * part (attachments all get b64'd) is non-7bit, ask for 8BITMIME.
1753 if(F_ON(F_ENABLE_8BIT
, ps_global
) && (bp
= first_text_8bit(body
)))
1754 smtp_opts
|= SOP_8BITMIME
;
1757 #ifndef DEBUGJOURNAL
1758 if(debug
> 5 || (flags
& CM_VERBOSE
))
1760 smtp_opts
|= SOP_DEBUG
;
1763 if(flags
& (CM_DSN_NEVER
| CM_DSN_DELAY
| CM_DSN_SUCCESS
| CM_DSN_FULL
)){
1764 smtp_opts
|= SOP_DSN
;
1765 if(!(flags
& CM_DSN_NEVER
)){ /* if never, don't turn others on */
1766 if(flags
& CM_DSN_DELAY
)
1767 smtp_opts
|= SOP_DSN_NOTIFY_DELAY
;
1768 if(flags
& CM_DSN_SUCCESS
)
1769 smtp_opts
|= SOP_DSN_NOTIFY_SUCCESS
;
1772 * If it isn't Never, then we're always going to let them
1773 * know about failures. This means we don't allow for the
1774 * possibility of setting delay or success without failure.
1776 smtp_opts
|= SOP_DSN_NOTIFY_FAILURE
;
1778 if(flags
& CM_DSN_FULL
)
1779 smtp_opts
|= SOP_DSN_RETURN_FULL
;
1785 * Set global header pointer so post_rfc822_output can get at it when
1786 * it's called back from c-client's sending routine...
1788 send_header
= header
;
1791 * Fabricate a fake ENVELOPE to hand c-client's SMTP engine.
1792 * The purpose is to give smtp_mail the list for SMTP RCPT when
1793 * there are recipients in pine's METAENV that are outside c-client's
1796 * NOTE: If there aren't any, don't bother. Dealt with it below.
1798 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1799 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
1800 && !(*pf
->addr
== header
->env
->to
|| *pf
->addr
== header
->env
->cc
1801 || *pf
->addr
== header
->env
->bcc
))
1807 fake_env
= (ENVELOPE
*)fs_get(sizeof(ENVELOPE
));
1808 memset(fake_env
, 0, sizeof(ENVELOPE
));
1809 fake_env
->return_path
= rfc822_cpy_adr(header
->env
->return_path
);
1810 tail
= &(fake_env
->to
);
1811 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
1812 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
1813 *tail
= rfc822_cpy_adr(*pf
->addr
);
1815 tail
= &((*tail
)->next
);
1820 * Install our rfc822 output routine
1822 sending_hooks
.rfc822_out
= mail_parameters(NULL
, GET_RFC822OUTPUT
, NULL
);
1823 (void)mail_parameters(NULL
, SET_RFC822OUTPUT
, (void *)post_rfc822_output
);
1826 * Allow for verbose posting
1828 (void) mail_parameters(NULL
, SET_SMTPVERBOSE
,
1829 (void *) pine_smtp_verbose_out
);
1832 * We do this because we want mm_log to put the error message into
1833 * c_client_error instead of showing it itself.
1835 ps_global
->noshow_error
= 1;
1838 * OK, who posts what? We tried an mta_handoff above, but there
1839 * was either none specified or we decided not to use it. So,
1840 * if there's an smtp-server defined anywhere,
1842 if(alt_smtp_servers
&& alt_smtp_servers
[0] && alt_smtp_servers
[0][0]){
1843 /*---------- SMTP ----------*/
1844 dprint((4, "call_mailer: via TCP (%s)\n",
1845 alt_smtp_servers
[0]));
1846 TIME_STAMP("smtp-open start (tcp)", 1);
1847 sending_stream
= smtp_open(alt_smtp_servers
, smtp_opts
);
1849 else if(ps_global
->VAR_SMTP_SERVER
&& ps_global
->VAR_SMTP_SERVER
[0]
1850 && ps_global
->VAR_SMTP_SERVER
[0][0]){
1851 /*---------- SMTP ----------*/
1852 dprint((4, "call_mailer: via TCP\n"));
1853 TIME_STAMP("smtp-open start (tcp)", 1);
1854 sending_stream
= smtp_open(ps_global
->VAR_SMTP_SERVER
, smtp_opts
);
1856 else if((postcmd
= smtp_command(ps_global
->c_client_error
, sizeof(ps_global
->c_client_error
))) != NULL
){
1859 /*----- Send via LOCAL SMTP agent ------*/
1860 dprint((4, "call_mailer: via \"%s\"\n", postcmd
));
1862 TIME_STAMP("smtp-open start (pipe)", 1);
1863 fs_give((void **) &postcmd
);
1864 cmdlist
[0] = "localhost";
1866 sending_stream
= smtp_open_full(&piped_io
, cmdlist
, "smtp",
1867 SMTPTCPPORT
, smtp_opts
);
1868 /* BUG: should provide separate stderr output! */
1871 ps_global
->noshow_error
= 0;
1873 TIME_STAMP("smtp open", 1);
1875 unsigned short save_encoding
= 0, added_encoding
= 0;
1877 dprint((1, "Opened SMTP server \"%s\"\n",
1878 net_host(sending_stream
->netstream
)
1879 ? net_host(sending_stream
->netstream
) : "?"));
1881 if(flags
& CM_VERBOSE
){
1882 TIME_STAMP("verbose start", 1);
1883 if((verbose_file
= temp_nam(NULL
, "sd")) != NULL
){
1884 if((verbose_send_output
= our_fopen(verbose_file
, "w")) != NULL
){
1885 if(!smtp_verbose(sending_stream
)){
1886 snprintf(error_mess
= error_buf
, sizeof(error_buf
),
1887 "Mail not sent. VERBOSE mode error%s%.50s.",
1888 (sending_stream
&& sending_stream
->reply
)
1890 (sending_stream
&& sending_stream
->reply
)
1891 ? sending_stream
->reply
: "");
1892 error_buf
[sizeof(error_buf
)-1] = '\0';
1896 our_unlink(verbose_file
);
1897 strncpy(error_mess
= error_buf
,
1898 "Can't open tmp file for VERBOSE mode.", sizeof(error_buf
));
1899 error_buf
[sizeof(error_buf
)-1] = '\0';
1903 strncpy(error_mess
= error_buf
,
1904 "Can't create tmp file name for VERBOSE mode.", sizeof(error_buf
));
1905 error_buf
[sizeof(error_buf
)-1] = '\0';
1908 TIME_STAMP("verbose end", 1);
1912 * Before we actually send data, see if we have to protect
1913 * the first text body part from getting encoded. We protect
1914 * it from getting encoded in "pine_rfc822_output_body" by
1915 * temporarily inventing a synonym for ENC8BIT...
1916 * This works like so:
1917 * Suppose bp->encoding is set to ENC8BIT.
1918 * We change that here to some unused value (added_encoding) and
1919 * set body_encodings[added_encoding] to "8BIT".
1920 * Then post_rfc822_output is called which calls
1921 * pine_rfc822_output_body. Inside that routine
1922 * pine_write_body_header writes out the encoding for the
1923 * part. Normally it would see encoding == ENC8BIT and it would
1924 * change that to QUOTED-PRINTABLE, but since encoding has been
1925 * set to added_encoding it uses body_encodings[added_encoding]
1926 * which is "8BIT" instead. Then the actual body is written by
1927 * pine_write_body_header which does not do the gf_8bit_qp
1928 * filtering because encoding != ENC8BIT (instead it's equal
1929 * to added_encoding).
1931 if(bp
&& sending_stream
->protocol
.esmtp
.eightbit
.ok
1932 && sending_stream
->protocol
.esmtp
.eightbit
.want
){
1935 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++)
1938 if(i
> ENCMAX
){ /* no empty encoding slots! */
1943 body_encodings
[added_encoding
] = body_encodings
[ENC8BIT
];
1944 save_encoding
= bp
->encoding
;
1945 bp
->encoding
= added_encoding
;
1947 if(ps_global
->smime
&& ps_global
->smime
->do_sign
1948 && body
->nested
.part
->next
1949 && body
->nested
.part
->next
->body
.contents
.text
.data
1950 && body
->nested
.part
->next
->body
.mime
.text
.data
){
1953 so
= (STORE_S
*) body
->nested
.part
->next
->body
.contents
.text
.data
;
1955 body
->nested
.part
->next
->body
.contents
.text
.data
= body
->nested
.part
->next
->body
.mime
.text
.data
;
1956 body
->nested
.part
->next
->body
.mime
.text
.data
= NULL
;
1962 if(sending_stream
->protocol
.esmtp
.ok
1963 && sending_stream
->protocol
.esmtp
.dsn
.want
1964 && !sending_stream
->protocol
.esmtp
.dsn
.ok
)
1965 q_status_message(SM_ORDER
,3,3,
1966 _("Delivery Status Notification not available from this server."));
1968 TIME_STAMP("smtp start", 1);
1969 if(!error_mess
&& !smtp_mail(sending_stream
, "MAIL",
1970 fake_env
? fake_env
: header
->env
, body
)){
1972 snprintf(error_buf
, sizeof(error_buf
),
1973 _("Mail not sent. Sending error%s%s"),
1974 (sending_stream
&& sending_stream
->reply
) ? ": ": ".",
1975 (sending_stream
&& sending_stream
->reply
)
1976 ? sending_stream
->reply
: "");
1977 error_buf
[sizeof(error_buf
)-1] = '\0';
1978 dprint((1, error_buf
));
1979 addr_error_count
= 0;
1981 for(a
= fake_env
->to
; a
!= NULL
; a
= a
->next
)
1982 if(a
->error
!= NULL
){
1983 if(addr_error_count
++ < MAX_ADDR_ERROR
){
1986 * Too complicated to figure out which header line
1987 * has the error in the fake_env case, so just
1988 * leave cursor at default.
1992 if(error_mess
) /* previous error? */
1993 q_status_message(SM_ORDER
, 4, 7, error_mess
);
1995 error_mess
= tidy_smtp_mess(a
->error
,
1996 _("Mail not sent: %.80s"),
1997 error_buf
, sizeof(error_buf
));
2000 dprint((1, "Send Error: \"%s\"\n",
2005 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2006 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
)
2007 for(a
= *pf
->addr
; a
!= NULL
; a
= a
->next
)
2008 if(a
->error
!= NULL
){
2009 if(addr_error_count
++ < MAX_ADDR_ERROR
){
2011 if(error_mess
) /* previous error? */
2012 q_status_message(SM_ORDER
, 4, 7, error_mess
);
2014 error_mess
= tidy_smtp_mess(a
->error
,
2015 _("Mail not sent: %.80s"),
2016 error_buf
, sizeof(error_buf
));
2019 dprint((1, "Send Error: \"%s\"\n",
2025 error_mess
= error_buf
;
2028 /* repair modified "body_encodings" array? */
2029 if(bp
&& sending_stream
->protocol
.esmtp
.eightbit
.ok
2030 && sending_stream
->protocol
.esmtp
.eightbit
.want
){
2031 body_encodings
[added_encoding
] = NULL
;
2032 bp
->encoding
= save_encoding
;
2035 TIME_STAMP("smtp closing", 1);
2036 smtp_close(sending_stream
);
2037 sending_stream
= NULL
;
2038 TIME_STAMP("smtp done", 1);
2040 else if(!error_mess
){
2041 snprintf(error_mess
= error_buf
, sizeof(error_buf
), _("Error sending%.2s%.80s"),
2042 ps_global
->c_client_error
[0] ? ": " : "",
2043 ps_global
->c_client_error
);
2044 error_buf
[sizeof(error_buf
)-1] = '\0';
2048 if(verbose_send_output
){
2049 TIME_STAMP("verbose start", 1);
2050 fclose(verbose_send_output
);
2051 verbose_send_output
= NULL
;
2052 q_status_message(SM_ORDER
, 0, 3, "Verbose SMTP output received");
2055 (*bigresult_f
)(verbose_file
, CM_BR_VERBOSE
);
2057 TIME_STAMP("verbose end", 1);
2060 fs_give((void **)&verbose_file
);
2064 * Restore original 822 emitter...
2066 (void) mail_parameters(NULL
, SET_RFC822OUTPUT
, sending_hooks
.rfc822_out
);
2069 mail_free_envelope(&fake_env
);
2074 /* Free replacement encrypted body */
2075 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && body
!= origBody
){
2077 if(body
->type
== TYPEMULTIPART
){
2078 /* Just get rid of first part, it's actually origBody */
2079 void *x
= body
->nested
.part
;
2081 body
->nested
.part
= body
->nested
.part
->next
;
2086 pine_free_body(&body
);
2093 TIME_STAMP("call_mailer done", 1);
2094 /*-------- Did message make it ? ----------*/
2096 /*---- Error sending mail -----*/
2097 if(lmc
.so
&& !lmc
.all_written
)
2101 q_status_message(SM_ORDER
| SM_DING
, 4, 7, error_mess
);
2102 dprint((1, "call_mailer ERROR: %s\n", error_mess
));
2108 lmc
.all_written
= 1;
2115 * write_postponed - exported method to write the given message
2116 * to the postponed folder
2119 write_postponed(METAENV
*header
, struct mail_bodystruct
*body
)
2123 CONTEXT_S
*fcc_cntxt
= NULL
;
2125 static char *writem
[] = {"To", "References", "Fcc", "X-Reply-UID", NULL
};
2127 if(!ps_global
->VAR_POSTPONED_FOLDER
2128 || !ps_global
->VAR_POSTPONED_FOLDER
[0]){
2129 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2130 _("No postponed file defined"));
2134 folder
= cpystr(ps_global
->VAR_POSTPONED_FOLDER
);
2136 lmc
.all_written
= lmc
.text_written
= lmc
.text_only
= 0;
2138 lmc
.so
= open_fcc(folder
, &fcc_cntxt
, 1, NULL
, NULL
);
2141 /* BUG: writem sufficient ? */
2142 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2143 for(pp
= writem
; *pp
; pp
++)
2144 if(!strucmp(pf
->name
, *pp
)){
2151 * Work around c-client reply-to bug. C-client will
2152 * return a reply_to in an envelope even if there is
2153 * no reply-to header field. We want to note here whether
2154 * the reply-to is real or not.
2156 if(header
->env
->reply_to
|| hdr_is_in_list("reply-to", header
->custom
))
2157 for(pf
= header
->local
; pf
; pf
= pf
->next
)
2158 if(!strcmp(pf
->name
, "Reply-To")){
2161 if(header
->env
->reply_to
)
2162 pf
->textbuf
= cpystr("Full");
2164 pf
->textbuf
= cpystr("Empty");
2168 * Write the list of custom headers to the
2169 * X-Our-Headers header so that we can recover the
2173 for(pf
= header
->custom
; pf
&& pf
->name
; pf
= pf
->next
)
2174 sz
+= strlen(pf
->name
) + 1;
2178 char *pstart
, *pend
;
2180 for(i
= 0, pf
= header
->local
; i
!= N_OURHDRS
; i
++, pf
= pf
->next
)
2185 pf
->textbuf
= pstart
= pend
= (char *) fs_get(sz
+ 1);
2186 pf
->text
= &pf
->textbuf
;
2187 pf
->textbuf
[sz
] = '\0'; /* tie off overflow */
2188 /* note: "pf" overloaded */
2189 for(pf
= header
->custom
; pf
&& pf
->name
; pf
= pf
->next
){
2190 int r
= sz
- (pend
- pstart
); /* remaining buffer */
2192 if(r
> 0 && r
!= sz
){
2197 sstrncpy(&pend
, pf
->name
, r
);
2201 if(pine_rfc822_output(header
, body
, NULL
, NULL
) < 0
2202 || write_fcc(folder
, fcc_cntxt
, lmc
.so
, NULL
, "postponed message", NULL
) < 0)
2208 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2209 "Can't allocate internal storage: %s ",
2210 error_description(errno
));
2214 fs_give((void **) &folder
);
2220 commence_fcc(char *fcc
, CONTEXT_S
**fcc_cntxt
, int forced
)
2223 lmc
.all_written
= lmc
.text_written
= 0;
2224 lmc
.text_only
= F_ON(F_NO_FCC_ATTACH
, ps_global
) != 0;
2225 return((lmc
.so
= open_fcc(fcc
, fcc_cntxt
, 0, NULL
, NULL
)) != NULL
);
2235 wrapup_fcc(char *fcc
, CONTEXT_S
*fcc_cntxt
, METAENV
*header
, struct mail_bodystruct
*body
)
2240 if(!header
|| pine_rfc822_output(header
, body
, NULL
, NULL
)){
2243 strncpy(label
, "Fcc", sizeof(label
));
2244 label
[sizeof(label
)-1] = '\0';
2245 if(strcmp(fcc
, ps_global
->VAR_DEFAULT_FCC
)){
2246 snprintf(label
+ 3, sizeof(label
)-3, " to %.40s", fcc
);
2247 label
[sizeof(label
)-1] = '\0';
2250 rv
= write_fcc(fcc
,fcc_cntxt
,lmc
.so
,NULL
,NULL
,NULL
);
2263 /*----------------------------------------------------------------------
2264 Checks to make sure the fcc is available and can be opened
2266 Args: fcc -- the name of the fcc to create. It can't be NULL.
2267 fcc_cntxt -- Returns the context the fcc is in.
2268 force -- suppress user option prompt
2270 Returns allocated storage object on success, NULL on failure
2273 open_fcc(char *fcc
, CONTEXT_S
**fcc_cntxt
, int force
, char *err_prefix
, char *err_suffix
)
2277 ps_global
->mm_log_error
= 0;
2280 * check for fcc's existence...
2282 TIME_STAMP("open_fcc start", 1);
2283 if(!is_absolute_path(fcc
) && context_isambig(fcc
)
2284 && (strucmp(ps_global
->inbox_name
, fcc
) != 0)){
2288 * Don't want to preclude a user from Fcc'ing a .name'd folder
2290 if(F_OFF(F_ENABLE_DOT_FOLDERS
, ps_global
)){
2292 F_TURN_ON(F_ENABLE_DOT_FOLDERS
, ps_global
);
2296 * We only want to set the "context" if fcc is an ambiguous
2297 * name. Otherwise, our "relativeness" rules for contexts
2298 * (implemented in context.c) might cause the name to be
2299 * interpreted in the wrong context...
2301 if(!(*fcc_cntxt
|| (*fcc_cntxt
= default_save_context(ps_global
->context_list
))))
2302 *fcc_cntxt
= ps_global
->context_list
;
2304 build_folder_list(NULL
, *fcc_cntxt
, fcc
, NULL
, BFL_FLDRONLY
);
2305 if(folder_index(fcc
, *fcc_cntxt
, FI_FOLDER
) < 0){
2307 || (pith_opt_save_create_prompt
2308 && (*pith_opt_save_create_prompt
)(*fcc_cntxt
, fcc
, 0) > 0)){
2310 ps_global
->noshow_error
= 1;
2312 if(context_create(*fcc_cntxt
, NULL
, fcc
))
2315 ps_global
->noshow_error
= 0;
2318 ok
--; /* declined! */
2324 F_TURN_OFF(F_ENABLE_DOT_FOLDERS
, ps_global
);
2326 free_folder_list(*fcc_cntxt
);
2328 else if((exists
= folder_exists(NULL
, fcc
)) != FEX_ERROR
){
2329 if(exists
& (FEX_ISFILE
| FEX_ISDIR
)){
2334 || (pith_opt_save_create_prompt
2335 && (*pith_opt_save_create_prompt
)(NULL
, fcc
, 0) > 0)){
2337 ps_global
->mm_log_error
= 0;
2338 ps_global
->noshow_error
= 1;
2340 ok
= pine_mail_create(NULL
, fcc
) != 0L;
2342 ps_global
->noshow_error
= 0;
2345 ok
--; /* declined! */
2349 TIME_STAMP("open_fcc done.", 1);
2351 return(so_get(FCC_SOURCE
, NULL
, WRITE_ACCESS
));
2354 int l1
, l2
, l3
, wid
, w
;
2355 char *errstr
, tmp
[MAILTMPLEN
];
2359 if(ps_global
->mm_log_error
){
2360 s1
= err_prefix
? err_prefix
: "Fcc Error: ";
2361 s2
= err_suffix
? err_suffix
: " Message NOT sent or copied.";
2365 l3
= strlen(ps_global
->c_client_error
);
2366 wid
= (ps_global
->ttyo
&& ps_global
->ttyo
->screen_cols
> 0)
2367 ? ps_global
->ttyo
->screen_cols
: 80;
2368 w
= wid
- l1
- l2
- 5;
2370 snprintf(errstr
= tmp
, sizeof(tmp
),
2371 "%.99s\"%.*s%.99s\".%.99s",
2373 (l3
> w
) ? MAX(w
-3,0) : MAX(w
,0),
2374 ps_global
->c_client_error
,
2375 (l3
> w
) ? "..." : "",
2377 tmp
[sizeof(tmp
)-1] = '\0';
2381 errstr
= _("Fcc creation error. Message NOT sent or copied.");
2384 errstr
= _("Fcc creation rejected. Message NOT sent or copied.");
2386 q_status_message(SM_ORDER
| SM_DING
, 3, 3, errstr
);
2393 /*----------------------------------------------------------------------
2394 mail_append() the fcc accumulated in temp_storage to proper destination
2396 Args: fcc -- name of folder
2397 fcc_cntxt -- context for folder
2398 temp_storage -- String of file where Fcc has been accumulated
2400 This copies the string of file to the actual folder, which might be IMAP
2401 or a disk folder. The temp_storage is freed after it is written.
2402 An error message is produced if this fails.
2405 write_fcc(char *fcc
, CONTEXT_S
*fcc_cntxt
, STORE_S
*tmp_storage
,
2406 MAILSTREAM
*stream
, char *label
, char *flags
)
2415 TIME_STAMP("write_fcc start.", 1);
2416 dprint((4, "Writing %s\n", (label
&& *label
) ? label
: ""));
2417 if(label
&& *label
){
2420 strncpy(msg_buf
, "Writing ", sizeof(msg_buf
));
2421 msg_buf
[sizeof(msg_buf
)-1] = '\0';
2422 strncat(msg_buf
, label
, sizeof(msg_buf
)-10);
2423 we_cancel
= busy_cue(msg_buf
, NULL
, 0);
2426 we_cancel
= busy_cue(NULL
, NULL
, 1);
2428 so_seek(tmp_storage
, 0L, 0);
2431 * Before changing this note that these lines depend on the
2432 * definition of FCC_SOURCE.
2434 INIT(&msg
, mail_string
, (void *)so_text(tmp_storage
),
2435 strlen((char *)so_text(tmp_storage
)));
2439 if(!context_append_full(cntxt
, stream
, fcc
, flags
, NULL
, &msg
)){
2440 cancel_busy_cue(-1);
2443 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
2444 "Write to \"%s\" FAILED!!!", fcc
);
2445 dprint((1, "ERROR appending %s in \"%s\"",
2447 (cntxt
&& cntxt
->context
) ? cntxt
->context
: "NULL"));
2452 cancel_busy_cue(label
? 0 : -1);
2454 dprint((4, "done.\n"));
2455 TIME_STAMP("write_fcc done.", 1);
2461 * first_text_8bit - return TRUE if somewhere in the body 8BIT data's
2465 first_text_8bit(struct mail_bodystruct
*body
)
2467 if(body
->type
== TYPEMULTIPART
) /* advance to first contained part */
2468 body
= &body
->nested
.part
->body
;
2470 return((body
->type
== TYPETEXT
&& body
->encoding
!= ENC7BIT
)
2476 * Build and return the "From:" address for outbound messages from
2482 ADDRESS
*addr
= mail_newaddr();
2483 if(ps_global
->VAR_PERSONAL_NAME
){
2484 addr
->personal
= cpystr(ps_global
->VAR_PERSONAL_NAME
);
2485 removing_leading_and_trailing_white_space(addr
->personal
);
2486 if(addr
->personal
[0] == '\0')
2487 fs_give((void **)&addr
->personal
);
2490 addr
->mailbox
= cpystr(ps_global
->VAR_USER_ID
);
2491 addr
->host
= cpystr(ps_global
->maildomain
);
2492 removing_leading_and_trailing_white_space(addr
->mailbox
);
2493 removing_leading_and_trailing_white_space(addr
->host
);
2499 * set_mime_type_by_grope - sniff the given storage object to determine its
2500 * type, subtype, encoding, and charset
2502 * "Type" and "encoding" must be set before calling this routine.
2503 * If "type" is set to something other than TYPEOTHER on entry,
2504 * then that is the "type" we wish to use. Same for "encoding"
2505 * using ENCOTHER instead of TYPEOTHER. Otherwise, we
2506 * figure them out here. If "type" is already set, we also
2507 * leave subtype alone. If not, we figure out subtype here.
2508 * There is a chance that we will upgrade encoding to a "higher"
2509 * level. For example, if it comes in as 7BIT we may change
2510 * that to 8BIT if we find a From_ we want to escape.
2511 * We may also set the charset attribute if the type is TEXT.
2513 * NOTE: this is rather inefficient if the store object is a CharStar
2514 * but the win is all types are handled the same
2517 set_mime_type_by_grope(struct mail_bodystruct
*body
)
2519 #define RBUFSZ (8193)
2520 unsigned char *buf
, *p
, *bol
;
2523 eight_bit_chars
= 0L,
2526 STORE_S
*so
= (STORE_S
*)body
->contents
.text
.data
;
2527 unsigned short new_encoding
= ENCOTHER
;
2530 short froms
= 0, dots
= 0,
2531 bmap
= 0x1, dmap
= 0x1;
2534 we_cancel
= busy_cue(NULL
, NULL
, 1);
2536 buf
= (unsigned char *)fs_get(RBUFSZ
);
2539 for(n
= 0; n
< RBUFSZ
-1 && so_readc(&buf
[n
], so
) != 0; n
++)
2544 if(n
){ /* check first few bytes to look for magic numbers */
2545 if(body
->type
== TYPEOTHER
){
2546 if(buf
[0] == 'G' && buf
[1] == 'I' && buf
[2] == 'F'){
2547 body
->type
= TYPEIMAGE
;
2548 body
->subtype
= cpystr("GIF");
2550 else if((n
> 9) && buf
[0] == 0xFF && buf
[1] == 0xD8
2551 && buf
[2] == 0xFF && buf
[3] == 0xE0
2552 && !strncmp((char *)&buf
[6], "JFIF", 4)){
2553 body
->type
= TYPEIMAGE
;
2554 body
->subtype
= cpystr("JPEG");
2556 else if((buf
[0] == 'M' && buf
[1] == 'M')
2557 || (buf
[0] == 'I' && buf
[1] == 'I')){
2558 body
->type
= TYPEIMAGE
;
2559 body
->subtype
= cpystr("TIFF");
2561 else if((buf
[0] == '%' && buf
[1] == '!')
2562 || (buf
[0] == '\004' && buf
[1] == '%' && buf
[2] == '!')){
2563 body
->type
= TYPEAPPLICATION
;
2564 body
->subtype
= cpystr("PostScript");
2566 else if(buf
[0] == '%' && !strncmp((char *)buf
+1, "PDF-", 4)){
2567 body
->type
= TYPEAPPLICATION
;
2568 body
->subtype
= cpystr("PDF");
2570 else if(buf
[0] == '.' && !strncmp((char *)buf
+1, "snd", 3)){
2571 body
->type
= TYPEAUDIO
;
2572 body
->subtype
= cpystr("Basic");
2574 else if((n
> 3) && buf
[0] == 0x00 && buf
[1] == 0x05
2575 && buf
[2] == 0x16 && buf
[3] == 0x00){
2576 body
->type
= TYPEAPPLICATION
;
2577 body
->subtype
= cpystr("APPLEFILE");
2579 else if((n
> 3) && buf
[0] == 0x50 && buf
[1] == 0x4b
2580 && buf
[2] == 0x03 && buf
[3] == 0x04){
2581 body
->type
= TYPEAPPLICATION
;
2582 body
->subtype
= cpystr("ZIP");
2586 * if type was set above, but no encoding specified, go
2587 * ahead and make it BASE64...
2589 if(body
->type
!= TYPEOTHER
&& body
->encoding
== ENCOTHER
)
2590 body
->encoding
= ENCBINARY
;
2595 if(body
->type
== TYPEOTHER
){
2596 body
->type
= TYPEAPPLICATION
;
2597 body
->subtype
= cpystr("octet-stream");
2598 if(body
->encoding
== ENCOTHER
)
2599 body
->encoding
= ENCBINARY
;
2603 if (body
->encoding
== ENCOTHER
|| body
->type
== TYPEOTHER
){
2604 #if defined(DOS) || defined(OS2) /* for binary file detection */
2605 int lastchar
= '\0';
2606 #define BREAKOUT 300 /* a value that a character can't be */
2612 /* Some people don't like quoted-printable caused by leading Froms */
2614 Find_Froms(froms
, dots
, bmap
, dmap
, *p
);
2617 max_line
= MAX(max_line
, line_so_far
+ p
- bol
);
2618 bol
= NULL
; /* clear beginning of line */
2619 line_so_far
= 0L; /* clear line count */
2620 #if defined(DOS) || defined(OS2)
2621 /* LF with no CR!! */
2622 if(lastchar
!= '\r') /* must be non-text data! */
2623 lastchar
= BREAKOUT
;
2630 /* NULL found. Unless we're told otherwise, must be binary */
2631 if(body
->type
== TYPEOTHER
){
2632 body
->type
= TYPEAPPLICATION
;
2633 body
->subtype
= cpystr("octet-stream");
2637 * The "TYPETEXT" here handles the case that the NULL
2638 * comes from imported text generated by some external
2639 * editor that permits or inserts NULLS. Otherwise,
2640 * assume it's a binary segment...
2642 new_encoding
= (body
->type
==TYPETEXT
) ? ENC8BIT
: ENCBINARY
;
2645 * Since we've already set encoding, count this as a
2646 * hi bit char and continue. The reason is that if this
2647 * is text, there may be a high percentage of encoded
2648 * characters, so base64 may get set below...
2650 if(body
->type
== TYPETEXT
)
2656 #if defined(DOS) || defined(OS2) /* for binary file detection */
2657 if(lastchar
!= BREAKOUT
)
2661 /* read another buffer in */
2664 line_so_far
+= p
- bol
;
2666 for (n
= 0; n
< RBUFSZ
-1 && so_readc(&buf
[n
], so
) != 0; n
++)
2676 * If there's no beginning-of-line pointer, then we must
2677 * have seen an end-of-line. Set bol to the start of the
2683 #if defined(DOS) || defined(OS2) /* for binary file detection */
2684 /* either a lone \r or lone \n indicate binary file */
2685 if(lastchar
== '\r' || lastchar
== BREAKOUT
){
2686 if(lastchar
== BREAKOUT
|| n
== 0 || *p
!= '\n'){
2687 if(body
->type
== TYPEOTHER
){
2688 body
->type
= TYPEAPPLICATION
;
2689 body
->subtype
= cpystr("octet-stream");
2692 new_encoding
= ENCBINARY
;
2700 /* stash away for later */
2701 so_attr(so
, "maxline", long2string(max_line
));
2703 if(body
->encoding
== ENCOTHER
|| body
->type
== TYPEOTHER
){
2705 * Since the type or encoding aren't set yet, fall thru a
2706 * series of tests to make sure an adequate type and
2707 * encoding are set...
2710 if(max_line
>= 1000L){ /* 1000 comes from rfc821 */
2711 if(body
->type
== TYPEOTHER
){
2713 * Since the types not set, then we didn't find a NULL.
2714 * If there's no NULL, then this is likely text. However,
2715 * since we can't be *completely* sure, we set it to
2718 body
->type
= TYPEAPPLICATION
;
2719 body
->subtype
= cpystr("octet-stream");
2722 if(new_encoding
!= ENCBINARY
)
2724 * As with NULL handling, if we're told it's text,
2725 * qp-encode it, else it gets base 64...
2727 new_encoding
= (body
->type
== TYPETEXT
) ? ENC8BIT
: ENCBINARY
;
2730 if(eight_bit_chars
== 0L){
2731 if(body
->type
== TYPEOTHER
)
2732 body
->type
= TYPETEXT
;
2734 if(new_encoding
== ENCOTHER
)
2735 new_encoding
= ENC7BIT
; /* short lines, no 8 bit */
2737 else if(len
<= 3000L || (eight_bit_chars
* 100L)/len
< 30L){
2739 * The 30% threshold is based on qp encoded readability
2742 if(body
->type
== TYPEOTHER
)
2743 body
->type
= TYPETEXT
;
2745 if(new_encoding
!= ENCBINARY
)
2746 new_encoding
= ENC8BIT
; /* short lines, < 30% 8 bit chars */
2749 if(body
->type
== TYPEOTHER
){
2750 body
->type
= TYPEAPPLICATION
;
2751 body
->subtype
= cpystr("octet-stream");
2755 * Apply maximal encoding regardless of previous
2756 * setting. This segment's either not text, or is
2757 * unlikely to be readable with > 30% of the
2758 * text encoded anyway, so we might as well save space...
2760 new_encoding
= ENCBINARY
; /* > 30% 8 bit chars */
2765 /* If there were From_'s at the beginning of a line or standalone dots */
2766 if((froms
|| dots
) && new_encoding
!= ENCBINARY
)
2767 new_encoding
= ENC8BIT
;
2770 /* Set the subtype */
2771 if(body
->subtype
== NULL
)
2772 body
->subtype
= cpystr(rfc822_default_subtype(body
->type
));
2774 if(body
->encoding
== ENCOTHER
)
2775 body
->encoding
= new_encoding
;
2777 fs_give((void **)&buf
);
2780 cancel_busy_cue(-1);
2785 * Call this to set the charset of an attachment we have
2786 * created. If the attachment contains any non-ascii characters
2787 * then we'll set the charset to the passed in charset, otherwise
2788 * we'll make it us-ascii.
2791 set_charset_possibly_to_ascii(struct mail_bodystruct
*body
, char *charset
)
2794 int can_be_ascii
= 1;
2795 STORE_S
*so
= (STORE_S
*)body
->contents
.text
.data
;
2798 if(!body
|| body
->type
!= TYPETEXT
)
2801 we_cancel
= busy_cue(NULL
, NULL
, 1);
2805 while(can_be_ascii
&& so_readc(&c
, so
))
2810 set_parameter(&body
->parameter
, "charset", "US-ASCII");
2811 else if(charset
&& *charset
&& strucmp(charset
, "US-ASCII"))
2812 set_parameter(&body
->parameter
, "charset", charset
);
2815 * Else we don't know. There are non ascii characters but we either
2816 * don't have a charset to set it to or that charset is just us_ascii,
2817 * which is impossible. So we label it unknown. An alternative would
2818 * have been to strip the high bits instead and label it ascii.
2820 set_parameter(&body
->parameter
, "charset", UNKNOWN_CHARSET
);
2824 cancel_busy_cue(-1);
2829 * since encoding happens on the way out the door, this is basically
2830 * just needed to handle TYPEMULTIPART
2833 pine_encode_body (struct mail_bodystruct
*body
)
2837 dprint((4, "-- pine_encode_body: %d\n", body
? body
->type
: 0));
2838 if (body
) switch (body
->type
) {
2841 case TYPEMULTIPART
: /* multi-part */
2842 if(!(freethis
=parameter_val(body
->parameter
, "BOUNDARY"))){
2843 char tmp
[MAILTMPLEN
]; /* make cookie not in BASE64 or QUOTEPRINT*/
2845 snprintf (tmp
,sizeof(tmp
),"%ld%ld%ld%ld",gethostid (),random (),(long) time (0),
2847 tmp
[sizeof(tmp
)-1] = '\0';
2848 set_parameter(&body
->parameter
, "boundary", tmp
);
2852 fs_give((void **) &freethis
);
2854 part
= body
->nested
.part
; /* encode body parts */
2855 do pine_encode_body (&part
->body
);
2856 while ((part
= part
->next
) != NULL
); /* until done */
2861 * If the part is text we edited, then it is UTF-8.
2862 * The user may be asking us to send it as something else
2863 * or we may want to downconvert to a more-specific characterset.
2864 * Mark it for conversion here so the right MIME header's written.
2865 * Do conversion pine_rfc822_output_body.
2866 * Attachments are left as is.
2868 if(body
->contents
.text
.data
2869 && so_attr((STORE_S
*) body
->contents
.text
.data
, "edited", NULL
)){
2870 char *charset
, *posting_charset
, *lp
;
2872 if(!((charset
= parameter_val(body
->parameter
, "charset"))
2873 && !strucmp(charset
, UNKNOWN_CHARSET
))
2874 && (posting_charset
= posting_characterset(body
, charset
, MsgBody
))){
2876 set_parameter(&body
->parameter
, "charset", posting_charset
);
2879 * Fix iso-2022-jp encoding to ENC7BIT since it's escape based
2880 * and doesn't use anything but ASCII characters.
2881 * Why is it not ENC7BIT already? Because when we set the encoding
2882 * in set_mime_type_by_grope we were groping through UTF-8 text
2883 * not 2022 text. Not only that, but we didn't know at that point
2884 * that it wouldn't stay UTF-8 when we sent it, which would require
2887 if(!strucmp(posting_charset
, "iso-2022-jp")
2888 && (lp
= so_attr((STORE_S
*) body
->contents
.text
.data
, "maxline", NULL
))
2890 body
->encoding
= ENC7BIT
;
2894 fs_give((void **)&charset
);
2899 /* case MESSAGE: */ /* here for documentation */
2900 /* Encapsulated messages are always treated as text objects at this point.
2901 This means that you must replace body->contents.msg with
2902 body->contents.text, which probably involves copying
2903 body->contents.msg.text to body->contents.text */
2904 default: /* all else has some encoding */
2906 * but we'll delay encoding it until the message is on the way
2907 * into the mail slot...
2915 * pine_header_line - simple wrapper around c-client call to contain
2916 * repeated code, and to write fcc if required.
2919 pine_header_line(char *field
, METAENV
*header
, char *text
, soutr_t f
, void *s
,
2920 int writehdr
, int localcopy
)
2925 char *value
, *folded
= NULL
, *cs
;
2931 converted
= utf8_to_charset(text
, cs
= posting_characterset(text
, NULL
, HdrText
), 0);
2934 if(cs
&& !strucmp(cs
, "us-ascii"))
2937 value
= encode_header_value(tmp_20k_buf
, SIZEOF_20KBUF
,
2938 (unsigned char *) converted
, cs
,
2939 encode_whole_header(field
, header
));
2941 if(value
&& value
== converted
){ /* no encoding was done, have to fold */
2945 len
= ((header
&& header
->env
&& header
->env
->remail
)
2946 ? strlen("ReSent-") : 0) +
2947 (field
? strlen(field
) : 0) + 2;
2949 actual_field
= (char *)fs_get((len
+1) * sizeof(char));
2950 snprintf(actual_field
, len
+1, "%s%s: ",
2951 (header
&& header
->env
&& header
->env
->remail
) ? "ReSent-" : "",
2952 field
? field
: "");
2953 actual_field
[len
] = '\0';
2956 * We were folding everything except message-id, but that wasn't
2957 * sufficient. Since 822 only allows folding where linear-white-space
2958 * is allowed we'd need a smarter folder than "fold" to do it. So,
2959 * instead of inventing that smarter folder (which would have to
2962 * We could just alloc space and copy the actual_field followed by
2963 * the value into it, but since that's what fold does anyway we'll
2964 * waste some cpu time and use fold with a big fold parameter.
2966 * We upped the references folding from 75 to 256 because we were
2967 * encountering longer-than-75 message ids, and to break one line
2968 * in references is to break them all.
2970 * Also, some users are adding long text without spaces to the subject
2971 * line (e.g. URLs) so we up that number too. For the moment this is
2972 * going to 512 (a url that is about 6 lines long.)
2975 if(field
&& !strucmp("Subject", field
)){
2977 flags
|= FLD_NEXTSPC
;
2979 else if(field
&& !strucmp("References", field
))
2984 folded
= fold(value
, fold_by
, big
, actual_field
, " ", flags
);
2987 fs_give((void **)&actual_field
);
2989 else if(value
){ /* encoding was done */
2994 * rfc1522_encode already inserted continuation lines and did
2995 * the necessary folding so we don't have to do it. Let
2996 * rfc822_header_line add the trailing crlf and the resent- if
2997 * necessary. The 20 could actually be a 12.
2999 ll
= strlen(field
) + strlen(value
) + 20;
3000 folded
= (char *) fs_get(ll
* sizeof(char));
3002 rbuf
.f
= dummy_soutr
;
3006 rbuf
.end
= folded
+ll
-1;
3007 rfc822_output_header_line(&rbuf
, field
,
3008 (header
&& header
->env
&& header
->env
->remail
) ? LONGT
: 0L, value
);
3012 if(value
&& folded
){
3014 ret
= (*f
)(s
, folded
);
3016 if(ret
&& localcopy
&& lmc
.so
&& !lmc
.all_written
)
3017 ret
= so_puts(lmc
.so
, folded
);
3021 fs_give((void **)&folded
);
3023 if(converted
&& converted
!= text
)
3024 fs_give((void **) &converted
);
3034 * Do appropriate encoding of text header lines.
3035 * For some field types (those that consist of 822 *text) we just encode
3036 * the whole thing. For structured fields we encode only within comments
3039 * Args d -- Destination buffer if needed. (tmp_20k_buf)
3040 * s -- Source string.
3041 * charset -- Charset to encode with.
3042 * encode_all -- If set, encode the whole string. If not, try to encode
3043 * only within comments if possible.
3045 * Returns S is returned if no encoding is done. D is returned if encoding
3049 encode_header_value(char *d
, size_t dlen
, unsigned char *s
, char *charset
, int encode_all
)
3051 char *p
, *q
, *r
, *start_of_comment
= NULL
, *value
= NULL
;
3057 if(dlen
< SIZEOF_20KBUF
)
3058 alpine_panic("bad call to encode_header_value");
3062 * We don't have to worry about keeping track of quoted-strings because
3063 * none of these fields which aren't addresses contain quoted-strings.
3064 * We do keep track of escaped parens inside of comments and comment
3068 for(q
= (char *)s
; *q
; q
++){
3071 if(in_comment
++ == 0)
3072 start_of_comment
= q
;
3077 if(--in_comment
== 0){
3078 /* encode the comment, excluding the outer parens */
3083 r
= rfc1522_encode(d
+14000, dlen
-14000,
3084 (unsigned char *)start_of_comment
+1,
3086 if(r
!= start_of_comment
+1)
3087 value
= d
+7000; /* some encoding was done */
3089 start_of_comment
= NULL
;
3091 sstrncpy(&p
, r
, dlen
-1-(p
-d
));
3097 else if(in_comment
< 0){
3106 if(!in_comment
&& *(q
+1)){
3116 if(!in_comment
&& p
-d
< dlen
-1)
3124 /* Unterminated comment (wasn't really a comment) */
3125 if(start_of_comment
)
3126 sstrncpy(&p
, start_of_comment
, dlen
-1-(p
-d
));
3133 * We have to check if there is anything that needs to be encoded that
3134 * wasn't in a comment. If there is, we'd better just start over and
3135 * encode the whole thing. So, if no encoding has been done within
3136 * comments, or if encoding is needed both within and outside of
3137 * comments, then we encode the whole thing. Otherwise, go with
3138 * the version that has only comments encoded.
3140 if(!value
|| rfc1522_encode(d
, dlen
,
3141 (unsigned char *)value
, charset
) != value
)
3142 return(rfc1522_encode(d
, dlen
, s
, charset
));
3144 strncpy(d
, value
, dlen
-1);
3152 * pine_address_line - write a header field containing addresses,
3153 * one by one (so there's no buffer limit), and
3154 * wrapping where necessary.
3155 * Note: we use c-client functions to properly build the text string,
3156 * but have to screw around with pointers to fool c-client functions
3157 * into not blatting all the text into a single buffer. Yeah, I know.
3160 pine_address_line(char *field
, METAENV
*header
, struct mail_address
*alist
,
3161 soutr_t f
, void *s
, int writehdr
, int localcopy
)
3163 char tmp
[MAX_SINGLE_ADDR
], *tmpptr
= NULL
;
3164 size_t alloced
= 0, sz
;
3165 char *delim
, *ptmp
, *mtmp
, buftmp
[MAILTMPLEN
];
3166 char *converted
, *cs
;
3169 int in_group
= 0, was_start_of_group
= 0, fix_lcc
= 0, failed
= 0;
3171 static char comma
[] = ", ";
3172 static char end_group
[] = ";";
3173 #define no_comma (&comma[1])
3175 if(!alist
) /* nothing in field! */
3178 if(!alist
->host
&& alist
->mailbox
){ /* c-client group convention */
3180 was_start_of_group
++;
3181 /* encode mailbox of group */
3182 mtmp
= alist
->mailbox
;
3184 snprintf(buftmp
, sizeof(buftmp
), "%s", mtmp
);
3185 buftmp
[sizeof(buftmp
)-1] = '\0';
3186 converted
= utf8_to_charset(buftmp
, cs
= posting_characterset(buftmp
, NULL
, HdrText
), 0);
3188 alist
->mailbox
= cpystr(rfc1522_encode(tmp_20k_buf
, SIZEOF_20KBUF
,
3189 (unsigned char *) converted
, cs
));
3190 if(converted
&& converted
!= buftmp
)
3191 fs_give((void **) &converted
);
3202 ptmp
= alist
->personal
; /* remember personal name */
3203 /* make sure personal name is encoded */
3205 snprintf(buftmp
, sizeof(buftmp
), "%s", ptmp
);
3206 buftmp
[sizeof(buftmp
)-1] = '\0';
3207 converted
= utf8_to_charset(buftmp
, cs
= posting_characterset(buftmp
, NULL
, HdrText
), 0);
3209 alist
->personal
= cpystr(rfc1522_encode(tmp_20k_buf
, SIZEOF_20KBUF
,
3210 (unsigned char *) converted
, cs
));
3211 if(converted
&& converted
!= buftmp
)
3212 fs_give((void **) &converted
);
3221 alist
->next
= NULL
; /* digest only first address! */
3223 /* use automatic buffer unless it isn't big enough */
3224 if((alloced
= est_size(alist
)) > sizeof(tmp
)){
3225 tmpptr
= (char *)fs_get(alloced
);
3233 rbuf
.f
= dummy_soutr
;
3237 rbuf
.end
= tmpptr
+sz
-1;
3238 rfc822_output_address_line(&rbuf
, field
,
3239 (header
&& header
->env
&& header
->env
->remail
) ? LONGT
: 0L, alist
, NULL
);
3242 alist
->next
= atmp
; /* restore pointer to next addr */
3244 if(alist
->personal
&& alist
->personal
!= ptmp
)
3245 fs_give((void **) &alist
->personal
);
3247 alist
->personal
= ptmp
; /* in case it changed, restore name */
3250 if(alist
->mailbox
&& alist
->mailbox
!= mtmp
)
3251 fs_give((void **) &alist
->mailbox
);
3253 alist
->mailbox
= mtmp
;
3256 if((count
= strlen(tmpptr
)) > 2){ /* back over CRLF */
3258 tmpptr
[count
] = '\0';
3262 * If there is no sending_stream and we are writing the Lcc header,
3263 * then we are piping it to sendmail -t which expects it to be a bcc,
3266 * When we write it to the fcc or postponed (the lmc.so),
3267 * we want it to be lcc, not bcc, so we put it back.
3269 if(!sending_stream
&& writehdr
&& struncmp("lcc:", tmpptr
, 4) == 0)
3272 if(writehdr
&& f
&& *tmpptr
){
3276 failed
= !(*f
)(s
, tmpptr
);
3284 if(localcopy
&& lmc
.so
&&
3285 !lmc
.all_written
&& *tmpptr
&& !so_puts(lmc
.so
, tmpptr
))
3288 for(alist
= atmp
; alist
; alist
= alist
->next
){
3290 /* account for c-client's representation of group names */
3292 if(!alist
->host
){ /* end of group */
3294 was_start_of_group
= 0;
3296 * Rfc822_write_address no longer writes out the end of group
3297 * unless the whole group address is passed to it, so we do
3302 else if(!localcopy
|| !lmc
.so
|| lmc
.all_written
)
3305 /* start of new group, print phrase below */
3306 else if(!alist
->host
&& alist
->mailbox
){
3308 was_start_of_group
++;
3311 /* no comma before first address in group syntax */
3312 if(was_start_of_group
&& alist
->host
){
3314 was_start_of_group
= 0;
3317 /* write delimiter */
3318 if(((!in_group
||was_start_of_group
) && writehdr
&& f
&& !(*f
)(s
, delim
))
3319 || (localcopy
&& lmc
.so
&& !lmc
.all_written
3320 && !so_puts(lmc
.so
, delim
)))
3323 ptmp
= alist
->personal
; /* remember personal name */
3324 snprintf(buftmp
, sizeof(buftmp
), "%.200s", ptmp
? ptmp
: "");
3325 buftmp
[sizeof(buftmp
)-1] = '\0';
3326 converted
= utf8_to_charset(buftmp
, cs
= posting_characterset(buftmp
, NULL
, HdrText
), 0);
3328 alist
->personal
= cpystr(rfc1522_encode(tmp_20k_buf
, SIZEOF_20KBUF
,
3329 (unsigned char *) converted
, cs
));
3330 if(converted
&& converted
!= buftmp
)
3331 fs_give((void **) &converted
);
3339 alist
->next
= NULL
; /* tie off linked list */
3340 if((i
= est_size(alist
)) > MAX(sizeof(tmp
), alloced
)){
3343 fs_resize((void **)&tmpptr
, alloced
);
3347 /* make sure we don't write out group end with rfc822_write_address */
3348 if(alist
->host
|| alist
->mailbox
){
3349 rbuf
.f
= dummy_soutr
;
3353 rbuf
.end
= tmpptr
+sz
-1;
3354 rfc822_output_address_list(&rbuf
, alist
, 0L, NULL
);
3358 alist
->next
= atmp
; /* restore next pointer */
3360 if(alist
->personal
&& alist
->personal
!= ptmp
)
3361 fs_give((void **) &alist
->personal
);
3363 alist
->personal
= ptmp
; /* in case it changed, restore name */
3367 * With group syntax addresses we no longer have two identical
3368 * streams of output. Instead, for the fcc/postpone copy we include
3369 * all of the addresses inside the :; of the group, and for the
3370 * mail we're sending we don't include them. That means we aren't
3371 * correctly keeping track of the column to wrap in, below. That is,
3372 * we are keeping track of the fcc copy but we aren't keeping track
3373 * of the regular copy. It could result in too long or too short
3374 * lines. Should almost never come up since group addresses are almost
3375 * never followed by other addresses in the same header, and even
3376 * when they are, you have to go out of your way to get the headers
3379 if(count
+ 2 + (i
= strlen(tmpptr
)) > 78){ /* wrap long lines... */
3381 if((!in_group
&& writehdr
&& f
&& !(*f
)(s
, "\015\012 "))
3382 || (localcopy
&& lmc
.so
&& !lmc
.all_written
&&
3383 !so_puts(lmc
.so
, "\015\012 ")))
3389 if(((!in_group
|| was_start_of_group
)
3390 && writehdr
&& *tmpptr
&& f
&& !(*f
)(s
, tmpptr
))
3391 || (localcopy
&& lmc
.so
&& !lmc
.all_written
3392 && *tmpptr
&& !so_puts(lmc
.so
, tmpptr
)))
3397 if(tmpptr
&& tmpptr
!= tmp
)
3398 fs_give((void **)&tmpptr
);
3403 return((writehdr
&& f
? (*f
)(s
, "\015\012") : 1)
3404 && ((localcopy
&& lmc
.so
3405 && !lmc
.all_written
) ? so_puts(lmc
.so
, "\015\012") : 1));
3410 * mutated pine version of c-client's rfc822_header() function.
3411 * changed to call pine-wrapped header and address functions
3412 * so we don't have to limit the header size to a fixed buffer.
3413 * This function also calls pine's body_header write function
3414 * because encoding is delayed until output_body() is called.
3417 pine_rfc822_header(METAENV
*header
, struct mail_bodystruct
*body
, soutr_t f
, void *s
)
3422 if(header
->env
->remail
){ /* if remailing */
3423 long i
= strlen (header
->env
->remail
);
3424 if(i
> 4 && header
->env
->remail
[i
-4] == '\015')
3425 header
->env
->remail
[i
-2] = '\0'; /* flush extra blank line */
3427 if((f
&& !(*f
)(s
, header
->env
->remail
))
3428 || (lmc
.so
&& !lmc
.all_written
3429 && !so_puts(lmc
.so
, header
->env
->remail
)))
3430 return(0L); /* start with remail header */
3434 for(pf
= header
->sending_order
[j
]; pf
; pf
= header
->sending_order
[++j
]){
3437 * Warning: This is confusing. The 2nd to last argument used to
3438 * be just pf->writehdr. We want Bcc lines to be written out
3439 * if we are handing off to a sendmail temp file but not if we
3440 * are talking smtp, so bcc's writehdr is set to 0 and
3441 * pine_address_line was sending if writehdr OR !sending_stream.
3442 * That works as long as we want to write everything when
3443 * !sending_stream (an mta handoff to sendmail). But then we
3444 * added the undisclosed recipients line which should only get
3445 * written if writehdr is set, and not when we pass to a
3446 * sendmail temp file. So pine_address_line has been changed
3447 * so it bases its decision solely on the writehdr passed to it,
3448 * and the logic that worries about Bcc and sending_stream
3449 * was moved up to the caller (here) to decide when to set it.
3452 * undisclosed recipients:; This will just be written
3453 * if writehdr was set and not
3454 * otherwise, nothing magical.
3455 *** We may want to change this, because sendmail -t doesn't handle
3456 *** the empty group syntax well unless it has been configured to
3457 *** do so. It isn't configured by default, or in any of the
3458 *** sendmail v8 configs. So we may want to not write this line
3459 *** if we're doing an mta_handoff (!sending_stream).
3461 * !sending_stream (which means a handoff to a sendmail -t)
3462 * bcc or lcc both set the arg so they'll get written
3463 * (There is also Lcc hocus pocus in pine_address_line
3464 * which converts the Lcc: to Bcc: for sendmail
3466 * sending_stream (which means an smtp handoff)
3467 * bcc and lcc will never have writehdr set, so
3468 * will never be written (They both do have rcptto set,
3469 * so they both do cause RCPT TO commands.)
3471 * The localcopy is independent of sending_stream and is just
3472 * written if it is set for all of these.
3475 if(!pine_address_line(pf
->name
,
3477 pf
->addr
? *pf
->addr
: NULL
,
3480 (!strucmp("bcc",pf
->name
? pf
->name
: "")
3481 || !strucmp("Lcc",pf
->name
? pf
->name
: ""))
3492 if(!pine_header_line(pf
->name
, header
,
3493 pf
->text
? *pf
->text
: NULL
,
3494 f
, s
, pf
->writehdr
, pf
->localcopy
))
3500 q_status_message1(SM_ORDER
,3,7,"Unknown header type: %.200s",
3507 #if (defined(DOS) || defined(OS2)) && !defined(NOAUTH)
3509 * Add comforting "X-" header line indicating what sort of
3510 * authenticity the receiver can expect...
3512 if(F_OFF(F_DISABLE_SENDER
, ps_global
)){
3514 char sstring
[MAILTMPLEN
], *label
; /* place to write */
3516 int i
, anonymous
= 1;
3518 for(i
= 0; anonymous
&& i
< ps_global
->s_pool
.nstream
; i
++){
3519 m
= ps_global
->s_pool
.streams
[i
];
3520 if(m
&& sp_flagged(m
, SP_LOCKED
)
3521 && mail_valid_net_parse(m
->mailbox
, &netmbox
)
3522 && !netmbox
.anoflag
)
3527 char last_char
= netmbox
.host
[strlen(netmbox
.host
) - 1],
3528 *user
= (*netmbox
.user
)
3530 : cached_user_name(netmbox
.mailbox
);
3531 snprintf(sstring
, sizeof(sstring
), "%.300s@%s%.300s%s", user
? user
: "NULL",
3532 isdigit((unsigned char)last_char
) ? "[" : "",
3534 isdigit((unsigned char) last_char
) ? "]" : "");
3535 sstring
[sizeof(sstring
)-1] = '\0';
3536 label
= "X-X-Sender"; /* Jeez. */
3537 if(F_ON(F_USE_SENDER_NOT_X
,ps_global
))
3541 strncpy(sstring
,"UNAuthenticated Sender", sizeof(sstring
));
3542 sstring
[sizeof(sstring
)-1] = '\0';
3543 label
= "X-Warning";
3546 if(!pine_header_line(label
, header
, sstring
, f
, s
, 1, 1))
3551 if(body
&& !header
->env
->remail
){ /* not if remail or no body */
3552 if((f
&& !(*f
)(s
, MIME_VER
))
3553 || (lmc
.so
&& !lmc
.all_written
&& !so_puts(lmc
.so
, MIME_VER
))
3554 || !pine_write_body_header(body
, f
, s
))
3557 else{ /* write terminating newline */
3558 if((f
&& !(*f
)(s
, "\015\012"))
3559 || (lmc
.so
&& !lmc
.all_written
&& !so_puts(lmc
.so
, "\015\012")))
3568 * pine_rfc822_output - pine's version of c-client call. Necessary here
3569 * since we're not using its structures as intended!
3572 pine_rfc822_output(METAENV
*header
, struct mail_bodystruct
*body
, soutr_t f
, void *s
)
3577 dprint((4, "-- pine_rfc822_output\n"));
3579 we_cancel
= busy_cue(NULL
, NULL
, 1);
3580 pine_encode_body(body
); /* encode body as necessary */
3581 /* build and output RFC822 header, output body */
3582 retval
= pine_rfc822_header(header
, body
, f
, s
)
3583 && (body
? pine_rfc822_output_body(body
, f
, s
) : 1L);
3586 cancel_busy_cue(-1);
3593 * post_rfc822_output - cloak for pine's 822 output routine. Since
3594 * we can't pass opaque envelope thru c-client posting
3595 * logic, we need to wrap the real output inside
3596 * something that c-client knows how to call.
3599 post_rfc822_output(char *tmp
,
3601 struct mail_bodystruct
*body
,
3606 return(pine_rfc822_output(send_header
, body
, f
, s
));
3611 * posting_characterset- determine what transliteration is reasonable
3612 * for posting the given non-ascii message data.
3614 * preferred_charset is the charset the original data was labeled in.
3615 * If we can keep that we do.
3617 * Returns: always returns the preferred character set.
3620 posting_characterset(void *data
, char *preferred_charset
, MsgPart mp
)
3622 unsigned long *charsetmap
= NULL
;
3623 unsigned long validbitmap
;
3624 static char *ascii
= "us-ascii";
3625 static char *utf8
= "utf-8";
3628 if(!ps_global
->post_utf8
){
3633 UCS
*ucs
= NULL
, *ucsp
= NULL
;
3635 text
= (char *) data
;
3637 /* convert text in header to UCS characters */
3639 ucsp
= ucs
= utf8_to_ucs4_cpystr(text
);
3645 * After the while loop is done the validbitmap has
3646 * a 1 bit for all the character sets that can
3647 * represent all of the characters of this header.
3649 charsetmap
= init_charsetchecker(preferred_charset
);
3652 if (ucs
!= NULL
) fs_give((void **) &ucs
);
3657 while((validbitmap
& ~0x1) && (*ucsp
)){
3659 fs_give((void **) &ucs
);
3663 validbitmap
&= charsetmap
[(unsigned long) (*ucsp
++)];
3666 fs_give((void **) &ucs
);
3668 notcjk
= validbitmap
& 0x1;
3669 validbitmap
&= ~0x1;
3675 struct mail_bodystruct
*body
= NULL
;
3676 STORE_S
*the_text
= NULL
;
3682 cbuf
.cbuf
[0] = '\0';
3683 cbuf
.cbufp
= cbuf
.cbuf
;
3684 cbuf
.cbufend
= cbuf
.cbuf
;
3686 body
= (struct mail_bodystruct
*) data
;
3688 if(body
&& body
->type
== TYPEMULTIPART
)
3689 body
= &body
->nested
.part
->body
;
3691 if(body
&& body
->type
== TYPETEXT
)
3692 the_text
= (STORE_S
*) body
->contents
.text
.data
;
3697 so_seek(the_text
, 0L, 0); /* rewind */
3699 charsetmap
= init_charsetchecker(preferred_charset
);
3707 * Read a stream of UTF-8 characters from the_text
3708 * and convert them to UCS-4 characters for the translatable
3711 while((validbitmap
& ~0x1) && so_readc(&c
, the_text
)){
3712 if((outchars
= utf8_to_ucs4_oneatatime(c
, &cbuf
, &ucs
, NULL
)) > 0){
3713 /* got a ucs character */
3717 validbitmap
&= charsetmap
[(unsigned long) ucs
];
3721 notcjk
= validbitmap
& 0x1;
3722 validbitmap
&= ~0x1;
3728 /* user chooses something other than UTF-8 */
3729 if(strucmp(ps_global
->posting_charmap
, utf8
)){
3731 * If we're to post in other than UTF-8, and it can be
3732 * transliterated without losing fidelity, do it.
3736 /* if ascii works, always use that */
3737 if(representable_in_charset(validbitmap
, ascii
))
3740 /* does the user's posting character set work? */
3741 if(representable_in_charset(validbitmap
, ps_global
->posting_charmap
))
3742 return(ps_global
->posting_charmap
);
3744 /* this is the charset the message we are replying to was in */
3745 if(preferred_charset
3746 && strucmp(preferred_charset
, ascii
)
3747 && representable_in_charset(validbitmap
, preferred_charset
))
3748 return(preferred_charset
);
3750 /* else, use UTF-8 */
3753 /* user chooses nothing, going with the default */
3754 else if(ps_global
->vars
[V_POST_CHAR_SET
].main_user_val
.p
== NULL
3755 && ps_global
->vars
[V_POST_CHAR_SET
].post_user_val
.p
== NULL
3756 && ps_global
->vars
[V_POST_CHAR_SET
].fixed_val
.p
== NULL
){
3757 char *most_preferred
;
3760 * In this case the user didn't specify a posting character set
3761 * and we will choose the most-specific one from our list.
3765 if(representable_in_charset(validbitmap
, ascii
))
3768 /* Can we keep the original from the message we're replying to? */
3769 if(preferred_charset
3770 && strucmp(preferred_charset
, ascii
)
3771 && representable_in_charset(validbitmap
, preferred_charset
))
3772 return(preferred_charset
);
3774 /* choose the best of the rest */
3775 most_preferred
= most_preferred_charset(validbitmap
);
3780 * If the text we're labeling contains something like
3781 * smart quotes but no CJK characters, then instead of
3782 * labeling it as ISO-2022-JP we want to use UTF-8.
3787 cs
= utf8_charset(most_preferred
);
3789 || cs
->script
== SC_CHINESE_SIMPLIFIED
3790 || cs
->script
== SC_CHINESE_TRADITIONAL
3791 || cs
->script
== SC_JAPANESE
3792 || cs
->script
== SC_KOREAN
)
3796 return(most_preferred
);
3798 /* user explicitly chooses UTF-8 */
3800 /* if ascii works, always use that */
3801 if(representable_in_charset(validbitmap
, ascii
))
3804 /* else, use UTF-8 */
3813 static char **charsetlist
= NULL
;
3814 static int items_in_charsetlist
= 0;
3815 static unsigned long *charsetmap
= NULL
;
3817 static char *downgrades
[] = {
3837 init_charsetchecker(char *preferred_charset
)
3839 int i
, count
= 0, reset
= 0;
3840 char *ascii
= "US-ASCII";
3841 char *utf8
= "UTF-8";
3844 * When user doesn't set a posting character set posting_charmap ends up
3845 * set to UTF-8. That also happens if user sets UTF-8 explicitly.
3846 * That's where the strange set of if-else's come from.
3849 /* user chooses something other than UTF-8 */
3850 if(strucmp(ps_global
->posting_charmap
, utf8
)){
3851 count
++; /* US-ASCII */
3852 if(items_in_charsetlist
< 1 || strucmp(charsetlist
[0], ascii
))
3855 /* if posting_charmap is valid, include it in list */
3856 if(ps_global
->posting_charmap
&& ps_global
->posting_charmap
[0]
3857 && strucmp(ps_global
->posting_charmap
, ascii
)
3858 && strucmp(ps_global
->posting_charmap
, utf8
)
3859 && utf8_charset(ps_global
->posting_charmap
)){
3862 && (items_in_charsetlist
< count
3863 || strucmp(charsetlist
[count
-1], ps_global
->posting_charmap
)))
3867 if(preferred_charset
&& preferred_charset
[0]
3868 && strucmp(preferred_charset
, ascii
)
3869 && strucmp(preferred_charset
, utf8
)
3870 && (count
< 2 || strucmp(preferred_charset
, ps_global
->posting_charmap
))){
3873 && (items_in_charsetlist
< count
3874 || strucmp(charsetlist
[count
-1], preferred_charset
)))
3878 if(items_in_charsetlist
!= count
)
3883 free_list_array(&charsetlist
);
3885 items_in_charsetlist
= count
;
3886 charsetlist
= (char **) fs_get((count
+ 1) * sizeof(char *));
3889 charsetlist
[i
++] = cpystr(ascii
);
3891 if(ps_global
->posting_charmap
&& ps_global
->posting_charmap
[0]
3892 && strucmp(ps_global
->posting_charmap
, ascii
)
3893 && strucmp(ps_global
->posting_charmap
, utf8
)
3894 && utf8_charset(ps_global
->posting_charmap
))
3895 charsetlist
[i
++] = cpystr(ps_global
->posting_charmap
);
3897 if(preferred_charset
&& preferred_charset
[0]
3898 && strucmp(preferred_charset
, ascii
)
3899 && strucmp(preferred_charset
, utf8
)
3900 && (i
< 2 || strucmp(preferred_charset
, ps_global
->posting_charmap
)))
3901 charsetlist
[i
++] = cpystr(preferred_charset
);
3903 charsetlist
[i
] = NULL
;
3906 /* user chooses nothing, going with the default */
3907 else if(ps_global
->vars
[V_POST_CHAR_SET
].main_user_val
.p
== NULL
3908 && ps_global
->vars
[V_POST_CHAR_SET
].post_user_val
.p
== NULL
3909 && ps_global
->vars
[V_POST_CHAR_SET
].fixed_val
.p
== NULL
){
3910 int add_preferred
= 0;
3912 /* does preferred_charset have to be added to the list? */
3913 if(preferred_charset
&& preferred_charset
[0] && strucmp(preferred_charset
, utf8
)){
3915 for(i
= 0; add_preferred
&& i
< sizeof(downgrades
)/sizeof(downgrades
[0]); i
++)
3916 if(!strucmp(downgrades
[i
], preferred_charset
))
3921 /* existing list is right size already */
3922 if(items_in_charsetlist
== sizeof(downgrades
)/sizeof(downgrades
[0]) + 1){
3923 /* just check to see if last list item is the preferred_charset */
3924 if(strucmp(preferred_charset
, charsetlist
[items_in_charsetlist
-1])){
3927 fs_give((void **) &charsetlist
[items_in_charsetlist
-1]);
3928 charsetlist
[items_in_charsetlist
-1] = cpystr(preferred_charset
);
3934 free_list_array(&charsetlist
);
3936 count
= sizeof(downgrades
)/sizeof(downgrades
[0]) + 1;
3937 items_in_charsetlist
= count
;
3938 charsetlist
= (char **) fs_get((count
+ 1) * sizeof(char *));
3939 for(i
= 0; i
< sizeof(downgrades
)/sizeof(downgrades
[0]); i
++)
3940 charsetlist
[i
] = cpystr(downgrades
[i
]);
3942 charsetlist
[i
++] = cpystr(preferred_charset
);
3943 charsetlist
[i
] = NULL
;
3947 /* if list is same size as downgrades, consider it good */
3948 if(items_in_charsetlist
!= sizeof(downgrades
)/sizeof(downgrades
[0]))
3953 free_list_array(&charsetlist
);
3955 count
= sizeof(downgrades
)/sizeof(downgrades
[0]);
3956 items_in_charsetlist
= count
;
3957 charsetlist
= (char **) fs_get((count
+ 1) * sizeof(char *));
3958 for(i
= 0; i
< sizeof(downgrades
)/sizeof(downgrades
[0]); i
++)
3959 charsetlist
[i
] = cpystr(downgrades
[i
]);
3961 charsetlist
[i
] = NULL
;
3965 /* user explicitly chooses UTF-8 */
3967 /* include possibility of ascii even if they explicitly ask for UTF-8 */
3968 count
++; /* US-ASCII */
3969 if(items_in_charsetlist
< 1 || strucmp(charsetlist
[0], ascii
))
3972 if(items_in_charsetlist
!= count
)
3977 free_list_array(&charsetlist
);
3979 /* the list is just ascii and nothing else */
3980 items_in_charsetlist
= count
;
3981 charsetlist
= (char **) fs_get((count
+ 1) * sizeof(char *));
3984 charsetlist
[i
++] = cpystr(ascii
);
3985 charsetlist
[i
] = NULL
;
3992 fs_give((void **) &charsetmap
);
3995 charsetmap
= utf8_csvalidmap(charsetlist
);
4004 free_charsetchecker(void)
4007 free_list_array(&charsetlist
);
4009 items_in_charsetlist
= 0;
4012 fs_give((void **) &charsetmap
);
4017 representable_in_charset(unsigned long validbitmap
, char *charset
)
4019 int i
, done
= 0, ret
= 0;
4022 if(!(charset
&& charset
[0]))
4025 if(!strucmp(charset
, "UTF-8"))
4028 for(i
= 0; !done
&& i
< items_in_charsetlist
; i
++){
4029 if(!strucmp(charset
, charsetlist
[i
])){
4043 most_preferred_charset(unsigned long validbitmap
)
4049 if(!(validbitmap
&& items_in_charsetlist
> 0))
4052 /* careful, find_rightmost_bit modifies the bitmap */
4054 rm
= find_rightmost_bit(&bm
);
4055 index
= MIN(MAX(rm
-1,0), items_in_charsetlist
-1);
4057 return(charsetlist
[index
]);
4061 remove_parameter(PARAMETER
**param
, char *paramname
)
4065 if(param
== NULL
|| *param
== NULL
4066 || paramname
== NULL
|| *paramname
== '\0')
4070 pm
!= NULL
&& pm
->attribute
!= NULL
&& strucmp(pm
->attribute
, paramname
);
4076 for(qm
= &om
; qm
->next
!= pm
; qm
= qm
->next
);
4077 qm
->next
= copy_parameters(pm
->next
);
4078 mail_free_body_parameter(&pm
);
4085 * Set parameter to new value.
4088 set_parameter(PARAMETER
**param
, char *paramname
, char *new_value
)
4092 if(!param
|| !(paramname
&& *paramname
))
4096 pm
= (*param
) = mail_newbody_parameter();
4097 pm
->attribute
= cpystr(paramname
);
4103 (nomatch
=strucmp(pm
->attribute
, paramname
)) && pm
->next
!= NULL
;
4105 ;/* searching for paramname parameter */
4107 if(nomatch
){ /* add charset parameter */
4108 pm
->next
= mail_newbody_parameter();
4110 pm
->attribute
= cpystr(paramname
);
4112 /* else pm is existing paramname parameter */
4116 if(!(pm
->value
&& new_value
&& !strcmp(pm
->value
, new_value
))){
4118 fs_give((void **) &pm
->value
);
4121 pm
->value
= cpystr(new_value
);
4127 /*----------------------------------------------------------------------
4128 Remove the leading digits from SMTP error messages
4131 tidy_smtp_mess(char *error
, char *printstring
, char *outbuf
, size_t outbuflen
)
4133 while(isdigit((unsigned char)*error
) || isspace((unsigned char)*error
) ||
4134 (*error
== '.' && isdigit((unsigned char)*(error
+1))))
4137 snprintf(outbuf
, outbuflen
, printstring
, error
);
4138 outbuf
[outbuflen
-1] = '\0';
4144 * Local globals pine's body output routine needs
4147 static TCPSTREAM
*l_stream
;
4148 static unsigned c_in_buf
= 0;
4151 * def to make our pipe write's more friendly
4154 #if PIPE_MAX > 20000
4160 #define PIPE_MAX 1024
4165 * l_flust_net - empties gf_io terminal function's buffer
4168 l_flush_net(int force
)
4170 if(c_in_buf
&& c_in_buf
< SIZEOF_20KBUF
){
4171 char *p
= &tmp_20k_buf
[0], *lp
= NULL
, c
= '\0';
4173 tmp_20k_buf
[c_in_buf
] = '\0';
4176 * The start of each write is expected to be the start of a
4177 * "record" (i.e., a CRLF terminated line). Make sure that is true
4178 * else we might screw up SMTP dot quoting...
4180 for(p
= tmp_20k_buf
, lp
= NULL
;
4181 (p
= strstr(p
, "\015\012")) != NULL
;
4186 if(!lp
&& c_in_buf
> 2) /* no CRLF! */
4187 for(p
= &tmp_20k_buf
[c_in_buf
] - 2;
4188 p
> &tmp_20k_buf
[0] && *p
== '.';
4189 p
--) /* find last non-dot */
4192 if(lp
&& *lp
&& lp
>= tmp_20k_buf
&& lp
< tmp_20k_buf
+ SIZEOF_20KBUF
){
4193 /* snippet remains */
4199 if((l_f
&& !(*l_f
)(l_stream
, tmp_20k_buf
))
4200 || (lmc
.so
&& !lmc
.all_written
4201 && !(lmc
.text_only
&& lmc
.text_written
)
4202 && !so_puts(lmc
.so
, tmp_20k_buf
)))
4206 if(lp
&& lp
>= tmp_20k_buf
&& lp
< tmp_20k_buf
+ SIZEOF_20KBUF
&& (*lp
= c
)) /* Shift text left? */
4207 while(c_in_buf
< SIZEOF_20KBUF
&& (tmp_20k_buf
[c_in_buf
] = *lp
))
4216 * l_putc - gf_io terminal function that calls smtp's soutr_t function.
4222 if(c_in_buf
>= 0 && c_in_buf
< SIZEOF_20KBUF
)
4223 tmp_20k_buf
[c_in_buf
++] = (char) c
;
4225 return((c_in_buf
>= PIPE_MAX
) ? l_flush_net(FALSE
) : TRUE
);
4231 * pine_rfc822_output_body - pine's version of c-client call. Again,
4232 * necessary since c-client doesn't know about how
4233 * we're treating attachments
4236 pine_rfc822_output_body(struct mail_bodystruct
*body
, soutr_t f
, void *s
)
4241 char *t
, *cookie
= NIL
, *encode_error
;
4242 char tmp
[MAILTMPLEN
];
4243 int add_trailing_crlf
;
4247 dprint((4, "-- pine_rfc822_output_body: %d\n",
4248 body
? body
->type
: 0));
4250 bodyso
= (STORE_S
*) body
->contents
.text
.data
;
4252 if(body
->type
== TYPEMULTIPART
) { /* multipart gets special handling */
4253 part
= body
->nested
.part
; /* first body part */
4255 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
4256 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
4257 if (!cookie
) cookie
= "-"; /* yucky default */
4260 * Output a bit of text before the first multipart delimiter
4261 * to warn unsuspecting users of non-mime-aware ua's that
4262 * they should expect weirdness. We do not add this when signing a
4263 * message, though...
4266 if(!ps_global
->smime
|| !ps_global
->smime
->do_sign
)
4268 if(f
&& !(*f
)(s
, " This message is in MIME format. The first part should be readable text,\015\012 while the remaining parts are likely unreadable without MIME-aware tools.\015\012\015\012"))
4271 do { /* for each part */
4273 snprintf (tmp
, sizeof(tmp
), "--%s\015\012", cookie
);
4274 tmp
[sizeof(tmp
)-1] = '\0';
4275 /* append cookie,mini-hdr,contents */
4276 if((f
&& !(*f
)(s
, tmp
))
4277 || (lmc
.so
&& !lmc
.all_written
&& !so_puts(lmc
.so
, tmp
))
4278 || !pine_write_body_header(&part
->body
,f
,s
)
4279 || !pine_rfc822_output_body (&part
->body
,f
,s
))
4281 #if 0 /* temporariy disable this code */
4282 if(part
== body
->nested
.part
4284 && ps_global
->smime
->do_sign
4285 && ((f
&& !(*f
)(s
, "\015\012"))
4286 || (lmc
.so
&& !lmc
.all_written
&& !so_puts(lmc
.so
, "\015\012"))))
4289 } while ((part
= part
->next
) != NULL
); /* until done */
4290 /* output trailing cookie */
4291 snprintf (t
= tmp
, sizeof(tmp
), "--%s--",cookie
);
4292 tmp
[sizeof(tmp
)-1] = '\0';
4294 if(ps_global
->smime
&& ps_global
->smime
->do_sign
4295 && strlen(tmp
) < sizeof(tmp
)-2)
4296 strncat(tmp
, "\015\012", sizeof(tmp
) - strlen(tmp
) - 1);
4298 if(lmc
.so
&& !lmc
.all_written
){
4300 so_puts(lmc
.so
, "\015\012");
4303 return(f
? ((*f
) (s
,t
) && (*f
) (s
,"\015\012")) : 1);
4306 l_f
= f
; /* set up for writing chars... */
4307 l_stream
= s
; /* out other end of pipe... */
4309 dprint((4, "-- pine_rfc822_output_body: segment %ld bytes\n",
4313 gf_set_so_readc(&gc
, bodyso
);
4318 * Don't add trailing line if it is ExternalText, which already guarantees
4319 * a trailing newline.
4321 add_trailing_crlf
= !(bodyso
->src
== ExternalText
);
4323 so_seek(bodyso
, 0L, 0);
4325 if(body
->type
!= TYPEMESSAGE
){ /* NOT encapsulated message */
4328 if(body
->type
== TYPETEXT
4329 && so_attr(bodyso
, "edited", NULL
)
4330 && (charset
= parameter_val(body
->parameter
, "charset"))){
4331 if(strucmp(charset
, "utf-8") && strucmp(charset
, "us-ascii")){
4332 if(!strucmp(charset
, "iso-2022-jp")){
4334 gf_link_filter(gf_line_test
,
4335 gf_line_test_opt(translate_utf8_to_2022_jp
,&ljp
));
4338 void *table
= utf8_rmap(charset
);
4341 gf_link_filter(gf_convert_utf8_charset
,
4342 gf_convert_utf8_charset_opt(table
,0));
4345 /* else, just send it? */
4346 set_parameter(&body
->parameter
, "charset", "UTF-8");
4351 fs_give((void **)&charset
);
4355 * Convert text pieces to canonical form
4356 * BEFORE applying any encoding (rfc1341: appendix G)...
4357 * NOTE: almost all filters expect CRLF newlines
4359 if(body
->type
== TYPETEXT
4360 && body
->encoding
!= ENCBASE64
4361 && !so_attr(bodyso
, "rawbody", NULL
)){
4362 gf_link_filter(gf_local_nvtnl
, NULL
);
4365 switch (body
->encoding
) { /* all else needs filtering */
4366 case ENC8BIT
: /* encode 8BIT into QUOTED-PRINTABLE */
4367 gf_link_filter(gf_8bit_qp
, NULL
);
4370 case ENCBINARY
: /* encode binary into BASE64 */
4371 gf_link_filter(gf_binary_b64
, NULL
);
4374 default: /* otherwise text */
4379 if((encode_error
= gf_pipe(gc
, l_putc
)) != NULL
){ /* shove body part down pipe */
4380 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
4381 _("Encoding Error \"%s\""), encode_error
);
4382 display_message('x');
4385 gf_clear_so_readc(bodyso
);
4387 if(encode_error
|| !l_flush_net(TRUE
))
4390 send_bytes_sent
+= gf_bytes_piped();
4391 so_release((STORE_S
*)body
->contents
.text
.data
);
4393 if(lmc
.so
&& !lmc
.all_written
&& lmc
.text_only
){
4394 if(lmc
.text_written
){ /* we have some splainin' to do */
4395 char tmp
[MAILTMPLEN
];
4398 if(!(so_puts(lmc
.so
,_("The following attachment was sent,\015\012"))
4399 && so_puts(lmc
.so
,_("but NOT saved in the Fcc copy:\015\012"))))
4403 * BUG: If this name is not ascii it's going to cause trouble.
4405 name
= parameter_val(body
->parameter
, "name");
4406 snprintf(tmp
, sizeof(tmp
),
4407 " A %s/%s%s%s%s segment of about %s bytes.\015\012",
4408 body_type_names(body
->type
),
4409 body
->subtype
? body
->subtype
: "Unknown",
4410 name
? " (Name=\"" : "",
4413 comatose(body
->size
.bytes
));
4414 tmp
[sizeof(tmp
)-1] = '\0';
4416 fs_give((void **)&name
);
4418 if(!so_puts(lmc
.so
, tmp
))
4421 else /* suppress everything after first text part */
4422 lmc
.text_written
= (body
->type
== TYPETEXT
4424 || !strucmp(body
->subtype
, "plain")));
4427 if(add_trailing_crlf
)
4428 return((f
? (*f
)(s
, "\015\012") : 1) /* output final stuff */
4429 && ((lmc
.so
&& !lmc
.all_written
) ? so_puts(lmc
.so
,"\015\012") : 1));
4435 ToLower(char *s
, char *t
)
4439 for(i
= 0; s
!= NULL
&& s
[i
] != '\0'; i
++)
4440 t
[i
] = s
[i
] + ((s
[i
] >= 'A' && s
[i
] <= 'Z') ? ('a' - 'A') : 0);
4447 * pine_write_body_header - another c-client clone. This time
4448 * so the final encoding labels get set
4449 * correctly since it hasn't happened yet,
4450 * and to be paranoid about line lengths.
4452 * Returns: TRUE/nonzero on success, zero on error
4455 pine_write_body_header(struct mail_bodystruct
*body
, soutr_t f
, void *s
)
4457 char tmp
[MAILTMPLEN
];
4463 extern const char *tspecials
;
4465 if((so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
){
4466 if(!(so_puts(so
, "Content-Type: ")
4467 && so_puts(so
, ToLower(body_types
[body
->type
], tmp
))
4469 && so_puts(so
, ToLower(body
->subtype
4471 : rfc822_default_subtype (body
->type
),tmp
))))
4472 return(pwbh_finish(0, so
));
4474 if(body
->parameter
){
4475 if(!pine_write_params(body
->parameter
, so
))
4476 return(pwbh_finish(0, so
));
4478 else if ((body
->type
!= TYPEMESSAGE
4479 || (body
->subtype
&& strucmp(body
->subtype
, "RFC822")))
4480 && (!so_puts(so
, "; CHARSET=US-ASCII")))
4481 return(pwbh_finish(0, so
));
4483 if(!so_puts(so
, "\015\012"))
4484 return(pwbh_finish(0, so
));
4486 /* do not change the encoding of a TYPEMESSAGE part */
4487 if ((body
->encoding
/* note: encoding 7BIT never output! */
4488 && !(so_puts(so
, "Content-Transfer-Encoding: ")
4489 && so_puts(so
, body_encodings
[(body
->encoding
==ENCBINARY
)
4490 ? (body
->type
== TYPEMESSAGE
? ENCBINARY
: ENCBASE64
)
4491 : (body
->encoding
== ENC8BIT
)
4492 ?(body
->type
== TYPEMESSAGE
? ENC8BIT
: ENCQUOTEDPRINTABLE
)
4493 : (body
->encoding
<= ENCMAX
)
4496 && so_puts(so
, "\015\012")))
4498 * If requested, strip Content-ID headers that don't look like they
4499 * are needed. Microsoft's Outlook XP has a bug that causes it to
4500 * not show that there is an attachment when there is a Content-ID
4501 * header present on that attachment.
4503 * If user has quell-content-id turned on, don't output content-id
4504 * unless it is of type message/external-body.
4505 * Since this code doesn't look inside messages being forwarded
4506 * type message content-ids will remain as is and type multipart
4507 * alternative will remain as is. We don't create those on our
4508 * own. If we did, we'd have to worry about getting this right.
4510 || (body
->id
&& (F_OFF(F_QUELL_CONTENT_ID
, ps_global
)
4511 || (body
->type
== TYPEMESSAGE
4513 && !strucmp(body
->subtype
, "external-body")))
4514 && !(so_puts(so
, "Content-ID: ") && so_puts(so
, body
->id
)
4515 && so_puts(so
, "\015\012")))
4516 || (body
->description
4517 && strlen(body
->description
) < 5000 /* arbitrary! */
4518 && !pine_write_header_line("Content-Description: ", body
->description
, so
))
4520 && !(so_puts(so
, "Content-MD5: ")
4521 && so_puts(so
, body
->md5
)
4522 && so_puts(so
, "\015\012"))))
4523 return(pwbh_finish(0, so
));
4525 if ((stl
= body
->language
) != NULL
) {
4526 if(!so_puts(so
, "Content-Language: "))
4527 return(pwbh_finish(0, so
));
4530 if(strlen((char *)stl
->text
.data
) > 500) /* arbitrary! */
4531 return(pwbh_finish(0, so
));
4534 rbuf
.f
= dummy_soutr
;
4538 rbuf
.end
= tmp
+sizeof(tmp
)-1;
4539 rfc822_output_cat(&rbuf
, (char *)stl
->text
.data
, tspecials
);
4542 if(!so_puts(so
, tmp
)
4543 || ((stl
= stl
->next
) && !so_puts(so
, ", ")))
4544 return(pwbh_finish(0, so
));
4548 if(!so_puts(so
, "\015\012"))
4549 return(pwbh_finish(0, so
));
4552 if (body
->disposition
.type
) {
4553 if(!(so_puts(so
, "Content-Disposition: ")
4554 && so_puts(so
, body
->disposition
.type
)))
4555 return(pwbh_finish(0, so
));
4557 if(!pine_write_params(body
->disposition
.parameter
, so
))
4558 return(pwbh_finish(0, so
));
4560 if(!so_puts(so
, "\015\012"))
4561 return(pwbh_finish(0, so
));
4564 /* copy out of so, a line at a time (or less than a K)
4565 * and send it down the pike
4569 while(so_readc(&c
, so
))
4570 if((tmp
[i
++] = c
) == '\012' || i
> sizeof(tmp
) - 3){
4572 if((f
&& !(*f
)(s
, tmp
))
4573 || !lmc_body_header_line(tmp
, i
<= sizeof(tmp
) - 3))
4574 return(pwbh_finish(0, so
));
4579 /* Finally, write blank line */
4580 if((f
&& !(*f
)(s
, "\015\012")) || !lmc_body_header_finish())
4581 return(pwbh_finish(0, so
));
4583 return(pwbh_finish(i
== 0, so
)); /* better of ended on LF */
4591 * pine_write_header - convert, encode (if needed) and
4592 * write "header-name: field-body"
4595 pine_write_header_line(char *hdr
, char *val
, STORE_S
*so
)
4600 cs
= posting_characterset(val
, NULL
, HdrText
);
4601 cv
= utf8_to_charset(val
, cs
, 0);
4602 vp
= rfc1522_encode(tmp_20k_buf
, SIZEOF_20KBUF
,
4603 (unsigned char *) cv
, cs
);
4605 rv
= (so_puts(so
, hdr
) && so_puts(so
, vp
) && so_puts(so
, "\015\012"));
4608 fs_give((void **) &cv
);
4616 * pine_write_param - convert, encode and write MIME header-field parameters
4619 pine_write_params(PARAMETER
*param
, STORE_S
*so
)
4621 for(; param
; param
= param
->next
){
4624 extern const char *tspecials
;
4626 cs
= posting_characterset(param
->value
, NULL
, HdrText
);
4627 cv
= utf8_to_charset(param
->value
, cs
, 0);
4628 rv
= (so_puts(so
, "; ")
4629 && rfc2231_output(so
, param
->attribute
, cv
, (char *) tspecials
, cs
));
4631 if(cv
&& cv
!= param
->value
)
4632 fs_give((void **) &cv
);
4644 lmc_body_header_line(char *line
, int beginning
)
4646 if(lmc
.so
&& !lmc
.all_written
){
4647 if(beginning
&& lmc
.text_only
&& lmc
.text_written
4648 && (!struncmp(line
, "content-type:", 13)
4649 || !struncmp(line
, "content-transfer-encoding:", 26)
4650 || !struncmp(line
, "content-disposition:", 20))){
4652 * "comment out" the real values since our comment isn't
4653 * likely the same type, disposition nor encoding...
4655 if(!so_puts(lmc
.so
, "X-"))
4659 return(so_puts(lmc
.so
, line
));
4667 lmc_body_header_finish(void)
4669 if(lmc
.so
&& !lmc
.all_written
){
4670 if(lmc
.text_only
&& lmc
.text_written
4671 && !so_puts(lmc
.so
, "Content-Type: TEXT/PLAIN\015\012"))
4674 return(so_puts(lmc
.so
, "\015\012"));
4683 pwbh_finish(int rv
, STORE_S
*so
)
4693 * pine_free_body - c-client call wrapper so the body data pointer we
4694 * we're using in a way c-client doesn't know about
4695 * gets free'd appropriately.
4698 pine_free_body(struct mail_bodystruct
**body
)
4701 * Preempt c-client's contents.text.data clean up since we've
4702 * usurped it's meaning for our own purposes...
4704 pine_free_body_data (*body
);
4706 /* Then let c-client handle the rest... */
4707 mail_free_body(body
);
4712 * pine_free_body_data - free pine's interpretations of the body part's
4716 pine_free_body_data(struct mail_bodystruct
*body
)
4719 if(body
->type
== TYPEMULTIPART
){
4720 PART
*part
= body
->nested
.part
;
4721 do /* for each part */
4722 pine_free_body_data(&part
->body
);
4723 while ((part
= part
->next
) != NULL
); /* until done */
4725 else if(body
->contents
.text
.data
)
4726 so_give((STORE_S
**) &body
->contents
.text
.data
);
4732 send_body_size(struct mail_bodystruct
*body
)
4737 if(body
->type
== TYPEMULTIPART
) { /* multipart gets special handling */
4738 part
= body
->nested
.part
; /* first body part */
4739 do /* for each part */
4740 l
+= send_body_size(&part
->body
);
4741 while ((part
= part
->next
) != NULL
); /* until done */
4745 return(l
+ body
->size
.bytes
);
4752 int i
= (int) (((send_bytes_sent
+ gf_bytes_piped()) * 100)
4753 / send_bytes_to_send
);
4754 return(MIN(i
, 100));
4759 * pine_smtp_verbose_out - write
4762 pine_smtp_verbose_out(char *s
)
4767 if(verbose_send_output
&& s
){
4768 char *p
, last
= '\0';
4778 * The stream is opened in Unicode mode, so we need to fix the
4779 * argument to fputs.
4781 slpt
= utf8_to_lptstr((LPSTR
) s
);
4783 _fputts(slpt
, verbose_send_output
);
4784 fs_give((void **) &slpt
);
4788 _fputtc(L
'\n', verbose_send_output
);
4790 fputs(s
, verbose_send_output
);
4792 fputc('\n', verbose_send_output
);
4800 * pine_header_forbidden - is this name a "forbidden" header?
4802 * name - the header name to check
4803 * We don't allow user to change these.
4806 pine_header_forbidden(char *name
)
4809 static char *forbidden_headers
[] = {
4818 "resent-message-id",
4829 "list-help", /* rfc 2369, section 3 */
4838 for(p
= forbidden_headers
; *p
; p
++)
4839 if(!strucmp(name
, *p
))
4842 return((*p
) ? 1 : 0);
4847 * hdr_is_in_list - is there a custom value for this header?
4849 * hdr - the header name to check
4850 * custom - the list to check in
4851 * Returns 1 if there is a custom value, 0 otherwise.
4854 hdr_is_in_list(char *hdr
, PINEFIELD
*custom
)
4858 for(pf
= custom
; pf
&& pf
->name
; pf
= pf
->next
)
4859 if(strucmp(pf
->name
, hdr
) == 0)
4867 * count_custom_hdrs_pf - returns number of custom headers in arg
4868 * custom -- the list to be counted
4869 * only_nonstandard -- only count headers which aren't standard pine headers
4872 count_custom_hdrs_pf(PINEFIELD
*custom
, int only_nonstandard
)
4876 for(; custom
&& custom
->name
; custom
= custom
->next
)
4877 if(!only_nonstandard
|| !custom
->standard
)
4885 * count_custom_hdrs_list - returns number of custom headers in arg
4888 count_custom_hdrs_list(char **list
)
4898 for(p
= list
; (q
= *p
) != NULL
; p
++){
4900 /* remove leading whitespace */
4901 name
= skip_white_space(q
);
4903 /* look for colon or space or end */
4905 *t
&& !isspace((unsigned char)*t
) && *t
!= ':'; t
++)
4910 if(!pine_header_forbidden(name
))
4923 * set_default_hdrval - put the user's default value for this header
4925 * setthis - the pinefield to be set
4926 * custom - where to look for the default
4929 set_default_hdrval(PINEFIELD
*setthis
, PINEFIELD
*custom
)
4932 CustomType ret
= NoMatch
;
4934 if(!setthis
|| !setthis
->name
){
4935 q_status_message(SM_ORDER
,3,7,"Internal error setting default header");
4939 setthis
->textbuf
= NULL
;
4941 for(pf
= custom
; pf
&& pf
->name
; pf
= pf
->next
){
4942 if(strucmp(pf
->name
, setthis
->name
) != 0)
4947 /* turn on editing */
4948 if(strucmp(pf
->name
, "From") == 0 || strucmp(pf
->name
, "Reply-To") == 0)
4949 setthis
->canedit
= 1;
4952 setthis
->textbuf
= cpystr(pf
->val
);
4955 if(!setthis
->textbuf
)
4956 setthis
->textbuf
= cpystr("");
4963 * pine_header_standard - is this name a "standard" header?
4965 * name - the header name to check
4968 pine_header_standard(char *name
)
4972 /* check to see if this is a standard header */
4973 for(i
= 0; pf_template
[i
].name
; i
++)
4974 if(!strucmp(name
, pf_template
[i
].name
))
4975 return(pf_template
[i
].type
);
4977 return(TypeUnknown
);
4982 * customized_hdr_setup - setup the PINEFIELDS for all the customized headers
4983 * Allocates space for each name and addr ptr.
4984 * Allocates space for default in textbuf, even if empty.
4986 * head - the first PINEFIELD to fill in
4987 * list - the list to parse
4990 customized_hdr_setup(PINEFIELD
*head
, char **list
, CustomType cstmtype
)
4992 char **p
, *q
, *t
, *name
, *value
, save
;
4998 for(p
= list
; (q
= *p
) != NULL
; p
++){
5002 /* anything after leading whitespace? */
5003 if(!*(name
= skip_white_space(q
)))
5006 /* look for colon or space or end */
5008 *t
&& !isspace((unsigned char)*t
) && *t
!= ':'; t
++)
5011 /* if there is a space in the field-name, skip it */
5012 if(isspace((unsigned char)*t
)){
5013 q_status_message1(SM_ORDER
, 3, 3,
5014 _("Space not allowed in header name (%s)"),
5022 /* Don't allow any of the forbidden headers. */
5023 if(pine_header_forbidden(name
)){
5024 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
5025 _("Not allowed to change header \"%s\""),
5033 if(pine_header_standard(name
) != TypeUnknown
)
5036 pf
->name
= cpystr(name
);
5037 pf
->type
= FreeText
;
5038 pf
->cstmtype
= cstmtype
;
5043 * Some mailers apparently break if we change
5044 * user@domain into Fred <user@domain> for
5045 * return-receipt-to,
5046 * so we'll just call this a FreeText field, too.
5049 * For now, all custom headers are FreeText except for
5050 * this one that we happen to know about. We might
5051 * have to add some syntax to the config option so that
5052 * people can tell us their custom header takes addresses.
5054 if(!strucmp(pf
->name
, "Return-Receipt-to")){
5056 pf
->addr
= (ADDRESS
**)fs_get(sizeof(ADDRESS
*));
5057 *pf
->addr
= (ADDRESS
*)NULL
;
5063 /* remove space between name and colon */
5064 value
= skip_white_space(t
);
5066 /* give them an alloc'd default, even if empty */
5067 pf
->textbuf
= cpystr((*value
== ':')
5068 ? skip_white_space(++value
) : "");
5069 /* The instruction to remove the double quotes existed in
5070 * pine, but it was removed in alpine, but I do not know
5071 * why, so we will restore it until we understand why it
5072 * was removed. Also see:
5073 * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=981781
5075 if(pf
->textbuf
&& pf
->textbuf
[0]){
5076 removing_double_quotes(pf
->textbuf
);
5077 pf
->val
= cpystr(pf
->textbuf
);
5088 /* fix last next pointer */
5089 if(head
&& pf
!= head
)
5090 (pf
-1)->next
= NULL
;
5095 * add_defaults_from_list - the PINEFIELDS list given by "head" is already
5096 * setup except that it doesn't have default values.
5097 * Add those defaults if they exist in "list".
5099 * head - the first PINEFIELD to add a default to
5100 * list - the list to get the defaults from
5103 add_defaults_from_list(PINEFIELD
*head
, char **list
)
5105 char **p
, *q
, *t
, *name
, *value
, save
;
5108 for(pf
= head
; pf
&& list
; pf
= pf
->next
){
5112 for(p
= list
; (q
= *p
) != NULL
; p
++){
5116 /* anything after leading whitespace? */
5117 if(!*(name
= skip_white_space(q
)))
5120 /* look for colon or space or end */
5122 *t
&& !isspace((unsigned char)*t
) && *t
!= ':'; t
++)
5125 /* if there is a space in the field-name, skip it */
5126 if(isspace((unsigned char)*t
))
5132 if(strucmp(name
, pf
->name
) != 0){
5140 * Found the right header. See if it has a default
5144 /* remove space between name and colon */
5145 value
= skip_white_space(t
);
5150 defval
= skip_white_space(++value
);
5151 if(defval
&& *defval
){
5153 fs_give((void **)&pf
->val
);
5155 pf
->val
= cpystr(defval
);
5159 break; /* on to next pf */
5167 * parse_custom_hdrs - allocate PINEFIELDS for custom headers
5168 * fill in the defaults
5169 * Args - list -- The list to parse.
5170 * cstmtype -- Fill the in cstmtype field with this value
5173 parse_custom_hdrs(char **list
, CustomType cstmtype
)
5179 * add one for possible use by fcc
5180 * What is this "possible use"? I don't see it. I don't
5181 * think it exists anymore. I think +1 would be ok. hubert 2000-08-02
5183 i
= (count_custom_hdrs_list(list
) + 2) * sizeof(PINEFIELD
);
5184 pfields
= (PINEFIELD
*)fs_get((size_t) i
);
5185 memset(pfields
, 0, (size_t) i
);
5187 /* set up the custom header pfields */
5188 customized_hdr_setup(pfields
, list
, cstmtype
);
5195 * Combine the two lists of headers into one list which is allocated here
5196 * and freed by the caller. Eliminate duplicates with values from the role
5197 * taking precedence over values from the default.
5200 combine_custom_headers(PINEFIELD
*dflthdrs
, PINEFIELD
*rolehdrs
)
5202 PINEFIELD
*pfields
, *pf
, *pf2
;
5205 max_hdrs
= count_custom_hdrs_pf(rolehdrs
,0) +
5206 count_custom_hdrs_pf(dflthdrs
,0);
5208 pfields
= (PINEFIELD
*)fs_get((size_t)(max_hdrs
+1)*sizeof(PINEFIELD
));
5209 memset(pfields
, 0, (size_t)(max_hdrs
+1)*sizeof(PINEFIELD
));
5212 for(pf2
= rolehdrs
; pf2
&& pf2
->name
; pf2
= pf2
->next
){
5213 pf
->name
= pf2
->name
? cpystr(pf2
->name
) : NULL
;
5214 pf
->type
= pf2
->type
;
5215 pf
->cstmtype
= pf2
->cstmtype
;
5216 pf
->textbuf
= pf2
->textbuf
? cpystr(pf2
->textbuf
) : NULL
;
5217 pf
->val
= pf2
->val
? cpystr(pf2
->val
) : NULL
;
5218 pf
->standard
= pf2
->standard
;
5223 /* if these aren't already there, add them */
5224 for(pf2
= dflthdrs
; pf2
&& pf2
->name
; pf2
= pf2
->next
){
5225 /* check for already there */
5227 pfields
[i
].name
&& strucmp(pfields
[i
].name
, pf2
->name
);
5231 if(!pfields
[i
].name
){ /* this is a new one */
5232 pf
->name
= pf2
->name
? cpystr(pf2
->name
) : NULL
;
5233 pf
->type
= pf2
->type
;
5234 pf
->cstmtype
= pf2
->cstmtype
;
5235 pf
->textbuf
= pf2
->textbuf
? cpystr(pf2
->textbuf
) : NULL
;
5236 pf
->val
= pf2
->val
? cpystr(pf2
->val
) : NULL
;
5237 pf
->standard
= pf2
->standard
;
5243 /* fix last next pointer */
5245 (pf
-1)->next
= NULL
;
5252 * free_customs - free misc. resources associated with custom header fields
5254 * pf - pointer to first custom field
5257 free_customs(PINEFIELD
*head
)
5261 for(pf
= head
; pf
&& pf
->name
; pf
= pf
->next
){
5263 fs_give((void **)&pf
->name
);
5266 fs_give((void **)&pf
->val
);
5268 /* only true for FreeText */
5270 fs_give((void **)&pf
->textbuf
);
5272 /* only true for Address */
5273 if(pf
->addr
&& *pf
->addr
)
5274 mail_free_address(pf
->addr
);
5277 fs_give((void **)&head
);
5282 * encode_whole_header
5284 * Returns 1 if whole value should be encoded
5285 * 0 to encode only within comments
5288 encode_whole_header(char *field
, METAENV
*header
)
5293 if(field
&& (!strucmp(field
, "Subject") ||
5294 !strucmp(field
, "Comment") ||
5295 !struncmp(field
, "X-", 2)))
5297 else if(field
&& *field
&& header
&& header
->custom
){
5298 for(pf
= header
->custom
; pf
&& pf
->name
; pf
= pf
->next
){
5300 if(!pf
->standard
&& !strucmp(pf
->name
, field
)){
5311 /*----------------------------------------------------------------------
5312 Post news via NNTP or inews
5314 Args: env -- envelope of message to post
5315 body -- body of message to post
5317 Returns: -1 if failed or cancelled, 1 if succeeded
5319 WARNING: This call function has the side effect of writing the message
5320 to the lmc.so object.
5323 news_poster(METAENV
*header
, struct mail_bodystruct
*body
, char **alt_nntp_servers
,
5324 void (*pipecb_f
)(PIPE_S
*, int, void *))
5326 char *error_mess
, error_buf
[200], **news_servers
;
5327 char **servers_to_use
;
5328 int we_cancel
= 0, server_no
= 0, done_posting
= 0;
5329 void *orig_822_output
;
5332 error_buf
[0] = '\0';
5333 we_cancel
= busy_cue("Posting news", NULL
, 0);
5335 dprint((4, "Posting: [%s]\n",
5336 (header
&& header
->env
&& header
->env
->newsgroups
)
5337 ? header
->env
->newsgroups
: "?"));
5339 if((alt_nntp_servers
&& alt_nntp_servers
[0] && alt_nntp_servers
[0][0])
5340 || (ps_global
->VAR_NNTP_SERVER
&& ps_global
->VAR_NNTP_SERVER
[0]
5341 && ps_global
->VAR_NNTP_SERVER
[0][0])){
5342 /*---------- NNTP server defined ----------*/
5344 servers_to_use
= (alt_nntp_servers
&& alt_nntp_servers
[0]
5345 && alt_nntp_servers
[0][0])
5346 ? alt_nntp_servers
: ps_global
->VAR_NNTP_SERVER
;
5349 * Install our rfc822 output routine
5351 orig_822_output
= mail_parameters(NULL
, GET_RFC822OUTPUT
, NULL
);
5352 (void) mail_parameters(NULL
, SET_RFC822OUTPUT
,
5353 (void *)post_rfc822_output
);
5356 news_servers
= (char **)fs_get(2 * sizeof(char *));
5357 news_servers
[1] = NULL
;
5358 while(!done_posting
&& servers_to_use
[server_no
] &&
5359 servers_to_use
[server_no
][0]){
5360 news_servers
[0] = servers_to_use
[server_no
];
5361 ps_global
->noshow_error
= 1;
5363 sending_stream
= nntp_open(news_servers
, debug
? NOP_DEBUG
: 0L);
5365 sending_stream
= nntp_open(news_servers
, 0L);
5367 ps_global
->noshow_error
= 0;
5369 if(sending_stream
!= NULL
) {
5370 unsigned short save_encoding
, added_encoding
;
5373 * Fake that we've got clearance from the transport agent
5374 * for 8bit transport for the benefit of our output routines...
5376 if(F_ON(F_ENABLE_8BIT_NNTP
, ps_global
)
5377 && (bp
= first_text_8bit(body
))){
5380 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++)
5383 if(i
> ENCMAX
){ /* no empty encoding slots! */
5388 body_encodings
[added_encoding
] = body_encodings
[ENC8BIT
];
5389 save_encoding
= bp
->encoding
;
5390 bp
->encoding
= added_encoding
;
5395 * Set global header pointer so we can get at it later...
5397 send_header
= header
;
5398 ps_global
->noshow_error
= 1;
5399 if(nntp_mail(sending_stream
, header
->env
, body
) == 0)
5400 snprintf(error_mess
= error_buf
, sizeof(error_buf
),
5401 _("Error posting message: %s"),
5402 sending_stream
->reply
);
5405 error_buf
[0] = '\0';
5409 error_buf
[sizeof(error_buf
)-1] = '\0';
5410 smtp_close(sending_stream
);
5411 ps_global
->noshow_error
= 0;
5412 sending_stream
= NULL
;
5413 if(F_ON(F_ENABLE_8BIT_NNTP
, ps_global
) && bp
){
5414 body_encodings
[added_encoding
] = NULL
;
5415 bp
->encoding
= save_encoding
;
5419 /*---- Open of NNTP connection failed ------ */
5420 snprintf(error_mess
= error_buf
, sizeof(error_buf
),
5421 _("Error connecting to news server: %s"),
5422 ps_global
->c_client_error
);
5423 error_buf
[sizeof(error_buf
)-1] = '\0';
5424 dprint((1, error_buf
));
5428 fs_give((void **)&news_servers
);
5429 (void) mail_parameters (NULL
, SET_RFC822OUTPUT
, orig_822_output
);
5431 /*----- Post via local mechanism -------*/
5433 snprintf(error_mess
= error_buf
, sizeof(error_buf
),
5434 _("Can't post, NNTP-server must be defined!"));
5436 error_mess
= post_handoff(header
, body
, error_buf
, sizeof(error_buf
), NULL
,
5445 if(lmc
.so
&& !lmc
.all_written
)
5446 so_give(&lmc
.so
); /* clean up any fcc data */
5448 q_status_message(SM_ORDER
| SM_DING
, 4, 5, error_mess
);
5452 lmc
.all_written
= 1;
5457 /* ----------------------------------------------------------------------
5458 Figure out command to start local SMTP agent
5460 Args: errbuf -- buffer for reporting errors (assumed non-NULL)
5462 Returns an alloc'd copy of the local SMTP agent invocation or NULL
5466 smtp_command(char *errbuf
, size_t errbuflen
)
5469 if(!(ps_global
->VAR_SMTP_SERVER
&& ps_global
->VAR_SMTP_SERVER
[0]
5470 && ps_global
->VAR_SMTP_SERVER
[0][0]))
5471 strncpy(errbuf
,_("SMTP-server must be defined!"),errbuflen
);
5473 errbuf
[errbuflen
-1] = '\0';
5475 # if defined(SENDMAIL) && defined(SENDMAILFLAGS)
5476 #define SENDTMPLEN 256
5477 char tmp
[SENDTMPLEN
];
5478 /* SENDTMPLEN == sizeof(tmp) */
5479 snprintf(tmp
, sizeof(tmp
), "%.*s %.*s", (SENDTMPLEN
-3)/2, SENDMAIL
,
5480 (SENDTMPLEN
-3)/2, SENDMAILFLAGS
);
5481 return(cpystr(tmp
));
5483 strncpy(errbuf
, _("No default posting command."), errbuflen
);
5484 errbuf
[errbuflen
-1] = '\0';
5494 /*----------------------------------------------------------------------
5495 Hand off given message to local posting agent
5497 Args: envelope -- The envelope for the BCC and debugging
5498 header -- The text of the message header
5499 errbuf -- buffer for reporting errors (assumed non-NULL)
5500 len -- Length of errbuf
5504 mta_handoff(METAENV
*header
, struct mail_bodystruct
*body
,
5505 char *errbuf
, size_t len
,
5506 char **alt_smtp_servers
,
5507 void (*bigresult_f
) (char *, int),
5508 void (*pipecb_f
)(PIPE_S
*, int, void *))
5510 #ifdef DF_SENDMAIL_PATH
5516 * A bit of complicated policy implemented here.
5517 * There are two posting variables sendmail-path and smtp-server.
5518 * Precedence is in that order.
5519 * They can be set one of 4 ways: fixed, command-line, user, or globally.
5520 * Precedence is in that order.
5521 * Said differently, the order goes something like what's below.
5523 * NOTE: the fixed/command-line/user precedence handling is also
5524 * indicated by what's pointed to by ps_global->VAR_*, but since
5525 * that also includes the global defaults, it's not sufficient.
5528 if(ps_global
->FIX_SENDMAIL_PATH
5529 && ps_global
->FIX_SENDMAIL_PATH
[0]){
5530 cmd
= ps_global
->FIX_SENDMAIL_PATH
;
5532 else if(!(ps_global
->FIX_SMTP_SERVER
5533 && ps_global
->FIX_SMTP_SERVER
[0])){
5534 if(ps_global
->COM_SENDMAIL_PATH
5535 && ps_global
->COM_SENDMAIL_PATH
[0]){
5536 cmd
= ps_global
->COM_SENDMAIL_PATH
;
5538 else if(!(ps_global
->COM_SMTP_SERVER
5539 && ps_global
->COM_SMTP_SERVER
[0])
5540 && !(alt_smtp_servers
5541 && alt_smtp_servers
[0]
5542 && alt_smtp_servers
[0][0])){
5543 if((ps_global
->vars
[V_SENDMAIL_PATH
].post_user_val
.p
5544 && ps_global
->vars
[V_SENDMAIL_PATH
].post_user_val
.p
[0]) ||
5545 (ps_global
->vars
[V_SENDMAIL_PATH
].main_user_val
.p
5546 && ps_global
->vars
[V_SENDMAIL_PATH
].main_user_val
.p
[0])){
5547 if(ps_global
->vars
[V_SENDMAIL_PATH
].post_user_val
.p
5548 && ps_global
->vars
[V_SENDMAIL_PATH
].post_user_val
.p
[0])
5549 cmd
= ps_global
->vars
[V_SENDMAIL_PATH
].post_user_val
.p
;
5551 cmd
= ps_global
->vars
[V_SENDMAIL_PATH
].main_user_val
.p
;
5553 else if(!((ps_global
->vars
[V_SMTP_SERVER
].post_user_val
.l
5554 && ps_global
->vars
[V_SMTP_SERVER
].post_user_val
.l
[0]) ||
5555 (ps_global
->vars
[V_SMTP_SERVER
].main_user_val
.l
5556 && ps_global
->vars
[V_SMTP_SERVER
].main_user_val
.l
[0]))){
5557 if(ps_global
->GLO_SENDMAIL_PATH
5558 && ps_global
->GLO_SENDMAIL_PATH
[0]){
5559 cmd
= ps_global
->GLO_SENDMAIL_PATH
;
5561 #ifdef DF_SENDMAIL_PATH
5563 * This defines the default method of posting. So,
5564 * unless we're told otherwise use it...
5566 else if(!(ps_global
->GLO_SMTP_SERVER
5567 && ps_global
->GLO_SMTP_SERVER
[0])){
5568 strncpy(cmd
= cmd_buf
, DF_SENDMAIL_PATH
, sizeof(cmd_buf
)-1);
5569 cmd_buf
[sizeof(cmd_buf
)-1] = '\0';
5578 dprint((4, "call_mailer via cmd: %s\n", cmd
? cmd
: "?"));
5580 (void) mta_parse_post(header
, body
, cmd
, errbuf
, len
, bigresult_f
, pipecb_f
);
5589 /*----------------------------------------------------------------------
5590 Hand off given message to local posting agent
5592 Args: envelope -- The envelope for the BCC and debugging
5593 header -- The text of the message header
5594 errbuf -- buffer for reporting errors (assumed non-NULL)
5595 errbuflen -- Length of errbuf
5597 Fork off mailer process and pipe the message into it
5598 Called to post news via Inews when NNTP is unavailable
5602 post_handoff(METAENV
*header
, struct mail_bodystruct
*body
, char *errbuf
,
5604 void (*bigresult_f
) (char *, int),
5605 void (*pipecb_f
)(PIPE_S
*, int, void *))
5612 if((s
= strstr(header
->env
->date
," (")) != NULL
) /* fix the date format for news */
5615 if((err
= mta_parse_post(header
, body
, SENDNEWS
, errbuf
, errbuflen
, bigresult_f
, pipecb_f
))){
5616 strncpy(tmp
, err
, sizeof(tmp
)-1);
5617 tmp
[sizeof(tmp
)-1] = '\0';
5618 snprintf(err
= errbuf
, errbuflen
, _("News not posted: \"%s\": %s"),
5623 *s
= ' '; /* restore the date */
5625 #else /* !SENDNEWS */ /* this is the default case */
5626 strncpy(err
= errbuf
, _("Can't post, NNTP-server must be defined!"), errbuflen
-1);
5627 err
[errbuflen
-1] = '\0';
5628 #endif /* !SENDNEWS */
5634 /*----------------------------------------------------------------------
5635 Hand off message to local MTA; it parses recipients from 822 header
5637 Args: header -- struct containing header data
5638 body -- struct containing message body data
5639 cmd -- command to use for handoff (%s says where file should go)
5640 errs -- pointer to buf to hold errors
5644 mta_parse_post(METAENV
*header
, struct mail_bodystruct
*body
, char *cmd
,
5645 char *errs
, size_t errslen
, void (*bigresult_f
)(char *, int),
5646 void (*pipecb_f
)(PIPE_S
*, int, void *))
5648 char *result
= NULL
;
5651 dprint((1, "=== mta_parse_post(%s) ===\n", cmd
? cmd
: "?"));
5653 if((pipe
= open_system_pipe(cmd
, &result
, NULL
,
5654 PIPE_STDERR
|PIPE_WRITE
|PIPE_PROT
|PIPE_NOSHELL
|PIPE_DESC
,
5655 0, pipecb_f
, pipe_report_error
)) != NULL
){
5656 if(!pine_rfc822_output(header
, body
, pine_pipe_soutr_nl
,
5657 (TCPSTREAM
*) pipe
)){
5658 strncpy(errs
, _("Error posting."), errslen
-1);
5659 errs
[errslen
-1] = '\0';
5662 if(close_system_pipe(&pipe
, NULL
, pipecb_f
) && !*errs
){
5663 snprintf(errs
, errslen
, _("Posting program %s returned error"), cmd
);
5664 if(result
&& bigresult_f
)
5665 (*bigresult_f
)(result
, CM_BR_ERROR
);
5669 snprintf(errs
, errslen
, _("Error running \"%s\""), cmd
);
5673 fs_give((void **)&result
);
5676 return(*errs
? errs
: NULL
);
5681 * pine_pipe_soutr - Replacement for tcp_soutr that writes one of our
5682 * pipes rather than a tcp stream
5685 pine_pipe_soutr_nl (void *stream
, char *s
)
5692 if((n
= (p
= strstr(s
, "\015\012")) ? p
- s
: strlen(s
)) != 0){
5693 while((rv
= write(((PIPE_S
*)stream
)->out
.d
, s
, n
)) != n
)
5707 s
= p
+ 2; /* write UNIX EOL */
5708 while((rv
= write(((PIPE_S
*)stream
)->out
.d
,"\n",1)) != 1)
5709 if(rv
< 0 && errno
!= EINTR
){
5723 /**************** "PIPE" READING POSTING I/O ROUTINES ****************/
5729 #define S(X) ((PIPE_S *)(X))
5730 #define GETBUFLEN (4 * MAILTMPLEN)
5734 piped_smtp_open (char *host
, char *service
, long unsigned int port
)
5739 if(strucmp(host
, "localhost")){
5740 char tmp
[MAILTMPLEN
];
5741 /* MAILTMPLEN = sizeof(tmp) */
5742 snprintf(tmp
, sizeof(tmp
), _("Unexpected hostname for piped SMTP: %.*s"),
5743 MAILTMPLEN
-50, host
);
5744 tmp
[sizeof(tmp
)-1] = '\0';
5747 else if((postcmd
= smtp_command(ps_global
->c_client_error
, sizeof(ps_global
->c_client_error
))) != NULL
){
5748 rv
= open_system_pipe(postcmd
, NULL
, NULL
,
5749 PIPE_READ
|PIPE_STDERR
|PIPE_WRITE
|PIPE_PROT
|PIPE_NOSHELL
|PIPE_DESC
,
5750 0, NULL
, pipe_report_error
);
5751 fs_give((void **) &postcmd
);
5754 mm_log(ps_global
->c_client_error
, ERROR
);
5761 piped_aopen (NETMBX
*mb
, char *service
, char *user
)
5768 * piped_soutr - Replacement for tcp_soutr that writes one of our
5769 * pipes rather than a tcp stream
5772 piped_soutr (void *stream
, char *s
)
5774 return(piped_sout(stream
, s
, strlen(s
)));
5779 * piped_sout - Replacement for tcp_soutr that writes one of our
5780 * pipes rather than a tcp stream
5783 piped_sout (void *stream
, char *s
, long unsigned int size
)
5787 if(S(stream
)->out
.d
< 0)
5790 if((i
= (int) size
) != 0){
5791 while((o
= write(S(stream
)->out
.d
, s
, i
)) != i
)
5794 piped_abort(stream
);
5799 s
+= o
; /* try again, fix up counts */
5809 * piped_getline - Replacement for tcp_getline that reads one
5810 * of our pipes rather than a tcp pipe
5812 * C-client expects that the \r\n will be stripped off.
5815 piped_getline (void *stream
)
5820 char *ret
, *s
, *sp
, c
= '\0', d
;
5822 if(S(stream
)->in
.d
< 0)
5825 if(!S(stream
)->tmp
){ /* initialize! */
5826 /* alloc space to collect input (freed in close_system_pipe) */
5827 S(stream
)->tmp
= (char *) fs_get(GETBUFLEN
);
5828 memset(S(stream
)->tmp
, 0, GETBUFLEN
);
5833 while((cnt
= read(S(stream
)->in
.d
, S(stream
)->tmp
, GETBUFLEN
)) < 0)
5835 piped_abort(stream
);
5840 piped_abort(stream
);
5844 ptr
= S(stream
)->tmp
;
5851 if((c
== '\015') && (d
== '\012')){
5852 ret
= (char *)fs_get (n
--);
5861 /* copy partial string from buffer */
5862 memcpy((ret
= sp
= (char *) fs_get (n
)), s
, n
);
5865 memset(S(stream
)->tmp
, 0, GETBUFLEN
);
5866 while((cnt
= read(S(stream
)->in
.d
, S(stream
)->tmp
, GETBUFLEN
)) < 0)
5868 fs_give((void **) &ret
);
5869 piped_abort(stream
);
5875 ret
[n
-1] = '\0'; /* to try to get error message logged */
5877 piped_abort(stream
);
5882 ptr
= S(stream
)->tmp
;
5885 if(c
== '\015' && *ptr
== '\012'){
5888 ret
[n
- 1] = '\0'; /* tie off string with null */
5890 else if ((s
= piped_getline(stream
)) != NULL
) {
5891 ret
= (char *) fs_get(n
+ 1 + (m
= strlen (s
)));
5892 memcpy(ret
, sp
, n
); /* copy first part */
5893 memcpy(ret
+ n
, s
, m
); /* and second part */
5894 fs_give((void **) &sp
); /* flush first part */
5895 fs_give((void **) &s
); /* flush second part */
5896 ret
[n
+ m
] = '\0'; /* tie off string with null */
5904 * piped_close - Replacement for tcp_close that closes pipes to our
5905 * child rather than a tcp connection
5908 piped_close(void *stream
)
5911 * Uninstall our hooks into smtp_send since it's being used by
5912 * the nntp driver as well...
5914 (void) close_system_pipe((PIPE_S
**) &stream
, NULL
, NULL
);
5919 * piped_abort - close down the pipe we were using to post
5922 piped_abort(void *stream
)
5924 if(S(stream
)->in
.d
>= 0){
5925 close(S(stream
)->in
.d
);
5926 S(stream
)->in
.d
= -1;
5929 if(S(stream
)->out
.d
){
5930 close(S(stream
)->out
.d
);
5931 S(stream
)->out
.d
= -1;
5937 piped_host(void *stream
)
5939 return(ps_global
->hostname
? ps_global
->hostname
: "localhost");
5944 piped_port(void *stream
)