1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Code here for forward and reply to mail
21 A few support routines as well
23 This code will forward and reply to MIME messages. The Alpine composer
24 at this time will only support non-text segments at the end of a
25 message so, things don't always come out as one would like. If you
26 always forward a message in MIME format, all will be correct. Forwarding
27 of nested MULTIPART messages will work. There's still a problem with
28 MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
29 the equivalent parts. Ideally, we should probably such segments as a
30 single attachment when forwarding/replying. It would also be real nice to
31 flatten out the nesting in the composer so pieces inside can get snipped.
33 The evolution continues...
49 #include "../pith/state.h"
50 #include "../pith/conf.h"
51 #include "../pith/init.h"
52 #include "../pith/filter.h"
53 #include "../pith/pattern.h"
54 #include "../pith/charset.h"
55 #include "../pith/mimedesc.h"
56 #include "../pith/remote.h"
57 #include "../pith/news.h"
58 #include "../pith/util.h"
59 #include "../pith/detoken.h"
60 #include "../pith/newmail.h"
61 #include "../pith/readfile.h"
62 #include "../pith/tempfile.h"
63 #include "../pith/busy.h"
64 #include "../pith/ablookup.h"
70 int reply_poster_followup(ENVELOPE
*);
71 int sigedit_exit_for_pico(struct headerentry
*, void (*)(void), int, char **);
72 long new_mail_for_pico(int, int);
73 void cmd_input_for_pico(void);
74 int display_message_for_pico(int);
75 char *checkpoint_dir_for_pico(char *, size_t);
76 void resize_for_pico(void);
77 PCOLORS
*colors_for_pico(void);
78 void free_pcolors(PCOLORS
**);
81 /*----------------------------------------------------------------------
82 Fill in an outgoing message for reply and pass off to send
84 Args: pine_state -- The usual pine structure
86 Result: Reply is formatted and passed off to composer/mailer
90 - put senders address in To field
91 - search to and cc fields to see if we aren't the only recipients
92 - if other than us, ask if we should reply to all.
93 - if answer yes, fill out the To and Cc fields
94 - fill in the fcc argument
95 - fill in the subject field
96 - fill out the body and the attachments
97 - pass off to pine_send()
100 reply(struct pine
*pine_state
, ACTION_S
*role_arg
)
102 ADDRESS
*saved_from
, *saved_to
, *saved_cc
, *saved_resent
;
103 ADDRESS
*us_in_to_and_cc
, *ap
;
104 ENVELOPE
*env
, *outgoing
;
105 BODY
*body
, *orig_body
= NULL
;
107 void *msgtext
= NULL
;
108 char *tmpfix
= NULL
, *prefix
= NULL
, *fcc
= NULL
, *errmsg
= NULL
;
110 long msgno
, j
, totalm
, rflags
, *seq
= NULL
;
111 int i
, include_text
= 0, times
= -1, warned
= 0, rv
= 0,
112 flags
= RSF_QUERY_REPLY_ALL
, reply_raw_body
= 0;
113 int rolemsg
= 0, copytomsg
= 0;
116 REDRAFT_POS_S
*redraft_pos
= NULL
;
117 ACTION_S
*role
= NULL
, *nrole
;
118 #if defined(DOS) && !defined(_WINDOWS)
122 outgoing
= mail_newenvelope();
123 totalm
= mn_total_cur(pine_state
->msgmap
);
124 seq
= (long *)fs_get(((size_t)totalm
+ 1) * sizeof(long));
126 dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm
)));
128 saved_from
= (ADDRESS
*) NULL
;
129 saved_to
= (ADDRESS
*) NULL
;
130 saved_cc
= (ADDRESS
*) NULL
;
131 saved_resent
= (ADDRESS
*) NULL
;
133 us_in_to_and_cc
= (ADDRESS
*) NULL
;
135 outgoing
->subject
= NULL
;
137 memset((void *)&reply
, 0, sizeof(reply
));
139 if(ps_global
->full_header
== 2
140 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
144 * We may have to loop through first to figure out what default
145 * reply-indent-string to offer...
147 if(mn_total_cur(pine_state
->msgmap
) > 1 &&
148 (F_ON(F_ALT_REPLY_MENU
, pine_state
)
149 || F_ON(F_ENABLE_EDIT_REPLY_INDENT
, pine_state
)) &&
150 reply_quote_str_contains_tokens()){
151 for(msgno
= mn_first_cur(pine_state
->msgmap
);
152 msgno
> 0L && !tmpfix
;
153 msgno
= mn_next_cur(pine_state
->msgmap
)){
155 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
156 mn_m2raw(pine_state
->msgmap
, msgno
),
159 q_status_message1(SM_ORDER
,3,4,
160 _("Error fetching message %s. Can't reply to it."),
165 if(!tmpfix
){ /* look for prefix? */
166 tmpfix
= reply_quote_str(env
);
168 i
= strcmp(tmpfix
, prefix
);
169 fs_give((void **) &tmpfix
);
170 if(i
){ /* don't check back if dissimilar */
171 fs_give((void **) &prefix
);
173 * We free prefix, not tmpfix. We set tmpfix to prefix
174 * so that we won't come check again.
176 tmpfix
= prefix
= cpystr("> ");
181 tmpfix
= NULL
; /* check back later? */
190 * Loop thru the selected messages building the
191 * outgoing envelope's destinations...
193 for(msgno
= mn_first_cur(pine_state
->msgmap
);
195 msgno
= mn_next_cur(pine_state
->msgmap
)){
197 /*--- Grab current envelope ---*/
198 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
199 seq
[++times
] = mn_m2raw(pine_state
->msgmap
, msgno
),
202 q_status_message1(SM_ORDER
,3,4,
203 _("Error fetching message %s. Can't reply to it."),
209 * We check for the prefix here if we didn't do it in the first
210 * loop above. This is just to save having to go through the loop
211 * twice in the cases where we don't need to.
214 tmpfix
= reply_quote_str(env
);
216 i
= strcmp(tmpfix
, prefix
);
217 fs_give((void **) &tmpfix
);
218 if(i
){ /* don't check back if dissimilar */
219 fs_give((void **) &prefix
);
220 tmpfix
= prefix
= cpystr("> ");
225 tmpfix
= NULL
; /* check back later? */
230 * For consistency, the first question is always "include text?"
232 if(!times
){ /* only first time */
233 char *p
= cpystr(prefix
);
235 if((include_text
=reply_text_query(pine_state
,totalm
,env
,&prefix
)) < 0)
239 if(strcmp(p
, prefix
))
240 tmpfix
= prefix
; /* stop looking */
242 fs_give((void **)&p
);
246 * If we're agg-replying or there's a newsgroup and the user wants
247 * to post to news *and* via email, add relevant addresses to the
248 * outgoing envelope...
250 * The single message case gets us around the aggregate reply
251 * to messages in a mixed mail-news archive where some might
252 * have newsgroups and others not or whatever.
254 if(totalm
> 1L || ((i
= reply_news_test(env
, outgoing
)) & 1)){
256 flags
|= RSF_FORCE_REPLY_TO
;
258 if(!reply_harvest(pine_state
, seq
[times
], NULL
, env
,
259 &saved_from
, &saved_to
, &saved_cc
,
260 &saved_resent
, &flags
))
266 /* collect a list of addresses that are us in to and cc fields */
268 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
269 NULL
, us_in_to_and_cc
, NULL
,
270 env
->to
, RCA_ONLY_US
)) != NULL
)
271 reply_append_addr(&us_in_to_and_cc
, ap
);
274 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
275 NULL
, us_in_to_and_cc
, NULL
,
276 env
->cc
, RCA_ONLY_US
)) != NULL
)
277 reply_append_addr(&us_in_to_and_cc
, ap
);
279 /*------------ Format the subject line ---------------*/
280 if(outgoing
->subject
){
282 * if reply to more than one message, and all subjects
283 * match, so be it. otherwise set it to something generic...
285 if(!same_subject(outgoing
->subject
,
286 reply_subject(env
->subject
,tmp_20k_buf
,SIZEOF_20KBUF
))){
287 fs_give((void **)&outgoing
->subject
);
288 outgoing
->subject
= cpystr("Re: several messages");
292 outgoing
->subject
= reply_subject(env
->subject
, NULL
, 0);
295 /* fill reply header */
296 reply_seed(pine_state
, outgoing
, env
, saved_from
,
297 saved_to
, saved_cc
, saved_resent
,
298 &fcc
, flags
& RSF_FORCE_REPLY_ALL
, &errmsg
);
301 q_status_message1(SM_ORDER
, 3, 3, "%.200s", errmsg
);
302 display_message(NO_OP_COMMAND
);
305 fs_give((void **)&errmsg
);
308 if(sp_expunge_count(pine_state
->mail_stream
)) /* cur msg expunged */
311 /* Setup possible role */
312 if (ps_global
->reply
.role_chosen
)
313 role
= ps_global
->reply
.role_chosen
;
315 role
= copy_action(role_arg
);
319 if(!ps_global
->reply
.role_chosen
&& nonempty_patterns(rflags
, &dummy
)){
320 /* setup default role */
322 j
= mn_first_cur(pine_state
->msgmap
);
325 nrole
= set_role_from_msg(pine_state
, rflags
,
326 mn_m2raw(pine_state
->msgmap
, j
),
328 } while(nrole
&& (!role
|| nrole
== role
)
329 && (j
=mn_next_cur(pine_state
->msgmap
)) > 0L);
331 if(!role
|| nrole
== role
)
336 if(confirm_role(rflags
, &role
))
337 role
= combine_inherited_role(role
);
338 else{ /* cancel reply */
340 cmd_cancelled("Reply");
347 * Reply_seed may call c-client in get_fcc_based_on_to, so env may
348 * no longer be valid. Get it again.
349 * Similarly for set_role_from_message.
351 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, seq
[times
], NULL
);
355 /* override fcc gotten in reply_seed */
357 fs_give((void **) &fcc
);
360 if(F_ON(F_COPY_TO_TO_FROM
, pine_state
) && !(role
&& role
->from
)){
362 * A list of all of our addresses that appear in the To
363 * and cc fields is in us_in_to_and_cc.
364 * If there is exactly one address in that list then
365 * use it for the outgoing From.
367 if(us_in_to_and_cc
&& !us_in_to_and_cc
->next
){
368 PINEFIELD
*custom
, *pf
;
373 * Check to see if this address is different from what
374 * we would have used anyway. If it is, notify the user
375 * with a status message. This is pretty hokey because we're
376 * mimicking how pine_send would set the From address and
377 * there is no coordination between the two.
380 /* in case user has a custom From value */
381 custom
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
383 pf
= (PINEFIELD
*) fs_get(sizeof(*pf
));
384 memset((void *) pf
, 0, sizeof(*pf
));
385 pf
->name
= cpystr("From");
387 if(set_default_hdrval(pf
, custom
) >= UseAsDef
388 && pf
->textbuf
&& pf
->textbuf
[0]){
389 removing_trailing_white_space(pf
->textbuf
);
390 (void)removing_double_quotes(pf
->textbuf
);
391 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
392 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
394 fs_give((void **) &addr
);
398 *pf
->addr
= generate_from();
400 if(*pf
->addr
&& !address_is_same(*pf
->addr
, us_in_to_and_cc
)){
403 role
= (ACTION_S
*) fs_get(sizeof(*role
));
404 memset((void *) role
, 0, sizeof(*role
));
408 role
->from
= us_in_to_and_cc
;
409 us_in_to_and_cc
= NULL
;
412 free_customs(custom
);
418 if(rolemsg
&& copytomsg
)
419 q_status_message1(SM_ORDER
, 3, 4,
420 _("Replying using role \"%s\" and To as From"), role
->nick
);
422 q_status_message1(SM_ORDER
, 3, 4,
423 _("Replying using role \"%s\""), role
->nick
);
425 q_status_message(SM_ORDER
, 3, 4,
426 _("Replying using incoming To as outgoing From"));
430 mail_free_address(&us_in_to_and_cc
);
433 seq
[++times
] = -1L; /* mark end of sequence list */
435 /*========== Other miscellaneous fields ===================*/
436 outgoing
->in_reply_to
= reply_in_reply_to(env
);
437 outgoing
->references
= reply_build_refs(env
);
438 outgoing
->message_id
= generate_message_id(role
);
443 !outgoing
->newsgroups
)
444 q_status_message(SM_ORDER
| SM_DING
, 3, 6,
445 _("Warning: no valid addresses to reply to!"));
448 /*==================== Now fix up the message body ====================*/
451 * create storage object to be used for message text
453 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
454 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
455 _("Error allocating message text"));
459 gf_set_so_writec(&pc
, (STORE_S
*) msgtext
);
461 /*---- Include the original text if requested ----*/
462 if(include_text
&& totalm
> 1L){
464 int impl
, template_len
= 0, leave_cursor_at_top
= 0;
468 if(role
&& role
->template){
472 filtered
= detoken(role
, env
, 0,
473 ps_global
->reply
.signature_bottom
,
474 0, &redraft_pos
, &impl
);
477 so_puts((STORE_S
*)msgtext
, filtered
);
479 template_len
= strlen(filtered
);
481 leave_cursor_at_top
++;
484 fs_give((void **)&filtered
);
492 if((sig
= reply_signature(role
, env
, &redraft_pos
, &impl
)) &&
493 !ps_global
->reply
.signature_bottom
){
496 * If CURSORPOS was set explicitly in sig_file, and there was a
497 * template file before that, we need to adjust the offset by the
498 * length of the template file. However, if the template had
499 * a set CURSORPOS in it then impl was 2 before getting to the
500 * signature, so offset wouldn't have been reset by the signature
501 * CURSORPOS and offset would already be correct. That case will
502 * be ok here because template_len will be 0 and adding it does
503 * nothing. If template
504 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
505 * by the CURSORPOS in the sig. In that case we have to adjust the
506 * offset. That's what the next line does. It adjusts it if
507 * template_len is nonzero and if CURSORPOS was set in sig_file.
510 redraft_pos
->offset
+= template_len
;
513 so_puts((STORE_S
*)msgtext
, sig
);
516 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
517 * is set, we won't have used it yet and want it to be non-NULL.
519 fs_give((void **)&sig
);
523 * Only put cursor in sig if there is a cursorpos there but not
524 * one in the template, and sig-at-bottom.
526 if(!(sig
&& impl
== 2 && !leave_cursor_at_top
))
527 leave_cursor_at_top
++;
529 body
= mail_newbody();
530 body
->type
= TYPETEXT
;
531 body
->contents
.text
.data
= msgtext
;
533 for(msgno
= mn_first_cur(pine_state
->msgmap
);
535 msgno
= mn_next_cur(pine_state
->msgmap
)){
537 if(env
){ /* put 2 between messages */
538 gf_puts(NEWLINE
, pc
);
539 gf_puts(NEWLINE
, pc
);
542 /*--- Grab current envelope ---*/
543 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
544 mn_m2raw(pine_state
->msgmap
, msgno
),
547 q_status_message1(SM_ORDER
,3,4,
548 _("Error fetching message %s. Can't reply to it."),
549 long2string(mn_get_cur(pine_state
->msgmap
)));
553 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| reply_raw_body
) {
554 reply_delimiter(env
, role
, pc
);
555 if(ps_global
->reply
.include_header
)
556 reply_forward_header(pine_state
->mail_stream
,
557 mn_m2raw(pine_state
->msgmap
,msgno
),
558 NULL
, env
, pc
, prefix
);
560 get_body_part_text(pine_state
->mail_stream
, reply_raw_body
? NULL
: orig_body
,
561 mn_m2raw(pine_state
->msgmap
, msgno
),
562 reply_raw_body
? NULL
: "1", 0L, pc
, prefix
,
565 else if(orig_body
->type
== TYPEMULTIPART
) {
567 q_status_message(SM_ORDER
,3,7,
568 _("WARNING! Attachments not included in multiple reply."));
570 if(orig_body
->nested
.part
571 && orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
572 /*---- First part of the message is text -----*/
573 reply_delimiter(env
, role
, pc
);
574 if(ps_global
->reply
.include_header
)
575 reply_forward_header(pine_state
->mail_stream
,
576 mn_m2raw(pine_state
->msgmap
,
578 NULL
, env
, pc
, prefix
);
580 get_body_part_text(pine_state
->mail_stream
,
581 &orig_body
->nested
.part
->body
,
582 mn_m2raw(pine_state
->msgmap
, msgno
),
583 "1", 0L, pc
, prefix
, NULL
, GBPT_NONE
);
586 q_status_message(SM_ORDER
,0,3,
587 _("Multipart with no leading text part."));
591 /*---- Single non-text message of some sort ----*/
592 q_status_message(SM_ORDER
,3,3,
593 _("Non-text message not included."));
597 if(!leave_cursor_at_top
){
601 /* rewind and count chars to start of sig file */
602 so_seek((STORE_S
*)msgtext
, 0L, 0);
603 while(so_readc(&c
, (STORE_S
*)msgtext
))
607 redraft_pos
= (REDRAFT_POS_S
*)fs_get(sizeof(*redraft_pos
));
608 memset((void *)redraft_pos
, 0,sizeof(*redraft_pos
));
609 redraft_pos
->hdrname
= cpystr(":");
613 * If explicit cursor positioning in sig file,
614 * add offset to start of sig file plus offset into sig file.
615 * Else, just offset to start of sig file.
617 redraft_pos
->offset
+= cnt
;
622 so_puts((STORE_S
*)msgtext
, sig
);
624 fs_give((void **)&sig
);
628 msgno
= mn_m2raw(pine_state
->msgmap
,
629 mn_get_cur(pine_state
->msgmap
));
631 /*--- Grab current envelope ---*/
632 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, msgno
,
636 * If the charset of the body part is different from ascii and
637 * charset conversion is _not_ happening, then preserve the original
638 * charset from the message so that if we don't enter any new
639 * chars with the hibit set we can use the original charset.
640 * If not all those things, then don't try to preserve it.
645 charset
= parameter_val(orig_body
->parameter
, "charset");
646 if(charset
&& strucmp(charset
, "us-ascii") != 0){
650 * There is a non-ascii charset, is there conversion happening?
652 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
653 reply
.orig_charset
= charset
;
659 fs_give((void **) &charset
);
663 if(!(body
= reply_body(pine_state
->mail_stream
, env
, orig_body
,
664 msgno
, NULL
, msgtext
, prefix
,
665 include_text
, role
, 1, &redraft_pos
)))
669 q_status_message1(SM_ORDER
,3,4,
670 _("Error fetching message %s. Can't reply to it."),
671 long2string(mn_get_cur(pine_state
->msgmap
)));
676 /* fill in reply structure */
677 reply
.prefix
= prefix
;
678 reply
.mailbox
= cpystr(pine_state
->mail_stream
->mailbox
);
679 reply
.origmbox
= cpystr(pine_state
->mail_stream
->original_mailbox
680 ? pine_state
->mail_stream
->original_mailbox
681 : pine_state
->mail_stream
->mailbox
);
682 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((times
+ 1) * sizeof(imapuid_t
));
683 if((reply
.data
.uid
.validity
= pine_state
->mail_stream
->uid_validity
) != 0){
685 for(i
= 0; i
< times
; i
++)
686 reply
.data
.uid
.msgs
[i
] = mail_uid(pine_state
->mail_stream
, seq
[i
]);
690 for(i
= 0; i
< times
; i
++)
691 reply
.data
.uid
.msgs
[i
] = seq
[i
];
694 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
696 #if defined(DOS) && !defined(_WINDOWS)
697 free((void *)reserve
);
700 /* partially formatted outgoing message */
701 pine_send(outgoing
, &body
, _("COMPOSE MESSAGE REPLY"),
702 role
, fcc
, &reply
, redraft_pos
, NULL
, NULL
, 0);
705 pine_free_body(&body
);
707 fs_give((void **) &reply
.mailbox
);
709 fs_give((void **) &reply
.origmbox
);
710 if(reply
.orig_charset
)
711 fs_give((void **) &reply
.orig_charset
);
712 fs_give((void **) &reply
.data
.uid
.msgs
);
714 if((STORE_S
*) msgtext
)
715 gf_clear_so_writec((STORE_S
*) msgtext
);
717 mail_free_envelope(&outgoing
);
718 mail_free_address(&saved_from
);
719 mail_free_address(&saved_to
);
720 mail_free_address(&saved_cc
);
721 mail_free_address(&saved_resent
);
723 fs_give((void **)&seq
);
726 fs_give((void **)&prefix
);
729 fs_give((void **) &fcc
);
731 free_redraft_pos(&redraft_pos
);
738 * Ask user to confirm role choice, or choose another role.
740 * Args role -- A pointer into the pattern_h space at the default
741 * role to use. This can't be a copy, the comparison
742 * relies on it pointing at the actual role.
743 * This arg is also used to return a pointer to the
746 * Returns 1 -- Yes, use role which is now in *role. This may not be
747 * the same as the role passed in and it may even be NULL.
751 confirm_role(long int rflags
, ACTION_S
**role
)
753 ACTION_S
*role_p
= NULL
;
754 ACTION_S
*default_role
= NULL
;
755 char prompt
[80], *prompt_fodder
;
756 int cmd
, done
, ret
= 1;
757 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
758 (*redraw
)(void) = ps_global
->redrawer
;
764 if(!nonempty_patterns(ROLE_DO_ROLES
, &pstate
) || !role
)
768 * If this is a reply or forward and the role doesn't require confirmation,
769 * then we just return with what was passed in.
771 if(((rflags
& ROLE_REPLY
) &&
772 *role
&& (*role
)->repl_type
== ROLE_REPL_NOCONF
) ||
773 ((rflags
& ROLE_FORWARD
) &&
774 *role
&& (*role
)->forw_type
== ROLE_FORW_NOCONF
) ||
775 ((rflags
& ROLE_COMPOSE
) &&
776 *role
&& (*role
)->comp_type
== ROLE_COMP_NOCONF
) ||
777 (!*role
&& F_OFF(F_ROLE_CONFIRM_DEFAULT
, ps_global
)
778 && !ps_global
->default_role
))
782 * Check that there is at least one role available. This is among all
783 * roles, not just the reply roles or just the forward roles. That's
784 * because we have ^T take us to all the roles, not the category-specific
787 if(!(pat
= last_pattern(&pstate
)))
796 ekey
[2].ch
= ctrl('T');
802 /* check for more than one role available (or no role set) */
803 if(pat
== first_pattern(&pstate
) && *role
) /* no ^T */
808 * Go through the loop just in case default_role doesn't point
809 * to a real current role.
811 if(ps_global
->default_role
){
812 for(pat
= first_pattern(&pstate
);
814 pat
= next_pattern(&pstate
)){
815 if(pat
->action
== ps_global
->default_role
){
816 default_role
= ps_global
->default_role
;
824 /* override default */
826 for(pat
= first_pattern(&pstate
);
828 pat
= next_pattern(&pstate
)){
829 if(pat
->action
== *role
){
836 if(rflags
& ROLE_REPLY
)
837 prompt_fodder
= _("Reply");
838 else if(rflags
& ROLE_FORWARD
)
839 prompt_fodder
= _("Forward");
841 prompt_fodder
= _("Compose");
848 help
= h_role_confirm
;
850 ekey
[0].label
= N_("Yes");
853 ekey
[1].label
= N_("No, use default role");
855 ekey
[1].label
= N_("No, use default settings");
857 ekey
[2].label
= N_("To Select Alternate Role");
859 if(curpat
->patgrp
&& curpat
->patgrp
->nick
)
860 /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
861 snprintf(prompt
, sizeof(prompt
), _("Use role \"%s\" for %s? "),
862 short_str(curpat
->patgrp
->nick
, buf
, sizeof(buf
), 50, MidDots
),
865 snprintf(prompt
, sizeof(prompt
),
866 _("Use role \"<a role without a nickname>\" for %s? "),
870 help
= h_norole_confirm
;
871 ekey
[0].name
= "Ret";
872 ekey
[0].label
= prompt_fodder
;
875 ekey
[2].label
= N_("To Select Role");
876 snprintf(prompt
, sizeof(prompt
),
877 _("Press Return to %s using %s role, or ^T to select a role "),
878 prompt_fodder
, default_role
? _("default") : _("no"));
881 prompt
[sizeof(prompt
)-1] = '\0';
883 cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
884 'y', 'x', help
, RB_NORM
);
887 case 'y': /* Accept */
889 *role
= curpat
? curpat
->action
: default_role
;
892 case 'x': /* Cancel */
896 case 'n': /* NoRole */
898 *role
= default_role
;
902 if(role_select_screen(ps_global
, &role_p
, 0) >= 0){
904 for(pat
= first_pattern(&pstate
);
906 pat
= next_pattern(&pstate
)){
907 if(pat
->action
== role_p
){
918 ps_global
->mangled_body
= 1;
919 ps_global
->prev_screen
= prev_screen
;
920 ps_global
->redrawer
= redraw
;
930 * reply_to_all_query - Ask user about replying to all recipients
932 * Returns: -1 if cancel, 0 otherwise
933 * by reference: flagp
936 reply_to_all_query(int *flagp
)
945 ekey
[0].label
= N_("Yes");
949 ekey
[1].label
= N_("No");
955 ps_global
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps_global
);
957 ekey
[2].label
= ps_global
->reply
.preserve_fields
? N_("Not Preserve") : N_("Preserve");
958 snprintf(prompt
, sizeof(prompt
), _("Reply to all recipients%s"),
959 ps_global
->reply
.preserve_fields
? _(" (preserving fields)? ") : "? ");
961 prompt
[sizeof(prompt
)-1] = '\0';
963 switch(cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
964 'n', 'x', h_preserve_field
, RB_NORM
)){
969 case 'y' : /* set reply-all bit */
970 (*flagp
) |= RSF_FORCE_REPLY_ALL
;
973 case 'n' : /* clear reply-all bit */
974 (*flagp
) &= ~RSF_FORCE_REPLY_ALL
;
978 ps_global
->reply
.preserve_fields
=
979 (ps_global
->reply
.preserve_fields
+ 1) % 2;
980 goto loop
; /* ugly, but saves me a variable */
989 * reply_using_replyto_query - Ask user about replying with reply-to value
991 * Returns: 'y' if yes
995 reply_using_replyto_query(void)
997 return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
998 'y', 'x', NO_HELP
,WT_SEQ_SENSITIVE
));
1003 * reply_text_query - Ask user about replying with text, or in the case
1004 * of alternate reply menu, set values to the answer to all questions
1005 * asked during reply.
1007 * Returns: 1 if include the text
1008 * 0 if we're NOT to include the text
1009 * -1 on cancel or error
1011 #define MAX_REPLY_OPTIONS 10
1013 reply_text_query(struct pine
*ps
, long int many
, ENVELOPE
*env
, char **prefix
)
1015 int ret
, edited
= 0, headers
= 0;
1016 static ESCKEY_S compose_style
[MAX_REPLY_OPTIONS
];
1020 orig_sf
= ps
->reply
.use_flowed
= *prefix
&& **prefix
? (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1021 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1022 && (strcmp(*prefix
, "> ") == 0
1023 || strcmp(*prefix
, ">") == 0)) : 0;
1024 ps
->reply
.strip_signature
= ps
->full_header
== 0
1025 && (F_ON(F_ENABLE_STRIP_SIGDASHES
, ps
)
1026 || F_ON(F_ENABLE_SIGDASHES
, ps
));
1027 ps
->reply
.keep_attach
= F_ON(F_ATTACHMENTS_IN_REPLY
, ps
);
1028 ps
->reply
.include_header
= F_ON(F_INCLUDE_HEADER
, ps
);
1029 ps
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps
);
1030 ps
->reply
.signature_bottom
= F_ON(F_SIG_AT_BOTTOM
, ps
);
1031 ps
->reply
.role_chosen
= NULL
;
1033 if(F_OFF(F_ALT_REPLY_MENU
, ps
)
1034 && F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
)
1035 && F_OFF(F_ENABLE_EDIT_REPLY_INDENT
, ps
)
1036 && F_OFF(F_ALT_REPLY_MENU
,ps
))
1040 compose_style
[ekey_num
= 0].ch
= 'y';
1041 compose_style
[ekey_num
].rval
= 'y';
1042 compose_style
[ekey_num
].name
= "Y";
1043 compose_style
[ekey_num
++].label
= N_("Yes");
1045 compose_style
[ekey_num
].ch
= 'n';
1046 compose_style
[ekey_num
].rval
= 'n';
1047 compose_style
[ekey_num
].name
= "N";
1048 compose_style
[ekey_num
++].label
= N_("No");
1050 if (F_OFF(F_ALT_REPLY_MENU
, ps
)){ /**** Standard menu ****/
1051 /* TRANSLATORS: The final five %s's can probably be safely ignored */
1052 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "),
1053 (many
> 1L) ? comatose(many
) : "",
1054 (many
> 1L) ? " " : "",
1055 (many
> 1L) ? "s" : "",
1056 (many
> 1L) ? "s" : "",
1057 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? " (using \"" : "",
1058 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? *prefix
: "",
1059 ps
->reply
.role_chosen
? "\" and role \"" : "",
1060 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "",
1061 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? "\")" : "");
1062 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1064 if (F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
)){
1065 compose_style
[ekey_num
].ch
= ctrl('R');
1066 compose_style
[ekey_num
].rval
= 'r';
1067 compose_style
[ekey_num
].name
= "^R";
1068 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1070 } else { /***** Alternate Reply Menu ********/
1071 unsigned which_help
;
1073 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply (using \"%s%s%s\")? "),
1074 (many
> 1L) ? comatose(many
) : "",
1075 (many
> 1L) ? " " : "",
1076 (many
> 1L) ? "s" : "",
1078 ps
->reply
.role_chosen
? "\" and role \"" : "",
1079 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "");
1080 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1082 compose_style
[ekey_num
].ch
= 'h';
1083 compose_style
[ekey_num
].rval
= 'H';
1084 compose_style
[ekey_num
].name
= "H";
1085 compose_style
[ekey_num
++].label
= ps
->reply
.include_header
1086 ? N_("No Header") : N_("Inc Headr");
1088 compose_style
[ekey_num
].ch
= 's';
1089 compose_style
[ekey_num
].rval
= 'S';
1090 compose_style
[ekey_num
].name
= "S";
1091 compose_style
[ekey_num
++].label
= ps
->reply
.strip_signature
1092 ? N_("No Strip"): N_("Strip Sig");
1094 compose_style
[ekey_num
].ch
= 'a';
1095 compose_style
[ekey_num
].rval
= 'A';
1096 compose_style
[ekey_num
].name
= "A";
1097 compose_style
[ekey_num
++].label
= ps
->reply
.keep_attach
1098 ? N_("No Attach"): N_("Inc Attach");
1100 compose_style
[ekey_num
].ch
= 'b';
1101 compose_style
[ekey_num
].rval
= 'B';
1102 compose_style
[ekey_num
].name
= "B";
1103 compose_style
[ekey_num
++].label
= ps
->reply
.signature_bottom
1104 ? N_("Sig Top") : N_("Sig Bottom");
1106 compose_style
[ekey_num
].ch
= 'r';
1107 compose_style
[ekey_num
].rval
= 'R';
1108 compose_style
[ekey_num
].name
= "R";
1109 compose_style
[ekey_num
++].label
= N_("Set Role");
1111 compose_style
[ekey_num
].ch
= ctrl('R');
1112 compose_style
[ekey_num
].rval
= 'r';
1113 compose_style
[ekey_num
].name
= "^R";
1114 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1116 /***** End Alt Reply Menu *********/
1119 compose_style
[ekey_num
].ch
= -1;
1120 compose_style
[ekey_num
].name
= NULL
;
1121 compose_style
[ekey_num
].label
= NULL
;
1123 switch(ret
= radio_buttons(tmp_20k_buf
,
1124 ps
->ttyo
->screen_rows
> 4
1125 ? -FOOTER_ROWS(ps
) : -1,
1127 (edited
|| headers
|| F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
))
1129 'x', NO_HELP
, RB_SEQ_SENSITIVE
)){
1131 cmd_cancelled("Reply");
1135 ps
->reply
.keep_attach
= (ps
->reply
.keep_attach
+ 1) % 2;
1139 ps
->reply
.signature_bottom
= (ps
->reply
.signature_bottom
+ 1) % 2;
1143 ps
->reply
.use_flowed
= (ps
->reply
.use_flowed
+ 1) % 2;
1147 ps
->reply
.strip_signature
= (ps
->reply
.strip_signature
+ 1) % 2;
1151 ps
->reply
.include_header
= (ps
->reply
.include_header
+ 1) % 2;
1152 headers
= ps
->reply
.include_header
;
1157 void (*prev_screen
)(struct pine
*) = ps
->prev_screen
,
1158 (*redraw
)(void) = ps
->redrawer
;
1159 ps
->redrawer
= NULL
;
1160 ps
->next_screen
= SCREEN_FUN_NULL
;
1161 if(role_select_screen(ps
, &ps
->reply
.role_chosen
, 1) < 0){
1162 cmd_cancelled("Reply");
1163 ps
->next_screen
= prev_screen
;
1164 ps
->redrawer
= redraw
;
1169 ps
->next_screen
= prev_screen
;
1170 ps
->redrawer
= redraw
;
1171 if(ps
->reply
.role_chosen
)
1172 ps
->reply
.role_chosen
= combine_inherited_role(ps
->reply
.role_chosen
);
1179 if(prefix
&& *prefix
){
1185 strncpy(buf
, *prefix
, sizeof(buf
)-1);
1186 buf
[sizeof(buf
)-1] = '\0';
1188 flags
= OE_APPEND_CURRENT
|
1189 OE_KEEP_TRAILING_SPACE
|
1193 switch(optionally_enter(buf
, ps
->ttyo
->screen_rows
> 4
1194 ? -FOOTER_ROWS(ps
) : -1,
1195 0, sizeof(buf
), "Reply prefix : ",
1196 NULL
, NO_HELP
, &flags
)){
1197 case 0: /* entry successful, continue */
1198 if(flags
& OE_USER_MODIFIED
){
1199 fs_give((void **)prefix
);
1200 *prefix
= removing_quotes(cpystr(buf
));
1201 orig_sf
= ps
->reply
.use_flowed
= *prefix
&& **prefix
?
1202 (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1203 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1204 && (strcmp(*prefix
, "> ") == 0
1205 || strcmp(*prefix
, ">") == 0)) : 0;
1213 cmd_cancelled("Reply");
1222 if(ps_global
->redrawer
!= NULL
)
1233 q_status_message(SM_ORDER
, 3, 4,
1234 "Programmer botch in reply_text_query()");
1249 q_status_message1(SM_ORDER
, 3, 4,
1250 "Invalid rval \'%s\'", pretty_command(ret
));
1258 * reply_poster_followup - return TRUE if "followup-to" set to "poster"
1260 * NOTE: queues status message indicating such
1263 reply_poster_followup(ENVELOPE
*e
)
1265 if(e
&& e
->followup_to
&& !strucmp(e
->followup_to
, "poster")){
1266 q_status_message(SM_ORDER
, 2, 3,
1267 _("Replying to Poster as specified in \"Followup-To\""));
1276 * reply_news_test - Test given envelope for newsgroup data and copy
1277 * it at the users request
1279 * 0 if error or cancel
1281 * 2 follow-up via news
1285 reply_news_test(ENVELOPE
*env
, ENVELOPE
*outgoing
)
1288 static ESCKEY_S news_opt
[] = { {'f', 'f', "F", N_("Follow-up")},
1289 {'r', 'r', "R", N_("Reply")},
1290 {'b', 'b', "B", N_("Both")},
1291 {-1, 0, NULL
, NULL
} };
1293 if(env
->newsgroups
&& *env
->newsgroups
&& !reply_poster_followup(env
))
1295 * Now that we know a newsgroups field is present,
1296 * ask if the user is posting a follow-up article...
1298 switch(radio_buttons(
1299 _("Follow-up to news group(s), Reply via email to author or Both? "),
1300 -FOOTER_ROWS(ps_global
), news_opt
, 'r', 'x',
1302 case 'r' : /* Reply */
1306 case 'f' : /* Follow-Up via news ONLY! */
1310 case 'b' : /* BOTH */
1314 case 'x' : /* cancel or unknown response */
1316 cmd_cancelled("Reply");
1322 if(env
->followup_to
){
1323 q_status_message(SM_ORDER
, 2, 3,
1324 _("Posting to specified Followup-To groups"));
1325 outgoing
->newsgroups
= cpystr(env
->followup_to
);
1327 else if(!outgoing
->newsgroups
)
1328 outgoing
->newsgroups
= cpystr(env
->newsgroups
);
1335 /*----------------------------------------------------------------------
1336 Acquire the pinerc defined signature file
1337 It is allocated here and freed by the caller.
1339 file -- use this file
1340 prenewlines -- prefix the file contents with this many newlines
1341 postnewlines -- postfix the file contents with this many newlines
1342 is_sig -- this is a signature (not a template)
1345 get_signature_file(char *file
, int prenewlines
, int postnewlines
, int is_sig
)
1347 char *sig
, *tmp_sig
= NULL
, sig_path
[MAXPATH
+1];
1348 int len
, do_the_pipe_thang
= 0;
1349 long sigsize
= 0L, cntdown
;
1352 if(!signature_path(file
, sig_path
, MAXPATH
))
1355 dprint((5, "get_signature(%s)\n", sig_path
));
1357 if(sig_path
[(len
=strlen(sig_path
))-1] == '|'){
1358 if(is_sig
&& F_ON(F_DISABLE_PIPES_IN_SIGS
, ps_global
)){
1359 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1360 _("Pipes for signatures are administratively disabled"));
1363 else if(!is_sig
&& F_ON(F_DISABLE_PIPES_IN_TEMPLATES
, ps_global
)){
1364 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1365 _("Pipes for templates are administratively disabled"));
1369 sig_path
[len
-1] = '\0';
1370 removing_trailing_white_space(sig_path
);
1371 do_the_pipe_thang
++;
1374 if(!IS_REMOTE(sig_path
) && ps_global
->VAR_OPER_DIR
&&
1375 !in_dir(ps_global
->VAR_OPER_DIR
, sig_path
)){
1376 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1377 /* TRANSLATORS: First arg is the directory name, second is
1378 the file user wants to read but can't. */
1379 _("Can't read file outside %s: %s"),
1380 ps_global
->VAR_OPER_DIR
, file
);
1385 if(IS_REMOTE(sig_path
) || can_access(sig_path
, ACCESS_EXISTS
) == 0){
1386 if(do_the_pipe_thang
){
1387 if(can_access(sig_path
, EXECUTE_ACCESS
) == 0){
1394 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1396 flags
= PIPE_READ
| PIPE_STDERR
| PIPE_NOSHELL
;
1400 if((syspipe
= open_system_pipe(sig_path
, NULL
, NULL
, flags
, 5,
1401 pipe_callback
, pipe_report_error
)) != NULL
){
1405 gf_set_so_writec(&pc
, store
);
1406 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, READ_FROM_LOCALE
);
1409 if((error
= gf_pipe(gc
, pc
)) != NULL
){
1410 (void)close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1411 gf_clear_so_writec(store
);
1413 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1414 _("Can't get file: %s"), error
);
1418 if(close_system_pipe(&syspipe
, NULL
, pipe_callback
)){
1422 q_status_message2(SM_ORDER
, 3, 4,
1423 _("Error running program \"%s\"%s"),
1425 (now
- start
> 4) ? ": timed out" : "");
1428 gf_clear_so_writec(store
);
1430 /* rewind and count chars */
1431 so_seek(store
, 0L, 0);
1432 while(so_readc(&c
, store
) && sigsize
< 100000L)
1435 /* allocate space */
1436 tmp_sig
= fs_get((sigsize
+ 1) * sizeof(char));
1440 /* rewind and copy chars, no prenewlines... */
1441 so_seek(store
, 0L, 0);
1443 while(so_readc(&c
, store
) && cntdown
-- > 0L)
1451 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1452 _("Error running program \"%s\""),
1457 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1458 "Error allocating space for sig or template program");
1461 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1462 /* TRANSLATORS: Arg is a program name */
1463 _("Can't execute \"%s\": Permission denied"),
1466 else if((IS_REMOTE(sig_path
) &&
1467 (tmp_sig
= simple_read_remote_file(sig_path
, REMOTE_SIG_SUBTYPE
))) ||
1468 (tmp_sig
= read_file(sig_path
, READ_FROM_LOCALE
)))
1469 sigsize
= strlen(tmp_sig
);
1471 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1472 /* TRANSLATORS: First arg is error description, 2nd is
1474 _("Error \"%s\" reading file \"%s\""),
1475 error_description(errno
), sig_path
);
1478 sig
= get_signature_lit(tmp_sig
, prenewlines
, postnewlines
, is_sig
, 0);
1480 fs_give((void **)&tmp_sig
);
1487 /*----------------------------------------------------------------------
1488 Partially set up message to forward and pass off to composer/mailer
1490 Args: pine_state -- The usual pine structure
1492 Result: outgoing envelope and body created and passed off to composer/mailer
1494 Create the outgoing envelope for the mail being forwarded, which is
1495 not much more than filling in the subject, and create the message body
1496 of the outgoing message which requires formatting the header from the
1497 envelope of the original messasge.
1498 ----------------------------------------------------------------------*/
1500 forward(struct pine
*ps
, ACTION_S
*role_arg
)
1503 int ret
, forward_raw_body
= 0, rv
= 0, i
;
1504 long msgno
, j
, totalmsgs
, rflags
;
1505 ENVELOPE
*env
, *outgoing
;
1506 BODY
*orig_body
, *body
= NULL
;
1508 void *msgtext
= NULL
;
1510 int impl
, template_len
= 0;
1512 REDRAFT_POS_S
*redraft_pos
= NULL
;
1513 ACTION_S
*role
= NULL
, *nrole
;
1514 #if defined(DOS) && !defined(_WINDOWS)
1518 dprint((4, "\n - forward -\n"));
1520 memset((void *)&reply
, 0, sizeof(reply
));
1521 outgoing
= mail_newenvelope();
1523 if(ps_global
->full_header
== 2
1524 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
1525 forward_raw_body
= 1;
1527 if((totalmsgs
= mn_total_cur(ps
->msgmap
)) > 1L){
1528 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s forwarded messages...", comatose(totalmsgs
));
1529 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1530 outgoing
->subject
= cpystr(tmp_20k_buf
);
1533 /*---------- Get the envelope of message we're forwarding ------*/
1534 msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
1535 if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
))
1536 && (outgoing
->subject
= forward_subject(env
, 0)))){
1537 q_status_message1(SM_ORDER
,3,4,
1538 _("Error fetching message %s. Can't forward it."),
1539 long2string(msgno
));
1545 * as with all text bound for the composer, build it in
1546 * a storage object of the type it understands...
1548 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
1549 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1550 _("Error allocating message text"));
1554 ret
= (F_ON(F_FORWARD_AS_ATTACHMENT
, ps_global
))
1557 ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1558 : (ps
->full_header
== 2)
1559 ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP
, WT_SEQ_SENSITIVE
)
1563 cmd_cancelled("Forward");
1564 so_give((STORE_S
**)&msgtext
);
1568 /* Setup possible role */
1570 role
= copy_action(role_arg
);
1573 rflags
= ROLE_FORWARD
;
1574 if(nonempty_patterns(rflags
, &dummy
)){
1575 /* setup default role */
1577 j
= mn_first_cur(ps
->msgmap
);
1580 nrole
= set_role_from_msg(ps
, rflags
,
1581 mn_m2raw(ps
->msgmap
, j
), NULL
);
1582 } while(nrole
&& (!role
|| nrole
== role
)
1583 && (j
=mn_next_cur(ps
->msgmap
)) > 0L);
1585 if(!role
|| nrole
== role
)
1590 if(confirm_role(rflags
, &role
))
1591 role
= combine_inherited_role(role
);
1592 else{ /* cancel reply */
1594 cmd_cancelled("Forward");
1595 so_give((STORE_S
**)&msgtext
);
1602 q_status_message1(SM_ORDER
, 3, 4,
1603 _("Forwarding using role \"%s\""), role
->nick
);
1605 outgoing
->message_id
= generate_message_id(role
);
1607 if(role
&& role
->template){
1611 filtered
= detoken(role
, (totalmsgs
== 1L) ? env
: NULL
,
1612 0, 0, 0, &redraft_pos
, &impl
);
1615 so_puts((STORE_S
*)msgtext
, filtered
);
1617 template_len
= strlen(filtered
);
1620 fs_give((void **)&filtered
);
1626 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
1628 redraft_pos
->offset
+= template_len
;
1630 so_puts((STORE_S
*)msgtext
, *sig
? sig
: NEWLINE
);
1632 fs_give((void **)&sig
);
1635 so_puts((STORE_S
*)msgtext
, NEWLINE
);
1637 gf_set_so_writec(&pc
, (STORE_S
*)msgtext
);
1639 #if defined(DOS) && !defined(_WINDOWS)
1640 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1641 #define IN_RESERVE 8192
1643 #define IN_RESERVE 16384
1645 if((reserve
=(char *)malloc(IN_RESERVE
)) == NULL
){
1646 gf_clear_so_writec((STORE_S
*) msgtext
);
1647 so_give((STORE_S
**)&msgtext
);
1648 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1649 _("Insufficient memory for message text"));
1655 * If we're forwarding multiple messages *or* the forward-as-mime
1656 * is turned on and the users wants it done that way, package things
1659 if(ret
== 'y'){ /* attach message[s]!!! */
1661 long totalsize
= 0L;
1663 /*---- New Body to start with ----*/
1664 body
= mail_newbody();
1665 body
->type
= TYPEMULTIPART
;
1667 /*---- The TEXT part/body ----*/
1668 body
->nested
.part
= mail_newbody_part();
1669 body
->nested
.part
->body
.type
= TYPETEXT
;
1670 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1673 /*---- The MULTIPART/DIGEST part ----*/
1674 body
->nested
.part
->next
= mail_newbody_part();
1675 body
->nested
.part
->next
->body
.type
= TYPEMULTIPART
;
1676 body
->nested
.part
->next
->body
.subtype
= cpystr("Digest");
1677 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Digest of %s messages", comatose(totalmsgs
));
1678 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1679 body
->nested
.part
->next
->body
.description
= cpystr(tmp_20k_buf
);
1680 pp
= &(body
->nested
.part
->next
->body
.nested
.part
);
1683 pp
= &(body
->nested
.part
->next
);
1685 /*---- The Message body subparts ----*/
1686 for(msgno
= mn_first_cur(ps
->msgmap
);
1688 msgno
= mn_next_cur(ps
->msgmap
)){
1690 msgno
= mn_m2raw(ps
->msgmap
, msgno
);
1691 env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
);
1693 if(forward_mime_msg(ps
->mail_stream
,msgno
,NULL
,env
,pp
,msgtext
)){
1694 totalsize
+= (*pp
)->body
.size
.bytes
;
1695 pp
= &((*pp
)->next
);
1702 body
->nested
.part
->next
->body
.size
.bytes
= totalsize
;
1704 else if(totalmsgs
> 1L){
1706 body
= mail_newbody();
1707 body
->type
= TYPETEXT
;
1708 body
->contents
.text
.data
= msgtext
;
1711 for(msgno
= mn_first_cur(ps
->msgmap
);
1713 msgno
= mn_next_cur(ps
->msgmap
)){
1715 if(env
){ /* put 2 between messages */
1716 gf_puts(NEWLINE
, pc
);
1717 gf_puts(NEWLINE
, pc
);
1720 /*--- Grab current envelope ---*/
1721 env
= pine_mail_fetchstructure(ps
->mail_stream
,
1722 mn_m2raw(ps
->msgmap
, msgno
),
1724 if(!env
|| !orig_body
){
1725 q_status_message1(SM_ORDER
,3,4,
1726 _("Error fetching message %s. Can't forward it."),
1727 long2string(msgno
));
1731 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| forward_raw_body
) {
1732 forward_delimiter(pc
);
1733 reply_forward_header(ps
->mail_stream
,
1734 mn_m2raw(ps
->msgmap
, msgno
),
1737 if(!get_body_part_text(ps
->mail_stream
, forward_raw_body
? NULL
: orig_body
,
1738 mn_m2raw(ps
->msgmap
, msgno
),
1739 forward_raw_body
? NULL
: "1", 0L, pc
,
1740 NULL
, NULL
, GBPT_NONE
))
1742 } else if(orig_body
->type
== TYPEMULTIPART
) {
1744 q_status_message(SM_ORDER
,3,7,
1745 _("WARNING! Attachments not included in multiple forward."));
1747 if(orig_body
->nested
.part
&&
1748 orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
1749 /*---- First part of the message is text -----*/
1750 forward_delimiter(pc
);
1751 reply_forward_header(ps
->mail_stream
,
1752 mn_m2raw(ps
->msgmap
,msgno
),
1755 if(!get_body_part_text(ps
->mail_stream
,
1756 &orig_body
->nested
.part
->body
,
1757 mn_m2raw(ps
->msgmap
, msgno
),
1759 NULL
, NULL
, GBPT_NONE
))
1762 q_status_message(SM_ORDER
,0,3,
1763 _("Multipart with no leading text part!"));
1766 /*---- Single non-text message of some sort ----*/
1767 q_status_message(SM_ORDER
,0,3,
1768 _("Non-text message not included!"));
1772 else if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
,
1774 && (body
= forward_body(ps
->mail_stream
, env
, orig_body
, msgno
,
1777 q_status_message1(SM_ORDER
,3,4,
1778 _("Error fetching message %s. Can't forward it."),
1779 long2string(msgno
));
1783 if(ret
!= 'y' && totalmsgs
== 1L && orig_body
){
1786 charset
= parameter_val(orig_body
->parameter
, "charset");
1787 if(charset
&& strucmp(charset
, "us-ascii") != 0){
1791 * There is a non-ascii charset, is there conversion happening?
1793 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
1794 reply
.orig_charset
= charset
;
1800 fs_give((void **) &charset
);
1803 * I don't think orig_charset is ever used except possibly
1804 * right here. Hubert 2008-01-15.
1806 if(reply
.orig_charset
)
1810 /* fill in reply structure */
1811 reply
.forwarded
= 1;
1812 reply
.mailbox
= cpystr(ps
->mail_stream
->mailbox
);
1813 reply
.origmbox
= cpystr(ps
->mail_stream
->original_mailbox
1814 ? ps
->mail_stream
->original_mailbox
1815 : ps
->mail_stream
->mailbox
);
1816 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((totalmsgs
+ 1) * sizeof(imapuid_t
));
1817 if((reply
.data
.uid
.validity
= ps
->mail_stream
->uid_validity
) != 0){
1819 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1821 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1822 reply
.data
.uid
.msgs
[i
] = mail_uid(ps
->mail_stream
, mn_m2raw(ps
->msgmap
, msgno
));
1826 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1828 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1829 reply
.data
.uid
.msgs
[i
] = mn_m2raw(ps
->msgmap
, msgno
);
1832 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
1834 #if defined(DOS) && !defined(_WINDOWS)
1835 free((void *)reserve
);
1837 pine_send(outgoing
, &body
, "FORWARD MESSAGE",
1838 role
, NULL
, &reply
, redraft_pos
,
1844 pine_free_body(&body
);
1846 if((STORE_S
*) msgtext
)
1847 gf_clear_so_writec((STORE_S
*) msgtext
);
1849 mail_free_envelope(&outgoing
);
1850 free_redraft_pos(&redraft_pos
);
1853 if(reply
.orig_charset
)
1854 fs_give((void **)&reply
.orig_charset
);
1859 q_status_message(SM_ORDER
| SM_DING
, 4, 5,
1860 _("Error fetching message contents. Can't forward message."));
1865 /*----------------------------------------------------------------------
1866 Partially set up message to forward and pass off to composer/mailer
1868 Args: pine_state -- The usual pine structure
1869 message -- The MESSAGECACHE of entry to reply to
1871 Result: outgoing envelope and body created and passed off to composer/mailer
1873 Create the outgoing envelope for the mail being forwarded, which is
1874 not much more than filling in the subject, and create the message body
1875 of the outgoing message which requires formatting the header from the
1876 envelope of the original messasge.
1877 ----------------------------------------------------------------------*/
1879 forward_text(struct pine
*pine_state
, void *text
, SourceType source
)
1885 char *enc_error
, *sig
;
1886 ACTION_S
*role
= NULL
;
1888 long rflags
= ROLE_COMPOSE
;
1890 if((msgtext
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1891 env
= mail_newenvelope();
1892 body
= mail_newbody();
1893 body
->type
= TYPETEXT
;
1894 body
->contents
.text
.data
= (void *) msgtext
;
1896 if(nonempty_patterns(rflags
, &dummy
)){
1898 * This is really more like Compose, even though it
1899 * is called Forward.
1901 if(confirm_role(rflags
, &role
))
1902 role
= combine_inherited_role(role
);
1904 cmd_cancelled("Composition");
1905 display_message('x');
1906 mail_free_envelope(&env
);
1907 pine_free_body(&body
);
1913 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
1916 env
->message_id
= generate_message_id(role
);
1918 sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
);
1919 so_puts(msgtext
, (sig
&& *sig
) ? sig
: NEWLINE
);
1920 so_puts(msgtext
, NEWLINE
);
1921 so_puts(msgtext
, "----- Included text -----");
1922 so_puts(msgtext
, NEWLINE
);
1924 fs_give((void **)&sig
);
1927 gf_set_so_writec(&pc
, msgtext
);
1928 gf_set_readc(&gc
,text
,(source
== CharStar
) ? strlen((char *)text
) : 0L,
1931 if((enc_error
= gf_pipe(gc
, pc
)) == NULL
){
1932 pine_send(env
, &body
, "SEND MESSAGE", role
, NULL
, NULL
, NULL
,
1934 pine_state
->mangled_screen
= 1;
1937 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1938 _("Error reading text \"%s\""),enc_error
);
1939 display_message('x');
1942 gf_clear_so_writec(msgtext
);
1943 mail_free_envelope(&env
);
1944 pine_free_body(&body
);
1947 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1948 _("Error allocating message text"));
1949 display_message('x');
1956 /*----------------------------------------------------------------------
1957 Partially set up message to resend and pass off to mailer
1959 Args: pine_state -- The usual pine structure
1961 Result: outgoing envelope and body created and passed off to mailer
1963 Create the outgoing envelope for the mail being resent, which is
1964 not much more than filling in the subject, and create the message body
1965 of the outgoing message which requires formatting the header from the
1966 envelope of the original messasge.
1967 ----------------------------------------------------------------------*/
1969 bounce(struct pine
*pine_state
, ACTION_S
*role
)
1973 char *save_to
= NULL
, **save_toptr
= NULL
, *errstr
= NULL
,
1974 *prmpt_who
= NULL
, *prmpt_cnf
= NULL
;
1976 dprint((4, "\n - bounce -\n"));
1978 if(mn_total_cur(pine_state
->msgmap
) > 1L){
1979 save_toptr
= &save_to
;
1981 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1982 mn_total_cur(pine_state
->msgmap
), role
->nick
);
1984 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages to : "),
1985 mn_total_cur(pine_state
->msgmap
));
1986 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1987 prmpt_who
= cpystr(tmp_20k_buf
);
1988 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Send %ld messages "),
1989 mn_total_cur(pine_state
->msgmap
));
1990 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1991 prmpt_cnf
= cpystr(tmp_20k_buf
);
1994 for(msgno
= mn_first_cur(pine_state
->msgmap
);
1996 msgno
= mn_next_cur(pine_state
->msgmap
)){
1998 rawno
= mn_m2raw(pine_state
->msgmap
, msgno
);
1999 if((env
= pine_mail_fetchstructure(pine_state
->mail_stream
, rawno
, NULL
)) != NULL
)
2000 errstr
= bounce_msg(pine_state
->mail_stream
, rawno
, NULL
, role
,
2001 save_toptr
, env
->subject
, prmpt_who
, prmpt_cnf
);
2003 errstr
= _("Can't fetch Subject for Bounce");
2008 q_status_message(SM_ORDER
| SM_DING
, 4, 7, errstr
);
2015 fs_give((void **)&save_to
);
2018 fs_give((void **) &prmpt_who
);
2021 fs_give((void **) &prmpt_cnf
);
2023 return(errstr
? 0 : 1);
2029 bounce_msg(MAILSTREAM
*stream
,
2038 char *errstr
= NULL
;
2045 /* When we bounce a message, we will leave the original message
2046 * intact, which means that it will not be signed or encrypted,
2047 * so we turn off signing and encrypting now. It will be turned
2048 * on again in send_exit_for_pico().
2050 if(ps_global
->smime
)
2051 ps_global
->smime
->do_sign
= ps_global
->smime
->do_encrypt
= 0;
2054 if((errstr
= bounce_msg_body(stream
, rawno
, part
, to
, subject
, &outgoing
, &body
, &was_seen
)) == NULL
){
2055 if(pine_simple_send(outgoing
, &body
, &role
, pmt_who
, pmt_cnf
, to
,
2056 !(to
&& *to
) ? SS_PROMPTFORTO
: 0) < 0){
2057 errstr
= ""; /* p_s_s() better have explained! */
2058 /* clear seen flag */
2059 if(was_seen
== 0 && rawno
> 0L
2060 && stream
&& rawno
<= stream
->nmsgs
2061 && (mc
= mail_elt(stream
, rawno
)) && mc
->seen
)
2062 mail_flag(stream
, long2string(rawno
), "\\SEEN", 0);
2066 /* Just for good measure... */
2067 mail_free_envelope(&outgoing
);
2068 pine_free_body(&body
);
2070 return(errstr
); /* no problem-o */
2074 /*----------------------------------------------------------------------
2075 Serve up the current signature within pico for editing
2079 Result: signature changed or not.
2082 signature_edit(char *sigfile
, char *title
)
2085 char sig_path
[MAXPATH
+1], errbuf
[2000], *errstr
= NULL
;
2087 STORE_S
*msgso
, *tmpso
= NULL
;
2090 struct variable
*vars
= ps_global
->vars
;
2091 REMDATA_S
*rd
= NULL
;
2093 if(!signature_path(sigfile
, sig_path
, MAXPATH
))
2094 return(cpystr(_("No signature file defined.")));
2096 if(IS_REMOTE(sigfile
)){
2097 rd
= rd_create_remote(RemImap
, sig_path
, REMOTE_SIG_SUBTYPE
,
2099 _("Can't access remote configuration."));
2101 return(cpystr(_("Error attempting to edit remote configuration")));
2103 (void)rd_read_metadata(rd
);
2105 if(rd
->access
== MaybeRorW
){
2106 if(rd
->read_status
== 'R')
2107 rd
->access
= ReadOnly
;
2109 rd
->access
= ReadWrite
;
2112 if(rd
->access
!= NoExists
){
2114 rd_check_remvalid(rd
, 1L);
2117 * If the cached info says it is readonly but
2118 * it looks like it's been fixed now, change it to readwrite.
2120 if(rd
->read_status
== 'R'){
2121 rd_check_readonly_access(rd
);
2122 if(rd
->read_status
== 'W'){
2123 rd
->access
= ReadWrite
;
2124 rd
->flags
|= REM_OUTOFDATE
;
2127 rd
->access
= ReadOnly
;
2131 if(rd
->flags
& REM_OUTOFDATE
){
2132 if(rd_update_local(rd
) != 0){
2135 "signature_edit: rd_update_local failed\n"));
2136 rd_close_remdata(&rd
);
2137 return(cpystr(_("Can't access remote sig")));
2143 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
2144 rd_close_remdata(&rd
);
2145 return(cpystr(_("Can't get write permission for remote sig")));
2148 rd
->flags
|= DO_REMTRIM
;
2150 strncpy(sig_path
, rd
->lf
, sizeof(sig_path
)-1);
2151 sig_path
[sizeof(sig_path
)-1] = '\0';
2154 standard_picobuf_setup(&pbf
);
2155 pbf
.tty_fix
= PineRaw
;
2156 pbf
.composer_help
= h_composer_sigedit
;
2157 pbf
.exittest
= sigedit_exit_for_pico
;
2158 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2159 ? upload_msg_to_pico
: NULL
;
2160 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2161 ? VAR_EDITOR
: NULL
;
2162 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2163 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2164 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2165 pbf
.allow_flowed_text
= 0;
2167 pbf
.pine_anchor
= set_titlebar(title
,
2168 ps_global
->mail_stream
,
2169 ps_global
->context_current
,
2170 ps_global
->cur_folder
,
2172 0, FolderName
, 0, 0, NULL
);
2174 /* NOTE: at this point, a lot of pico struct fields are null'd out
2175 * thanks to the leading memset; in particular "headents" which tells
2176 * pico to behave like a normal editor (though modified slightly to
2177 * let the caller dictate the file to edit and such)...
2180 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, sig_path
)){
2183 l
= strlen(VAR_OPER_DIR
) + 100;
2184 ret
= (char *) fs_get((l
+1) * sizeof(char));
2185 snprintf(ret
, l
+1, _("Can't edit file outside of %s"), VAR_OPER_DIR
);
2191 * Now alloc and init the text to pass pico
2193 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2194 ret
= cpystr(_("Error allocating space for file"));
2195 dprint((1, "Can't alloc space for signature_edit"));
2199 pbf
.msgtext
= so_text(msgso
);
2201 if(can_access(sig_path
, READ_ACCESS
) == 0
2202 && !(tmpso
= so_get(FileStar
, sig_path
, READ_ACCESS
|READ_FROM_LOCALE
))){
2203 char *problem
= error_description(errno
);
2205 snprintf(errbuf
, sizeof(errbuf
), _("Error editing \"%s\": %s"),
2206 sig_path
, problem
? problem
: "<NULL>");
2207 errbuf
[sizeof(errbuf
)-1] = '\0';
2208 ret
= cpystr(errbuf
);
2210 dprint((1, "signature_edit: can't open %s: %s", sig_path
,
2211 problem
? problem
: "<NULL>"));
2214 else if(tmpso
){ /* else, fill pico's edit buffer */
2215 gf_set_so_readc(&gc
, tmpso
); /* read from file, write pico buf */
2216 gf_set_so_writec(&pc
, msgso
);
2217 gf_filter_init(); /* no filters needed */
2218 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2219 snprintf(errbuf
, sizeof(errbuf
), _("Error reading file: \"%s\""), errstr
);
2220 errbuf
[sizeof(errbuf
)-1] = '\0';
2221 ret
= cpystr(errbuf
);
2224 gf_clear_so_readc(tmpso
);
2225 gf_clear_so_writec(msgso
);
2231 mswin_setwindowmenu (MENU_COMPOSER
);
2234 /*------ OK, Go edit the signature ------*/
2235 editor_result
= pico(&pbf
);
2238 mswin_setwindowmenu (MENU_DEFAULT
);
2240 if(editor_result
& COMP_GOTHUP
){
2241 hup_signal(); /* do what's normal for a hup */
2244 fix_windsize(ps_global
);
2248 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2251 /*------ Must have an edited buffer, write it to .sig -----*/
2252 our_unlink(sig_path
); /* blast old copy */
2253 if((tmpso
= so_get(FileStar
, sig_path
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
2254 so_seek(msgso
, 0L, 0);
2255 gf_set_so_readc(&gc
, msgso
); /* read from pico buf */
2256 gf_set_so_writec(&pc
, tmpso
); /* write sig file */
2257 gf_filter_init(); /* no filters needed */
2258 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2259 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2261 errbuf
[sizeof(errbuf
)-1] = '\0';
2262 ret
= cpystr(errbuf
);
2265 gf_clear_so_readc(msgso
);
2266 gf_clear_so_writec(tmpso
);
2267 if(so_give(&tmpso
)){
2268 errstr
= error_description(errno
);
2269 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2271 errbuf
[sizeof(errbuf
)-1] = '\0';
2272 ret
= cpystr(errbuf
);
2275 if(IS_REMOTE(sigfile
)){
2281 we_cancel
= busy_cue("Copying to remote sig", NULL
, 1);
2282 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
2284 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2285 _("Error opening temporary sig file %s: %s"),
2286 rd
->lf
, error_description(errno
));
2288 "write_remote_sig: error opening temp file %s\n",
2289 rd
->lf
? rd
->lf
: "?"));
2292 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2293 _("Error copying to %s: %s"),
2294 rd
->rn
, error_description(errno
));
2296 "write_remote_sig: error copying from %s to %s\n",
2297 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
2300 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
2301 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2304 rd_update_metadata(rd
, datebuf
);
2305 rd
->read_status
= 'W';
2308 rd_close_remdata(&rd
);
2311 cancel_busy_cue(-1);
2315 snprintf(errbuf
, sizeof(errbuf
), _("Error writing \"%s\""), sig_path
);
2316 errbuf
[sizeof(errbuf
)-1] = '\0';
2317 ret
= cpystr(errbuf
);
2318 dprint((1, "signature_edit: can't write %s",
2324 standard_picobuf_teardown(&pbf
);
2330 /*----------------------------------------------------------------------
2331 Serve up the current signature within pico for editing
2333 Args: literal signature to edit
2335 Result: raw edited signature is returned in result arg
2338 signature_edit_lit(char *litsig
, char **result
, char *title
, HelpType composer_help
)
2341 char *errstr
= NULL
;
2345 struct variable
*vars
= ps_global
->vars
;
2347 standard_picobuf_setup(&pbf
);
2348 pbf
.tty_fix
= PineRaw
;
2349 pbf
.search_help
= h_sigedit_search
;
2350 pbf
.composer_help
= composer_help
;
2351 pbf
.exittest
= sigedit_exit_for_pico
;
2352 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2353 ? upload_msg_to_pico
: NULL
;
2354 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2355 ? VAR_EDITOR
: NULL
;
2356 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2357 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2358 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2359 pbf
.allow_flowed_text
= 0;
2361 pbf
.pine_anchor
= set_titlebar(title
,
2362 ps_global
->mail_stream
,
2363 ps_global
->context_current
,
2364 ps_global
->cur_folder
,
2366 0, FolderName
, 0, 0, NULL
);
2368 /* NOTE: at this point, a lot of pico struct fields are null'd out
2369 * thanks to the leading memset; in particular "headents" which tells
2370 * pico to behave like a normal editor (though modified slightly to
2371 * let the caller dictate the file to edit and such)...
2375 * Now alloc and init the text to pass pico
2377 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2378 ret
= cpystr(_("Error allocating space"));
2379 dprint((1, "Can't alloc space for signature_edit_lit"));
2383 pbf
.msgtext
= so_text(msgso
);
2385 so_puts(msgso
, litsig
? litsig
: "");
2390 mswin_setwindowmenu (MENU_COMPOSER
);
2393 /*------ OK, Go edit the signature ------*/
2394 editor_result
= pico(&pbf
);
2397 mswin_setwindowmenu (MENU_DEFAULT
);
2399 if(editor_result
& COMP_GOTHUP
){
2400 hup_signal(); /* do what's normal for a hup */
2403 fix_windsize(ps_global
);
2407 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2408 ret
= cpystr(_("Edit Cancelled"));
2411 /*------ Must have an edited buffer, write it to .sig -----*/
2416 so_seek(msgso
, 0L, 0);
2417 while(so_readc(&c
, msgso
))
2420 *result
= (char *)fs_get((cnt
+1) * sizeof(char));
2422 so_seek(msgso
, 0L, 0);
2423 while(so_readc(&c
, msgso
))
2430 standard_picobuf_teardown(&pbf
);
2437 * Returns 0 for Save Changes and exit
2439 * -1 exit but Dont Save Changes
2442 sigedit_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
2447 void (*redraw
)(void) = ps_global
->redrawer
;
2448 static ESCKEY_S opts
[] = {
2449 {'s', 's', "S", N_("Save changes")},
2450 {'d', 'd', "D", N_("Don't save changes")},
2454 ps_global
->redrawer
= redraw_pico
;
2455 fix_windsize(ps_global
);
2458 rv
= radio_buttons(_("Exit editor? "),
2459 -FOOTER_ROWS(ps_global
), opts
,
2460 's', 'x', h_exit_editor
, RB_NORM
);
2461 if(rv
== 's'){ /* user ACCEPTS! */
2464 else if(rv
== 'd'){ /* Declined! */
2465 rstr
= _("No Changes Saved");
2468 else if(rv
== 'x'){ /* Cancelled! */
2469 rstr
= _("Exit Cancelled");
2477 ps_global
->redrawer
= redraw
;
2478 return((rv
== 's') ? 0 : (rv
== 'd') ? -1 : 1);
2483 * Common stuff we almost always want to set when calling pico.
2486 standard_picobuf_setup(PICO
*pbf
)
2488 memset(pbf
, 0, sizeof(*pbf
));
2490 pbf
->pine_version
= ALPINE_VERSION
;
2491 pbf
->fillcolumn
= ps_global
->composer_fillcol
;
2492 pbf
->menu_rows
= FOOTER_ROWS(ps_global
) - 1;
2493 pbf
->colors
= colors_for_pico();
2494 pbf
->wordseps
= user_wordseps(ps_global
->VAR_WORDSEPS
);
2495 pbf
->helper
= helper
;
2496 pbf
->showmsg
= display_message_for_pico
;
2497 pbf
->suspend
= do_suspend
;
2498 pbf
->keybinput
= cmd_input_for_pico
;
2499 pbf
->tty_fix
= ttyfix
; /* watch out for this one */
2500 pbf
->newmail
= new_mail_for_pico
;
2501 pbf
->ckptdir
= checkpoint_dir_for_pico
;
2502 pbf
->resize
= resize_for_pico
;
2503 pbf
->input_cs
= ps_global
->input_cs
;
2504 pbf
->winch_cleanup
= winch_cleanup
;
2505 pbf
->search_help
= h_composer_search
;
2506 pbf
->ins_help
= h_composer_ins
;
2507 pbf
->ins_m_help
= h_composer_ins_m
;
2508 pbf
->composer_help
= h_composer
;
2509 pbf
->browse_help
= h_composer_browse
;
2510 pbf
->attach_help
= h_composer_ctrl_j
;
2513 ( (F_ON(F_CAN_SUSPEND
,ps_global
) ? P_SUSPEND
: 0L)
2514 | (F_ON(F_USE_FK
,ps_global
) ? P_FKEYS
: 0L)
2515 | (ps_global
->restricted
? P_SECURE
: 0L)
2516 | (F_ON(F_ALT_ED_NOW
,ps_global
) ? P_ALTNOW
: 0L)
2517 | (F_ON(F_USE_CURRENT_DIR
,ps_global
) ? P_CURDIR
: 0L)
2518 | (F_ON(F_SUSPEND_SPAWNS
,ps_global
) ? P_SUBSHELL
: 0L)
2519 | (F_ON(F_COMPOSE_MAPS_DEL
,ps_global
) ? P_DELRUBS
: 0L)
2520 | (F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
) ? P_COMPLETE
: 0L)
2521 | (F_ON(F_SHOW_CURSOR
,ps_global
) ? P_SHOCUR
: 0L)
2522 | (F_ON(F_DEL_FROM_DOT
,ps_global
) ? P_DOTKILL
: 0L)
2523 | (F_ON(F_ENABLE_DOT_FILES
,ps_global
) ? P_DOTFILES
: 0L)
2524 | (F_ON(F_ALLOW_GOTO
,ps_global
) ? P_ALLOW_GOTO
: 0L)
2525 | (F_ON(F_ENABLE_SEARCH_AND_REPL
,ps_global
) ? P_REPLACE
: 0L)
2526 | (!ps_global
->pass_ctrl_chars
2527 && !ps_global
->pass_c1_ctrl_chars
? P_HICTRL
: 0L)
2528 | ((F_ON(F_ENABLE_ALT_ED
,ps_global
)
2529 || F_ON(F_ALT_ED_NOW
,ps_global
)
2530 || (ps_global
->VAR_EDITOR
2531 && ps_global
->VAR_EDITOR
[0]
2532 && ps_global
->VAR_EDITOR
[0][0]))
2534 | ((!ps_global
->keyboard_charmap
2535 || !strucmp(ps_global
->keyboard_charmap
, "US-ASCII"))
2536 ? P_HIBITIGN
: 0L));
2538 if(ps_global
->VAR_OPER_DIR
){
2539 pbf
->oper_dir
= ps_global
->VAR_OPER_DIR
;
2540 pbf
->pine_flags
|= P_TREE
;
2543 pbf
->home_dir
= ps_global
->home_dir
;
2548 standard_picobuf_teardown(PICO
*pbf
)
2552 free_pcolors(&pbf
->colors
);
2555 fs_give((void **) &pbf
->wordseps
);
2560 /*----------------------------------------------------------------------
2561 Call back for pico to use to check for new mail.
2563 Args: cursor -- pointer to in to tell caller if cursor location changed
2564 if NULL, turn off cursor positioning.
2565 timing -- whether or not it's a good time to check
2568 Returns: returns 1 on success, zero on error.
2571 new_mail_for_pico(int timing
, int status
)
2574 * If we're not interested in the status, don't display the busy
2577 /* don't know where the cursor's been, reset it */
2579 return(new_mail(0, timing
,
2580 (status
? NM_STATUS_MSG
: NM_NONE
) | NM_DEFER_SORT
2581 | NM_FROM_COMPOSER
));
2586 cmd_input_for_pico(void)
2588 zero_new_mail_count();
2592 /*----------------------------------------------------------------------
2593 Call back for pico to get newmail status messages displayed
2595 Args: x -- char processed
2600 display_message_for_pico(int x
)
2604 clear_cursor_pos(); /* can't know where cursor is */
2605 mark_status_dirty(); /* don't count on cached text */
2606 fix_windsize(ps_global
);
2609 rv
= ps_global
->mangled_screen
;
2610 ps_global
->mangled_screen
= 0;
2615 /*----------------------------------------------------------------------
2616 Call back for pico to get desired directory for its check point file
2618 Args: s -- buffer to write directory name
2619 n -- length of that buffer
2621 Returns: pointer to static buffer
2624 checkpoint_dir_for_pico(char *s
, size_t n
)
2626 #if defined(DOS) || defined(OS2)
2628 * we can't assume anything about root or home dirs, so
2629 * just plunk it down in the same place as the pinerc
2631 if(!getenv("HOME")){
2632 char *lc
= last_cmpnt(ps_global
->pinerc
);
2635 strncpy(s
, ps_global
->pinerc
, MIN(n
-1,lc
-ps_global
->pinerc
));
2636 s
[MIN(n
-1,lc
-ps_global
->pinerc
)] = '\0';
2639 strncpy(s
, ".\\", n
-1);
2645 strncpy(s
, ps_global
->home_dir
, n
-1);
2652 /*----------------------------------------------------------------------
2653 Call back for pico to tell us the window size's changed
2657 Returns: none (but pine's ttyo structure may have been updated)
2660 resize_for_pico(void)
2662 fix_windsize(ps_global
);
2667 colors_for_pico(void)
2669 PCOLORS
*colors
= NULL
;
2670 struct variable
*vars
= ps_global
->vars
;
2672 if (pico_usingcolor()){
2673 colors
= (PCOLORS
*)fs_get(sizeof(PCOLORS
));
2675 colors
->tbcp
= current_titlebar_color();
2677 if (VAR_KEYLABEL_FORE_COLOR
&& VAR_KEYLABEL_BACK_COLOR
){
2678 colors
->klcp
= new_color_pair(VAR_KEYLABEL_FORE_COLOR
,
2679 VAR_KEYLABEL_BACK_COLOR
);
2680 if (!pico_is_good_colorpair(colors
->klcp
))
2681 free_color_pair(&colors
->klcp
);
2683 else colors
->klcp
= NULL
;
2685 if (colors
->klcp
&& VAR_KEYNAME_FORE_COLOR
&& VAR_KEYNAME_BACK_COLOR
){
2686 colors
->kncp
= new_color_pair(VAR_KEYNAME_FORE_COLOR
,
2687 VAR_KEYNAME_BACK_COLOR
);
2689 else colors
->kncp
= NULL
;
2691 if (VAR_STATUS_FORE_COLOR
&& VAR_STATUS_BACK_COLOR
){
2692 colors
->stcp
= new_color_pair(VAR_STATUS_FORE_COLOR
,
2693 VAR_STATUS_BACK_COLOR
);
2695 else colors
->stcp
= NULL
;
2697 if (VAR_PROMPT_FORE_COLOR
&& VAR_PROMPT_BACK_COLOR
){
2698 colors
->prcp
= new_color_pair(VAR_PROMPT_FORE_COLOR
,
2699 VAR_PROMPT_BACK_COLOR
);
2701 else colors
->prcp
= NULL
;
2703 if (VAR_QUOTE1_FORE_COLOR
&& VAR_QUOTE1_BACK_COLOR
){
2704 colors
->qlcp
= new_color_pair(VAR_QUOTE1_FORE_COLOR
,
2705 VAR_QUOTE1_BACK_COLOR
);
2707 else colors
->qlcp
= NULL
;
2709 if (VAR_QUOTE2_FORE_COLOR
&& VAR_QUOTE2_BACK_COLOR
){
2710 colors
->qllcp
= new_color_pair(VAR_QUOTE2_FORE_COLOR
,
2711 VAR_QUOTE2_BACK_COLOR
);
2713 else colors
->qllcp
= NULL
;
2715 if (VAR_QUOTE3_FORE_COLOR
&& VAR_QUOTE3_BACK_COLOR
){
2716 colors
->qlllcp
= new_color_pair(VAR_QUOTE3_FORE_COLOR
,
2717 VAR_QUOTE3_BACK_COLOR
);
2719 else colors
->qlllcp
= NULL
;
2721 if (VAR_NORM_FORE_COLOR
&& VAR_NORM_BACK_COLOR
){
2722 colors
->ntcp
= new_color_pair(VAR_NORM_FORE_COLOR
,
2723 VAR_NORM_BACK_COLOR
);
2725 else colors
->ntcp
= NULL
;
2727 if (VAR_REV_FORE_COLOR
&& VAR_REV_BACK_COLOR
){
2728 colors
->rtcp
= new_color_pair(VAR_REV_FORE_COLOR
,
2729 VAR_REV_BACK_COLOR
);
2731 else colors
->rtcp
= NULL
;
2733 if (VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
){
2734 colors
->sbcp
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
2735 VAR_SIGNATURE_BACK_COLOR
);
2737 else colors
->sbcp
= NULL
;
2745 free_pcolors(PCOLORS
**colors
)
2748 if ((*colors
)->tbcp
)
2749 free_color_pair(&(*colors
)->tbcp
);
2750 if ((*colors
)->kncp
)
2751 free_color_pair(&(*colors
)->kncp
);
2752 if ((*colors
)->klcp
)
2753 free_color_pair(&(*colors
)->klcp
);
2754 if ((*colors
)->stcp
)
2755 free_color_pair(&(*colors
)->stcp
);
2756 if ((*colors
)->prcp
)
2757 free_color_pair(&(*colors
)->prcp
);
2758 if ((*colors
)->qlcp
)
2759 free_color_pair(&(*colors
)->qlcp
);
2760 if ((*colors
)->qllcp
)
2761 free_color_pair(&(*colors
)->qllcp
);
2762 if ((*colors
)->qlllcp
)
2763 free_color_pair(&(*colors
)->qlllcp
);
2764 if ((*colors
)->ntcp
)
2765 free_color_pair(&(*colors
)->ntcp
);
2766 if ((*colors
)->rtcp
)
2767 free_color_pair(&(*colors
)->rtcp
);
2768 if ((*colors
)->sbcp
)
2769 free_color_pair(&(*colors
)->sbcp
);
2770 fs_give((void **)colors
);