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(UCS
);
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){
1387 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1389 flags
= PIPE_READ
| PIPE_STDERR
| PIPE_NOSHELL
;
1393 if((syspipe
= open_system_pipe(sig_path
, NULL
, NULL
, flags
, 5,
1394 pipe_callback
, pipe_report_error
)) != NULL
){
1398 gf_set_so_writec(&pc
, store
);
1399 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, READ_FROM_LOCALE
);
1402 if((error
= gf_pipe(gc
, pc
)) != NULL
){
1403 (void)close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1404 gf_clear_so_writec(store
);
1406 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1407 _("Can't get file: %s"), error
);
1411 if(close_system_pipe(&syspipe
, NULL
, pipe_callback
)){
1415 q_status_message2(SM_ORDER
, 3, 4,
1416 _("Error running program \"%s\"%s"),
1418 (now
- start
> 4) ? ": timed out" : "");
1421 gf_clear_so_writec(store
);
1423 /* rewind and count chars */
1424 so_seek(store
, 0L, 0);
1425 while(so_readc(&c
, store
) && sigsize
< 100000L)
1428 /* allocate space */
1429 tmp_sig
= fs_get((sigsize
+ 1) * sizeof(char));
1433 /* rewind and copy chars, no prenewlines... */
1434 so_seek(store
, 0L, 0);
1436 while(so_readc(&c
, store
) && cntdown
-- > 0L)
1444 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1445 _("Error running program \"%s\""),
1450 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1451 "Error allocating space for sig or template program");
1454 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1455 /* TRANSLATORS: Arg is a program name */
1456 _("Can't execute \"%s\": Permission denied"),
1459 else if((IS_REMOTE(sig_path
) &&
1460 (tmp_sig
= simple_read_remote_file(sig_path
, REMOTE_SIG_SUBTYPE
))) ||
1461 (tmp_sig
= read_file(sig_path
, READ_FROM_LOCALE
)))
1462 sigsize
= strlen(tmp_sig
);
1464 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1465 /* TRANSLATORS: First arg is error description, 2nd is
1467 _("Error \"%s\" reading file \"%s\""),
1468 error_description(errno
), sig_path
);
1471 sig
= get_signature_lit(tmp_sig
, prenewlines
, postnewlines
, is_sig
, 0);
1473 fs_give((void **)&tmp_sig
);
1480 /*----------------------------------------------------------------------
1481 Partially set up message to forward and pass off to composer/mailer
1483 Args: pine_state -- The usual pine structure
1485 Result: outgoing envelope and body created and passed off to composer/mailer
1487 Create the outgoing envelope for the mail being forwarded, which is
1488 not much more than filling in the subject, and create the message body
1489 of the outgoing message which requires formatting the header from the
1490 envelope of the original message.
1491 ----------------------------------------------------------------------*/
1493 forward(struct pine
*ps
, ACTION_S
*role_arg
)
1496 int ret
, forward_raw_body
= 0, rv
= 0, i
;
1497 long msgno
= 0L, j
, totalmsgs
, rflags
;
1498 ENVELOPE
*env
= NULL
, *outgoing
;
1499 BODY
*orig_body
, *body
= NULL
;
1501 void *msgtext
= NULL
;
1503 int impl
, template_len
= 0;
1505 REDRAFT_POS_S
*redraft_pos
= NULL
;
1506 ACTION_S
*role
= NULL
, *nrole
;
1507 #if defined(DOS) && !defined(_WINDOWS)
1511 dprint((4, "\n - forward -\n"));
1513 memset((void *)&reply
, 0, sizeof(reply
));
1514 outgoing
= mail_newenvelope();
1516 if(ps_global
->full_header
== 2
1517 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
1518 forward_raw_body
= 1;
1520 if((totalmsgs
= mn_total_cur(ps
->msgmap
)) > 1L){
1521 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s forwarded messages...", comatose(totalmsgs
));
1522 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1523 outgoing
->subject
= cpystr(tmp_20k_buf
);
1526 /*---------- Get the envelope of message we're forwarding ------*/
1527 msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
1528 if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
))
1529 && (outgoing
->subject
= forward_subject(env
, 0)))){
1530 q_status_message1(SM_ORDER
,3,4,
1531 _("Error fetching message %s. Can't forward it."),
1532 long2string(msgno
));
1538 * as with all text bound for the composer, build it in
1539 * a storage object of the type it understands...
1541 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
1542 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1543 _("Error allocating message text"));
1547 ret
= (F_ON(F_FORWARD_AS_ATTACHMENT
, ps_global
))
1550 ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1551 : (ps
->full_header
== 2)
1552 ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1556 cmd_cancelled("Forward");
1557 so_give((STORE_S
**)&msgtext
);
1561 /* Setup possible role */
1563 role
= copy_action(role_arg
);
1566 rflags
= ROLE_FORWARD
;
1567 if(nonempty_patterns(rflags
, &dummy
)){
1568 /* setup default role */
1570 j
= mn_first_cur(ps
->msgmap
);
1573 nrole
= set_role_from_msg(ps
, rflags
,
1574 mn_m2raw(ps
->msgmap
, j
), NULL
);
1575 } while(nrole
&& (!role
|| nrole
== role
)
1576 && (j
=mn_next_cur(ps
->msgmap
)) > 0L);
1578 if(!role
|| nrole
== role
)
1583 if(confirm_role(rflags
, &role
))
1584 role
= combine_inherited_role(role
);
1585 else{ /* cancel reply */
1587 cmd_cancelled("Forward");
1588 so_give((STORE_S
**)&msgtext
);
1595 q_status_message1(SM_ORDER
, 3, 4,
1596 _("Forwarding using role \"%s\""), role
->nick
);
1598 outgoing
->message_id
= generate_message_id(role
);
1600 if(role
&& role
->template){
1604 filtered
= detoken(role
, (totalmsgs
== 1L) ? env
: NULL
,
1605 0, 0, 0, &redraft_pos
, &impl
);
1608 so_puts((STORE_S
*)msgtext
, filtered
);
1610 template_len
= strlen(filtered
);
1613 fs_give((void **)&filtered
);
1619 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
1621 redraft_pos
->offset
+= template_len
;
1623 so_puts((STORE_S
*)msgtext
, *sig
? sig
: NEWLINE
);
1625 fs_give((void **)&sig
);
1628 so_puts((STORE_S
*)msgtext
, NEWLINE
);
1630 gf_set_so_writec(&pc
, (STORE_S
*)msgtext
);
1632 #if defined(DOS) && !defined(_WINDOWS)
1633 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1634 #define IN_RESERVE 8192
1636 #define IN_RESERVE 16384
1638 if((reserve
=(char *)malloc(IN_RESERVE
)) == NULL
){
1639 gf_clear_so_writec((STORE_S
*) msgtext
);
1640 so_give((STORE_S
**)&msgtext
);
1641 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1642 _("Insufficient memory for message text"));
1648 * If we're forwarding multiple messages *or* the forward-as-mime
1649 * is turned on and the users wants it done that way, package things
1652 if(ret
== 'y'){ /* attach message[s]!!! */
1654 long totalsize
= 0L;
1656 /*---- New Body to start with ----*/
1657 body
= mail_newbody();
1658 body
->type
= TYPEMULTIPART
;
1660 /*---- The TEXT part/body ----*/
1661 body
->nested
.part
= mail_newbody_part();
1662 body
->nested
.part
->body
.type
= TYPETEXT
;
1663 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1666 /*---- The MULTIPART/DIGEST part ----*/
1667 body
->nested
.part
->next
= mail_newbody_part();
1668 body
->nested
.part
->next
->body
.type
= TYPEMULTIPART
;
1669 body
->nested
.part
->next
->body
.subtype
= cpystr("Digest");
1670 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Digest of %s messages", comatose(totalmsgs
));
1671 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1672 body
->nested
.part
->next
->body
.description
= cpystr(tmp_20k_buf
);
1673 pp
= &(body
->nested
.part
->next
->body
.nested
.part
);
1676 pp
= &(body
->nested
.part
->next
);
1678 /*---- The Message body subparts ----*/
1679 for(msgno
= mn_first_cur(ps
->msgmap
);
1681 msgno
= mn_next_cur(ps
->msgmap
)){
1683 msgno
= mn_m2raw(ps
->msgmap
, msgno
);
1684 env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
);
1686 if(forward_mime_msg(ps
->mail_stream
,msgno
,NULL
,env
,pp
,msgtext
)){
1687 totalsize
+= (*pp
)->body
.size
.bytes
;
1688 pp
= &((*pp
)->next
);
1695 body
->nested
.part
->next
->body
.size
.bytes
= totalsize
;
1697 else if(totalmsgs
> 1L){
1699 body
= mail_newbody();
1700 body
->type
= TYPETEXT
;
1701 body
->contents
.text
.data
= msgtext
;
1704 for(msgno
= mn_first_cur(ps
->msgmap
);
1706 msgno
= mn_next_cur(ps
->msgmap
)){
1708 if(env
){ /* put 2 between messages */
1709 gf_puts(NEWLINE
, pc
);
1710 gf_puts(NEWLINE
, pc
);
1713 /*--- Grab current envelope ---*/
1714 env
= pine_mail_fetchstructure(ps
->mail_stream
,
1715 mn_m2raw(ps
->msgmap
, msgno
),
1717 if(!env
|| !orig_body
){
1718 q_status_message1(SM_ORDER
,3,4,
1719 _("Error fetching message %s. Can't forward it."),
1720 long2string(msgno
));
1724 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| forward_raw_body
) {
1725 forward_delimiter(pc
);
1726 reply_forward_header(ps
->mail_stream
,
1727 mn_m2raw(ps
->msgmap
, msgno
),
1730 if(!get_body_part_text(ps
->mail_stream
, forward_raw_body
? NULL
: orig_body
,
1731 mn_m2raw(ps
->msgmap
, msgno
),
1732 forward_raw_body
? NULL
: "1", 0L, pc
,
1733 NULL
, NULL
, GBPT_NONE
))
1735 } else if(orig_body
->type
== TYPEMULTIPART
) {
1737 q_status_message(SM_ORDER
,3,7,
1738 _("WARNING! Attachments not included in multiple forward."));
1740 if(orig_body
->nested
.part
&&
1741 orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
1742 /*---- First part of the message is text -----*/
1743 forward_delimiter(pc
);
1744 reply_forward_header(ps
->mail_stream
,
1745 mn_m2raw(ps
->msgmap
,msgno
),
1748 if(!get_body_part_text(ps
->mail_stream
,
1749 &orig_body
->nested
.part
->body
,
1750 mn_m2raw(ps
->msgmap
, msgno
),
1752 NULL
, NULL
, GBPT_NONE
))
1755 q_status_message(SM_ORDER
,0,3,
1756 _("Multipart with no leading text part!"));
1759 /*---- Single non-text message of some sort ----*/
1760 q_status_message(SM_ORDER
,0,3,
1761 _("Non-text message not included!"));
1765 else if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
,
1767 && (body
= forward_body(ps
->mail_stream
, env
, orig_body
, msgno
,
1770 q_status_message1(SM_ORDER
,3,4,
1771 _("Error fetching message %s. Can't forward it."),
1772 long2string(msgno
));
1776 if(ret
!= 'y' && totalmsgs
== 1L && orig_body
){
1779 charset
= parameter_val(orig_body
->parameter
, "charset");
1780 if(charset
&& strucmp(charset
, "us-ascii") != 0){
1784 * There is a non-ascii charset, is there conversion happening?
1786 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
1787 reply
.orig_charset
= charset
;
1793 fs_give((void **) &charset
);
1796 * I don't think orig_charset is ever used except possibly
1797 * right here. Hubert 2008-01-15.
1799 if(reply
.orig_charset
)
1803 /* fill in reply structure */
1804 reply
.forwarded
= 1;
1805 reply
.mailbox
= cpystr(ps
->mail_stream
->mailbox
);
1806 reply
.origmbox
= cpystr(ps
->mail_stream
->original_mailbox
1807 ? ps
->mail_stream
->original_mailbox
1808 : ps
->mail_stream
->mailbox
);
1809 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((totalmsgs
+ 1) * sizeof(imapuid_t
));
1810 if((reply
.data
.uid
.validity
= ps
->mail_stream
->uid_validity
) != 0){
1812 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1814 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1815 reply
.data
.uid
.msgs
[i
] = mail_uid(ps
->mail_stream
, mn_m2raw(ps
->msgmap
, msgno
));
1819 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1821 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1822 reply
.data
.uid
.msgs
[i
] = mn_m2raw(ps
->msgmap
, msgno
);
1825 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
1827 #if defined(DOS) && !defined(_WINDOWS)
1828 free((void *)reserve
);
1830 pine_send(outgoing
, &body
, "FORWARD MESSAGE",
1831 role
, NULL
, &reply
, redraft_pos
,
1837 pine_free_body(&body
);
1839 if((STORE_S
*) msgtext
)
1840 gf_clear_so_writec((STORE_S
*) msgtext
);
1842 mail_free_envelope(&outgoing
);
1843 free_redraft_pos(&redraft_pos
);
1846 if(reply
.orig_charset
)
1847 fs_give((void **)&reply
.orig_charset
);
1852 q_status_message(SM_ORDER
| SM_DING
, 4, 5,
1853 _("Error fetching message contents. Can't forward message."));
1858 /*----------------------------------------------------------------------
1859 Partially set up message to forward and pass off to composer/mailer
1861 Args: pine_state -- The usual pine structure
1862 message -- The MESSAGECACHE of entry to reply to
1864 Result: outgoing envelope and body created and passed off to composer/mailer
1866 Create the outgoing envelope for the mail being forwarded, which is
1867 not much more than filling in the subject, and create the message body
1868 of the outgoing message which requires formatting the header from the
1869 envelope of the original message.
1870 ----------------------------------------------------------------------*/
1872 forward_text(struct pine
*pine_state
, void *text
, SourceType source
)
1879 char *enc_error
, *sig
;
1880 ACTION_S
*role
= NULL
;
1882 long rflags
= ROLE_COMPOSE
;
1884 if((msgtext
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1885 env
= mail_newenvelope();
1886 body
= mail_newbody();
1887 body
->type
= TYPETEXT
;
1888 body
->contents
.text
.data
= (void *) msgtext
;
1890 if(nonempty_patterns(rflags
, &dummy
)){
1892 * This is really more like Compose, even though it
1893 * is called Forward.
1895 if(confirm_role(rflags
, &role
))
1896 role
= combine_inherited_role(role
);
1898 cmd_cancelled("Composition");
1899 display_message('x');
1900 mail_free_envelope(&env
);
1901 pine_free_body(&body
);
1907 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
1910 env
->message_id
= generate_message_id(role
);
1912 sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
);
1913 so_puts(msgtext
, (sig
&& *sig
) ? sig
: NEWLINE
);
1914 so_puts(msgtext
, NEWLINE
);
1915 so_puts(msgtext
, "----- Included text -----");
1916 so_puts(msgtext
, NEWLINE
);
1918 fs_give((void **)&sig
);
1921 gf_set_so_writec(&pc
, msgtext
);
1922 gf_set_readc(&gc
,text
,(source
== CharStar
) ? strlen((char *)text
) : 0L,
1925 if((enc_error
= gf_pipe(gc
, pc
)) == NULL
){
1926 pine_send(env
, &body
, "SEND MESSAGE", role
, NULL
, NULL
, NULL
,
1928 pine_state
->mangled_screen
= 1;
1931 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1932 _("Error reading text \"%s\""),enc_error
);
1933 display_message('x');
1936 gf_clear_so_writec(msgtext
);
1937 mail_free_envelope(&env
);
1938 pine_free_body(&body
);
1941 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1942 _("Error allocating message text"));
1943 display_message('x');
1950 /*----------------------------------------------------------------------
1951 Partially set up message to resend and pass off to mailer
1953 Args: pine_state -- The usual pine structure
1955 Result: outgoing envelope and body created and passed off to mailer
1957 Create the outgoing envelope for the mail being resent, which is
1958 not much more than filling in the subject, and create the message body
1959 of the outgoing message which requires formatting the header from the
1960 envelope of the original message.
1961 ----------------------------------------------------------------------*/
1963 bounce(struct pine
*pine_state
, ACTION_S
*role
)
1967 char *save_to
= NULL
, **save_toptr
= NULL
, *errstr
= NULL
,
1968 *prmpt_who
= NULL
, *prmpt_cnf
= NULL
;
1970 dprint((4, "\n - bounce -\n"));
1972 if(mn_total_cur(pine_state
->msgmap
) > 1L){
1973 save_toptr
= &save_to
;
1975 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1976 mn_total_cur(pine_state
->msgmap
), role
->nick
);
1978 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages to : "),
1979 mn_total_cur(pine_state
->msgmap
));
1980 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1981 prmpt_who
= cpystr(tmp_20k_buf
);
1982 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Send %ld messages "),
1983 mn_total_cur(pine_state
->msgmap
));
1984 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1985 prmpt_cnf
= cpystr(tmp_20k_buf
);
1988 for(msgno
= mn_first_cur(pine_state
->msgmap
);
1990 msgno
= mn_next_cur(pine_state
->msgmap
)){
1992 rawno
= mn_m2raw(pine_state
->msgmap
, msgno
);
1993 if((env
= pine_mail_fetchstructure(pine_state
->mail_stream
, rawno
, NULL
)) != NULL
)
1994 errstr
= bounce_msg(pine_state
->mail_stream
, rawno
, NULL
, role
,
1995 save_toptr
, env
->subject
, prmpt_who
, prmpt_cnf
);
1997 errstr
= _("Can't fetch Subject for Bounce");
2002 q_status_message(SM_ORDER
| SM_DING
, 4, 7, errstr
);
2009 fs_give((void **)&save_to
);
2012 fs_give((void **) &prmpt_who
);
2015 fs_give((void **) &prmpt_cnf
);
2017 return(errstr
? 0 : 1);
2023 bounce_msg(MAILSTREAM
*stream
,
2032 char *errstr
= NULL
;
2039 /* When we bounce a message, we will leave the original message
2040 * intact, which means that it will not be signed or encrypted,
2041 * so we turn off signing and encrypting now. It will be turned
2042 * on again in send_exit_for_pico().
2044 if(ps_global
->smime
)
2045 ps_global
->smime
->do_sign
= ps_global
->smime
->do_encrypt
= 0;
2048 if((errstr
= bounce_msg_body(stream
, rawno
, part
, to
, subject
, &outgoing
, &body
, &was_seen
)) == NULL
){
2049 if(pine_simple_send(outgoing
, &body
, &role
, pmt_who
, pmt_cnf
, to
,
2050 !(to
&& *to
) ? SS_PROMPTFORTO
: 0) < 0){
2051 errstr
= ""; /* p_s_s() better have explained! */
2052 /* clear seen flag */
2053 if(was_seen
== 0 && rawno
> 0L
2054 && stream
&& rawno
<= stream
->nmsgs
2055 && (mc
= mail_elt(stream
, rawno
)) && mc
->seen
)
2056 mail_flag(stream
, long2string(rawno
), "\\SEEN", 0);
2060 /* Just for good measure... */
2061 mail_free_envelope(&outgoing
);
2062 pine_free_body(&body
);
2064 return(errstr
); /* no problem-o */
2068 /*----------------------------------------------------------------------
2069 Serve up the current signature within pico for editing
2073 Result: signature changed or not.
2076 signature_edit(char *sigfile
, char *title
)
2079 char sig_path
[MAXPATH
+1], errbuf
[2000], *errstr
= NULL
;
2081 STORE_S
*msgso
, *tmpso
= NULL
;
2085 struct variable
*vars
= ps_global
->vars
;
2086 REMDATA_S
*rd
= NULL
;
2088 if(!signature_path(sigfile
, sig_path
, MAXPATH
))
2089 return(cpystr(_("No signature file defined.")));
2091 if(IS_REMOTE(sigfile
)){
2092 rd
= rd_create_remote(RemImap
, sig_path
, REMOTE_SIG_SUBTYPE
,
2094 _("Can't access remote configuration."));
2096 return(cpystr(_("Error attempting to edit remote configuration")));
2098 (void)rd_read_metadata(rd
);
2100 if(rd
->access
== MaybeRorW
){
2101 if(rd
->read_status
== 'R')
2102 rd
->access
= ReadOnly
;
2104 rd
->access
= ReadWrite
;
2107 if(rd
->access
!= NoExists
){
2109 rd_check_remvalid(rd
, 1L);
2112 * If the cached info says it is readonly but
2113 * it looks like it's been fixed now, change it to readwrite.
2115 if(rd
->read_status
== 'R'){
2116 rd_check_readonly_access(rd
);
2117 if(rd
->read_status
== 'W'){
2118 rd
->access
= ReadWrite
;
2119 rd
->flags
|= REM_OUTOFDATE
;
2122 rd
->access
= ReadOnly
;
2126 if(rd
->flags
& REM_OUTOFDATE
){
2127 if(rd_update_local(rd
) != 0){
2130 "signature_edit: rd_update_local failed\n"));
2131 rd_close_remdata(&rd
);
2132 return(cpystr(_("Can't access remote sig")));
2138 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
2139 rd_close_remdata(&rd
);
2140 return(cpystr(_("Can't get write permission for remote sig")));
2143 rd
->flags
|= DO_REMTRIM
;
2145 strncpy(sig_path
, rd
->lf
, sizeof(sig_path
)-1);
2146 sig_path
[sizeof(sig_path
)-1] = '\0';
2149 standard_picobuf_setup(&pbf
);
2150 pbf
.tty_fix
= PineRaw
;
2151 pbf
.composer_help
= h_composer_sigedit
;
2152 pbf
.exittest
= sigedit_exit_for_pico
;
2153 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2154 ? upload_msg_to_pico
: NULL
;
2155 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2156 ? VAR_EDITOR
: NULL
;
2157 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2158 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2159 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2160 pbf
.allow_flowed_text
= 0;
2162 pbf
.pine_anchor
= set_titlebar(title
,
2163 ps_global
->mail_stream
,
2164 ps_global
->context_current
,
2165 ps_global
->cur_folder
,
2167 0, FolderName
, 0, 0, NULL
);
2169 /* NOTE: at this point, a lot of pico struct fields are null'd out
2170 * thanks to the leading memset; in particular "headents" which tells
2171 * pico to behave like a normal editor (though modified slightly to
2172 * let the caller dictate the file to edit and such)...
2175 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, sig_path
)){
2178 l
= strlen(VAR_OPER_DIR
) + 100;
2179 ret
= (char *) fs_get((l
+1) * sizeof(char));
2180 snprintf(ret
, l
+1, _("Can't edit file outside of %s"), VAR_OPER_DIR
);
2186 * Now alloc and init the text to pass pico
2188 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2189 ret
= cpystr(_("Error allocating space for file"));
2190 dprint((1, "Can't alloc space for signature_edit"));
2194 pbf
.msgtext
= so_text(msgso
);
2196 if(can_access(sig_path
, READ_ACCESS
) == 0
2197 && !(tmpso
= so_get(FileStar
, sig_path
, READ_ACCESS
|READ_FROM_LOCALE
))){
2198 char *problem
= error_description(errno
);
2200 snprintf(errbuf
, sizeof(errbuf
), _("Error editing \"%s\": %s"),
2201 sig_path
, problem
? problem
: "<NULL>");
2202 errbuf
[sizeof(errbuf
)-1] = '\0';
2203 ret
= cpystr(errbuf
);
2205 dprint((1, "signature_edit: can't open %s: %s", sig_path
,
2206 problem
? problem
: "<NULL>"));
2209 else if(tmpso
){ /* else, fill pico's edit buffer */
2210 gf_set_so_readc(&gc
, tmpso
); /* read from file, write pico buf */
2211 gf_set_so_writec(&pc
, msgso
);
2212 gf_filter_init(); /* no filters needed */
2213 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2214 snprintf(errbuf
, sizeof(errbuf
), _("Error reading file: \"%s\""), errstr
);
2215 errbuf
[sizeof(errbuf
)-1] = '\0';
2216 ret
= cpystr(errbuf
);
2219 gf_clear_so_readc(tmpso
);
2220 gf_clear_so_writec(msgso
);
2226 mswin_setwindowmenu (MENU_COMPOSER
);
2229 /*------ OK, Go edit the signature ------*/
2230 editor_result
= pico(&pbf
);
2233 mswin_setwindowmenu (MENU_DEFAULT
);
2235 if(editor_result
& COMP_GOTHUP
){
2236 hup_signal(0); /* do what's normal for a hup */
2239 fix_windsize(ps_global
);
2243 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2246 /*------ Must have an edited buffer, write it to .sig -----*/
2247 our_unlink(sig_path
); /* blast old copy */
2248 if((tmpso
= so_get(FileStar
, sig_path
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
2249 so_seek(msgso
, 0L, 0);
2250 gf_set_so_readc(&gc
, msgso
); /* read from pico buf */
2251 gf_set_so_writec(&pc
, tmpso
); /* write sig file */
2252 gf_filter_init(); /* no filters needed */
2253 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2254 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2256 errbuf
[sizeof(errbuf
)-1] = '\0';
2257 ret
= cpystr(errbuf
);
2260 gf_clear_so_readc(msgso
);
2261 gf_clear_so_writec(tmpso
);
2262 if(so_give(&tmpso
)){
2263 errstr
= error_description(errno
);
2264 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2266 errbuf
[sizeof(errbuf
)-1] = '\0';
2267 ret
= cpystr(errbuf
);
2270 if(IS_REMOTE(sigfile
)){
2276 we_cancel
= busy_cue("Copying to remote sig", NULL
, 1);
2277 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
2279 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2280 _("Error opening temporary sig file %s: %s"),
2281 rd
->lf
, error_description(errno
));
2283 "write_remote_sig: error opening temp file %s\n",
2284 rd
->lf
? rd
->lf
: "?"));
2287 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2288 _("Error copying to %s: %s"),
2289 rd
->rn
, error_description(errno
));
2291 "write_remote_sig: error copying from %s to %s\n",
2292 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
2295 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
2296 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2299 rd_update_metadata(rd
, datebuf
);
2300 rd
->read_status
= 'W';
2303 rd_close_remdata(&rd
);
2306 cancel_busy_cue(-1);
2310 snprintf(errbuf
, sizeof(errbuf
), _("Error writing \"%s\""), sig_path
);
2311 errbuf
[sizeof(errbuf
)-1] = '\0';
2312 ret
= cpystr(errbuf
);
2313 dprint((1, "signature_edit: can't write %s",
2319 standard_picobuf_teardown(&pbf
);
2325 /*----------------------------------------------------------------------
2326 Serve up the current signature within pico for editing
2328 Args: literal signature to edit
2330 Result: raw edited signature is returned in result arg
2333 signature_edit_lit(char *litsig
, char **result
, char *title
, HelpType composer_help
)
2336 char *errstr
= NULL
;
2340 struct variable
*vars
= ps_global
->vars
;
2342 standard_picobuf_setup(&pbf
);
2343 pbf
.tty_fix
= PineRaw
;
2344 pbf
.search_help
= h_sigedit_search
;
2345 pbf
.composer_help
= composer_help
;
2346 pbf
.exittest
= sigedit_exit_for_pico
;
2347 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2348 ? upload_msg_to_pico
: NULL
;
2349 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2350 ? VAR_EDITOR
: NULL
;
2351 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2352 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2353 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2354 pbf
.allow_flowed_text
= 0;
2356 pbf
.pine_anchor
= set_titlebar(title
,
2357 ps_global
->mail_stream
,
2358 ps_global
->context_current
,
2359 ps_global
->cur_folder
,
2361 0, FolderName
, 0, 0, NULL
);
2363 /* NOTE: at this point, a lot of pico struct fields are null'd out
2364 * thanks to the leading memset; in particular "headents" which tells
2365 * pico to behave like a normal editor (though modified slightly to
2366 * let the caller dictate the file to edit and such)...
2370 * Now alloc and init the text to pass pico
2372 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2373 ret
= cpystr(_("Error allocating space"));
2374 dprint((1, "Can't alloc space for signature_edit_lit"));
2378 pbf
.msgtext
= so_text(msgso
);
2380 so_puts(msgso
, litsig
? litsig
: "");
2385 mswin_setwindowmenu (MENU_COMPOSER
);
2388 /*------ OK, Go edit the signature ------*/
2389 editor_result
= pico(&pbf
);
2392 mswin_setwindowmenu (MENU_DEFAULT
);
2394 if(editor_result
& COMP_GOTHUP
){
2395 hup_signal(0); /* do what's normal for a hup */
2398 fix_windsize(ps_global
);
2402 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2403 ret
= cpystr(_("Edit Cancelled"));
2406 /*------ Must have an edited buffer, write it to .sig -----*/
2411 so_seek(msgso
, 0L, 0);
2412 while(so_readc(&c
, msgso
))
2415 *result
= (char *)fs_get((cnt
+1) * sizeof(char));
2417 so_seek(msgso
, 0L, 0);
2418 while(so_readc(&c
, msgso
))
2425 standard_picobuf_teardown(&pbf
);
2432 * Returns 0 for Save Changes and exit
2434 * -1 exit but Dont Save Changes
2437 sigedit_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
2442 void (*redraw
)(void) = ps_global
->redrawer
;
2443 static ESCKEY_S opts
[] = {
2444 {'s', 's', "S", N_("Save changes")},
2445 {'d', 'd', "D", N_("Don't save changes")},
2449 ps_global
->redrawer
= redraw_pico
;
2450 fix_windsize(ps_global
);
2453 rv
= radio_buttons(_("Exit editor? "),
2454 -FOOTER_ROWS(ps_global
), opts
,
2455 's', 'x', h_exit_editor
, RB_NORM
);
2456 if(rv
== 's'){ /* user ACCEPTS! */
2459 else if(rv
== 'd'){ /* Declined! */
2460 rstr
= _("No Changes Saved");
2463 else if(rv
== 'x'){ /* Cancelled! */
2464 rstr
= _("Exit Cancelled");
2472 ps_global
->redrawer
= redraw
;
2473 return((rv
== 's') ? 0 : (rv
== 'd') ? -1 : 1);
2478 * Common stuff we almost always want to set when calling pico.
2481 standard_picobuf_setup(PICO
*pbf
)
2483 memset(pbf
, 0, sizeof(*pbf
));
2485 pbf
->pine_version
= ALPINE_VERSION
;
2486 pbf
->fillcolumn
= ps_global
->composer_fillcol
;
2487 pbf
->menu_rows
= FOOTER_ROWS(ps_global
) - 1;
2488 pbf
->colors
= colors_for_pico();
2489 pbf
->wordseps
= user_wordseps(ps_global
->VAR_WORDSEPS
);
2490 pbf
->helper
= helper
;
2491 pbf
->showmsg
= display_message_for_pico
;
2492 pbf
->suspend
= do_suspend
;
2493 pbf
->keybinput
= cmd_input_for_pico
;
2494 pbf
->tty_fix
= ttyfix
; /* watch out for this one */
2495 pbf
->newmail
= new_mail_for_pico
;
2496 pbf
->ckptdir
= checkpoint_dir_for_pico
;
2497 pbf
->resize
= resize_for_pico
;
2498 pbf
->input_cs
= ps_global
->input_cs
;
2499 pbf
->winch_cleanup
= winch_cleanup
;
2500 pbf
->search_help
= h_composer_search
;
2501 pbf
->ins_help
= h_composer_ins
;
2502 pbf
->ins_m_help
= h_composer_ins_m
;
2503 pbf
->composer_help
= h_composer
;
2504 pbf
->browse_help
= h_composer_browse
;
2505 pbf
->attach_help
= h_composer_ctrl_j
;
2508 ( (F_ON(F_CAN_SUSPEND
,ps_global
) ? P_SUSPEND
: 0L)
2509 | (F_ON(F_USE_FK
,ps_global
) ? P_FKEYS
: 0L)
2510 | (ps_global
->restricted
? P_SECURE
: 0L)
2511 | (F_ON(F_ALT_ED_NOW
,ps_global
) ? P_ALTNOW
: 0L)
2512 | (F_ON(F_USE_CURRENT_DIR
,ps_global
) ? P_CURDIR
: 0L)
2513 | (F_ON(F_SUSPEND_SPAWNS
,ps_global
) ? P_SUBSHELL
: 0L)
2514 | (F_ON(F_COMPOSE_MAPS_DEL
,ps_global
) ? P_DELRUBS
: 0L)
2515 | (F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
) ? P_COMPLETE
: 0L)
2516 | (F_ON(F_SHOW_CURSOR
,ps_global
) ? P_SHOCUR
: 0L)
2517 | (F_ON(F_DEL_FROM_DOT
,ps_global
) ? P_DOTKILL
: 0L)
2518 | (F_ON(F_ENABLE_DOT_FILES
,ps_global
) ? P_DOTFILES
: 0L)
2519 | (F_ON(F_ALLOW_GOTO
,ps_global
) ? P_ALLOW_GOTO
: 0L)
2520 | (F_ON(F_ENABLE_SEARCH_AND_REPL
,ps_global
) ? P_REPLACE
: 0L)
2521 | (!ps_global
->pass_ctrl_chars
2522 && !ps_global
->pass_c1_ctrl_chars
? P_HICTRL
: 0L)
2523 | ((F_ON(F_ENABLE_ALT_ED
,ps_global
)
2524 || F_ON(F_ALT_ED_NOW
,ps_global
)
2525 || (ps_global
->VAR_EDITOR
2526 && ps_global
->VAR_EDITOR
[0]
2527 && ps_global
->VAR_EDITOR
[0][0]))
2529 | ((!ps_global
->keyboard_charmap
2530 || !strucmp(ps_global
->keyboard_charmap
, "US-ASCII"))
2531 ? P_HIBITIGN
: 0L));
2533 if(ps_global
->VAR_OPER_DIR
){
2534 pbf
->oper_dir
= ps_global
->VAR_OPER_DIR
;
2535 pbf
->pine_flags
|= P_TREE
;
2538 pbf
->home_dir
= ps_global
->home_dir
;
2543 standard_picobuf_teardown(PICO
*pbf
)
2547 free_pcolors(&pbf
->colors
);
2550 fs_give((void **) &pbf
->wordseps
);
2555 /*----------------------------------------------------------------------
2556 Call back for pico to use to check for new mail.
2558 Args: cursor -- pointer to in to tell caller if cursor location changed
2559 if NULL, turn off cursor positioning.
2560 timing -- whether or not it's a good time to check
2563 Returns: returns 1 on success, zero on error.
2566 new_mail_for_pico(int timing
, int status
)
2569 * If we're not interested in the status, don't display the busy
2572 /* don't know where the cursor's been, reset it */
2574 return(new_mail(0, timing
,
2575 (status
? NM_STATUS_MSG
: NM_NONE
) | NM_DEFER_SORT
2576 | NM_FROM_COMPOSER
));
2581 cmd_input_for_pico(void)
2583 zero_new_mail_count();
2587 /*----------------------------------------------------------------------
2588 Call back for pico to get newmail status messages displayed
2590 Args: x -- char processed
2595 display_message_for_pico(UCS x
)
2599 clear_cursor_pos(); /* can't know where cursor is */
2600 mark_status_dirty(); /* don't count on cached text */
2601 fix_windsize(ps_global
);
2604 rv
= ps_global
->mangled_screen
;
2605 ps_global
->mangled_screen
= 0;
2610 /*----------------------------------------------------------------------
2611 Call back for pico to get desired directory for its check point file
2613 Args: s -- buffer to write directory name
2614 n -- length of that buffer
2616 Returns: pointer to static buffer
2619 checkpoint_dir_for_pico(char *s
, size_t n
)
2621 #if defined(DOS) || defined(OS2)
2623 * we can't assume anything about root or home dirs, so
2624 * just plunk it down in the same place as the pinerc
2626 if(!getenv("HOME")){
2627 char *lc
= last_cmpnt(ps_global
->pinerc
);
2630 strncpy(s
, ps_global
->pinerc
, MIN(n
-1,lc
-ps_global
->pinerc
));
2631 s
[MIN(n
-1,lc
-ps_global
->pinerc
)] = '\0';
2634 strncpy(s
, ".\\", n
-1);
2640 strncpy(s
, ps_global
->home_dir
, n
-1);
2647 /*----------------------------------------------------------------------
2648 Call back for pico to tell us the window size's changed
2652 Returns: none (but pine's ttyo structure may have been updated)
2655 resize_for_pico(void)
2657 fix_windsize(ps_global
);
2662 colors_for_pico(void)
2664 PCOLORS
*colors
= NULL
;
2665 struct variable
*vars
= ps_global
->vars
;
2667 if (pico_usingcolor()){
2668 colors
= (PCOLORS
*)fs_get(sizeof(PCOLORS
));
2670 colors
->tbcp
= current_titlebar_color();
2672 if (VAR_KEYLABEL_FORE_COLOR
&& VAR_KEYLABEL_BACK_COLOR
){
2673 colors
->klcp
= new_color_pair(VAR_KEYLABEL_FORE_COLOR
,
2674 VAR_KEYLABEL_BACK_COLOR
);
2675 if (!pico_is_good_colorpair(colors
->klcp
))
2676 free_color_pair(&colors
->klcp
);
2678 else colors
->klcp
= NULL
;
2680 if (colors
->klcp
&& VAR_KEYNAME_FORE_COLOR
&& VAR_KEYNAME_BACK_COLOR
){
2681 colors
->kncp
= new_color_pair(VAR_KEYNAME_FORE_COLOR
,
2682 VAR_KEYNAME_BACK_COLOR
);
2684 else colors
->kncp
= NULL
;
2686 if (VAR_STATUS_FORE_COLOR
&& VAR_STATUS_BACK_COLOR
){
2687 colors
->stcp
= new_color_pair(VAR_STATUS_FORE_COLOR
,
2688 VAR_STATUS_BACK_COLOR
);
2690 else colors
->stcp
= NULL
;
2692 if (VAR_PROMPT_FORE_COLOR
&& VAR_PROMPT_BACK_COLOR
){
2693 colors
->prcp
= new_color_pair(VAR_PROMPT_FORE_COLOR
,
2694 VAR_PROMPT_BACK_COLOR
);
2696 else colors
->prcp
= NULL
;
2698 if (VAR_QUOTE1_FORE_COLOR
&& VAR_QUOTE1_BACK_COLOR
){
2699 colors
->qlcp
= new_color_pair(VAR_QUOTE1_FORE_COLOR
,
2700 VAR_QUOTE1_BACK_COLOR
);
2702 else colors
->qlcp
= NULL
;
2704 if (VAR_QUOTE2_FORE_COLOR
&& VAR_QUOTE2_BACK_COLOR
){
2705 colors
->qllcp
= new_color_pair(VAR_QUOTE2_FORE_COLOR
,
2706 VAR_QUOTE2_BACK_COLOR
);
2708 else colors
->qllcp
= NULL
;
2710 if (VAR_QUOTE3_FORE_COLOR
&& VAR_QUOTE3_BACK_COLOR
){
2711 colors
->qlllcp
= new_color_pair(VAR_QUOTE3_FORE_COLOR
,
2712 VAR_QUOTE3_BACK_COLOR
);
2714 else colors
->qlllcp
= NULL
;
2716 if (VAR_NORM_FORE_COLOR
&& VAR_NORM_BACK_COLOR
){
2717 colors
->ntcp
= new_color_pair(VAR_NORM_FORE_COLOR
,
2718 VAR_NORM_BACK_COLOR
);
2720 else colors
->ntcp
= NULL
;
2722 if (VAR_REV_FORE_COLOR
&& VAR_REV_BACK_COLOR
){
2723 colors
->rtcp
= new_color_pair(VAR_REV_FORE_COLOR
,
2724 VAR_REV_BACK_COLOR
);
2726 else colors
->rtcp
= NULL
;
2728 if (VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
){
2729 colors
->sbcp
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
2730 VAR_SIGNATURE_BACK_COLOR
);
2732 else colors
->sbcp
= NULL
;
2740 free_pcolors(PCOLORS
**colors
)
2743 if ((*colors
)->tbcp
)
2744 free_color_pair(&(*colors
)->tbcp
);
2745 if ((*colors
)->kncp
)
2746 free_color_pair(&(*colors
)->kncp
);
2747 if ((*colors
)->klcp
)
2748 free_color_pair(&(*colors
)->klcp
);
2749 if ((*colors
)->stcp
)
2750 free_color_pair(&(*colors
)->stcp
);
2751 if ((*colors
)->prcp
)
2752 free_color_pair(&(*colors
)->prcp
);
2753 if ((*colors
)->qlcp
)
2754 free_color_pair(&(*colors
)->qlcp
);
2755 if ((*colors
)->qllcp
)
2756 free_color_pair(&(*colors
)->qllcp
);
2757 if ((*colors
)->qlllcp
)
2758 free_color_pair(&(*colors
)->qlllcp
);
2759 if ((*colors
)->ntcp
)
2760 free_color_pair(&(*colors
)->ntcp
);
2761 if ((*colors
)->rtcp
)
2762 free_color_pair(&(*colors
)->rtcp
);
2763 if ((*colors
)->sbcp
)
2764 free_color_pair(&(*colors
)->sbcp
);
2765 fs_give((void **)colors
);