2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
16 Code here for forward and reply to mail
17 A few support routines as well
19 This code will forward and reply to MIME messages. The Alpine composer
20 at this time will only support non-text segments at the end of a
21 message so, things don't always come out as one would like. If you
22 always forward a message in MIME format, all will be correct. Forwarding
23 of nested MULTIPART messages will work. There's still a problem with
24 MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
25 the equivalent parts. Ideally, we should probably such segments as a
26 single attachment when forwarding/replying. It would also be real nice to
27 flatten out the nesting in the composer so pieces inside can get snipped.
29 The evolution continues...
45 #include "../pith/state.h"
46 #include "../pith/conf.h"
47 #include "../pith/init.h"
48 #include "../pith/filter.h"
49 #include "../pith/pattern.h"
50 #include "../pith/charset.h"
51 #include "../pith/mimedesc.h"
52 #include "../pith/remote.h"
53 #include "../pith/news.h"
54 #include "../pith/util.h"
55 #include "../pith/detoken.h"
56 #include "../pith/newmail.h"
57 #include "../pith/readfile.h"
58 #include "../pith/tempfile.h"
59 #include "../pith/busy.h"
60 #include "../pith/ablookup.h"
66 int reply_poster_followup(ENVELOPE
*);
67 int sigedit_exit_for_pico(struct headerentry
*, void (*)(void), int, char **);
68 long new_mail_for_pico(int, int);
69 void cmd_input_for_pico(void);
70 int display_message_for_pico(int);
71 char *checkpoint_dir_for_pico(char *, size_t);
72 void resize_for_pico(void);
73 PCOLORS
*colors_for_pico(void);
74 void free_pcolors(PCOLORS
**);
77 /*----------------------------------------------------------------------
78 Fill in an outgoing message for reply and pass off to send
80 Args: pine_state -- The usual pine structure
82 Result: Reply is formatted and passed off to composer/mailer
86 - put senders address in To field
87 - search to and cc fields to see if we aren't the only recipients
88 - if other than us, ask if we should reply to all.
89 - if answer yes, fill out the To and Cc fields
90 - fill in the fcc argument
91 - fill in the subject field
92 - fill out the body and the attachments
93 - pass off to pine_send()
96 reply(struct pine
*pine_state
, ACTION_S
*role_arg
)
98 ADDRESS
*saved_from
, *saved_to
, *saved_cc
, *saved_resent
;
99 ADDRESS
*us_in_to_and_cc
, *ap
;
100 ENVELOPE
*env
= NULL
, *outgoing
;
101 BODY
*body
, *orig_body
= NULL
;
103 void *msgtext
= NULL
;
104 char *tmpfix
= NULL
, *prefix
= NULL
, *fcc
= NULL
, *errmsg
= NULL
;
105 long msgno
, j
, totalm
, rflags
, *seq
= NULL
;
106 int i
, include_text
= 0, times
= -1, warned
= 0, rv
= 0,
107 flags
= RSF_QUERY_REPLY_ALL
, reply_raw_body
= 0;
108 int rolemsg
= 0, copytomsg
= 0;
111 REDRAFT_POS_S
*redraft_pos
= NULL
;
112 ACTION_S
*role
= NULL
, *nrole
;
113 #if defined(DOS) && !defined(_WINDOWS)
117 outgoing
= mail_newenvelope();
118 totalm
= mn_total_cur(pine_state
->msgmap
);
119 seq
= (long *)fs_get(((size_t)totalm
+ 1) * sizeof(long));
121 dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm
)));
123 saved_from
= (ADDRESS
*) NULL
;
124 saved_to
= (ADDRESS
*) NULL
;
125 saved_cc
= (ADDRESS
*) NULL
;
126 saved_resent
= (ADDRESS
*) NULL
;
128 us_in_to_and_cc
= (ADDRESS
*) NULL
;
130 outgoing
->subject
= NULL
;
132 memset((void *)&reply
, 0, sizeof(reply
));
134 if(ps_global
->full_header
== 2
135 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
139 * We may have to loop through first to figure out what default
140 * reply-indent-string to offer...
142 if(mn_total_cur(pine_state
->msgmap
) > 1 &&
143 (F_ON(F_ALT_REPLY_MENU
, pine_state
)
144 || F_ON(F_ENABLE_EDIT_REPLY_INDENT
, pine_state
)) &&
145 reply_quote_str_contains_tokens()){
146 for(msgno
= mn_first_cur(pine_state
->msgmap
);
147 msgno
> 0L && !tmpfix
;
148 msgno
= mn_next_cur(pine_state
->msgmap
)){
150 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
151 mn_m2raw(pine_state
->msgmap
, msgno
),
154 q_status_message1(SM_ORDER
,3,4,
155 _("Error fetching message %s. Can't reply to it."),
160 if(!tmpfix
){ /* look for prefix? */
161 tmpfix
= reply_quote_str(env
);
163 i
= strcmp(tmpfix
, prefix
);
164 fs_give((void **) &tmpfix
);
165 if(i
){ /* don't check back if dissimilar */
166 fs_give((void **) &prefix
);
168 * We free prefix, not tmpfix. We set tmpfix to prefix
169 * so that we won't come check again.
171 tmpfix
= prefix
= cpystr("> ");
176 tmpfix
= NULL
; /* check back later? */
185 * Loop thru the selected messages building the
186 * outgoing envelope's destinations...
188 for(msgno
= mn_first_cur(pine_state
->msgmap
);
190 msgno
= mn_next_cur(pine_state
->msgmap
)){
192 /*--- Grab current envelope ---*/
193 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
194 seq
[++times
] = mn_m2raw(pine_state
->msgmap
, msgno
),
197 q_status_message1(SM_ORDER
,3,4,
198 _("Error fetching message %s. Can't reply to it."),
204 * We check for the prefix here if we didn't do it in the first
205 * loop above. This is just to save having to go through the loop
206 * twice in the cases where we don't need to.
209 tmpfix
= reply_quote_str(env
);
211 i
= strcmp(tmpfix
, prefix
);
212 fs_give((void **) &tmpfix
);
213 if(i
){ /* don't check back if dissimilar */
214 fs_give((void **) &prefix
);
215 tmpfix
= prefix
= cpystr("> ");
220 tmpfix
= NULL
; /* check back later? */
225 * For consistency, the first question is always "include text?"
227 if(!times
){ /* only first time */
228 char *p
= cpystr(prefix
);
230 if((include_text
=reply_text_query(pine_state
,totalm
,env
,&prefix
)) < 0)
234 if(strcmp(p
, prefix
))
235 tmpfix
= prefix
; /* stop looking */
237 fs_give((void **)&p
);
241 * If we're agg-replying or there's a newsgroup and the user wants
242 * to post to news *and* via email, add relevant addresses to the
243 * outgoing envelope...
245 * The single message case gets us around the aggregate reply
246 * to messages in a mixed mail-news archive where some might
247 * have newsgroups and others not or whatever.
249 if(totalm
> 1L || ((i
= reply_news_test(env
, outgoing
)) & 1)){
251 flags
|= RSF_FORCE_REPLY_TO
;
253 if(!reply_harvest(pine_state
, seq
[times
], NULL
, env
,
254 &saved_from
, &saved_to
, &saved_cc
,
255 &saved_resent
, &flags
))
261 /* collect a list of addresses that are us in to and cc fields */
263 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
264 NULL
, us_in_to_and_cc
, NULL
,
265 env
->to
, RCA_ONLY_US
)) != NULL
)
266 reply_append_addr(&us_in_to_and_cc
, ap
);
269 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
270 NULL
, us_in_to_and_cc
, NULL
,
271 env
->cc
, RCA_ONLY_US
)) != NULL
)
272 reply_append_addr(&us_in_to_and_cc
, ap
);
274 /*------------ Format the subject line ---------------*/
275 if(outgoing
->subject
){
277 * if reply to more than one message, and all subjects
278 * match, so be it. otherwise set it to something generic...
280 if(!same_subject(outgoing
->subject
,
281 reply_subject(env
->subject
,tmp_20k_buf
,SIZEOF_20KBUF
))){
282 fs_give((void **)&outgoing
->subject
);
283 outgoing
->subject
= cpystr("Re: several messages");
287 outgoing
->subject
= reply_subject(env
->subject
, NULL
, 0);
290 /* fill reply header */
291 reply_seed(pine_state
, outgoing
, env
, saved_from
,
292 saved_to
, saved_cc
, saved_resent
,
293 &fcc
, flags
& RSF_FORCE_REPLY_ALL
, &errmsg
);
296 q_status_message1(SM_ORDER
, 3, 3, "%.200s", errmsg
);
297 display_message(NO_OP_COMMAND
);
300 fs_give((void **)&errmsg
);
303 if(sp_expunge_count(pine_state
->mail_stream
)) /* cur msg expunged */
306 /* Setup possible role */
307 if (ps_global
->reply
.role_chosen
)
308 role
= ps_global
->reply
.role_chosen
;
310 role
= copy_action(role_arg
);
314 if(!ps_global
->reply
.role_chosen
&& nonempty_patterns(rflags
, &dummy
)){
315 /* setup default role */
317 j
= mn_first_cur(pine_state
->msgmap
);
320 nrole
= set_role_from_msg(pine_state
, rflags
,
321 mn_m2raw(pine_state
->msgmap
, j
),
323 } while(nrole
&& (!role
|| nrole
== role
)
324 && (j
=mn_next_cur(pine_state
->msgmap
)) > 0L);
326 if(!role
|| nrole
== role
)
331 if(confirm_role(rflags
, &role
))
332 role
= combine_inherited_role(role
);
333 else{ /* cancel reply */
335 cmd_cancelled("Reply");
342 * Reply_seed may call c-client in get_fcc_based_on_to, so env may
343 * no longer be valid. Get it again.
344 * Similarly for set_role_from_message.
346 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, seq
[times
], NULL
);
350 /* override fcc gotten in reply_seed */
352 fs_give((void **) &fcc
);
355 if(F_ON(F_COPY_TO_TO_FROM
, pine_state
) && !(role
&& role
->from
)){
357 * A list of all of our addresses that appear in the To
358 * and cc fields is in us_in_to_and_cc.
359 * If there is exactly one address in that list then
360 * use it for the outgoing From.
362 if(us_in_to_and_cc
&& !us_in_to_and_cc
->next
){
363 PINEFIELD
*custom
, *pf
;
368 * Check to see if this address is different from what
369 * we would have used anyway. If it is, notify the user
370 * with a status message. This is pretty hokey because we're
371 * mimicking how pine_send would set the From address and
372 * there is no coordination between the two.
375 /* in case user has a custom From value */
376 custom
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
378 pf
= (PINEFIELD
*) fs_get(sizeof(*pf
));
379 memset((void *) pf
, 0, sizeof(*pf
));
380 pf
->name
= cpystr("From");
382 if(set_default_hdrval(pf
, custom
) >= UseAsDef
383 && pf
->textbuf
&& pf
->textbuf
[0]){
384 removing_trailing_white_space(pf
->textbuf
);
385 (void)removing_double_quotes(pf
->textbuf
);
386 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
387 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
389 fs_give((void **) &addr
);
393 *pf
->addr
= generate_from();
395 if(*pf
->addr
&& !address_is_same(*pf
->addr
, us_in_to_and_cc
)){
398 role
= (ACTION_S
*) fs_get(sizeof(*role
));
399 memset((void *) role
, 0, sizeof(*role
));
403 role
->from
= us_in_to_and_cc
;
404 us_in_to_and_cc
= NULL
;
407 free_customs(custom
);
413 if(rolemsg
&& copytomsg
)
414 q_status_message1(SM_ORDER
, 3, 4,
415 _("Replying using role \"%s\" and To as From"), role
->nick
);
417 q_status_message1(SM_ORDER
, 3, 4,
418 _("Replying using role \"%s\""), role
->nick
);
420 q_status_message(SM_ORDER
, 3, 4,
421 _("Replying using incoming To as outgoing From"));
425 mail_free_address(&us_in_to_and_cc
);
428 seq
[++times
] = -1L; /* mark end of sequence list */
430 /*========== Other miscellaneous fields ===================*/
431 outgoing
->in_reply_to
= reply_in_reply_to(env
);
432 outgoing
->references
= reply_build_refs(env
);
433 outgoing
->message_id
= generate_message_id(role
);
438 !outgoing
->newsgroups
)
439 q_status_message(SM_ORDER
| SM_DING
, 3, 6,
440 _("Warning: no valid addresses to reply to!"));
443 /*==================== Now fix up the message body ====================*/
446 * create storage object to be used for message text
448 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
449 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
450 _("Error allocating message text"));
454 gf_set_so_writec(&pc
, (STORE_S
*) msgtext
);
456 /*---- Include the original text if requested ----*/
457 if(include_text
&& totalm
> 1L){
459 int impl
, template_len
= 0, leave_cursor_at_top
= 0;
463 if(role
&& role
->template){
467 filtered
= detoken(role
, env
, 0,
468 ps_global
->reply
.signature_bottom
,
469 0, &redraft_pos
, &impl
);
472 so_puts((STORE_S
*)msgtext
, filtered
);
474 template_len
= strlen(filtered
);
476 leave_cursor_at_top
++;
479 fs_give((void **)&filtered
);
487 if((sig
= reply_signature(role
, env
, &redraft_pos
, &impl
)) &&
488 !ps_global
->reply
.signature_bottom
){
491 * If CURSORPOS was set explicitly in sig_file, and there was a
492 * template file before that, we need to adjust the offset by the
493 * length of the template file. However, if the template had
494 * a set CURSORPOS in it then impl was 2 before getting to the
495 * signature, so offset wouldn't have been reset by the signature
496 * CURSORPOS and offset would already be correct. That case will
497 * be ok here because template_len will be 0 and adding it does
498 * nothing. If template
499 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
500 * by the CURSORPOS in the sig. In that case we have to adjust the
501 * offset. That's what the next line does. It adjusts it if
502 * template_len is nonzero and if CURSORPOS was set in sig_file.
505 redraft_pos
->offset
+= template_len
;
508 so_puts((STORE_S
*)msgtext
, sig
);
511 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
512 * is set, we won't have used it yet and want it to be non-NULL.
514 fs_give((void **)&sig
);
518 * Only put cursor in sig if there is a cursorpos there but not
519 * one in the template, and sig-at-bottom.
521 if(!(sig
&& impl
== 2 && !leave_cursor_at_top
))
522 leave_cursor_at_top
++;
524 body
= mail_newbody();
525 body
->type
= TYPETEXT
;
526 body
->contents
.text
.data
= msgtext
;
528 for(msgno
= mn_first_cur(pine_state
->msgmap
);
530 msgno
= mn_next_cur(pine_state
->msgmap
)){
532 if(env
){ /* put 2 between messages */
533 gf_puts(NEWLINE
, pc
);
534 gf_puts(NEWLINE
, pc
);
537 /*--- Grab current envelope ---*/
538 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
539 mn_m2raw(pine_state
->msgmap
, msgno
),
542 q_status_message1(SM_ORDER
,3,4,
543 _("Error fetching message %s. Can't reply to it."),
544 long2string(mn_get_cur(pine_state
->msgmap
)));
548 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| reply_raw_body
) {
549 reply_delimiter(env
, role
, pc
);
550 if(ps_global
->reply
.include_header
)
551 reply_forward_header(pine_state
->mail_stream
,
552 mn_m2raw(pine_state
->msgmap
,msgno
),
553 NULL
, env
, pc
, prefix
);
555 get_body_part_text(pine_state
->mail_stream
, reply_raw_body
? NULL
: orig_body
,
556 mn_m2raw(pine_state
->msgmap
, msgno
),
557 reply_raw_body
? NULL
: "1", 0L, pc
, prefix
,
560 else if(orig_body
->type
== TYPEMULTIPART
) {
562 q_status_message(SM_ORDER
,3,7,
563 _("WARNING! Attachments not included in multiple reply."));
565 if(orig_body
->nested
.part
566 && orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
567 /*---- First part of the message is text -----*/
568 reply_delimiter(env
, role
, pc
);
569 if(ps_global
->reply
.include_header
)
570 reply_forward_header(pine_state
->mail_stream
,
571 mn_m2raw(pine_state
->msgmap
,
573 NULL
, env
, pc
, prefix
);
575 get_body_part_text(pine_state
->mail_stream
,
576 &orig_body
->nested
.part
->body
,
577 mn_m2raw(pine_state
->msgmap
, msgno
),
578 "1", 0L, pc
, prefix
, NULL
, GBPT_NONE
);
581 q_status_message(SM_ORDER
,0,3,
582 _("Multipart with no leading text part."));
586 /*---- Single non-text message of some sort ----*/
587 q_status_message(SM_ORDER
,3,3,
588 _("Non-text message not included."));
592 if(!leave_cursor_at_top
){
596 /* rewind and count chars to start of sig file */
597 so_seek((STORE_S
*)msgtext
, 0L, 0);
598 while(so_readc(&c
, (STORE_S
*)msgtext
))
602 redraft_pos
= (REDRAFT_POS_S
*)fs_get(sizeof(*redraft_pos
));
603 memset((void *)redraft_pos
, 0,sizeof(*redraft_pos
));
604 redraft_pos
->hdrname
= cpystr(":");
608 * If explicit cursor positioning in sig file,
609 * add offset to start of sig file plus offset into sig file.
610 * Else, just offset to start of sig file.
612 redraft_pos
->offset
+= cnt
;
617 so_puts((STORE_S
*)msgtext
, sig
);
619 fs_give((void **)&sig
);
623 msgno
= mn_m2raw(pine_state
->msgmap
,
624 mn_get_cur(pine_state
->msgmap
));
626 /*--- Grab current envelope ---*/
627 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, msgno
,
631 * If the charset of the body part is different from ascii and
632 * charset conversion is _not_ happening, then preserve the original
633 * charset from the message so that if we don't enter any new
634 * chars with the hibit set we can use the original charset.
635 * If not all those things, then don't try to preserve it.
640 charset
= parameter_val(orig_body
->parameter
, "charset");
641 if(charset
&& strucmp(charset
, "us-ascii") != 0){
645 * There is a non-ascii charset, is there conversion happening?
647 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
648 reply
.orig_charset
= charset
;
654 fs_give((void **) &charset
);
658 if(!(body
= reply_body(pine_state
->mail_stream
, env
, orig_body
,
659 msgno
, NULL
, msgtext
, prefix
,
660 include_text
, role
, 1, &redraft_pos
)))
664 q_status_message1(SM_ORDER
,3,4,
665 _("Error fetching message %s. Can't reply to it."),
666 long2string(mn_get_cur(pine_state
->msgmap
)));
671 /* fill in reply structure */
672 reply
.prefix
= prefix
;
673 reply
.mailbox
= cpystr(pine_state
->mail_stream
->mailbox
);
674 reply
.origmbox
= cpystr(pine_state
->mail_stream
->original_mailbox
675 ? pine_state
->mail_stream
->original_mailbox
676 : pine_state
->mail_stream
->mailbox
);
677 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((times
+ 1) * sizeof(imapuid_t
));
678 if((reply
.data
.uid
.validity
= pine_state
->mail_stream
->uid_validity
) != 0){
680 for(i
= 0; i
< times
; i
++)
681 reply
.data
.uid
.msgs
[i
] = mail_uid(pine_state
->mail_stream
, seq
[i
]);
685 for(i
= 0; i
< times
; i
++)
686 reply
.data
.uid
.msgs
[i
] = seq
[i
];
689 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
691 #if defined(DOS) && !defined(_WINDOWS)
692 free((void *)reserve
);
695 /* partially formatted outgoing message */
696 pine_send(outgoing
, &body
, _("COMPOSE MESSAGE REPLY"),
697 role
, fcc
, &reply
, redraft_pos
, NULL
, NULL
, 0);
700 pine_free_body(&body
);
702 fs_give((void **) &reply
.mailbox
);
704 fs_give((void **) &reply
.origmbox
);
705 if(reply
.orig_charset
)
706 fs_give((void **) &reply
.orig_charset
);
707 fs_give((void **) &reply
.data
.uid
.msgs
);
709 if((STORE_S
*) msgtext
)
710 gf_clear_so_writec((STORE_S
*) msgtext
);
712 mail_free_envelope(&outgoing
);
713 mail_free_address(&saved_from
);
714 mail_free_address(&saved_to
);
715 mail_free_address(&saved_cc
);
716 mail_free_address(&saved_resent
);
718 fs_give((void **)&seq
);
721 fs_give((void **)&prefix
);
724 fs_give((void **) &fcc
);
726 free_redraft_pos(&redraft_pos
);
733 * Ask user to confirm role choice, or choose another role.
735 * Args role -- A pointer into the pattern_h space at the default
736 * role to use. This can't be a copy, the comparison
737 * relies on it pointing at the actual role.
738 * This arg is also used to return a pointer to the
741 * Returns 1 -- Yes, use role which is now in *role. This may not be
742 * the same as the role passed in and it may even be NULL.
746 confirm_role(long int rflags
, ACTION_S
**role
)
748 ACTION_S
*role_p
= NULL
;
749 ACTION_S
*default_role
= NULL
;
750 char prompt
[80], *prompt_fodder
;
751 int cmd
, done
, ret
= 1;
752 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
753 (*redraw
)(void) = ps_global
->redrawer
;
759 if(!nonempty_patterns(ROLE_DO_ROLES
, &pstate
) || !role
)
763 * If this is a reply or forward and the role doesn't require confirmation,
764 * then we just return with what was passed in.
766 if(((rflags
& ROLE_REPLY
) &&
767 *role
&& (*role
)->repl_type
== ROLE_REPL_NOCONF
) ||
768 ((rflags
& ROLE_FORWARD
) &&
769 *role
&& (*role
)->forw_type
== ROLE_FORW_NOCONF
) ||
770 ((rflags
& ROLE_COMPOSE
) &&
771 *role
&& (*role
)->comp_type
== ROLE_COMP_NOCONF
) ||
772 (!*role
&& F_OFF(F_ROLE_CONFIRM_DEFAULT
, ps_global
)
773 && !ps_global
->default_role
))
777 * Check that there is at least one role available. This is among all
778 * roles, not just the reply roles or just the forward roles. That's
779 * because we have ^T take us to all the roles, not the category-specific
782 if(!(pat
= last_pattern(&pstate
)))
791 ekey
[2].ch
= ctrl('T');
797 /* check for more than one role available (or no role set) */
798 if(pat
== first_pattern(&pstate
) && *role
) /* no ^T */
803 * Go through the loop just in case default_role doesn't point
804 * to a real current role.
806 if(ps_global
->default_role
){
807 for(pat
= first_pattern(&pstate
);
809 pat
= next_pattern(&pstate
)){
810 if(pat
->action
== ps_global
->default_role
){
811 default_role
= ps_global
->default_role
;
819 /* override default */
821 for(pat
= first_pattern(&pstate
);
823 pat
= next_pattern(&pstate
)){
824 if(pat
->action
== *role
){
831 if(rflags
& ROLE_REPLY
)
832 prompt_fodder
= _("Reply");
833 else if(rflags
& ROLE_FORWARD
)
834 prompt_fodder
= _("Forward");
836 prompt_fodder
= _("Compose");
843 help
= h_role_confirm
;
845 ekey
[0].label
= N_("Yes");
848 ekey
[1].label
= N_("No, use default role");
850 ekey
[1].label
= N_("No, use default settings");
852 ekey
[2].label
= N_("To Select Alternate Role");
854 if(curpat
->patgrp
&& curpat
->patgrp
->nick
)
855 /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
856 snprintf(prompt
, sizeof(prompt
), _("Use role \"%s\" for %s? "),
857 short_str(curpat
->patgrp
->nick
, buf
, sizeof(buf
), 50, MidDots
),
860 snprintf(prompt
, sizeof(prompt
),
861 _("Use role \"<a role without a nickname>\" for %s? "),
865 help
= h_norole_confirm
;
866 ekey
[0].name
= "Ret";
867 ekey
[0].label
= prompt_fodder
;
870 ekey
[2].label
= N_("To Select Role");
871 snprintf(prompt
, sizeof(prompt
),
872 _("Press Return to %s using %s role, or ^T to select a role "),
873 prompt_fodder
, default_role
? _("default") : _("no"));
876 prompt
[sizeof(prompt
)-1] = '\0';
878 cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
879 'y', 'x', help
, RB_NORM
);
882 case 'y': /* Accept */
884 *role
= curpat
? curpat
->action
: default_role
;
887 case 'x': /* Cancel */
891 case 'n': /* NoRole */
893 *role
= default_role
;
897 if(role_select_screen(ps_global
, &role_p
, 0) >= 0){
899 for(pat
= first_pattern(&pstate
);
901 pat
= next_pattern(&pstate
)){
902 if(pat
->action
== role_p
){
913 ps_global
->mangled_body
= 1;
914 ps_global
->prev_screen
= prev_screen
;
915 ps_global
->redrawer
= redraw
;
925 * reply_to_all_query - Ask user about replying to all recipients
927 * Returns: -1 if cancel, 0 otherwise
928 * by reference: flagp
931 reply_to_all_query(int *flagp
)
940 ekey
[0].label
= N_("Yes");
944 ekey
[1].label
= N_("No");
950 ps_global
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps_global
);
952 ekey
[2].label
= ps_global
->reply
.preserve_fields
? N_("Not Preserve") : N_("Preserve");
953 snprintf(prompt
, sizeof(prompt
), _("Reply to all recipients%s"),
954 ps_global
->reply
.preserve_fields
? _(" (preserving fields)? ") : "? ");
956 prompt
[sizeof(prompt
)-1] = '\0';
958 switch(cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
959 'n', 'x', h_preserve_field
, RB_NORM
)){
964 case 'y' : /* set reply-all bit */
965 (*flagp
) |= RSF_FORCE_REPLY_ALL
;
968 case 'n' : /* clear reply-all bit */
969 (*flagp
) &= ~RSF_FORCE_REPLY_ALL
;
973 ps_global
->reply
.preserve_fields
=
974 (ps_global
->reply
.preserve_fields
+ 1) % 2;
975 goto loop
; /* ugly, but saves me a variable */
984 * reply_using_replyto_query - Ask user about replying with reply-to value
986 * Returns: 'y' if yes
990 reply_using_replyto_query(void)
992 return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
993 'y', 'x', NO_HELP
,WT_SEQ_SENSITIVE
));
998 * reply_text_query - Ask user about replying with text, or in the case
999 * of alternate reply menu, set values to the answer to all questions
1000 * asked during reply.
1002 * Returns: 1 if include the text
1003 * 0 if we're NOT to include the text
1004 * -1 on cancel or error
1006 #define MAX_REPLY_OPTIONS 10
1008 reply_text_query(struct pine
*ps
, long int many
, ENVELOPE
*env
, char **prefix
)
1010 int ret
, edited
= 0, headers
= 0;
1011 static ESCKEY_S compose_style
[MAX_REPLY_OPTIONS
];
1014 ps
->reply
.use_flowed
= *prefix
&& **prefix
? (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1015 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1016 && (strcmp(*prefix
, "> ") == 0
1017 || strcmp(*prefix
, ">") == 0)) : 0;
1018 ps
->reply
.strip_signature
= ps
->full_header
== 0
1019 && (F_ON(F_ENABLE_STRIP_SIGDASHES
, ps
)
1020 || F_ON(F_ENABLE_SIGDASHES
, ps
));
1021 ps
->reply
.keep_attach
= F_ON(F_ATTACHMENTS_IN_REPLY
, ps
);
1022 ps
->reply
.include_header
= F_ON(F_INCLUDE_HEADER
, ps
);
1023 ps
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps
);
1024 ps
->reply
.signature_bottom
= F_ON(F_SIG_AT_BOTTOM
, ps
);
1025 ps
->reply
.role_chosen
= NULL
;
1027 if(F_OFF(F_ALT_REPLY_MENU
, ps
)
1028 && F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
)
1029 && F_OFF(F_ENABLE_EDIT_REPLY_INDENT
, ps
)
1030 && F_OFF(F_ALT_REPLY_MENU
,ps
))
1034 compose_style
[ekey_num
= 0].ch
= 'y';
1035 compose_style
[ekey_num
].rval
= 'y';
1036 compose_style
[ekey_num
].name
= "Y";
1037 compose_style
[ekey_num
++].label
= N_("Yes");
1039 compose_style
[ekey_num
].ch
= 'n';
1040 compose_style
[ekey_num
].rval
= 'n';
1041 compose_style
[ekey_num
].name
= "N";
1042 compose_style
[ekey_num
++].label
= N_("No");
1044 if (F_OFF(F_ALT_REPLY_MENU
, ps
)){ /**** Standard menu ****/
1045 /* TRANSLATORS: The final five %s's can probably be safely ignored */
1046 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "),
1047 (many
> 1L) ? comatose(many
) : "",
1048 (many
> 1L) ? " " : "",
1049 (many
> 1L) ? "s" : "",
1050 (many
> 1L) ? "s" : "",
1051 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? " (using \"" : "",
1052 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? *prefix
: "",
1053 ps
->reply
.role_chosen
? "\" and role \"" : "",
1054 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "",
1055 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? "\")" : "");
1056 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1058 if (F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
)){
1059 compose_style
[ekey_num
].ch
= ctrl('R');
1060 compose_style
[ekey_num
].rval
= 'r';
1061 compose_style
[ekey_num
].name
= "^R";
1062 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1064 } else { /***** Alternate Reply Menu ********/
1065 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply (using \"%s%s%s\")? "),
1066 (many
> 1L) ? comatose(many
) : "",
1067 (many
> 1L) ? " " : "",
1068 (many
> 1L) ? "s" : "",
1070 ps
->reply
.role_chosen
? "\" and role \"" : "",
1071 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "");
1072 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1074 compose_style
[ekey_num
].ch
= 'h';
1075 compose_style
[ekey_num
].rval
= 'H';
1076 compose_style
[ekey_num
].name
= "H";
1077 compose_style
[ekey_num
++].label
= ps
->reply
.include_header
1078 ? N_("No Header") : N_("Inc Headr");
1080 compose_style
[ekey_num
].ch
= 's';
1081 compose_style
[ekey_num
].rval
= 'S';
1082 compose_style
[ekey_num
].name
= "S";
1083 compose_style
[ekey_num
++].label
= ps
->reply
.strip_signature
1084 ? N_("No Strip"): N_("Strip Sig");
1086 compose_style
[ekey_num
].ch
= 'a';
1087 compose_style
[ekey_num
].rval
= 'A';
1088 compose_style
[ekey_num
].name
= "A";
1089 compose_style
[ekey_num
++].label
= ps
->reply
.keep_attach
1090 ? N_("No Attach"): N_("Inc Attach");
1092 compose_style
[ekey_num
].ch
= 'b';
1093 compose_style
[ekey_num
].rval
= 'B';
1094 compose_style
[ekey_num
].name
= "B";
1095 compose_style
[ekey_num
++].label
= ps
->reply
.signature_bottom
1096 ? N_("Sig Top") : N_("Sig Bottom");
1098 compose_style
[ekey_num
].ch
= 'r';
1099 compose_style
[ekey_num
].rval
= 'R';
1100 compose_style
[ekey_num
].name
= "R";
1101 compose_style
[ekey_num
++].label
= N_("Set Role");
1103 compose_style
[ekey_num
].ch
= ctrl('R');
1104 compose_style
[ekey_num
].rval
= 'r';
1105 compose_style
[ekey_num
].name
= "^R";
1106 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1108 /***** End Alt Reply Menu *********/
1111 compose_style
[ekey_num
].ch
= -1;
1112 compose_style
[ekey_num
].name
= NULL
;
1113 compose_style
[ekey_num
].label
= NULL
;
1115 switch(ret
= radio_buttons(tmp_20k_buf
,
1116 ps
->ttyo
->screen_rows
> 4
1117 ? -FOOTER_ROWS(ps
) : -1,
1119 (edited
|| headers
|| F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
))
1121 'x', NO_HELP
, RB_SEQ_SENSITIVE
)){
1123 cmd_cancelled("Reply");
1127 ps
->reply
.keep_attach
= (ps
->reply
.keep_attach
+ 1) % 2;
1131 ps
->reply
.signature_bottom
= (ps
->reply
.signature_bottom
+ 1) % 2;
1135 ps
->reply
.use_flowed
= (ps
->reply
.use_flowed
+ 1) % 2;
1139 ps
->reply
.strip_signature
= (ps
->reply
.strip_signature
+ 1) % 2;
1143 ps
->reply
.include_header
= (ps
->reply
.include_header
+ 1) % 2;
1144 headers
= ps
->reply
.include_header
;
1149 void (*prev_screen
)(struct pine
*) = ps
->prev_screen
,
1150 (*redraw
)(void) = ps
->redrawer
;
1151 ps
->redrawer
= NULL
;
1152 ps
->next_screen
= SCREEN_FUN_NULL
;
1153 if(role_select_screen(ps
, &ps
->reply
.role_chosen
, 1) < 0){
1154 cmd_cancelled("Reply");
1155 ps
->next_screen
= prev_screen
;
1156 ps
->redrawer
= redraw
;
1161 ps
->next_screen
= prev_screen
;
1162 ps
->redrawer
= redraw
;
1163 if(ps
->reply
.role_chosen
)
1164 ps
->reply
.role_chosen
= combine_inherited_role(ps
->reply
.role_chosen
);
1171 if(prefix
&& *prefix
){
1177 strncpy(buf
, *prefix
, sizeof(buf
)-1);
1178 buf
[sizeof(buf
)-1] = '\0';
1180 flags
= OE_APPEND_CURRENT
|
1181 OE_KEEP_TRAILING_SPACE
|
1185 switch(optionally_enter(buf
, ps
->ttyo
->screen_rows
> 4
1186 ? -FOOTER_ROWS(ps
) : -1,
1187 0, sizeof(buf
), "Reply prefix : ",
1188 NULL
, NO_HELP
, &flags
)){
1189 case 0: /* entry successful, continue */
1190 if(flags
& OE_USER_MODIFIED
){
1191 fs_give((void **)prefix
);
1192 *prefix
= removing_quotes(cpystr(buf
));
1193 ps
->reply
.use_flowed
= *prefix
&& **prefix
?
1194 (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1195 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1196 && (strcmp(*prefix
, "> ") == 0
1197 || strcmp(*prefix
, ">") == 0)) : 0;
1205 cmd_cancelled("Reply");
1214 if(ps_global
->redrawer
!= NULL
)
1225 q_status_message(SM_ORDER
, 3, 4,
1226 "Programmer botch in reply_text_query()");
1241 q_status_message1(SM_ORDER
, 3, 4,
1242 "Invalid rval \'%s\'", pretty_command(ret
));
1250 * reply_poster_followup - return TRUE if "followup-to" set to "poster"
1252 * NOTE: queues status message indicating such
1255 reply_poster_followup(ENVELOPE
*e
)
1257 if(e
&& e
->followup_to
&& !strucmp(e
->followup_to
, "poster")){
1258 q_status_message(SM_ORDER
, 2, 3,
1259 _("Replying to Poster as specified in \"Followup-To\""));
1268 * reply_news_test - Test given envelope for newsgroup data and copy
1269 * it at the users request
1271 * 0 if error or cancel
1273 * 2 follow-up via news
1277 reply_news_test(ENVELOPE
*env
, ENVELOPE
*outgoing
)
1280 static ESCKEY_S news_opt
[] = { {'f', 'f', "F", N_("Follow-up")},
1281 {'r', 'r', "R", N_("Reply")},
1282 {'b', 'b', "B", N_("Both")},
1283 {-1, 0, NULL
, NULL
} };
1285 if(env
->newsgroups
&& *env
->newsgroups
&& !reply_poster_followup(env
))
1287 * Now that we know a newsgroups field is present,
1288 * ask if the user is posting a follow-up article...
1290 switch(radio_buttons(
1291 _("Follow-up to news group(s), Reply via email to author or Both? "),
1292 -FOOTER_ROWS(ps_global
), news_opt
, 'r', 'x',
1294 case 'r' : /* Reply */
1298 case 'f' : /* Follow-Up via news ONLY! */
1302 case 'b' : /* BOTH */
1306 case 'x' : /* cancel or unknown response */
1308 cmd_cancelled("Reply");
1314 if(env
->followup_to
){
1315 q_status_message(SM_ORDER
, 2, 3,
1316 _("Posting to specified Followup-To groups"));
1317 outgoing
->newsgroups
= cpystr(env
->followup_to
);
1319 else if(!outgoing
->newsgroups
)
1320 outgoing
->newsgroups
= cpystr(env
->newsgroups
);
1327 /*----------------------------------------------------------------------
1328 Acquire the pinerc defined signature file
1329 It is allocated here and freed by the caller.
1331 file -- use this file
1332 prenewlines -- prefix the file contents with this many newlines
1333 postnewlines -- postfix the file contents with this many newlines
1334 is_sig -- this is a signature (not a template)
1337 get_signature_file(char *file
, int prenewlines
, int postnewlines
, int is_sig
)
1339 char *sig
, *tmp_sig
= NULL
, sig_path
[MAXPATH
+1];
1340 int len
, do_the_pipe_thang
= 0;
1341 long sigsize
= 0L, cntdown
;
1344 if(!signature_path(file
, sig_path
, MAXPATH
))
1347 dprint((5, "get_signature(%s)\n", sig_path
));
1349 if(sig_path
[(len
=strlen(sig_path
))-1] == '|'){
1350 if(is_sig
&& F_ON(F_DISABLE_PIPES_IN_SIGS
, ps_global
)){
1351 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1352 _("Pipes for signatures are administratively disabled"));
1355 else if(!is_sig
&& F_ON(F_DISABLE_PIPES_IN_TEMPLATES
, ps_global
)){
1356 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1357 _("Pipes for templates are administratively disabled"));
1361 sig_path
[len
-1] = '\0';
1362 removing_trailing_white_space(sig_path
);
1363 do_the_pipe_thang
++;
1366 if(!IS_REMOTE(sig_path
) && ps_global
->VAR_OPER_DIR
&&
1367 !in_dir(ps_global
->VAR_OPER_DIR
, sig_path
)){
1368 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1369 /* TRANSLATORS: First arg is the directory name, second is
1370 the file user wants to read but can't. */
1371 _("Can't read file outside %s: %s"),
1372 ps_global
->VAR_OPER_DIR
, file
);
1377 if(IS_REMOTE(sig_path
) || can_access(sig_path
, ACCESS_EXISTS
) == 0){
1378 if(do_the_pipe_thang
){
1379 if(can_access(sig_path
, EXECUTE_ACCESS
) == 0){
1386 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1388 flags
= PIPE_READ
| PIPE_STDERR
| PIPE_NOSHELL
;
1392 if((syspipe
= open_system_pipe(sig_path
, NULL
, NULL
, flags
, 5,
1393 pipe_callback
, pipe_report_error
)) != NULL
){
1397 gf_set_so_writec(&pc
, store
);
1398 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, READ_FROM_LOCALE
);
1401 if((error
= gf_pipe(gc
, pc
)) != NULL
){
1402 (void)close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1403 gf_clear_so_writec(store
);
1405 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1406 _("Can't get file: %s"), error
);
1410 if(close_system_pipe(&syspipe
, NULL
, pipe_callback
)){
1414 q_status_message2(SM_ORDER
, 3, 4,
1415 _("Error running program \"%s\"%s"),
1417 (now
- start
> 4) ? ": timed out" : "");
1420 gf_clear_so_writec(store
);
1422 /* rewind and count chars */
1423 so_seek(store
, 0L, 0);
1424 while(so_readc(&c
, store
) && sigsize
< 100000L)
1427 /* allocate space */
1428 tmp_sig
= fs_get((sigsize
+ 1) * sizeof(char));
1432 /* rewind and copy chars, no prenewlines... */
1433 so_seek(store
, 0L, 0);
1435 while(so_readc(&c
, store
) && cntdown
-- > 0L)
1443 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1444 _("Error running program \"%s\""),
1449 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1450 "Error allocating space for sig or template program");
1453 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1454 /* TRANSLATORS: Arg is a program name */
1455 _("Can't execute \"%s\": Permission denied"),
1458 else if((IS_REMOTE(sig_path
) &&
1459 (tmp_sig
= simple_read_remote_file(sig_path
, REMOTE_SIG_SUBTYPE
))) ||
1460 (tmp_sig
= read_file(sig_path
, READ_FROM_LOCALE
)))
1461 sigsize
= strlen(tmp_sig
);
1463 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1464 /* TRANSLATORS: First arg is error description, 2nd is
1466 _("Error \"%s\" reading file \"%s\""),
1467 error_description(errno
), sig_path
);
1470 sig
= get_signature_lit(tmp_sig
, prenewlines
, postnewlines
, is_sig
, 0);
1472 fs_give((void **)&tmp_sig
);
1479 /*----------------------------------------------------------------------
1480 Partially set up message to forward and pass off to composer/mailer
1482 Args: pine_state -- The usual pine structure
1484 Result: outgoing envelope and body created and passed off to composer/mailer
1486 Create the outgoing envelope for the mail being forwarded, which is
1487 not much more than filling in the subject, and create the message body
1488 of the outgoing message which requires formatting the header from the
1489 envelope of the original message.
1490 ----------------------------------------------------------------------*/
1492 forward(struct pine
*ps
, ACTION_S
*role_arg
)
1495 int ret
, forward_raw_body
= 0, rv
= 0, i
;
1496 long msgno
= 0L, j
, totalmsgs
, rflags
;
1497 ENVELOPE
*env
= NULL
, *outgoing
;
1498 BODY
*orig_body
, *body
= NULL
;
1500 void *msgtext
= NULL
;
1502 int impl
, template_len
= 0;
1504 REDRAFT_POS_S
*redraft_pos
= NULL
;
1505 ACTION_S
*role
= NULL
, *nrole
;
1506 #if defined(DOS) && !defined(_WINDOWS)
1510 dprint((4, "\n - forward -\n"));
1512 memset((void *)&reply
, 0, sizeof(reply
));
1513 outgoing
= mail_newenvelope();
1515 if(ps_global
->full_header
== 2
1516 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
1517 forward_raw_body
= 1;
1519 if((totalmsgs
= mn_total_cur(ps
->msgmap
)) > 1L){
1520 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s forwarded messages...", comatose(totalmsgs
));
1521 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1522 outgoing
->subject
= cpystr(tmp_20k_buf
);
1525 /*---------- Get the envelope of message we're forwarding ------*/
1526 msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
1527 if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
))
1528 && (outgoing
->subject
= forward_subject(env
, 0)))){
1529 q_status_message1(SM_ORDER
,3,4,
1530 _("Error fetching message %s. Can't forward it."),
1531 long2string(msgno
));
1537 * as with all text bound for the composer, build it in
1538 * a storage object of the type it understands...
1540 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
1541 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1542 _("Error allocating message text"));
1546 ret
= (F_ON(F_FORWARD_AS_ATTACHMENT
, ps_global
))
1549 ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1550 : (ps
->full_header
== 2)
1551 ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1555 cmd_cancelled("Forward");
1556 so_give((STORE_S
**)&msgtext
);
1560 /* Setup possible role */
1562 role
= copy_action(role_arg
);
1565 rflags
= ROLE_FORWARD
;
1566 if(nonempty_patterns(rflags
, &dummy
)){
1567 /* setup default role */
1569 j
= mn_first_cur(ps
->msgmap
);
1572 nrole
= set_role_from_msg(ps
, rflags
,
1573 mn_m2raw(ps
->msgmap
, j
), NULL
);
1574 } while(nrole
&& (!role
|| nrole
== role
)
1575 && (j
=mn_next_cur(ps
->msgmap
)) > 0L);
1577 if(!role
|| nrole
== role
)
1582 if(confirm_role(rflags
, &role
))
1583 role
= combine_inherited_role(role
);
1584 else{ /* cancel reply */
1586 cmd_cancelled("Forward");
1587 so_give((STORE_S
**)&msgtext
);
1594 q_status_message1(SM_ORDER
, 3, 4,
1595 _("Forwarding using role \"%s\""), role
->nick
);
1597 outgoing
->message_id
= generate_message_id(role
);
1599 if(role
&& role
->template){
1603 filtered
= detoken(role
, (totalmsgs
== 1L) ? env
: NULL
,
1604 0, 0, 0, &redraft_pos
, &impl
);
1607 so_puts((STORE_S
*)msgtext
, filtered
);
1609 template_len
= strlen(filtered
);
1612 fs_give((void **)&filtered
);
1618 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
1620 redraft_pos
->offset
+= template_len
;
1622 so_puts((STORE_S
*)msgtext
, *sig
? sig
: NEWLINE
);
1624 fs_give((void **)&sig
);
1627 so_puts((STORE_S
*)msgtext
, NEWLINE
);
1629 gf_set_so_writec(&pc
, (STORE_S
*)msgtext
);
1631 #if defined(DOS) && !defined(_WINDOWS)
1632 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1633 #define IN_RESERVE 8192
1635 #define IN_RESERVE 16384
1637 if((reserve
=(char *)malloc(IN_RESERVE
)) == NULL
){
1638 gf_clear_so_writec((STORE_S
*) msgtext
);
1639 so_give((STORE_S
**)&msgtext
);
1640 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1641 _("Insufficient memory for message text"));
1647 * If we're forwarding multiple messages *or* the forward-as-mime
1648 * is turned on and the users wants it done that way, package things
1651 if(ret
== 'y'){ /* attach message[s]!!! */
1653 long totalsize
= 0L;
1655 /*---- New Body to start with ----*/
1656 body
= mail_newbody();
1657 body
->type
= TYPEMULTIPART
;
1659 /*---- The TEXT part/body ----*/
1660 body
->nested
.part
= mail_newbody_part();
1661 body
->nested
.part
->body
.type
= TYPETEXT
;
1662 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1665 /*---- The MULTIPART/DIGEST part ----*/
1666 body
->nested
.part
->next
= mail_newbody_part();
1667 body
->nested
.part
->next
->body
.type
= TYPEMULTIPART
;
1668 body
->nested
.part
->next
->body
.subtype
= cpystr("Digest");
1669 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Digest of %s messages", comatose(totalmsgs
));
1670 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1671 body
->nested
.part
->next
->body
.description
= cpystr(tmp_20k_buf
);
1672 pp
= &(body
->nested
.part
->next
->body
.nested
.part
);
1675 pp
= &(body
->nested
.part
->next
);
1677 /*---- The Message body subparts ----*/
1678 for(msgno
= mn_first_cur(ps
->msgmap
);
1680 msgno
= mn_next_cur(ps
->msgmap
)){
1682 msgno
= mn_m2raw(ps
->msgmap
, msgno
);
1683 env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
);
1685 if(forward_mime_msg(ps
->mail_stream
,msgno
,NULL
,env
,pp
,msgtext
)){
1686 totalsize
+= (*pp
)->body
.size
.bytes
;
1687 pp
= &((*pp
)->next
);
1694 body
->nested
.part
->next
->body
.size
.bytes
= totalsize
;
1696 else if(totalmsgs
> 1L){
1698 body
= mail_newbody();
1699 body
->type
= TYPETEXT
;
1700 body
->contents
.text
.data
= msgtext
;
1703 for(msgno
= mn_first_cur(ps
->msgmap
);
1705 msgno
= mn_next_cur(ps
->msgmap
)){
1707 if(env
){ /* put 2 between messages */
1708 gf_puts(NEWLINE
, pc
);
1709 gf_puts(NEWLINE
, pc
);
1712 /*--- Grab current envelope ---*/
1713 env
= pine_mail_fetchstructure(ps
->mail_stream
,
1714 mn_m2raw(ps
->msgmap
, msgno
),
1716 if(!env
|| !orig_body
){
1717 q_status_message1(SM_ORDER
,3,4,
1718 _("Error fetching message %s. Can't forward it."),
1719 long2string(msgno
));
1723 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| forward_raw_body
) {
1724 forward_delimiter(pc
);
1725 reply_forward_header(ps
->mail_stream
,
1726 mn_m2raw(ps
->msgmap
, msgno
),
1729 if(!get_body_part_text(ps
->mail_stream
, forward_raw_body
? NULL
: orig_body
,
1730 mn_m2raw(ps
->msgmap
, msgno
),
1731 forward_raw_body
? NULL
: "1", 0L, pc
,
1732 NULL
, NULL
, GBPT_NONE
))
1734 } else if(orig_body
->type
== TYPEMULTIPART
) {
1736 q_status_message(SM_ORDER
,3,7,
1737 _("WARNING! Attachments not included in multiple forward."));
1739 if(orig_body
->nested
.part
&&
1740 orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
1741 /*---- First part of the message is text -----*/
1742 forward_delimiter(pc
);
1743 reply_forward_header(ps
->mail_stream
,
1744 mn_m2raw(ps
->msgmap
,msgno
),
1747 if(!get_body_part_text(ps
->mail_stream
,
1748 &orig_body
->nested
.part
->body
,
1749 mn_m2raw(ps
->msgmap
, msgno
),
1751 NULL
, NULL
, GBPT_NONE
))
1754 q_status_message(SM_ORDER
,0,3,
1755 _("Multipart with no leading text part!"));
1758 /*---- Single non-text message of some sort ----*/
1759 q_status_message(SM_ORDER
,0,3,
1760 _("Non-text message not included!"));
1764 else if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
,
1766 && (body
= forward_body(ps
->mail_stream
, env
, orig_body
, msgno
,
1769 q_status_message1(SM_ORDER
,3,4,
1770 _("Error fetching message %s. Can't forward it."),
1771 long2string(msgno
));
1775 if(ret
!= 'y' && totalmsgs
== 1L && orig_body
){
1778 charset
= parameter_val(orig_body
->parameter
, "charset");
1779 if(charset
&& strucmp(charset
, "us-ascii") != 0){
1783 * There is a non-ascii charset, is there conversion happening?
1785 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
1786 reply
.orig_charset
= charset
;
1792 fs_give((void **) &charset
);
1795 * I don't think orig_charset is ever used except possibly
1796 * right here. Hubert 2008-01-15.
1798 if(reply
.orig_charset
)
1802 /* fill in reply structure */
1803 reply
.forwarded
= 1;
1804 reply
.mailbox
= cpystr(ps
->mail_stream
->mailbox
);
1805 reply
.origmbox
= cpystr(ps
->mail_stream
->original_mailbox
1806 ? ps
->mail_stream
->original_mailbox
1807 : ps
->mail_stream
->mailbox
);
1808 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((totalmsgs
+ 1) * sizeof(imapuid_t
));
1809 if((reply
.data
.uid
.validity
= ps
->mail_stream
->uid_validity
) != 0){
1811 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1813 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1814 reply
.data
.uid
.msgs
[i
] = mail_uid(ps
->mail_stream
, mn_m2raw(ps
->msgmap
, msgno
));
1818 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1820 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1821 reply
.data
.uid
.msgs
[i
] = mn_m2raw(ps
->msgmap
, msgno
);
1824 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
1826 #if defined(DOS) && !defined(_WINDOWS)
1827 free((void *)reserve
);
1829 pine_send(outgoing
, &body
, "FORWARD MESSAGE",
1830 role
, NULL
, &reply
, redraft_pos
,
1836 pine_free_body(&body
);
1838 if((STORE_S
*) msgtext
)
1839 gf_clear_so_writec((STORE_S
*) msgtext
);
1841 mail_free_envelope(&outgoing
);
1842 free_redraft_pos(&redraft_pos
);
1845 if(reply
.orig_charset
)
1846 fs_give((void **)&reply
.orig_charset
);
1851 q_status_message(SM_ORDER
| SM_DING
, 4, 5,
1852 _("Error fetching message contents. Can't forward message."));
1857 /*----------------------------------------------------------------------
1858 Partially set up message to forward and pass off to composer/mailer
1860 Args: pine_state -- The usual pine structure
1861 message -- The MESSAGECACHE of entry to reply to
1863 Result: outgoing envelope and body created and passed off to composer/mailer
1865 Create the outgoing envelope for the mail being forwarded, which is
1866 not much more than filling in the subject, and create the message body
1867 of the outgoing message which requires formatting the header from the
1868 envelope of the original message.
1869 ----------------------------------------------------------------------*/
1871 forward_text(struct pine
*pine_state
, void *text
, SourceType source
)
1877 char *enc_error
, *sig
;
1878 ACTION_S
*role
= NULL
;
1880 long rflags
= ROLE_COMPOSE
;
1882 if((msgtext
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1883 env
= mail_newenvelope();
1884 body
= mail_newbody();
1885 body
->type
= TYPETEXT
;
1886 body
->contents
.text
.data
= (void *) msgtext
;
1888 if(nonempty_patterns(rflags
, &dummy
)){
1890 * This is really more like Compose, even though it
1891 * is called Forward.
1893 if(confirm_role(rflags
, &role
))
1894 role
= combine_inherited_role(role
);
1896 cmd_cancelled("Composition");
1897 display_message('x');
1898 mail_free_envelope(&env
);
1899 pine_free_body(&body
);
1905 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
1908 env
->message_id
= generate_message_id(role
);
1910 sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
);
1911 so_puts(msgtext
, (sig
&& *sig
) ? sig
: NEWLINE
);
1912 so_puts(msgtext
, NEWLINE
);
1913 so_puts(msgtext
, "----- Included text -----");
1914 so_puts(msgtext
, NEWLINE
);
1916 fs_give((void **)&sig
);
1919 gf_set_so_writec(&pc
, msgtext
);
1920 gf_set_readc(&gc
,text
,(source
== CharStar
) ? strlen((char *)text
) : 0L,
1923 if((enc_error
= gf_pipe(gc
, pc
)) == NULL
){
1924 pine_send(env
, &body
, "SEND MESSAGE", role
, NULL
, NULL
, NULL
,
1926 pine_state
->mangled_screen
= 1;
1929 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1930 _("Error reading text \"%s\""),enc_error
);
1931 display_message('x');
1934 gf_clear_so_writec(msgtext
);
1935 mail_free_envelope(&env
);
1936 pine_free_body(&body
);
1939 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1940 _("Error allocating message text"));
1941 display_message('x');
1948 /*----------------------------------------------------------------------
1949 Partially set up message to resend and pass off to mailer
1951 Args: pine_state -- The usual pine structure
1953 Result: outgoing envelope and body created and passed off to mailer
1955 Create the outgoing envelope for the mail being resent, which is
1956 not much more than filling in the subject, and create the message body
1957 of the outgoing message which requires formatting the header from the
1958 envelope of the original message.
1959 ----------------------------------------------------------------------*/
1961 bounce(struct pine
*pine_state
, ACTION_S
*role
)
1965 char *save_to
= NULL
, **save_toptr
= NULL
, *errstr
= NULL
,
1966 *prmpt_who
= NULL
, *prmpt_cnf
= NULL
;
1968 dprint((4, "\n - bounce -\n"));
1970 if(mn_total_cur(pine_state
->msgmap
) > 1L){
1971 save_toptr
= &save_to
;
1973 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1974 mn_total_cur(pine_state
->msgmap
), role
->nick
);
1976 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages to : "),
1977 mn_total_cur(pine_state
->msgmap
));
1978 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1979 prmpt_who
= cpystr(tmp_20k_buf
);
1980 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Send %ld messages "),
1981 mn_total_cur(pine_state
->msgmap
));
1982 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1983 prmpt_cnf
= cpystr(tmp_20k_buf
);
1986 for(msgno
= mn_first_cur(pine_state
->msgmap
);
1988 msgno
= mn_next_cur(pine_state
->msgmap
)){
1990 rawno
= mn_m2raw(pine_state
->msgmap
, msgno
);
1991 if((env
= pine_mail_fetchstructure(pine_state
->mail_stream
, rawno
, NULL
)) != NULL
)
1992 errstr
= bounce_msg(pine_state
->mail_stream
, rawno
, NULL
, role
,
1993 save_toptr
, env
->subject
, prmpt_who
, prmpt_cnf
);
1995 errstr
= _("Can't fetch Subject for Bounce");
2000 q_status_message(SM_ORDER
| SM_DING
, 4, 7, errstr
);
2007 fs_give((void **)&save_to
);
2010 fs_give((void **) &prmpt_who
);
2013 fs_give((void **) &prmpt_cnf
);
2015 return(errstr
? 0 : 1);
2021 bounce_msg(MAILSTREAM
*stream
,
2030 char *errstr
= NULL
;
2037 /* When we bounce a message, we will leave the original message
2038 * intact, which means that it will not be signed or encrypted,
2039 * so we turn off signing and encrypting now. It will be turned
2040 * on again in send_exit_for_pico().
2042 if(ps_global
->smime
)
2043 ps_global
->smime
->do_sign
= ps_global
->smime
->do_encrypt
= 0;
2046 if((errstr
= bounce_msg_body(stream
, rawno
, part
, to
, subject
, &outgoing
, &body
, &was_seen
)) == NULL
){
2047 if(pine_simple_send(outgoing
, &body
, &role
, pmt_who
, pmt_cnf
, to
,
2048 !(to
&& *to
) ? SS_PROMPTFORTO
: 0) < 0){
2049 errstr
= ""; /* p_s_s() better have explained! */
2050 /* clear seen flag */
2051 if(was_seen
== 0 && rawno
> 0L
2052 && stream
&& rawno
<= stream
->nmsgs
2053 && (mc
= mail_elt(stream
, rawno
)) && mc
->seen
)
2054 mail_flag(stream
, long2string(rawno
), "\\SEEN", 0);
2058 /* Just for good measure... */
2059 mail_free_envelope(&outgoing
);
2060 pine_free_body(&body
);
2062 return(errstr
); /* no problem-o */
2066 /*----------------------------------------------------------------------
2067 Serve up the current signature within pico for editing
2071 Result: signature changed or not.
2074 signature_edit(char *sigfile
, char *title
)
2077 char sig_path
[MAXPATH
+1], errbuf
[2000], *errstr
= NULL
;
2079 STORE_S
*msgso
, *tmpso
= NULL
;
2082 struct variable
*vars
= ps_global
->vars
;
2083 REMDATA_S
*rd
= NULL
;
2085 if(!signature_path(sigfile
, sig_path
, MAXPATH
))
2086 return(cpystr(_("No signature file defined.")));
2088 if(IS_REMOTE(sigfile
)){
2089 rd
= rd_create_remote(RemImap
, sig_path
, REMOTE_SIG_SUBTYPE
,
2091 _("Can't access remote configuration."));
2093 return(cpystr(_("Error attempting to edit remote configuration")));
2095 (void)rd_read_metadata(rd
);
2097 if(rd
->access
== MaybeRorW
){
2098 if(rd
->read_status
== 'R')
2099 rd
->access
= ReadOnly
;
2101 rd
->access
= ReadWrite
;
2104 if(rd
->access
!= NoExists
){
2106 rd_check_remvalid(rd
, 1L);
2109 * If the cached info says it is readonly but
2110 * it looks like it's been fixed now, change it to readwrite.
2112 if(rd
->read_status
== 'R'){
2113 rd_check_readonly_access(rd
);
2114 if(rd
->read_status
== 'W'){
2115 rd
->access
= ReadWrite
;
2116 rd
->flags
|= REM_OUTOFDATE
;
2119 rd
->access
= ReadOnly
;
2123 if(rd
->flags
& REM_OUTOFDATE
){
2124 if(rd_update_local(rd
) != 0){
2127 "signature_edit: rd_update_local failed\n"));
2128 rd_close_remdata(&rd
);
2129 return(cpystr(_("Can't access remote sig")));
2135 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
2136 rd_close_remdata(&rd
);
2137 return(cpystr(_("Can't get write permission for remote sig")));
2140 rd
->flags
|= DO_REMTRIM
;
2142 strncpy(sig_path
, rd
->lf
, sizeof(sig_path
)-1);
2143 sig_path
[sizeof(sig_path
)-1] = '\0';
2146 standard_picobuf_setup(&pbf
);
2147 pbf
.tty_fix
= PineRaw
;
2148 pbf
.composer_help
= h_composer_sigedit
;
2149 pbf
.exittest
= sigedit_exit_for_pico
;
2150 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2151 ? upload_msg_to_pico
: NULL
;
2152 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2153 ? VAR_EDITOR
: NULL
;
2154 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2155 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2156 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2157 pbf
.allow_flowed_text
= 0;
2159 pbf
.pine_anchor
= set_titlebar(title
,
2160 ps_global
->mail_stream
,
2161 ps_global
->context_current
,
2162 ps_global
->cur_folder
,
2164 0, FolderName
, 0, 0, NULL
);
2166 /* NOTE: at this point, a lot of pico struct fields are null'd out
2167 * thanks to the leading memset; in particular "headents" which tells
2168 * pico to behave like a normal editor (though modified slightly to
2169 * let the caller dictate the file to edit and such)...
2172 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, sig_path
)){
2175 l
= strlen(VAR_OPER_DIR
) + 100;
2176 ret
= (char *) fs_get((l
+1) * sizeof(char));
2177 snprintf(ret
, l
+1, _("Can't edit file outside of %s"), VAR_OPER_DIR
);
2183 * Now alloc and init the text to pass pico
2185 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2186 ret
= cpystr(_("Error allocating space for file"));
2187 dprint((1, "Can't alloc space for signature_edit"));
2191 pbf
.msgtext
= so_text(msgso
);
2193 if(can_access(sig_path
, READ_ACCESS
) == 0
2194 && !(tmpso
= so_get(FileStar
, sig_path
, READ_ACCESS
|READ_FROM_LOCALE
))){
2195 char *problem
= error_description(errno
);
2197 snprintf(errbuf
, sizeof(errbuf
), _("Error editing \"%s\": %s"),
2198 sig_path
, problem
? problem
: "<NULL>");
2199 errbuf
[sizeof(errbuf
)-1] = '\0';
2200 ret
= cpystr(errbuf
);
2202 dprint((1, "signature_edit: can't open %s: %s", sig_path
,
2203 problem
? problem
: "<NULL>"));
2206 else if(tmpso
){ /* else, fill pico's edit buffer */
2207 gf_set_so_readc(&gc
, tmpso
); /* read from file, write pico buf */
2208 gf_set_so_writec(&pc
, msgso
);
2209 gf_filter_init(); /* no filters needed */
2210 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2211 snprintf(errbuf
, sizeof(errbuf
), _("Error reading file: \"%s\""), errstr
);
2212 errbuf
[sizeof(errbuf
)-1] = '\0';
2213 ret
= cpystr(errbuf
);
2216 gf_clear_so_readc(tmpso
);
2217 gf_clear_so_writec(msgso
);
2223 mswin_setwindowmenu (MENU_COMPOSER
);
2226 /*------ OK, Go edit the signature ------*/
2227 editor_result
= pico(&pbf
);
2230 mswin_setwindowmenu (MENU_DEFAULT
);
2232 if(editor_result
& COMP_GOTHUP
){
2233 hup_signal(); /* do what's normal for a hup */
2236 fix_windsize(ps_global
);
2240 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2243 /*------ Must have an edited buffer, write it to .sig -----*/
2244 our_unlink(sig_path
); /* blast old copy */
2245 if((tmpso
= so_get(FileStar
, sig_path
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
2246 so_seek(msgso
, 0L, 0);
2247 gf_set_so_readc(&gc
, msgso
); /* read from pico buf */
2248 gf_set_so_writec(&pc
, tmpso
); /* write sig file */
2249 gf_filter_init(); /* no filters needed */
2250 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2251 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2253 errbuf
[sizeof(errbuf
)-1] = '\0';
2254 ret
= cpystr(errbuf
);
2257 gf_clear_so_readc(msgso
);
2258 gf_clear_so_writec(tmpso
);
2259 if(so_give(&tmpso
)){
2260 errstr
= error_description(errno
);
2261 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2263 errbuf
[sizeof(errbuf
)-1] = '\0';
2264 ret
= cpystr(errbuf
);
2267 if(IS_REMOTE(sigfile
)){
2273 we_cancel
= busy_cue("Copying to remote sig", NULL
, 1);
2274 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
2276 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2277 _("Error opening temporary sig file %s: %s"),
2278 rd
->lf
, error_description(errno
));
2280 "write_remote_sig: error opening temp file %s\n",
2281 rd
->lf
? rd
->lf
: "?"));
2284 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2285 _("Error copying to %s: %s"),
2286 rd
->rn
, error_description(errno
));
2288 "write_remote_sig: error copying from %s to %s\n",
2289 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
2292 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
2293 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2296 rd_update_metadata(rd
, datebuf
);
2297 rd
->read_status
= 'W';
2300 rd_close_remdata(&rd
);
2303 cancel_busy_cue(-1);
2307 snprintf(errbuf
, sizeof(errbuf
), _("Error writing \"%s\""), sig_path
);
2308 errbuf
[sizeof(errbuf
)-1] = '\0';
2309 ret
= cpystr(errbuf
);
2310 dprint((1, "signature_edit: can't write %s",
2316 standard_picobuf_teardown(&pbf
);
2322 /*----------------------------------------------------------------------
2323 Serve up the current signature within pico for editing
2325 Args: literal signature to edit
2327 Result: raw edited signature is returned in result arg
2330 signature_edit_lit(char *litsig
, char **result
, char *title
, HelpType composer_help
)
2333 char *errstr
= NULL
;
2337 struct variable
*vars
= ps_global
->vars
;
2339 standard_picobuf_setup(&pbf
);
2340 pbf
.tty_fix
= PineRaw
;
2341 pbf
.search_help
= h_sigedit_search
;
2342 pbf
.composer_help
= composer_help
;
2343 pbf
.exittest
= sigedit_exit_for_pico
;
2344 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2345 ? upload_msg_to_pico
: NULL
;
2346 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2347 ? VAR_EDITOR
: NULL
;
2348 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2349 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2350 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2351 pbf
.allow_flowed_text
= 0;
2353 pbf
.pine_anchor
= set_titlebar(title
,
2354 ps_global
->mail_stream
,
2355 ps_global
->context_current
,
2356 ps_global
->cur_folder
,
2358 0, FolderName
, 0, 0, NULL
);
2360 /* NOTE: at this point, a lot of pico struct fields are null'd out
2361 * thanks to the leading memset; in particular "headents" which tells
2362 * pico to behave like a normal editor (though modified slightly to
2363 * let the caller dictate the file to edit and such)...
2367 * Now alloc and init the text to pass pico
2369 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2370 ret
= cpystr(_("Error allocating space"));
2371 dprint((1, "Can't alloc space for signature_edit_lit"));
2375 pbf
.msgtext
= so_text(msgso
);
2377 so_puts(msgso
, litsig
? litsig
: "");
2382 mswin_setwindowmenu (MENU_COMPOSER
);
2385 /*------ OK, Go edit the signature ------*/
2386 editor_result
= pico(&pbf
);
2389 mswin_setwindowmenu (MENU_DEFAULT
);
2391 if(editor_result
& COMP_GOTHUP
){
2392 hup_signal(); /* do what's normal for a hup */
2395 fix_windsize(ps_global
);
2399 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2400 ret
= cpystr(_("Edit Cancelled"));
2403 /*------ Must have an edited buffer, write it to .sig -----*/
2408 so_seek(msgso
, 0L, 0);
2409 while(so_readc(&c
, msgso
))
2412 *result
= (char *)fs_get((cnt
+1) * sizeof(char));
2414 so_seek(msgso
, 0L, 0);
2415 while(so_readc(&c
, msgso
))
2422 standard_picobuf_teardown(&pbf
);
2429 * Returns 0 for Save Changes and exit
2431 * -1 exit but Dont Save Changes
2434 sigedit_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
2439 void (*redraw
)(void) = ps_global
->redrawer
;
2440 static ESCKEY_S opts
[] = {
2441 {'s', 's', "S", N_("Save changes")},
2442 {'d', 'd', "D", N_("Don't save changes")},
2446 ps_global
->redrawer
= redraw_pico
;
2447 fix_windsize(ps_global
);
2450 rv
= radio_buttons(_("Exit editor? "),
2451 -FOOTER_ROWS(ps_global
), opts
,
2452 's', 'x', h_exit_editor
, RB_NORM
);
2453 if(rv
== 's'){ /* user ACCEPTS! */
2456 else if(rv
== 'd'){ /* Declined! */
2457 rstr
= _("No Changes Saved");
2460 else if(rv
== 'x'){ /* Cancelled! */
2461 rstr
= _("Exit Cancelled");
2469 ps_global
->redrawer
= redraw
;
2470 return((rv
== 's') ? 0 : (rv
== 'd') ? -1 : 1);
2475 * Common stuff we almost always want to set when calling pico.
2478 standard_picobuf_setup(PICO
*pbf
)
2480 memset(pbf
, 0, sizeof(*pbf
));
2482 pbf
->pine_version
= ALPINE_VERSION
;
2483 pbf
->fillcolumn
= ps_global
->composer_fillcol
;
2484 pbf
->menu_rows
= FOOTER_ROWS(ps_global
) - 1;
2485 pbf
->colors
= colors_for_pico();
2486 pbf
->wordseps
= user_wordseps(ps_global
->VAR_WORDSEPS
);
2487 pbf
->helper
= helper
;
2488 pbf
->showmsg
= display_message_for_pico
;
2489 pbf
->suspend
= do_suspend
;
2490 pbf
->keybinput
= cmd_input_for_pico
;
2491 pbf
->tty_fix
= ttyfix
; /* watch out for this one */
2492 pbf
->newmail
= new_mail_for_pico
;
2493 pbf
->ckptdir
= checkpoint_dir_for_pico
;
2494 pbf
->resize
= resize_for_pico
;
2495 pbf
->input_cs
= ps_global
->input_cs
;
2496 pbf
->winch_cleanup
= winch_cleanup
;
2497 pbf
->search_help
= h_composer_search
;
2498 pbf
->ins_help
= h_composer_ins
;
2499 pbf
->ins_m_help
= h_composer_ins_m
;
2500 pbf
->composer_help
= h_composer
;
2501 pbf
->browse_help
= h_composer_browse
;
2502 pbf
->attach_help
= h_composer_ctrl_j
;
2505 ( (F_ON(F_CAN_SUSPEND
,ps_global
) ? P_SUSPEND
: 0L)
2506 | (F_ON(F_USE_FK
,ps_global
) ? P_FKEYS
: 0L)
2507 | (ps_global
->restricted
? P_SECURE
: 0L)
2508 | (F_ON(F_ALT_ED_NOW
,ps_global
) ? P_ALTNOW
: 0L)
2509 | (F_ON(F_USE_CURRENT_DIR
,ps_global
) ? P_CURDIR
: 0L)
2510 | (F_ON(F_SUSPEND_SPAWNS
,ps_global
) ? P_SUBSHELL
: 0L)
2511 | (F_ON(F_COMPOSE_MAPS_DEL
,ps_global
) ? P_DELRUBS
: 0L)
2512 | (F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
) ? P_COMPLETE
: 0L)
2513 | (F_ON(F_SHOW_CURSOR
,ps_global
) ? P_SHOCUR
: 0L)
2514 | (F_ON(F_DEL_FROM_DOT
,ps_global
) ? P_DOTKILL
: 0L)
2515 | (F_ON(F_ENABLE_DOT_FILES
,ps_global
) ? P_DOTFILES
: 0L)
2516 | (F_ON(F_ALLOW_GOTO
,ps_global
) ? P_ALLOW_GOTO
: 0L)
2517 | (F_ON(F_ENABLE_SEARCH_AND_REPL
,ps_global
) ? P_REPLACE
: 0L)
2518 | (!ps_global
->pass_ctrl_chars
2519 && !ps_global
->pass_c1_ctrl_chars
? P_HICTRL
: 0L)
2520 | ((F_ON(F_ENABLE_ALT_ED
,ps_global
)
2521 || F_ON(F_ALT_ED_NOW
,ps_global
)
2522 || (ps_global
->VAR_EDITOR
2523 && ps_global
->VAR_EDITOR
[0]
2524 && ps_global
->VAR_EDITOR
[0][0]))
2526 | ((!ps_global
->keyboard_charmap
2527 || !strucmp(ps_global
->keyboard_charmap
, "US-ASCII"))
2528 ? P_HIBITIGN
: 0L));
2530 if(ps_global
->VAR_OPER_DIR
){
2531 pbf
->oper_dir
= ps_global
->VAR_OPER_DIR
;
2532 pbf
->pine_flags
|= P_TREE
;
2535 pbf
->home_dir
= ps_global
->home_dir
;
2540 standard_picobuf_teardown(PICO
*pbf
)
2544 free_pcolors(&pbf
->colors
);
2547 fs_give((void **) &pbf
->wordseps
);
2552 /*----------------------------------------------------------------------
2553 Call back for pico to use to check for new mail.
2555 Args: cursor -- pointer to in to tell caller if cursor location changed
2556 if NULL, turn off cursor positioning.
2557 timing -- whether or not it's a good time to check
2560 Returns: returns 1 on success, zero on error.
2563 new_mail_for_pico(int timing
, int status
)
2566 * If we're not interested in the status, don't display the busy
2569 /* don't know where the cursor's been, reset it */
2571 return(new_mail(0, timing
,
2572 (status
? NM_STATUS_MSG
: NM_NONE
) | NM_DEFER_SORT
2573 | NM_FROM_COMPOSER
));
2578 cmd_input_for_pico(void)
2580 zero_new_mail_count();
2584 /*----------------------------------------------------------------------
2585 Call back for pico to get newmail status messages displayed
2587 Args: x -- char processed
2592 display_message_for_pico(int x
)
2596 clear_cursor_pos(); /* can't know where cursor is */
2597 mark_status_dirty(); /* don't count on cached text */
2598 fix_windsize(ps_global
);
2601 rv
= ps_global
->mangled_screen
;
2602 ps_global
->mangled_screen
= 0;
2607 /*----------------------------------------------------------------------
2608 Call back for pico to get desired directory for its check point file
2610 Args: s -- buffer to write directory name
2611 n -- length of that buffer
2613 Returns: pointer to static buffer
2616 checkpoint_dir_for_pico(char *s
, size_t n
)
2618 #if defined(DOS) || defined(OS2)
2620 * we can't assume anything about root or home dirs, so
2621 * just plunk it down in the same place as the pinerc
2623 if(!getenv("HOME")){
2624 char *lc
= last_cmpnt(ps_global
->pinerc
);
2627 strncpy(s
, ps_global
->pinerc
, MIN(n
-1,lc
-ps_global
->pinerc
));
2628 s
[MIN(n
-1,lc
-ps_global
->pinerc
)] = '\0';
2631 strncpy(s
, ".\\", n
-1);
2637 strncpy(s
, ps_global
->home_dir
, n
-1);
2644 /*----------------------------------------------------------------------
2645 Call back for pico to tell us the window size's changed
2649 Returns: none (but pine's ttyo structure may have been updated)
2652 resize_for_pico(void)
2654 fix_windsize(ps_global
);
2659 colors_for_pico(void)
2661 PCOLORS
*colors
= NULL
;
2662 struct variable
*vars
= ps_global
->vars
;
2664 if (pico_usingcolor()){
2665 colors
= (PCOLORS
*)fs_get(sizeof(PCOLORS
));
2667 colors
->tbcp
= current_titlebar_color();
2669 if (VAR_KEYLABEL_FORE_COLOR
&& VAR_KEYLABEL_BACK_COLOR
){
2670 colors
->klcp
= new_color_pair(VAR_KEYLABEL_FORE_COLOR
,
2671 VAR_KEYLABEL_BACK_COLOR
);
2672 if (!pico_is_good_colorpair(colors
->klcp
))
2673 free_color_pair(&colors
->klcp
);
2675 else colors
->klcp
= NULL
;
2677 if (colors
->klcp
&& VAR_KEYNAME_FORE_COLOR
&& VAR_KEYNAME_BACK_COLOR
){
2678 colors
->kncp
= new_color_pair(VAR_KEYNAME_FORE_COLOR
,
2679 VAR_KEYNAME_BACK_COLOR
);
2681 else colors
->kncp
= NULL
;
2683 if (VAR_STATUS_FORE_COLOR
&& VAR_STATUS_BACK_COLOR
){
2684 colors
->stcp
= new_color_pair(VAR_STATUS_FORE_COLOR
,
2685 VAR_STATUS_BACK_COLOR
);
2687 else colors
->stcp
= NULL
;
2689 if (VAR_PROMPT_FORE_COLOR
&& VAR_PROMPT_BACK_COLOR
){
2690 colors
->prcp
= new_color_pair(VAR_PROMPT_FORE_COLOR
,
2691 VAR_PROMPT_BACK_COLOR
);
2693 else colors
->prcp
= NULL
;
2695 if (VAR_QUOTE1_FORE_COLOR
&& VAR_QUOTE1_BACK_COLOR
){
2696 colors
->qlcp
= new_color_pair(VAR_QUOTE1_FORE_COLOR
,
2697 VAR_QUOTE1_BACK_COLOR
);
2699 else colors
->qlcp
= NULL
;
2701 if (VAR_QUOTE2_FORE_COLOR
&& VAR_QUOTE2_BACK_COLOR
){
2702 colors
->qllcp
= new_color_pair(VAR_QUOTE2_FORE_COLOR
,
2703 VAR_QUOTE2_BACK_COLOR
);
2705 else colors
->qllcp
= NULL
;
2707 if (VAR_QUOTE3_FORE_COLOR
&& VAR_QUOTE3_BACK_COLOR
){
2708 colors
->qlllcp
= new_color_pair(VAR_QUOTE3_FORE_COLOR
,
2709 VAR_QUOTE3_BACK_COLOR
);
2711 else colors
->qlllcp
= NULL
;
2713 if (VAR_NORM_FORE_COLOR
&& VAR_NORM_BACK_COLOR
){
2714 colors
->ntcp
= new_color_pair(VAR_NORM_FORE_COLOR
,
2715 VAR_NORM_BACK_COLOR
);
2717 else colors
->ntcp
= NULL
;
2719 if (VAR_REV_FORE_COLOR
&& VAR_REV_BACK_COLOR
){
2720 colors
->rtcp
= new_color_pair(VAR_REV_FORE_COLOR
,
2721 VAR_REV_BACK_COLOR
);
2723 else colors
->rtcp
= NULL
;
2725 if (VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
){
2726 colors
->sbcp
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
2727 VAR_SIGNATURE_BACK_COLOR
);
2729 else colors
->sbcp
= NULL
;
2737 free_pcolors(PCOLORS
**colors
)
2740 if ((*colors
)->tbcp
)
2741 free_color_pair(&(*colors
)->tbcp
);
2742 if ((*colors
)->kncp
)
2743 free_color_pair(&(*colors
)->kncp
);
2744 if ((*colors
)->klcp
)
2745 free_color_pair(&(*colors
)->klcp
);
2746 if ((*colors
)->stcp
)
2747 free_color_pair(&(*colors
)->stcp
);
2748 if ((*colors
)->prcp
)
2749 free_color_pair(&(*colors
)->prcp
);
2750 if ((*colors
)->qlcp
)
2751 free_color_pair(&(*colors
)->qlcp
);
2752 if ((*colors
)->qllcp
)
2753 free_color_pair(&(*colors
)->qllcp
);
2754 if ((*colors
)->qlllcp
)
2755 free_color_pair(&(*colors
)->qlllcp
);
2756 if ((*colors
)->ntcp
)
2757 free_color_pair(&(*colors
)->ntcp
);
2758 if ((*colors
)->rtcp
)
2759 free_color_pair(&(*colors
)->rtcp
);
2760 if ((*colors
)->sbcp
)
2761 free_color_pair(&(*colors
)->sbcp
);
2762 fs_give((void **)colors
);