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-2017 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
;
109 long msgno
, j
, totalm
, rflags
, *seq
= NULL
;
110 int i
, include_text
= 0, times
= -1, warned
= 0, rv
= 0,
111 flags
= RSF_QUERY_REPLY_ALL
, reply_raw_body
= 0;
112 int rolemsg
= 0, copytomsg
= 0;
115 REDRAFT_POS_S
*redraft_pos
= NULL
;
116 ACTION_S
*role
= NULL
, *nrole
;
117 #if defined(DOS) && !defined(_WINDOWS)
121 outgoing
= mail_newenvelope();
122 totalm
= mn_total_cur(pine_state
->msgmap
);
123 seq
= (long *)fs_get(((size_t)totalm
+ 1) * sizeof(long));
125 dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm
)));
127 saved_from
= (ADDRESS
*) NULL
;
128 saved_to
= (ADDRESS
*) NULL
;
129 saved_cc
= (ADDRESS
*) NULL
;
130 saved_resent
= (ADDRESS
*) NULL
;
132 us_in_to_and_cc
= (ADDRESS
*) NULL
;
134 outgoing
->subject
= NULL
;
136 memset((void *)&reply
, 0, sizeof(reply
));
138 if(ps_global
->full_header
== 2
139 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
143 * We may have to loop through first to figure out what default
144 * reply-indent-string to offer...
146 if(mn_total_cur(pine_state
->msgmap
) > 1 &&
147 (F_ON(F_ALT_REPLY_MENU
, pine_state
)
148 || F_ON(F_ENABLE_EDIT_REPLY_INDENT
, pine_state
)) &&
149 reply_quote_str_contains_tokens()){
150 for(msgno
= mn_first_cur(pine_state
->msgmap
);
151 msgno
> 0L && !tmpfix
;
152 msgno
= mn_next_cur(pine_state
->msgmap
)){
154 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
155 mn_m2raw(pine_state
->msgmap
, msgno
),
158 q_status_message1(SM_ORDER
,3,4,
159 _("Error fetching message %s. Can't reply to it."),
164 if(!tmpfix
){ /* look for prefix? */
165 tmpfix
= reply_quote_str(env
);
167 i
= strcmp(tmpfix
, prefix
);
168 fs_give((void **) &tmpfix
);
169 if(i
){ /* don't check back if dissimilar */
170 fs_give((void **) &prefix
);
172 * We free prefix, not tmpfix. We set tmpfix to prefix
173 * so that we won't come check again.
175 tmpfix
= prefix
= cpystr("> ");
180 tmpfix
= NULL
; /* check back later? */
189 * Loop thru the selected messages building the
190 * outgoing envelope's destinations...
192 for(msgno
= mn_first_cur(pine_state
->msgmap
);
194 msgno
= mn_next_cur(pine_state
->msgmap
)){
196 /*--- Grab current envelope ---*/
197 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
198 seq
[++times
] = mn_m2raw(pine_state
->msgmap
, msgno
),
201 q_status_message1(SM_ORDER
,3,4,
202 _("Error fetching message %s. Can't reply to it."),
208 * We check for the prefix here if we didn't do it in the first
209 * loop above. This is just to save having to go through the loop
210 * twice in the cases where we don't need to.
213 tmpfix
= reply_quote_str(env
);
215 i
= strcmp(tmpfix
, prefix
);
216 fs_give((void **) &tmpfix
);
217 if(i
){ /* don't check back if dissimilar */
218 fs_give((void **) &prefix
);
219 tmpfix
= prefix
= cpystr("> ");
224 tmpfix
= NULL
; /* check back later? */
229 * For consistency, the first question is always "include text?"
231 if(!times
){ /* only first time */
232 char *p
= cpystr(prefix
);
234 if((include_text
=reply_text_query(pine_state
,totalm
,env
,&prefix
)) < 0)
238 if(strcmp(p
, prefix
))
239 tmpfix
= prefix
; /* stop looking */
241 fs_give((void **)&p
);
245 * If we're agg-replying or there's a newsgroup and the user want's
246 * to post to news *and* via email, add relevant addresses to the
247 * outgoing envelope...
249 * The single message case gets us around the aggregate reply
250 * to messages in a mixed mail-news archive where some might
251 * have newsgroups and others not or whatever.
253 if(totalm
> 1L || ((i
= reply_news_test(env
, outgoing
)) & 1)){
255 flags
|= RSF_FORCE_REPLY_TO
;
257 if(!reply_harvest(pine_state
, seq
[times
], NULL
, env
,
258 &saved_from
, &saved_to
, &saved_cc
,
259 &saved_resent
, &flags
))
265 /* collect a list of addresses that are us in to and cc fields */
267 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
268 NULL
, us_in_to_and_cc
, NULL
,
269 env
->to
, RCA_ONLY_US
)) != NULL
)
270 reply_append_addr(&us_in_to_and_cc
, ap
);
273 if((ap
=reply_cp_addr(pine_state
, 0L, NULL
,
274 NULL
, us_in_to_and_cc
, NULL
,
275 env
->cc
, RCA_ONLY_US
)) != NULL
)
276 reply_append_addr(&us_in_to_and_cc
, ap
);
278 /*------------ Format the subject line ---------------*/
279 if(outgoing
->subject
){
281 * if reply to more than one message, and all subjects
282 * match, so be it. otherwise set it to something generic...
284 if(!same_subject(outgoing
->subject
,
285 reply_subject(env
->subject
,tmp_20k_buf
,SIZEOF_20KBUF
))){
286 fs_give((void **)&outgoing
->subject
);
287 outgoing
->subject
= cpystr("Re: several messages");
291 outgoing
->subject
= reply_subject(env
->subject
, NULL
, 0);
294 /* fill reply header */
295 reply_seed(pine_state
, outgoing
, env
, saved_from
,
296 saved_to
, saved_cc
, saved_resent
,
297 &fcc
, flags
& RSF_FORCE_REPLY_ALL
, &errmsg
);
300 q_status_message1(SM_ORDER
, 3, 3, "%.200s", errmsg
);
301 display_message(NO_OP_COMMAND
);
304 fs_give((void **)&errmsg
);
307 if(sp_expunge_count(pine_state
->mail_stream
)) /* cur msg expunged */
310 /* Setup possible role */
311 if (ps_global
->reply
.role_chosen
)
312 role
= ps_global
->reply
.role_chosen
;
314 role
= copy_action(role_arg
);
318 if(!ps_global
->reply
.role_chosen
&& nonempty_patterns(rflags
, &dummy
)){
319 /* setup default role */
321 j
= mn_first_cur(pine_state
->msgmap
);
324 nrole
= set_role_from_msg(pine_state
, rflags
,
325 mn_m2raw(pine_state
->msgmap
, j
),
327 } while(nrole
&& (!role
|| nrole
== role
)
328 && (j
=mn_next_cur(pine_state
->msgmap
)) > 0L);
330 if(!role
|| nrole
== role
)
335 if(confirm_role(rflags
, &role
))
336 role
= combine_inherited_role(role
);
337 else{ /* cancel reply */
339 cmd_cancelled("Reply");
346 * Reply_seed may call c-client in get_fcc_based_on_to, so env may
347 * no longer be valid. Get it again.
348 * Similarly for set_role_from_message.
350 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, seq
[times
], NULL
);
354 /* override fcc gotten in reply_seed */
356 fs_give((void **) &fcc
);
359 if(F_ON(F_COPY_TO_TO_FROM
, pine_state
) && !(role
&& role
->from
)){
361 * A list of all of our addresses that appear in the To
362 * and cc fields is in us_in_to_and_cc.
363 * If there is exactly one address in that list then
364 * use it for the outgoing From.
366 if(us_in_to_and_cc
&& !us_in_to_and_cc
->next
){
367 PINEFIELD
*custom
, *pf
;
372 * Check to see if this address is different from what
373 * we would have used anyway. If it is, notify the user
374 * with a status message. This is pretty hokey because we're
375 * mimicking how pine_send would set the From address and
376 * there is no coordination between the two.
379 /* in case user has a custom From value */
380 custom
= parse_custom_hdrs(ps_global
->VAR_CUSTOM_HDRS
, UseAsDef
);
382 pf
= (PINEFIELD
*) fs_get(sizeof(*pf
));
383 memset((void *) pf
, 0, sizeof(*pf
));
384 pf
->name
= cpystr("From");
386 if(set_default_hdrval(pf
, custom
) >= UseAsDef
387 && pf
->textbuf
&& pf
->textbuf
[0]){
388 removing_trailing_white_space(pf
->textbuf
);
389 (void)removing_double_quotes(pf
->textbuf
);
390 build_address(pf
->textbuf
, &addr
, NULL
, NULL
, NULL
);
391 rfc822_parse_adrlist(pf
->addr
, addr
, ps_global
->maildomain
);
393 fs_give((void **) &addr
);
397 *pf
->addr
= generate_from();
399 if(*pf
->addr
&& !address_is_same(*pf
->addr
, us_in_to_and_cc
)){
402 role
= (ACTION_S
*) fs_get(sizeof(*role
));
403 memset((void *) role
, 0, sizeof(*role
));
407 role
->from
= us_in_to_and_cc
;
408 us_in_to_and_cc
= NULL
;
411 free_customs(custom
);
417 if(rolemsg
&& copytomsg
)
418 q_status_message1(SM_ORDER
, 3, 4,
419 _("Replying using role \"%s\" and To as From"), role
->nick
);
421 q_status_message1(SM_ORDER
, 3, 4,
422 _("Replying using role \"%s\""), role
->nick
);
424 q_status_message(SM_ORDER
, 3, 4,
425 _("Replying using incoming To as outgoing From"));
429 mail_free_address(&us_in_to_and_cc
);
432 seq
[++times
] = -1L; /* mark end of sequence list */
434 /*========== Other miscelaneous fields ===================*/
435 outgoing
->in_reply_to
= reply_in_reply_to(env
);
436 outgoing
->references
= reply_build_refs(env
);
437 outgoing
->message_id
= generate_message_id();
442 !outgoing
->newsgroups
)
443 q_status_message(SM_ORDER
| SM_DING
, 3, 6,
444 _("Warning: no valid addresses to reply to!"));
447 /*==================== Now fix up the message body ====================*/
450 * create storage object to be used for message text
452 if((msgtext
= (void *)so_get(PicoText
, NULL
, EDIT_ACCESS
)) == NULL
){
453 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
454 _("Error allocating message text"));
458 gf_set_so_writec(&pc
, (STORE_S
*) msgtext
);
460 /*---- Include the original text if requested ----*/
461 if(include_text
&& totalm
> 1L){
463 int impl
, template_len
= 0, leave_cursor_at_top
= 0;
467 if(role
&& role
->template){
471 filtered
= detoken(role
, env
, 0,
472 ps_global
->reply
.signature_bottom
,
473 0, &redraft_pos
, &impl
);
476 so_puts((STORE_S
*)msgtext
, filtered
);
478 template_len
= strlen(filtered
);
480 leave_cursor_at_top
++;
483 fs_give((void **)&filtered
);
491 if((sig
= reply_signature(role
, env
, &redraft_pos
, &impl
)) &&
492 !ps_global
->reply
.signature_bottom
){
495 * If CURSORPOS was set explicitly in sig_file, and there was a
496 * template file before that, we need to adjust the offset by the
497 * length of the template file. However, if the template had
498 * a set CURSORPOS in it then impl was 2 before getting to the
499 * signature, so offset wouldn't have been reset by the signature
500 * CURSORPOS and offset would already be correct. That case will
501 * be ok here because template_len will be 0 and adding it does
502 * nothing. If template
503 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
504 * by the CURSORPOS in the sig. In that case we have to adjust the
505 * offset. That's what the next line does. It adjusts it if
506 * template_len is nonzero and if CURSORPOS was set in sig_file.
509 redraft_pos
->offset
+= template_len
;
512 so_puts((STORE_S
*)msgtext
, sig
);
515 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
516 * is set, we won't have used it yet and want it to be non-NULL.
518 fs_give((void **)&sig
);
522 * Only put cursor in sig if there is a cursorpos there but not
523 * one in the template, and sig-at-bottom.
525 if(!(sig
&& impl
== 2 && !leave_cursor_at_top
))
526 leave_cursor_at_top
++;
528 body
= mail_newbody();
529 body
->type
= TYPETEXT
;
530 body
->contents
.text
.data
= msgtext
;
532 for(msgno
= mn_first_cur(pine_state
->msgmap
);
534 msgno
= mn_next_cur(pine_state
->msgmap
)){
536 if(env
){ /* put 2 between messages */
537 gf_puts(NEWLINE
, pc
);
538 gf_puts(NEWLINE
, pc
);
541 /*--- Grab current envelope ---*/
542 env
= pine_mail_fetchstructure(pine_state
->mail_stream
,
543 mn_m2raw(pine_state
->msgmap
, msgno
),
546 q_status_message1(SM_ORDER
,3,4,
547 _("Error fetching message %s. Can't reply to it."),
548 long2string(mn_get_cur(pine_state
->msgmap
)));
552 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| reply_raw_body
) {
553 reply_delimiter(env
, role
, pc
);
554 if(ps_global
->reply
.include_header
)
555 reply_forward_header(pine_state
->mail_stream
,
556 mn_m2raw(pine_state
->msgmap
,msgno
),
557 NULL
, env
, pc
, prefix
);
559 get_body_part_text(pine_state
->mail_stream
, reply_raw_body
? NULL
: orig_body
,
560 mn_m2raw(pine_state
->msgmap
, msgno
),
561 reply_raw_body
? NULL
: "1", 0L, pc
, prefix
,
564 else if(orig_body
->type
== TYPEMULTIPART
) {
566 q_status_message(SM_ORDER
,3,7,
567 _("WARNING! Attachments not included in multiple reply."));
569 if(orig_body
->nested
.part
570 && orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
571 /*---- First part of the message is text -----*/
572 reply_delimiter(env
, role
, pc
);
573 if(ps_global
->reply
.include_header
)
574 reply_forward_header(pine_state
->mail_stream
,
575 mn_m2raw(pine_state
->msgmap
,
577 NULL
, env
, pc
, prefix
);
579 get_body_part_text(pine_state
->mail_stream
,
580 &orig_body
->nested
.part
->body
,
581 mn_m2raw(pine_state
->msgmap
, msgno
),
582 "1", 0L, pc
, prefix
, NULL
, GBPT_NONE
);
585 q_status_message(SM_ORDER
,0,3,
586 _("Multipart with no leading text part."));
590 /*---- Single non-text message of some sort ----*/
591 q_status_message(SM_ORDER
,3,3,
592 _("Non-text message not included."));
596 if(!leave_cursor_at_top
){
600 /* rewind and count chars to start of sig file */
601 so_seek((STORE_S
*)msgtext
, 0L, 0);
602 while(so_readc(&c
, (STORE_S
*)msgtext
))
606 redraft_pos
= (REDRAFT_POS_S
*)fs_get(sizeof(*redraft_pos
));
607 memset((void *)redraft_pos
, 0,sizeof(*redraft_pos
));
608 redraft_pos
->hdrname
= cpystr(":");
612 * If explicit cursor positioning in sig file,
613 * add offset to start of sig file plus offset into sig file.
614 * Else, just offset to start of sig file.
616 redraft_pos
->offset
+= cnt
;
621 so_puts((STORE_S
*)msgtext
, sig
);
623 fs_give((void **)&sig
);
627 msgno
= mn_m2raw(pine_state
->msgmap
,
628 mn_get_cur(pine_state
->msgmap
));
630 /*--- Grab current envelope ---*/
631 env
= pine_mail_fetchstructure(pine_state
->mail_stream
, msgno
,
635 * If the charset of the body part is different from ascii and
636 * charset conversion is _not_ happening, then preserve the original
637 * charset from the message so that if we don't enter any new
638 * chars with the hibit set we can use the original charset.
639 * If not all those things, then don't try to preserve it.
644 charset
= parameter_val(orig_body
->parameter
, "charset");
645 if(charset
&& strucmp(charset
, "us-ascii") != 0){
649 * There is a non-ascii charset, is there conversion happening?
651 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
652 reply
.orig_charset
= charset
;
658 fs_give((void **) &charset
);
662 if(!(body
= reply_body(pine_state
->mail_stream
, env
, orig_body
,
663 msgno
, NULL
, msgtext
, prefix
,
664 include_text
, role
, 1, &redraft_pos
)))
668 q_status_message1(SM_ORDER
,3,4,
669 _("Error fetching message %s. Can't reply to it."),
670 long2string(mn_get_cur(pine_state
->msgmap
)));
675 /* fill in reply structure */
676 reply
.prefix
= prefix
;
677 reply
.mailbox
= cpystr(pine_state
->mail_stream
->mailbox
);
678 reply
.origmbox
= cpystr(pine_state
->mail_stream
->original_mailbox
679 ? pine_state
->mail_stream
->original_mailbox
680 : pine_state
->mail_stream
->mailbox
);
681 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((times
+ 1) * sizeof(imapuid_t
));
682 if((reply
.data
.uid
.validity
= pine_state
->mail_stream
->uid_validity
) != 0){
684 for(i
= 0; i
< times
; i
++)
685 reply
.data
.uid
.msgs
[i
] = mail_uid(pine_state
->mail_stream
, seq
[i
]);
689 for(i
= 0; i
< times
; i
++)
690 reply
.data
.uid
.msgs
[i
] = seq
[i
];
693 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
695 #if defined(DOS) && !defined(_WINDOWS)
696 free((void *)reserve
);
699 /* partially formatted outgoing message */
700 pine_send(outgoing
, &body
, _("COMPOSE MESSAGE REPLY"),
701 role
, fcc
, &reply
, redraft_pos
, NULL
, NULL
, 0);
704 pine_free_body(&body
);
706 fs_give((void **) &reply
.mailbox
);
708 fs_give((void **) &reply
.origmbox
);
709 if(reply
.orig_charset
)
710 fs_give((void **) &reply
.orig_charset
);
711 fs_give((void **) &reply
.data
.uid
.msgs
);
713 if((STORE_S
*) msgtext
)
714 gf_clear_so_writec((STORE_S
*) msgtext
);
716 mail_free_envelope(&outgoing
);
717 mail_free_address(&saved_from
);
718 mail_free_address(&saved_to
);
719 mail_free_address(&saved_cc
);
720 mail_free_address(&saved_resent
);
722 fs_give((void **)&seq
);
725 fs_give((void **)&prefix
);
728 fs_give((void **) &fcc
);
730 free_redraft_pos(&redraft_pos
);
737 * Ask user to confirm role choice, or choose another role.
739 * Args role -- A pointer into the pattern_h space at the default
740 * role to use. This can't be a copy, the comparison
741 * relies on it pointing at the actual role.
742 * This arg is also used to return a pointer to the
745 * Returns 1 -- Yes, use role which is now in *role. This may not be
746 * the same as the role passed in and it may even be NULL.
750 confirm_role(long int rflags
, ACTION_S
**role
)
752 ACTION_S
*role_p
= NULL
;
753 ACTION_S
*default_role
= NULL
;
754 char prompt
[80], *prompt_fodder
;
755 int cmd
, done
, ret
= 1;
756 void (*prev_screen
)(struct pine
*) = ps_global
->prev_screen
,
757 (*redraw
)(void) = ps_global
->redrawer
;
763 if(!nonempty_patterns(ROLE_DO_ROLES
, &pstate
) || !role
)
767 * If this is a reply or forward and the role doesn't require confirmation,
768 * then we just return with what was passed in.
770 if(((rflags
& ROLE_REPLY
) &&
771 *role
&& (*role
)->repl_type
== ROLE_REPL_NOCONF
) ||
772 ((rflags
& ROLE_FORWARD
) &&
773 *role
&& (*role
)->forw_type
== ROLE_FORW_NOCONF
) ||
774 ((rflags
& ROLE_COMPOSE
) &&
775 *role
&& (*role
)->comp_type
== ROLE_COMP_NOCONF
) ||
776 (!*role
&& F_OFF(F_ROLE_CONFIRM_DEFAULT
, ps_global
)
777 && !ps_global
->default_role
))
781 * Check that there is at least one role available. This is among all
782 * roles, not just the reply roles or just the forward roles. That's
783 * because we have ^T take us to all the roles, not the category-specific
786 if(!(pat
= last_pattern(&pstate
)))
795 ekey
[2].ch
= ctrl('T');
801 /* check for more than one role available (or no role set) */
802 if(pat
== first_pattern(&pstate
) && *role
) /* no ^T */
807 * Go through the loop just in case default_role doesn't point
808 * to a real current role.
810 if(ps_global
->default_role
){
811 for(pat
= first_pattern(&pstate
);
813 pat
= next_pattern(&pstate
)){
814 if(pat
->action
== ps_global
->default_role
){
815 default_role
= ps_global
->default_role
;
823 /* override default */
825 for(pat
= first_pattern(&pstate
);
827 pat
= next_pattern(&pstate
)){
828 if(pat
->action
== *role
){
835 if(rflags
& ROLE_REPLY
)
836 prompt_fodder
= _("Reply");
837 else if(rflags
& ROLE_FORWARD
)
838 prompt_fodder
= _("Forward");
840 prompt_fodder
= _("Compose");
847 help
= h_role_confirm
;
849 ekey
[0].label
= N_("Yes");
852 ekey
[1].label
= N_("No, use default role");
854 ekey
[1].label
= N_("No, use default settings");
856 ekey
[2].label
= N_("To Select Alternate Role");
858 if(curpat
->patgrp
&& curpat
->patgrp
->nick
)
859 /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
860 snprintf(prompt
, sizeof(prompt
), _("Use role \"%s\" for %s? "),
861 short_str(curpat
->patgrp
->nick
, buf
, sizeof(buf
), 50, MidDots
),
864 snprintf(prompt
, sizeof(prompt
),
865 _("Use role \"<a role without a nickname>\" for %s? "),
869 help
= h_norole_confirm
;
870 ekey
[0].name
= "Ret";
871 ekey
[0].label
= prompt_fodder
;
874 ekey
[2].label
= N_("To Select Role");
875 snprintf(prompt
, sizeof(prompt
),
876 _("Press Return to %s using %s role, or ^T to select a role "),
877 prompt_fodder
, default_role
? _("default") : _("no"));
880 prompt
[sizeof(prompt
)-1] = '\0';
882 cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
883 'y', 'x', help
, RB_NORM
);
886 case 'y': /* Accept */
888 *role
= curpat
? curpat
->action
: default_role
;
891 case 'x': /* Cancel */
895 case 'n': /* NoRole */
897 *role
= default_role
;
901 if(role_select_screen(ps_global
, &role_p
, 0) >= 0){
903 for(pat
= first_pattern(&pstate
);
905 pat
= next_pattern(&pstate
)){
906 if(pat
->action
== role_p
){
917 ps_global
->mangled_body
= 1;
918 ps_global
->prev_screen
= prev_screen
;
919 ps_global
->redrawer
= redraw
;
929 * reply_to_all_query - Ask user about replying to all recipients
931 * Returns: -1 if cancel, 0 otherwise
932 * by reference: flagp
935 reply_to_all_query(int *flagp
)
944 ekey
[0].label
= N_("Yes");
948 ekey
[1].label
= N_("No");
954 ps_global
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps_global
);
956 ekey
[2].label
= ps_global
->reply
.preserve_fields
? N_("Not Preserve") : N_("Preserve");
957 snprintf(prompt
, sizeof(prompt
), _("Reply to all recipients%s"),
958 ps_global
->reply
.preserve_fields
? _(" (preserving fields)? ") : "? ");
960 prompt
[sizeof(prompt
)-1] = '\0';
962 switch(cmd
= radio_buttons(prompt
, -FOOTER_ROWS(ps_global
), ekey
,
963 'n', 'x', h_preserve_field
, RB_NORM
)){
968 case 'y' : /* set reply-all bit */
969 (*flagp
) |= RSF_FORCE_REPLY_ALL
;
972 case 'n' : /* clear reply-all bit */
973 (*flagp
) &= ~RSF_FORCE_REPLY_ALL
;
977 ps_global
->reply
.preserve_fields
=
978 (ps_global
->reply
.preserve_fields
+ 1) % 2;
979 goto loop
; /* ugly, but saves me a variable */
988 * reply_using_replyto_query - Ask user about replying with reply-to value
990 * Returns: 'y' if yes
994 reply_using_replyto_query(void)
996 return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
997 'y', 'x', NO_HELP
,WT_SEQ_SENSITIVE
));
1002 * reply_text_query - Ask user about replying with text, or in the case
1003 * of alternate reply menu, set values to the answer to all questions
1004 * asked during reply.
1006 * Returns: 1 if include the text
1007 * 0 if we're NOT to include the text
1008 * -1 on cancel or error
1010 #define MAX_REPLY_OPTIONS 10
1012 reply_text_query(struct pine
*ps
, long int many
, ENVELOPE
*env
, char **prefix
)
1014 int ret
, edited
= 0, headers
= 0;
1015 static ESCKEY_S compose_style
[MAX_REPLY_OPTIONS
];
1019 orig_sf
= ps
->reply
.use_flowed
= *prefix
&& **prefix
? (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1020 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1021 && (strcmp(*prefix
, "> ") == 0
1022 || strcmp(*prefix
, ">") == 0)) : 0;
1023 ps
->reply
.strip_signature
= ps
->full_header
== 0
1024 && (F_ON(F_ENABLE_STRIP_SIGDASHES
, ps
)
1025 || F_ON(F_ENABLE_SIGDASHES
, ps
));
1026 ps
->reply
.keep_attach
= F_ON(F_ATTACHMENTS_IN_REPLY
, ps
);
1027 ps
->reply
.include_header
= F_ON(F_INCLUDE_HEADER
, ps
);
1028 ps
->reply
.preserve_fields
= F_ON(F_PRESERVE_ORIGINAL_FIELD
, ps
);
1029 ps
->reply
.signature_bottom
= F_ON(F_SIG_AT_BOTTOM
, ps
);
1030 ps
->reply
.role_chosen
= NULL
;
1032 if(F_OFF(F_ALT_REPLY_MENU
, ps
)
1033 && F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
)
1034 && F_OFF(F_ENABLE_EDIT_REPLY_INDENT
, ps
)
1035 && F_OFF(F_ALT_REPLY_MENU
,ps
))
1039 compose_style
[ekey_num
= 0].ch
= 'y';
1040 compose_style
[ekey_num
].rval
= 'y';
1041 compose_style
[ekey_num
].name
= "Y";
1042 compose_style
[ekey_num
++].label
= N_("Yes");
1044 compose_style
[ekey_num
].ch
= 'n';
1045 compose_style
[ekey_num
].rval
= 'n';
1046 compose_style
[ekey_num
].name
= "N";
1047 compose_style
[ekey_num
++].label
= N_("No");
1049 if (F_OFF(F_ALT_REPLY_MENU
, ps
)){ /**** Standard menu ****/
1050 /* TRANSLATORS: The final five %s's can probably be safely ignored */
1051 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "),
1052 (many
> 1L) ? comatose(many
) : "",
1053 (many
> 1L) ? " " : "",
1054 (many
> 1L) ? "s" : "",
1055 (many
> 1L) ? "s" : "",
1056 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? " (using \"" : "",
1057 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? *prefix
: "",
1058 ps
->reply
.role_chosen
? "\" and role \"" : "",
1059 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "",
1060 F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
) ? "\")" : "");
1061 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1063 if (F_ON(F_ENABLE_EDIT_REPLY_INDENT
, ps
)){
1064 compose_style
[ekey_num
].ch
= ctrl('R');
1065 compose_style
[ekey_num
].rval
= 'r';
1066 compose_style
[ekey_num
].name
= "^R";
1067 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1069 } else { /***** Alternate Reply Menu ********/
1070 unsigned which_help
;
1072 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Include %s%soriginal message%s in Reply (using \"%s%s%s\")? "),
1073 (many
> 1L) ? comatose(many
) : "",
1074 (many
> 1L) ? " " : "",
1075 (many
> 1L) ? "s" : "",
1077 ps
->reply
.role_chosen
? "\" and role \"" : "",
1078 ps
->reply
.role_chosen
? ps
->reply
.role_chosen
->nick
: "");
1079 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1081 compose_style
[ekey_num
].ch
= 'h';
1082 compose_style
[ekey_num
].rval
= 'H';
1083 compose_style
[ekey_num
].name
= "H";
1084 compose_style
[ekey_num
++].label
= ps
->reply
.include_header
1085 ? N_("No Header") : N_("Inc Headr");
1087 compose_style
[ekey_num
].ch
= 's';
1088 compose_style
[ekey_num
].rval
= 'S';
1089 compose_style
[ekey_num
].name
= "S";
1090 compose_style
[ekey_num
++].label
= ps
->reply
.strip_signature
1091 ? N_("No Strip"): N_("Strip Sig");
1093 compose_style
[ekey_num
].ch
= 'a';
1094 compose_style
[ekey_num
].rval
= 'A';
1095 compose_style
[ekey_num
].name
= "A";
1096 compose_style
[ekey_num
++].label
= ps
->reply
.keep_attach
1097 ? N_("No Attach"): N_("Inc Attach");
1099 compose_style
[ekey_num
].ch
= 'b';
1100 compose_style
[ekey_num
].rval
= 'B';
1101 compose_style
[ekey_num
].name
= "B";
1102 compose_style
[ekey_num
++].label
= ps
->reply
.signature_bottom
1103 ? N_("Sig Top") : N_("Sig Bottom");
1105 compose_style
[ekey_num
].ch
= 'r';
1106 compose_style
[ekey_num
].rval
= 'R';
1107 compose_style
[ekey_num
].name
= "R";
1108 compose_style
[ekey_num
++].label
= N_("Set Role");
1110 compose_style
[ekey_num
].ch
= ctrl('R');
1111 compose_style
[ekey_num
].rval
= 'r';
1112 compose_style
[ekey_num
].name
= "^R";
1113 compose_style
[ekey_num
++].label
= N_("Edit Indent String");
1115 /***** End Alt Reply Menu *********/
1118 compose_style
[ekey_num
].ch
= -1;
1119 compose_style
[ekey_num
].name
= NULL
;
1120 compose_style
[ekey_num
].label
= NULL
;
1122 switch(ret
= radio_buttons(tmp_20k_buf
,
1123 ps
->ttyo
->screen_rows
> 4
1124 ? -FOOTER_ROWS(ps
) : -1,
1126 (edited
|| headers
|| F_ON(F_AUTO_INCLUDE_IN_REPLY
, ps
))
1128 'x', NO_HELP
, RB_SEQ_SENSITIVE
)){
1130 cmd_cancelled("Reply");
1134 ps
->reply
.keep_attach
= (ps
->reply
.keep_attach
+ 1) % 2;
1138 ps
->reply
.signature_bottom
= (ps
->reply
.signature_bottom
+ 1) % 2;
1142 ps
->reply
.use_flowed
= (ps
->reply
.use_flowed
+ 1) % 2;
1146 ps
->reply
.strip_signature
= (ps
->reply
.strip_signature
+ 1) % 2;
1150 ps
->reply
.include_header
= (ps
->reply
.include_header
+ 1) % 2;
1151 headers
= ps
->reply
.include_header
;
1156 void (*prev_screen
)(struct pine
*) = ps
->prev_screen
,
1157 (*redraw
)(void) = ps
->redrawer
;
1158 ps
->redrawer
= NULL
;
1159 ps
->next_screen
= SCREEN_FUN_NULL
;
1160 if(role_select_screen(ps
, &ps
->reply
.role_chosen
, 1) < 0){
1161 cmd_cancelled("Reply");
1162 ps
->next_screen
= prev_screen
;
1163 ps
->redrawer
= redraw
;
1168 ps
->next_screen
= prev_screen
;
1169 ps
->redrawer
= redraw
;
1170 if(ps
->reply
.role_chosen
)
1171 ps
->reply
.role_chosen
= combine_inherited_role(ps
->reply
.role_chosen
);
1178 if(prefix
&& *prefix
){
1184 strncpy(buf
, *prefix
, sizeof(buf
)-1);
1185 buf
[sizeof(buf
)-1] = '\0';
1187 flags
= OE_APPEND_CURRENT
|
1188 OE_KEEP_TRAILING_SPACE
|
1192 switch(optionally_enter(buf
, ps
->ttyo
->screen_rows
> 4
1193 ? -FOOTER_ROWS(ps
) : -1,
1194 0, sizeof(buf
), "Reply prefix : ",
1195 NULL
, NO_HELP
, &flags
)){
1196 case 0: /* entry successful, continue */
1197 if(flags
& OE_USER_MODIFIED
){
1198 fs_give((void **)prefix
);
1199 *prefix
= removing_quotes(cpystr(buf
));
1200 orig_sf
= ps
->reply
.use_flowed
= *prefix
&& **prefix
?
1201 (F_OFF(F_QUELL_FLOWED_TEXT
, ps
)
1202 && F_OFF(F_STRIP_WS_BEFORE_SEND
, ps
)
1203 && (strcmp(*prefix
, "> ") == 0
1204 || strcmp(*prefix
, ">") == 0)) : 0;
1212 cmd_cancelled("Reply");
1221 if(ps_global
->redrawer
!= NULL
)
1232 q_status_message(SM_ORDER
, 3, 4,
1233 "Programmer botch in reply_text_query()");
1248 q_status_message1(SM_ORDER
, 3, 4,
1249 "Invalid rval \'%s\'", pretty_command(ret
));
1257 * reply_poster_followup - return TRUE if "followup-to" set to "poster"
1259 * NOTE: queues status message indicating such
1262 reply_poster_followup(ENVELOPE
*e
)
1264 if(e
&& e
->followup_to
&& !strucmp(e
->followup_to
, "poster")){
1265 q_status_message(SM_ORDER
, 2, 3,
1266 _("Replying to Poster as specified in \"Followup-To\""));
1275 * reply_news_test - Test given envelope for newsgroup data and copy
1276 * it at the users request
1278 * 0 if error or cancel
1280 * 2 follow-up via news
1284 reply_news_test(ENVELOPE
*env
, ENVELOPE
*outgoing
)
1287 static ESCKEY_S news_opt
[] = { {'f', 'f', "F", N_("Follow-up")},
1288 {'r', 'r', "R", N_("Reply")},
1289 {'b', 'b', "B", N_("Both")},
1290 {-1, 0, NULL
, NULL
} };
1292 if(env
->newsgroups
&& *env
->newsgroups
&& !reply_poster_followup(env
))
1294 * Now that we know a newsgroups field is present,
1295 * ask if the user is posting a follow-up article...
1297 switch(radio_buttons(
1298 _("Follow-up to news group(s), Reply via email to author or Both? "),
1299 -FOOTER_ROWS(ps_global
), news_opt
, 'r', 'x',
1301 case 'r' : /* Reply */
1305 case 'f' : /* Follow-Up via news ONLY! */
1309 case 'b' : /* BOTH */
1313 case 'x' : /* cancel or unknown response */
1315 cmd_cancelled("Reply");
1321 if(env
->followup_to
){
1322 q_status_message(SM_ORDER
, 2, 3,
1323 _("Posting to specified Followup-To groups"));
1324 outgoing
->newsgroups
= cpystr(env
->followup_to
);
1326 else if(!outgoing
->newsgroups
)
1327 outgoing
->newsgroups
= cpystr(env
->newsgroups
);
1334 /*----------------------------------------------------------------------
1335 Acquire the pinerc defined signature file
1336 It is allocated here and freed by the caller.
1338 file -- use this file
1339 prenewlines -- prefix the file contents with this many newlines
1340 postnewlines -- postfix the file contents with this many newlines
1341 is_sig -- this is a signature (not a template)
1344 get_signature_file(char *file
, int prenewlines
, int postnewlines
, int is_sig
)
1346 char *sig
, *tmp_sig
= NULL
, sig_path
[MAXPATH
+1];
1347 int len
, do_the_pipe_thang
= 0;
1348 long sigsize
= 0L, cntdown
;
1351 if(!signature_path(file
, sig_path
, MAXPATH
))
1354 dprint((5, "get_signature(%s)\n", sig_path
));
1356 if(sig_path
[(len
=strlen(sig_path
))-1] == '|'){
1357 if(is_sig
&& F_ON(F_DISABLE_PIPES_IN_SIGS
, ps_global
)){
1358 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1359 _("Pipes for signatures are administratively disabled"));
1362 else if(!is_sig
&& F_ON(F_DISABLE_PIPES_IN_TEMPLATES
, ps_global
)){
1363 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1364 _("Pipes for templates are administratively disabled"));
1368 sig_path
[len
-1] = '\0';
1369 removing_trailing_white_space(sig_path
);
1370 do_the_pipe_thang
++;
1373 if(!IS_REMOTE(sig_path
) && ps_global
->VAR_OPER_DIR
&&
1374 !in_dir(ps_global
->VAR_OPER_DIR
, sig_path
)){
1375 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1376 /* TRANSLATORS: First arg is the directory name, second is
1377 the file user wants to read but can't. */
1378 _("Can't read file outside %s: %s"),
1379 ps_global
->VAR_OPER_DIR
, file
);
1384 if(IS_REMOTE(sig_path
) || can_access(sig_path
, ACCESS_EXISTS
) == 0){
1385 if(do_the_pipe_thang
){
1386 if(can_access(sig_path
, EXECUTE_ACCESS
) == 0){
1393 if((store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
1395 flags
= PIPE_READ
| PIPE_STDERR
| PIPE_NOSHELL
;
1399 if((syspipe
= open_system_pipe(sig_path
, NULL
, NULL
, flags
, 5,
1400 pipe_callback
, pipe_report_error
)) != NULL
){
1404 gf_set_so_writec(&pc
, store
);
1405 gf_set_readc(&gc
, (void *)syspipe
, 0, PipeStar
, READ_FROM_LOCALE
);
1408 if((error
= gf_pipe(gc
, pc
)) != NULL
){
1409 (void)close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1410 gf_clear_so_writec(store
);
1412 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1413 _("Can't get file: %s"), error
);
1417 if(close_system_pipe(&syspipe
, NULL
, pipe_callback
)){
1421 q_status_message2(SM_ORDER
, 3, 4,
1422 _("Error running program \"%s\"%s"),
1424 (now
- start
> 4) ? ": timed out" : "");
1427 gf_clear_so_writec(store
);
1429 /* rewind and count chars */
1430 so_seek(store
, 0L, 0);
1431 while(so_readc(&c
, store
) && sigsize
< 100000L)
1434 /* allocate space */
1435 tmp_sig
= fs_get((sigsize
+ 1) * sizeof(char));
1439 /* rewind and copy chars, no prenewlines... */
1440 so_seek(store
, 0L, 0);
1442 while(so_readc(&c
, store
) && cntdown
-- > 0L)
1450 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1451 _("Error running program \"%s\""),
1456 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1457 "Error allocating space for sig or template program");
1460 q_status_message1(SM_ORDER
| SM_DING
, 3, 4,
1461 /* TRANSLATORS: Arg is a program name */
1462 _("Can't execute \"%s\": Permission denied"),
1465 else if((IS_REMOTE(sig_path
) &&
1466 (tmp_sig
= simple_read_remote_file(sig_path
, REMOTE_SIG_SUBTYPE
))) ||
1467 (tmp_sig
= read_file(sig_path
, READ_FROM_LOCALE
)))
1468 sigsize
= strlen(tmp_sig
);
1470 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1471 /* TRANSLATORS: First arg is error description, 2nd is
1473 _("Error \"%s\" reading file \"%s\""),
1474 error_description(errno
), sig_path
);
1477 sig
= get_signature_lit(tmp_sig
, prenewlines
, postnewlines
, is_sig
, 0);
1479 fs_give((void **)&tmp_sig
);
1486 /*----------------------------------------------------------------------
1487 Partially set up message to forward and pass off to composer/mailer
1489 Args: pine_state -- The usual pine structure
1491 Result: outgoing envelope and body created and passed off to composer/mailer
1493 Create the outgoing envelope for the mail being forwarded, which is
1494 not much more than filling in the subject, and create the message body
1495 of the outgoing message which requires formatting the header from the
1496 envelope of the original messasge.
1497 ----------------------------------------------------------------------*/
1499 forward(struct pine
*ps
, ACTION_S
*role_arg
)
1502 int ret
, forward_raw_body
= 0, rv
= 0, i
;
1503 long msgno
, j
, totalmsgs
, rflags
;
1504 ENVELOPE
*env
, *outgoing
;
1505 BODY
*orig_body
, *body
= NULL
;
1507 void *msgtext
= NULL
;
1509 int impl
, template_len
= 0;
1511 REDRAFT_POS_S
*redraft_pos
= NULL
;
1512 ACTION_S
*role
= NULL
, *nrole
;
1513 #if defined(DOS) && !defined(_WINDOWS)
1517 dprint((4, "\n - forward -\n"));
1519 memset((void *)&reply
, 0, sizeof(reply
));
1520 outgoing
= mail_newenvelope();
1521 outgoing
->message_id
= generate_message_id();
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 if(role
&& role
->template){
1609 filtered
= detoken(role
, (totalmsgs
== 1L) ? env
: NULL
,
1610 0, 0, 0, &redraft_pos
, &impl
);
1613 so_puts((STORE_S
*)msgtext
, filtered
);
1615 template_len
= strlen(filtered
);
1618 fs_give((void **)&filtered
);
1624 if((sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
, &impl
)) != NULL
){
1626 redraft_pos
->offset
+= template_len
;
1628 so_puts((STORE_S
*)msgtext
, *sig
? sig
: NEWLINE
);
1630 fs_give((void **)&sig
);
1633 so_puts((STORE_S
*)msgtext
, NEWLINE
);
1635 gf_set_so_writec(&pc
, (STORE_S
*)msgtext
);
1637 #if defined(DOS) && !defined(_WINDOWS)
1638 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1639 #define IN_RESERVE 8192
1641 #define IN_RESERVE 16384
1643 if((reserve
=(char *)malloc(IN_RESERVE
)) == NULL
){
1644 gf_clear_so_writec((STORE_S
*) msgtext
);
1645 so_give((STORE_S
**)&msgtext
);
1646 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1647 _("Insufficient memory for message text"));
1653 * If we're forwarding multiple messages *or* the forward-as-mime
1654 * is turned on and the users wants it done that way, package things
1657 if(ret
== 'y'){ /* attach message[s]!!! */
1659 long totalsize
= 0L;
1661 /*---- New Body to start with ----*/
1662 body
= mail_newbody();
1663 body
->type
= TYPEMULTIPART
;
1665 /*---- The TEXT part/body ----*/
1666 body
->nested
.part
= mail_newbody_part();
1667 body
->nested
.part
->body
.type
= TYPETEXT
;
1668 body
->nested
.part
->body
.contents
.text
.data
= msgtext
;
1671 /*---- The MULTIPART/DIGEST part ----*/
1672 body
->nested
.part
->next
= mail_newbody_part();
1673 body
->nested
.part
->next
->body
.type
= TYPEMULTIPART
;
1674 body
->nested
.part
->next
->body
.subtype
= cpystr("Digest");
1675 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Digest of %s messages", comatose(totalmsgs
));
1676 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1677 body
->nested
.part
->next
->body
.description
= cpystr(tmp_20k_buf
);
1678 pp
= &(body
->nested
.part
->next
->body
.nested
.part
);
1681 pp
= &(body
->nested
.part
->next
);
1683 /*---- The Message body subparts ----*/
1684 for(msgno
= mn_first_cur(ps
->msgmap
);
1686 msgno
= mn_next_cur(ps
->msgmap
)){
1688 msgno
= mn_m2raw(ps
->msgmap
, msgno
);
1689 env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
, NULL
);
1691 if(forward_mime_msg(ps
->mail_stream
,msgno
,NULL
,env
,pp
,msgtext
)){
1692 totalsize
+= (*pp
)->body
.size
.bytes
;
1693 pp
= &((*pp
)->next
);
1700 body
->nested
.part
->next
->body
.size
.bytes
= totalsize
;
1702 else if(totalmsgs
> 1L){
1704 body
= mail_newbody();
1705 body
->type
= TYPETEXT
;
1706 body
->contents
.text
.data
= msgtext
;
1709 for(msgno
= mn_first_cur(ps
->msgmap
);
1711 msgno
= mn_next_cur(ps
->msgmap
)){
1713 if(env
){ /* put 2 between messages */
1714 gf_puts(NEWLINE
, pc
);
1715 gf_puts(NEWLINE
, pc
);
1718 /*--- Grab current envelope ---*/
1719 env
= pine_mail_fetchstructure(ps
->mail_stream
,
1720 mn_m2raw(ps
->msgmap
, msgno
),
1722 if(!env
|| !orig_body
){
1723 q_status_message1(SM_ORDER
,3,4,
1724 _("Error fetching message %s. Can't forward it."),
1725 long2string(msgno
));
1729 if(orig_body
== NULL
|| orig_body
->type
== TYPETEXT
|| forward_raw_body
) {
1730 forward_delimiter(pc
);
1731 reply_forward_header(ps
->mail_stream
,
1732 mn_m2raw(ps
->msgmap
, msgno
),
1735 if(!get_body_part_text(ps
->mail_stream
, forward_raw_body
? NULL
: orig_body
,
1736 mn_m2raw(ps
->msgmap
, msgno
),
1737 forward_raw_body
? NULL
: "1", 0L, pc
,
1738 NULL
, NULL
, GBPT_NONE
))
1740 } else if(orig_body
->type
== TYPEMULTIPART
) {
1742 q_status_message(SM_ORDER
,3,7,
1743 _("WARNING! Attachments not included in multiple forward."));
1745 if(orig_body
->nested
.part
&&
1746 orig_body
->nested
.part
->body
.type
== TYPETEXT
) {
1747 /*---- First part of the message is text -----*/
1748 forward_delimiter(pc
);
1749 reply_forward_header(ps
->mail_stream
,
1750 mn_m2raw(ps
->msgmap
,msgno
),
1753 if(!get_body_part_text(ps
->mail_stream
,
1754 &orig_body
->nested
.part
->body
,
1755 mn_m2raw(ps
->msgmap
, msgno
),
1757 NULL
, NULL
, GBPT_NONE
))
1760 q_status_message(SM_ORDER
,0,3,
1761 _("Multipart with no leading text part!"));
1764 /*---- Single non-text message of some sort ----*/
1765 q_status_message(SM_ORDER
,0,3,
1766 _("Non-text message not included!"));
1770 else if(!((env
= pine_mail_fetchstructure(ps
->mail_stream
, msgno
,
1772 && (body
= forward_body(ps
->mail_stream
, env
, orig_body
, msgno
,
1775 q_status_message1(SM_ORDER
,3,4,
1776 _("Error fetching message %s. Can't forward it."),
1777 long2string(msgno
));
1781 if(ret
!= 'y' && totalmsgs
== 1L && orig_body
){
1784 charset
= parameter_val(orig_body
->parameter
, "charset");
1785 if(charset
&& strucmp(charset
, "us-ascii") != 0){
1789 * There is a non-ascii charset, is there conversion happening?
1791 if(!(ct
=conversion_table(charset
, ps_global
->posting_charmap
)) || !ct
->table
){
1792 reply
.orig_charset
= charset
;
1798 fs_give((void **) &charset
);
1801 * I don't think orig_charset is ever used except possibly
1802 * right here. Hubert 2008-01-15.
1804 if(reply
.orig_charset
)
1808 /* fill in reply structure */
1809 reply
.forwarded
= 1;
1810 reply
.mailbox
= cpystr(ps
->mail_stream
->mailbox
);
1811 reply
.origmbox
= cpystr(ps
->mail_stream
->original_mailbox
1812 ? ps
->mail_stream
->original_mailbox
1813 : ps
->mail_stream
->mailbox
);
1814 reply
.data
.uid
.msgs
= (imapuid_t
*) fs_get((totalmsgs
+ 1) * sizeof(imapuid_t
));
1815 if((reply
.data
.uid
.validity
= ps
->mail_stream
->uid_validity
) != 0){
1817 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1819 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1820 reply
.data
.uid
.msgs
[i
] = mail_uid(ps
->mail_stream
, mn_m2raw(ps
->msgmap
, msgno
));
1824 for(msgno
= mn_first_cur(ps
->msgmap
), i
= 0;
1826 msgno
= mn_next_cur(ps
->msgmap
), i
++)
1827 reply
.data
.uid
.msgs
[i
] = mn_m2raw(ps
->msgmap
, msgno
);
1830 reply
.data
.uid
.msgs
[i
] = 0; /* tie off list */
1832 #if defined(DOS) && !defined(_WINDOWS)
1833 free((void *)reserve
);
1835 pine_send(outgoing
, &body
, "FORWARD MESSAGE",
1836 role
, NULL
, &reply
, redraft_pos
,
1842 pine_free_body(&body
);
1844 if((STORE_S
*) msgtext
)
1845 gf_clear_so_writec((STORE_S
*) msgtext
);
1847 mail_free_envelope(&outgoing
);
1848 free_redraft_pos(&redraft_pos
);
1851 if(reply
.orig_charset
)
1852 fs_give((void **)&reply
.orig_charset
);
1857 q_status_message(SM_ORDER
| SM_DING
, 4, 5,
1858 _("Error fetching message contents. Can't forward message."));
1863 /*----------------------------------------------------------------------
1864 Partially set up message to forward and pass off to composer/mailer
1866 Args: pine_state -- The usual pine structure
1867 message -- The MESSAGECACHE of entry to reply to
1869 Result: outgoing envelope and body created and passed off to composer/mailer
1871 Create the outgoing envelope for the mail being forwarded, which is
1872 not much more than filling in the subject, and create the message body
1873 of the outgoing message which requires formatting the header from the
1874 envelope of the original messasge.
1875 ----------------------------------------------------------------------*/
1877 forward_text(struct pine
*pine_state
, void *text
, SourceType source
)
1883 char *enc_error
, *sig
;
1884 ACTION_S
*role
= NULL
;
1886 long rflags
= ROLE_COMPOSE
;
1888 if((msgtext
= so_get(PicoText
, NULL
, EDIT_ACCESS
)) != NULL
){
1889 env
= mail_newenvelope();
1890 env
->message_id
= generate_message_id();
1891 body
= mail_newbody();
1892 body
->type
= TYPETEXT
;
1893 body
->contents
.text
.data
= (void *) msgtext
;
1895 if(nonempty_patterns(rflags
, &dummy
)){
1897 * This is really more like Compose, even though it
1898 * is called Forward.
1900 if(confirm_role(rflags
, &role
))
1901 role
= combine_inherited_role(role
);
1903 cmd_cancelled("Composition");
1904 display_message('x');
1905 mail_free_envelope(&env
);
1906 pine_free_body(&body
);
1912 q_status_message1(SM_ORDER
, 3, 4, _("Composing using role \"%s\""),
1915 sig
= detoken(role
, NULL
, 2, 0, 1, NULL
, NULL
);
1916 so_puts(msgtext
, (sig
&& *sig
) ? sig
: NEWLINE
);
1917 so_puts(msgtext
, NEWLINE
);
1918 so_puts(msgtext
, "----- Included text -----");
1919 so_puts(msgtext
, NEWLINE
);
1921 fs_give((void **)&sig
);
1924 gf_set_so_writec(&pc
, msgtext
);
1925 gf_set_readc(&gc
,text
,(source
== CharStar
) ? strlen((char *)text
) : 0L,
1928 if((enc_error
= gf_pipe(gc
, pc
)) == NULL
){
1929 pine_send(env
, &body
, "SEND MESSAGE", role
, NULL
, NULL
, NULL
,
1931 pine_state
->mangled_screen
= 1;
1934 q_status_message1(SM_ORDER
| SM_DING
, 3, 5,
1935 _("Error reading text \"%s\""),enc_error
);
1936 display_message('x');
1939 gf_clear_so_writec(msgtext
);
1940 mail_free_envelope(&env
);
1941 pine_free_body(&body
);
1944 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1945 _("Error allocating message text"));
1946 display_message('x');
1953 /*----------------------------------------------------------------------
1954 Partially set up message to resend and pass off to mailer
1956 Args: pine_state -- The usual pine structure
1958 Result: outgoing envelope and body created and passed off to mailer
1960 Create the outgoing envelope for the mail being resent, which is
1961 not much more than filling in the subject, and create the message body
1962 of the outgoing message which requires formatting the header from the
1963 envelope of the original messasge.
1964 ----------------------------------------------------------------------*/
1966 bounce(struct pine
*pine_state
, ACTION_S
*role
)
1970 char *save_to
= NULL
, **save_toptr
= NULL
, *errstr
= NULL
,
1971 *prmpt_who
= NULL
, *prmpt_cnf
= NULL
;
1973 dprint((4, "\n - bounce -\n"));
1975 if(mn_total_cur(pine_state
->msgmap
) > 1L){
1976 save_toptr
= &save_to
;
1978 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1979 mn_total_cur(pine_state
->msgmap
), role
->nick
);
1981 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("BOUNCE (redirect) %ld messages to : "),
1982 mn_total_cur(pine_state
->msgmap
));
1983 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1984 prmpt_who
= cpystr(tmp_20k_buf
);
1985 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Send %ld messages "),
1986 mn_total_cur(pine_state
->msgmap
));
1987 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1988 prmpt_cnf
= cpystr(tmp_20k_buf
);
1991 for(msgno
= mn_first_cur(pine_state
->msgmap
);
1993 msgno
= mn_next_cur(pine_state
->msgmap
)){
1995 rawno
= mn_m2raw(pine_state
->msgmap
, msgno
);
1996 if((env
= pine_mail_fetchstructure(pine_state
->mail_stream
, rawno
, NULL
)) != NULL
)
1997 errstr
= bounce_msg(pine_state
->mail_stream
, rawno
, NULL
, role
,
1998 save_toptr
, env
->subject
, prmpt_who
, prmpt_cnf
);
2000 errstr
= _("Can't fetch Subject for Bounce");
2005 q_status_message(SM_ORDER
| SM_DING
, 4, 7, errstr
);
2012 fs_give((void **)&save_to
);
2015 fs_give((void **) &prmpt_who
);
2018 fs_give((void **) &prmpt_cnf
);
2020 return(errstr
? 0 : 1);
2026 bounce_msg(MAILSTREAM
*stream
,
2035 char *errstr
= NULL
;
2042 /* When we bounce a message, we will leave the original message
2043 * intact, which means that it will not be signed or encrypted,
2044 * so we turn off signing and encrypting now. It will be turned
2045 * on again in send_exit_for_pico().
2047 if(ps_global
->smime
)
2048 ps_global
->smime
->do_sign
= ps_global
->smime
->do_encrypt
= 0;
2051 if((errstr
= bounce_msg_body(stream
, rawno
, part
, to
, subject
, &outgoing
, &body
, &was_seen
)) == NULL
){
2052 if(pine_simple_send(outgoing
, &body
, &role
, pmt_who
, pmt_cnf
, to
,
2053 !(to
&& *to
) ? SS_PROMPTFORTO
: 0) < 0){
2054 errstr
= ""; /* p_s_s() better have explained! */
2055 /* clear seen flag */
2056 if(was_seen
== 0 && rawno
> 0L
2057 && stream
&& rawno
<= stream
->nmsgs
2058 && (mc
= mail_elt(stream
, rawno
)) && mc
->seen
)
2059 mail_flag(stream
, long2string(rawno
), "\\SEEN", 0);
2063 /* Just for good measure... */
2064 mail_free_envelope(&outgoing
);
2065 pine_free_body(&body
);
2067 return(errstr
); /* no problem-o */
2071 /*----------------------------------------------------------------------
2072 Serve up the current signature within pico for editing
2076 Result: signature changed or not.
2079 signature_edit(char *sigfile
, char *title
)
2082 char sig_path
[MAXPATH
+1], errbuf
[2000], *errstr
= NULL
;
2084 STORE_S
*msgso
, *tmpso
= NULL
;
2087 struct variable
*vars
= ps_global
->vars
;
2088 REMDATA_S
*rd
= NULL
;
2090 if(!signature_path(sigfile
, sig_path
, MAXPATH
))
2091 return(cpystr(_("No signature file defined.")));
2093 if(IS_REMOTE(sigfile
)){
2094 rd
= rd_create_remote(RemImap
, sig_path
, REMOTE_SIG_SUBTYPE
,
2096 _("Can't access remote configuration."));
2098 return(cpystr(_("Error attempting to edit remote configuration")));
2100 (void)rd_read_metadata(rd
);
2102 if(rd
->access
== MaybeRorW
){
2103 if(rd
->read_status
== 'R')
2104 rd
->access
= ReadOnly
;
2106 rd
->access
= ReadWrite
;
2109 if(rd
->access
!= NoExists
){
2111 rd_check_remvalid(rd
, 1L);
2114 * If the cached info says it is readonly but
2115 * it looks like it's been fixed now, change it to readwrite.
2117 if(rd
->read_status
== 'R'){
2118 rd_check_readonly_access(rd
);
2119 if(rd
->read_status
== 'W'){
2120 rd
->access
= ReadWrite
;
2121 rd
->flags
|= REM_OUTOFDATE
;
2124 rd
->access
= ReadOnly
;
2128 if(rd
->flags
& REM_OUTOFDATE
){
2129 if(rd_update_local(rd
) != 0){
2132 "signature_edit: rd_update_local failed\n"));
2133 rd_close_remdata(&rd
);
2134 return(cpystr(_("Can't access remote sig")));
2140 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
2141 rd_close_remdata(&rd
);
2142 return(cpystr(_("Can't get write permission for remote sig")));
2145 rd
->flags
|= DO_REMTRIM
;
2147 strncpy(sig_path
, rd
->lf
, sizeof(sig_path
)-1);
2148 sig_path
[sizeof(sig_path
)-1] = '\0';
2151 standard_picobuf_setup(&pbf
);
2152 pbf
.tty_fix
= PineRaw
;
2153 pbf
.composer_help
= h_composer_sigedit
;
2154 pbf
.exittest
= sigedit_exit_for_pico
;
2155 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2156 ? upload_msg_to_pico
: NULL
;
2157 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2158 ? VAR_EDITOR
: NULL
;
2159 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2160 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2161 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2162 pbf
.allow_flowed_text
= 0;
2164 pbf
.pine_anchor
= set_titlebar(title
,
2165 ps_global
->mail_stream
,
2166 ps_global
->context_current
,
2167 ps_global
->cur_folder
,
2169 0, FolderName
, 0, 0, NULL
);
2171 /* NOTE: at this point, alot of pico struct fields are null'd out
2172 * thanks to the leading memset; in particular "headents" which tells
2173 * pico to behave like a normal editor (though modified slightly to
2174 * let the caller dictate the file to edit and such)...
2177 if(VAR_OPER_DIR
&& !in_dir(VAR_OPER_DIR
, sig_path
)){
2180 l
= strlen(VAR_OPER_DIR
) + 100;
2181 ret
= (char *) fs_get((l
+1) * sizeof(char));
2182 snprintf(ret
, l
+1, _("Can't edit file outside of %s"), VAR_OPER_DIR
);
2188 * Now alloc and init the text to pass pico
2190 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2191 ret
= cpystr(_("Error allocating space for file"));
2192 dprint((1, "Can't alloc space for signature_edit"));
2196 pbf
.msgtext
= so_text(msgso
);
2198 if(can_access(sig_path
, READ_ACCESS
) == 0
2199 && !(tmpso
= so_get(FileStar
, sig_path
, READ_ACCESS
|READ_FROM_LOCALE
))){
2200 char *problem
= error_description(errno
);
2202 snprintf(errbuf
, sizeof(errbuf
), _("Error editing \"%s\": %s"),
2203 sig_path
, problem
? problem
: "<NULL>");
2204 errbuf
[sizeof(errbuf
)-1] = '\0';
2205 ret
= cpystr(errbuf
);
2207 dprint((1, "signature_edit: can't open %s: %s", sig_path
,
2208 problem
? problem
: "<NULL>"));
2211 else if(tmpso
){ /* else, fill pico's edit buffer */
2212 gf_set_so_readc(&gc
, tmpso
); /* read from file, write pico buf */
2213 gf_set_so_writec(&pc
, msgso
);
2214 gf_filter_init(); /* no filters needed */
2215 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2216 snprintf(errbuf
, sizeof(errbuf
), _("Error reading file: \"%s\""), errstr
);
2217 errbuf
[sizeof(errbuf
)-1] = '\0';
2218 ret
= cpystr(errbuf
);
2221 gf_clear_so_readc(tmpso
);
2222 gf_clear_so_writec(msgso
);
2228 mswin_setwindowmenu (MENU_COMPOSER
);
2231 /*------ OK, Go edit the signature ------*/
2232 editor_result
= pico(&pbf
);
2235 mswin_setwindowmenu (MENU_DEFAULT
);
2237 if(editor_result
& COMP_GOTHUP
){
2238 hup_signal(); /* do what's normal for a hup */
2241 fix_windsize(ps_global
);
2245 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2248 /*------ Must have an edited buffer, write it to .sig -----*/
2249 our_unlink(sig_path
); /* blast old copy */
2250 if((tmpso
= so_get(FileStar
, sig_path
, WRITE_ACCESS
|WRITE_TO_LOCALE
)) != NULL
){
2251 so_seek(msgso
, 0L, 0);
2252 gf_set_so_readc(&gc
, msgso
); /* read from pico buf */
2253 gf_set_so_writec(&pc
, tmpso
); /* write sig file */
2254 gf_filter_init(); /* no filters needed */
2255 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
2256 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2258 errbuf
[sizeof(errbuf
)-1] = '\0';
2259 ret
= cpystr(errbuf
);
2262 gf_clear_so_readc(msgso
);
2263 gf_clear_so_writec(tmpso
);
2264 if(so_give(&tmpso
)){
2265 errstr
= error_description(errno
);
2266 snprintf(errbuf
, sizeof(errbuf
), _("Error writing file: \"%s\""),
2268 errbuf
[sizeof(errbuf
)-1] = '\0';
2269 ret
= cpystr(errbuf
);
2272 if(IS_REMOTE(sigfile
)){
2278 we_cancel
= busy_cue("Copying to remote sig", NULL
, 1);
2279 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
2281 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2282 _("Error opening temporary sig file %s: %s"),
2283 rd
->lf
, error_description(errno
));
2285 "write_remote_sig: error opening temp file %s\n",
2286 rd
->lf
? rd
->lf
: "?"));
2289 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
2290 _("Error copying to %s: %s"),
2291 rd
->rn
, error_description(errno
));
2293 "write_remote_sig: error copying from %s to %s\n",
2294 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
2297 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
2298 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2301 rd_update_metadata(rd
, datebuf
);
2302 rd
->read_status
= 'W';
2305 rd_close_remdata(&rd
);
2308 cancel_busy_cue(-1);
2312 snprintf(errbuf
, sizeof(errbuf
), _("Error writing \"%s\""), sig_path
);
2313 errbuf
[sizeof(errbuf
)-1] = '\0';
2314 ret
= cpystr(errbuf
);
2315 dprint((1, "signature_edit: can't write %s",
2321 standard_picobuf_teardown(&pbf
);
2327 /*----------------------------------------------------------------------
2328 Serve up the current signature within pico for editing
2330 Args: literal signature to edit
2332 Result: raw edited signature is returned in result arg
2335 signature_edit_lit(char *litsig
, char **result
, char *title
, HelpType composer_help
)
2338 char *errstr
= NULL
;
2342 struct variable
*vars
= ps_global
->vars
;
2344 standard_picobuf_setup(&pbf
);
2345 pbf
.tty_fix
= PineRaw
;
2346 pbf
.search_help
= h_sigedit_search
;
2347 pbf
.composer_help
= composer_help
;
2348 pbf
.exittest
= sigedit_exit_for_pico
;
2349 pbf
.upload
= (VAR_UPLOAD_CMD
&& VAR_UPLOAD_CMD
[0])
2350 ? upload_msg_to_pico
: NULL
;
2351 pbf
.alt_ed
= (VAR_EDITOR
&& VAR_EDITOR
[0] && VAR_EDITOR
[0][0])
2352 ? VAR_EDITOR
: NULL
;
2353 pbf
.alt_spell
= (VAR_SPELLER
&& VAR_SPELLER
[0]) ? VAR_SPELLER
: NULL
;
2354 pbf
.always_spell_check
= F_ON(F_ALWAYS_SPELL_CHECK
, ps_global
);
2355 pbf
.strip_ws_before_send
= F_ON(F_STRIP_WS_BEFORE_SEND
, ps_global
);
2356 pbf
.allow_flowed_text
= 0;
2358 pbf
.pine_anchor
= set_titlebar(title
,
2359 ps_global
->mail_stream
,
2360 ps_global
->context_current
,
2361 ps_global
->cur_folder
,
2363 0, FolderName
, 0, 0, NULL
);
2365 /* NOTE: at this point, alot of pico struct fields are null'd out
2366 * thanks to the leading memset; in particular "headents" which tells
2367 * pico to behave like a normal editor (though modified slightly to
2368 * let the caller dictate the file to edit and such)...
2372 * Now alloc and init the text to pass pico
2374 if(!(msgso
= so_get(PicoText
, NULL
, EDIT_ACCESS
))){
2375 ret
= cpystr(_("Error allocating space"));
2376 dprint((1, "Can't alloc space for signature_edit_lit"));
2380 pbf
.msgtext
= so_text(msgso
);
2382 so_puts(msgso
, litsig
? litsig
: "");
2387 mswin_setwindowmenu (MENU_COMPOSER
);
2390 /*------ OK, Go edit the signature ------*/
2391 editor_result
= pico(&pbf
);
2394 mswin_setwindowmenu (MENU_DEFAULT
);
2396 if(editor_result
& COMP_GOTHUP
){
2397 hup_signal(); /* do what's normal for a hup */
2400 fix_windsize(ps_global
);
2404 if(editor_result
& (COMP_SUSPEND
| COMP_GOTHUP
| COMP_CANCEL
)){
2405 ret
= cpystr(_("Edit Cancelled"));
2408 /*------ Must have an edited buffer, write it to .sig -----*/
2413 so_seek(msgso
, 0L, 0);
2414 while(so_readc(&c
, msgso
))
2417 *result
= (char *)fs_get((cnt
+1) * sizeof(char));
2419 so_seek(msgso
, 0L, 0);
2420 while(so_readc(&c
, msgso
))
2427 standard_picobuf_teardown(&pbf
);
2434 * Returns 0 for Save Changes and exit
2436 * -1 exit but Dont Save Changes
2439 sigedit_exit_for_pico(struct headerentry
*he
, void (*redraw_pico
)(void), int allow_flowed
,
2444 void (*redraw
)(void) = ps_global
->redrawer
;
2445 static ESCKEY_S opts
[] = {
2446 {'s', 's', "S", N_("Save changes")},
2447 {'d', 'd', "D", N_("Don't save changes")},
2451 ps_global
->redrawer
= redraw_pico
;
2452 fix_windsize(ps_global
);
2455 rv
= radio_buttons(_("Exit editor? "),
2456 -FOOTER_ROWS(ps_global
), opts
,
2457 's', 'x', h_exit_editor
, RB_NORM
);
2458 if(rv
== 's'){ /* user ACCEPTS! */
2461 else if(rv
== 'd'){ /* Declined! */
2462 rstr
= _("No Changes Saved");
2465 else if(rv
== 'x'){ /* Cancelled! */
2466 rstr
= _("Exit Cancelled");
2474 ps_global
->redrawer
= redraw
;
2475 return((rv
== 's') ? 0 : (rv
== 'd') ? -1 : 1);
2480 * Common stuff we almost always want to set when calling pico.
2483 standard_picobuf_setup(PICO
*pbf
)
2485 memset(pbf
, 0, sizeof(*pbf
));
2487 pbf
->pine_version
= ALPINE_VERSION
;
2488 pbf
->fillcolumn
= ps_global
->composer_fillcol
;
2489 pbf
->menu_rows
= FOOTER_ROWS(ps_global
) - 1;
2490 pbf
->colors
= colors_for_pico();
2491 pbf
->wordseps
= user_wordseps(ps_global
->VAR_WORDSEPS
);
2492 pbf
->helper
= helper
;
2493 pbf
->showmsg
= display_message_for_pico
;
2494 pbf
->suspend
= do_suspend
;
2495 pbf
->keybinput
= cmd_input_for_pico
;
2496 pbf
->tty_fix
= ttyfix
; /* watch out for this one */
2497 pbf
->newmail
= new_mail_for_pico
;
2498 pbf
->ckptdir
= checkpoint_dir_for_pico
;
2499 pbf
->resize
= resize_for_pico
;
2500 pbf
->input_cs
= ps_global
->input_cs
;
2501 pbf
->winch_cleanup
= winch_cleanup
;
2502 pbf
->search_help
= h_composer_search
;
2503 pbf
->ins_help
= h_composer_ins
;
2504 pbf
->ins_m_help
= h_composer_ins_m
;
2505 pbf
->composer_help
= h_composer
;
2506 pbf
->browse_help
= h_composer_browse
;
2507 pbf
->attach_help
= h_composer_ctrl_j
;
2510 ( (F_ON(F_CAN_SUSPEND
,ps_global
) ? P_SUSPEND
: 0L)
2511 | (F_ON(F_USE_FK
,ps_global
) ? P_FKEYS
: 0L)
2512 | (ps_global
->restricted
? P_SECURE
: 0L)
2513 | (F_ON(F_ALT_ED_NOW
,ps_global
) ? P_ALTNOW
: 0L)
2514 | (F_ON(F_USE_CURRENT_DIR
,ps_global
) ? P_CURDIR
: 0L)
2515 | (F_ON(F_SUSPEND_SPAWNS
,ps_global
) ? P_SUBSHELL
: 0L)
2516 | (F_ON(F_COMPOSE_MAPS_DEL
,ps_global
) ? P_DELRUBS
: 0L)
2517 | (F_ON(F_ENABLE_TAB_COMPLETE
,ps_global
) ? P_COMPLETE
: 0L)
2518 | (F_ON(F_SHOW_CURSOR
,ps_global
) ? P_SHOCUR
: 0L)
2519 | (F_ON(F_DEL_FROM_DOT
,ps_global
) ? P_DOTKILL
: 0L)
2520 | (F_ON(F_ENABLE_DOT_FILES
,ps_global
) ? P_DOTFILES
: 0L)
2521 | (F_ON(F_ALLOW_GOTO
,ps_global
) ? P_ALLOW_GOTO
: 0L)
2522 | (F_ON(F_ENABLE_SEARCH_AND_REPL
,ps_global
) ? P_REPLACE
: 0L)
2523 | (!ps_global
->pass_ctrl_chars
2524 && !ps_global
->pass_c1_ctrl_chars
? P_HICTRL
: 0L)
2525 | ((F_ON(F_ENABLE_ALT_ED
,ps_global
)
2526 || F_ON(F_ALT_ED_NOW
,ps_global
)
2527 || (ps_global
->VAR_EDITOR
2528 && ps_global
->VAR_EDITOR
[0]
2529 && ps_global
->VAR_EDITOR
[0][0]))
2531 | ((!ps_global
->keyboard_charmap
2532 || !strucmp(ps_global
->keyboard_charmap
, "US-ASCII"))
2533 ? P_HIBITIGN
: 0L));
2535 if(ps_global
->VAR_OPER_DIR
){
2536 pbf
->oper_dir
= ps_global
->VAR_OPER_DIR
;
2537 pbf
->pine_flags
|= P_TREE
;
2540 pbf
->home_dir
= ps_global
->home_dir
;
2545 standard_picobuf_teardown(PICO
*pbf
)
2549 free_pcolors(&pbf
->colors
);
2552 fs_give((void **) &pbf
->wordseps
);
2557 /*----------------------------------------------------------------------
2558 Call back for pico to use to check for new mail.
2560 Args: cursor -- pointer to in to tell caller if cursor location changed
2561 if NULL, turn off cursor positioning.
2562 timing -- whether or not it's a good time to check
2565 Returns: returns 1 on success, zero on error.
2568 new_mail_for_pico(int timing
, int status
)
2571 * If we're not interested in the status, don't display the busy
2574 /* don't know where the cursor's been, reset it */
2576 return(new_mail(0, timing
,
2577 (status
? NM_STATUS_MSG
: NM_NONE
) | NM_DEFER_SORT
2578 | NM_FROM_COMPOSER
));
2583 cmd_input_for_pico(void)
2585 zero_new_mail_count();
2589 /*----------------------------------------------------------------------
2590 Call back for pico to get newmail status messages displayed
2592 Args: x -- char processed
2597 display_message_for_pico(int x
)
2601 clear_cursor_pos(); /* can't know where cursor is */
2602 mark_status_dirty(); /* don't count on cached text */
2603 fix_windsize(ps_global
);
2606 rv
= ps_global
->mangled_screen
;
2607 ps_global
->mangled_screen
= 0;
2612 /*----------------------------------------------------------------------
2613 Call back for pico to get desired directory for its check point file
2615 Args: s -- buffer to write directory name
2616 n -- length of that buffer
2618 Returns: pointer to static buffer
2621 checkpoint_dir_for_pico(char *s
, size_t n
)
2623 #if defined(DOS) || defined(OS2)
2625 * we can't assume anything about root or home dirs, so
2626 * just plunk it down in the same place as the pinerc
2628 if(!getenv("HOME")){
2629 char *lc
= last_cmpnt(ps_global
->pinerc
);
2632 strncpy(s
, ps_global
->pinerc
, MIN(n
-1,lc
-ps_global
->pinerc
));
2633 s
[MIN(n
-1,lc
-ps_global
->pinerc
)] = '\0';
2636 strncpy(s
, ".\\", n
-1);
2642 strncpy(s
, ps_global
->home_dir
, n
-1);
2649 /*----------------------------------------------------------------------
2650 Call back for pico to tell us the window size's changed
2654 Returns: none (but pine's ttyo structure may have been updated)
2657 resize_for_pico(void)
2659 fix_windsize(ps_global
);
2664 colors_for_pico(void)
2666 PCOLORS
*colors
= NULL
;
2667 struct variable
*vars
= ps_global
->vars
;
2669 if (pico_usingcolor()){
2670 colors
= (PCOLORS
*)fs_get(sizeof(PCOLORS
));
2672 colors
->tbcp
= current_titlebar_color();
2674 if (VAR_KEYLABEL_FORE_COLOR
&& VAR_KEYLABEL_BACK_COLOR
){
2675 colors
->klcp
= new_color_pair(VAR_KEYLABEL_FORE_COLOR
,
2676 VAR_KEYLABEL_BACK_COLOR
);
2677 if (!pico_is_good_colorpair(colors
->klcp
))
2678 free_color_pair(&colors
->klcp
);
2680 else colors
->klcp
= NULL
;
2682 if (colors
->klcp
&& VAR_KEYNAME_FORE_COLOR
&& VAR_KEYNAME_BACK_COLOR
){
2683 colors
->kncp
= new_color_pair(VAR_KEYNAME_FORE_COLOR
,
2684 VAR_KEYNAME_BACK_COLOR
);
2686 else colors
->kncp
= NULL
;
2688 if (VAR_STATUS_FORE_COLOR
&& VAR_STATUS_BACK_COLOR
){
2689 colors
->stcp
= new_color_pair(VAR_STATUS_FORE_COLOR
,
2690 VAR_STATUS_BACK_COLOR
);
2692 else colors
->stcp
= NULL
;
2694 if (VAR_PROMPT_FORE_COLOR
&& VAR_PROMPT_BACK_COLOR
){
2695 colors
->prcp
= new_color_pair(VAR_PROMPT_FORE_COLOR
,
2696 VAR_PROMPT_BACK_COLOR
);
2698 else colors
->prcp
= NULL
;
2700 if (VAR_QUOTE1_FORE_COLOR
&& VAR_QUOTE1_BACK_COLOR
){
2701 colors
->qlcp
= new_color_pair(VAR_QUOTE1_FORE_COLOR
,
2702 VAR_QUOTE1_BACK_COLOR
);
2704 else colors
->qlcp
= NULL
;
2706 if (VAR_QUOTE2_FORE_COLOR
&& VAR_QUOTE2_BACK_COLOR
){
2707 colors
->qllcp
= new_color_pair(VAR_QUOTE2_FORE_COLOR
,
2708 VAR_QUOTE2_BACK_COLOR
);
2710 else colors
->qllcp
= NULL
;
2712 if (VAR_QUOTE3_FORE_COLOR
&& VAR_QUOTE3_BACK_COLOR
){
2713 colors
->qlllcp
= new_color_pair(VAR_QUOTE3_FORE_COLOR
,
2714 VAR_QUOTE3_BACK_COLOR
);
2716 else colors
->qlllcp
= NULL
;
2718 if (VAR_NORM_FORE_COLOR
&& VAR_NORM_BACK_COLOR
){
2719 colors
->ntcp
= new_color_pair(VAR_NORM_FORE_COLOR
,
2720 VAR_NORM_BACK_COLOR
);
2722 else colors
->ntcp
= NULL
;
2724 if (VAR_REV_FORE_COLOR
&& VAR_REV_BACK_COLOR
){
2725 colors
->rtcp
= new_color_pair(VAR_REV_FORE_COLOR
,
2726 VAR_REV_BACK_COLOR
);
2728 else colors
->rtcp
= NULL
;
2730 if (VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
){
2731 colors
->sbcp
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
2732 VAR_SIGNATURE_BACK_COLOR
);
2734 else colors
->sbcp
= NULL
;
2742 free_pcolors(PCOLORS
**colors
)
2745 if ((*colors
)->tbcp
)
2746 free_color_pair(&(*colors
)->tbcp
);
2747 if ((*colors
)->kncp
)
2748 free_color_pair(&(*colors
)->kncp
);
2749 if ((*colors
)->klcp
)
2750 free_color_pair(&(*colors
)->klcp
);
2751 if ((*colors
)->stcp
)
2752 free_color_pair(&(*colors
)->stcp
);
2753 if ((*colors
)->prcp
)
2754 free_color_pair(&(*colors
)->prcp
);
2755 if ((*colors
)->qlcp
)
2756 free_color_pair(&(*colors
)->qlcp
);
2757 if ((*colors
)->qllcp
)
2758 free_color_pair(&(*colors
)->qllcp
);
2759 if ((*colors
)->qlllcp
)
2760 free_color_pair(&(*colors
)->qlllcp
);
2761 if ((*colors
)->ntcp
)
2762 free_color_pair(&(*colors
)->ntcp
);
2763 if ((*colors
)->rtcp
)
2764 free_color_pair(&(*colors
)->rtcp
);
2765 if ((*colors
)->sbcp
)
2766 free_color_pair(&(*colors
)->sbcp
);
2767 fs_give((void **)colors
);