1
#if !defined(lint) && !defined(DOS)
2
static char rcsid[] = "$Id: reply.c 394 2007-01-25 20:29:45Z hubert@u.washington.edu $";
6
* ========================================================================
7
* Copyright 2006-2007 University of Washington
9
* Licensed under the Apache License, Version 2.0 (the "License");
10
* you may not use this file except in compliance with the License.
11
* You may obtain a copy of the License at
13
* http://www.apache.org/licenses/LICENSE-2.0
15
* ========================================================================
18
/*======================================================================
19
Code here for forward and reply to mail
20
A few support routines as well
22
This code will forward and reply to MIME messages. The Alpine composer
23
at this time will only support non-text segments at the end of a
24
message so, things don't always come out as one would like. If you
25
always forward a message in MIME format, all will be correct. Forwarding
26
of nested MULTIPART messages will work. There's still a problem with
27
MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
28
the equivalent parts. Ideally, we should probably such segments as a
29
single attachment when forwarding/replying. It would also be real nice to
30
flatten out the nesting in the composer so pieces inside can get snipped.
32
The evolution continues...
47
#include "../pith/state.h"
48
#include "../pith/conf.h"
49
#include "../pith/init.h"
50
#include "../pith/filter.h"
51
#include "../pith/pattern.h"
52
#include "../pith/charset.h"
53
#include "../pith/rfc2231.h"
54
#include "../pith/remote.h"
55
#include "../pith/news.h"
56
#include "../pith/util.h"
57
#include "../pith/detoken.h"
58
#include "../pith/newmail.h"
59
#include "../pith/readfile.h"
65
int reply_poster_followup(ENVELOPE *);
66
char *sigedit_exit_for_pico(struct headerentry *, void (*)(void), int);
67
long new_mail_for_pico(int, int);
68
void cmd_input_for_pico(void);
69
int display_message_for_pico(int);
70
char *checkpoint_dir_for_pico(char *, size_t);
71
void resize_for_pico(void);
72
PCOLORS *colors_for_pico(void);
73
void free_pcolors(PCOLORS **);
76
* standard type of storage object used for body parts...
78
#if defined(DOS) && !defined(WIN32)
79
#define PART_SO_TYPE TmpFileStar
81
#define PART_SO_TYPE CharStar
85
/*----------------------------------------------------------------------
86
Fill in an outgoing message for reply and pass off to send
88
Args: pine_state -- The usual pine structure
90
Result: Reply is formatted and passed off to composer/mailer
94
- put senders address in To field
95
- search to and cc fields to see if we aren't the only recipients
96
- if other than us, ask if we should reply to all.
97
- if answer yes, fill out the To and Cc fields
98
- fill in the fcc argument
99
- fill in the subject field
100
- fill out the body and the attachments
101
- pass off to pine_send()
104
reply(struct pine *pine_state, ACTION_S *role_arg)
106
ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
107
ENVELOPE *env, *outgoing;
108
BODY *body, *orig_body = NULL;
110
void *msgtext = NULL;
111
char *tmpfix = NULL, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
112
long msgno, j, totalm, rflags, *seq = NULL;
113
int i, include_text = 0, times = -1, warned = 0, rv = 0,
114
flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
117
REDRAFT_POS_S *redraft_pos = NULL;
118
ACTION_S *role = NULL, *nrole;
119
#if defined(DOS) && !defined(_WINDOWS)
123
outgoing = mail_newenvelope();
124
totalm = mn_total_cur(pine_state->msgmap);
125
seq = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));
127
dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm)));
129
saved_from = (ADDRESS *) NULL;
130
saved_to = (ADDRESS *) NULL;
131
saved_cc = (ADDRESS *) NULL;
132
saved_resent = (ADDRESS *) NULL;
133
outgoing->subject = NULL;
135
memset((void *)&reply, 0, sizeof(reply));
137
if(ps_global->full_header == 2
138
&& F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
142
* We may have to loop through first to figure out what default
143
* reply-indent-string to offer...
145
if(mn_total_cur(pine_state->msgmap) > 1 &&
146
F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state) &&
147
reply_quote_str_contains_tokens()){
148
for(msgno = mn_first_cur(pine_state->msgmap);
149
msgno > 0L && !tmpfix;
150
msgno = mn_next_cur(pine_state->msgmap)){
152
env = pine_mail_fetchstructure(pine_state->mail_stream,
153
mn_m2raw(pine_state->msgmap, msgno),
156
q_status_message1(SM_ORDER,3,4,
157
_("Error fetching message %s. Can't reply to it."),
162
if(!tmpfix){ /* look for prefix? */
163
tmpfix = reply_quote_str(env);
165
i = strcmp(tmpfix, prefix);
166
fs_give((void **) &tmpfix);
167
if(i){ /* don't check back if dissimilar */
168
fs_give((void **) &prefix);
170
* We free prefix, not tmpfix. We set tmpfix to prefix
171
* so that we won't come check again.
173
tmpfix = prefix = cpystr("> ");
178
tmpfix = NULL; /* check back later? */
187
* Loop thru the selected messages building the
188
* outgoing envelope's destinations...
190
for(msgno = mn_first_cur(pine_state->msgmap);
192
msgno = mn_next_cur(pine_state->msgmap)){
194
/*--- Grab current envelope ---*/
195
env = pine_mail_fetchstructure(pine_state->mail_stream,
196
seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
199
q_status_message1(SM_ORDER,3,4,
200
_("Error fetching message %s. Can't reply to it."),
206
* We check for the prefix here if we didn't do it in the first
207
* loop above. This is just to save having to go through the loop
208
* twice in the cases where we don't need to.
211
tmpfix = reply_quote_str(env);
213
i = strcmp(tmpfix, prefix);
214
fs_give((void **) &tmpfix);
215
if(i){ /* don't check back if dissimilar */
216
fs_give((void **) &prefix);
217
tmpfix = prefix = cpystr("> ");
222
tmpfix = NULL; /* check back later? */
227
* For consistency, the first question is always "include text?"
229
if(!times){ /* only first time */
230
char *p = cpystr(prefix);
232
if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0)
236
if(strcmp(p, prefix))
237
tmpfix = prefix; /* stop looking */
239
fs_give((void **)&p);
243
* If we're agg-replying or there's a newsgroup and the user want's
244
* to post to news *and* via email, add relevant addresses to the
245
* outgoing envelope...
247
* The single message case gets us around the aggregate reply
248
* to messages in a mixed mail-news archive where some might
249
* have newsgroups and others not or whatever.
251
if(totalm > 1L || ((i = reply_news_test(env, outgoing)) & 1)){
253
flags |= RSF_FORCE_REPLY_TO;
255
if(!reply_harvest(pine_state, seq[times], NULL, env,
256
&saved_from, &saved_to, &saved_cc,
257
&saved_resent, &flags))
263
/*------------ Format the subject line ---------------*/
264
if(outgoing->subject){
266
* if reply to more than one message, and all subjects
267
* match, so be it. otherwise set it to something generic...
269
if(strucmp(outgoing->subject,
270
reply_subject(env->subject,tmp_20k_buf,SIZEOF_20KBUF))){
271
fs_give((void **)&outgoing->subject);
272
outgoing->subject = cpystr("Re: several messages");
276
outgoing->subject = reply_subject(env->subject, NULL, 0);
279
/* fill reply header */
280
reply_seed(pine_state, outgoing, env, saved_from,
281
saved_to, saved_cc, saved_resent,
282
&fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
285
q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
286
display_message(NO_OP_COMMAND);
289
fs_give((void **)&errmsg);
292
if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */
295
/* Setup possible role */
297
role = copy_action(role_arg);
301
if(nonempty_patterns(rflags, &dummy)){
302
/* setup default role */
304
j = mn_first_cur(pine_state->msgmap);
307
nrole = set_role_from_msg(pine_state, rflags,
308
mn_m2raw(pine_state->msgmap, j),
310
} while(nrole && (!role || nrole == role)
311
&& (j=mn_next_cur(pine_state->msgmap)) > 0L);
313
if(!role || nrole == role)
318
if(confirm_role(rflags, &role))
319
role = combine_inherited_role(role);
320
else{ /* cancel reply */
322
cmd_cancelled("Reply");
329
* Reply_seed may call c-client in get_fcc_based_on_to, so env may
330
* no longer be valid. Get it again.
331
* Similarly for set_role_from_message.
333
env = pine_mail_fetchstructure(pine_state->mail_stream, seq[times], NULL);
336
q_status_message1(SM_ORDER, 3, 4,
337
_("Replying using role \"%s\""), role->nick);
339
/* override fcc gotten in reply_seed */
341
fs_give((void **) &fcc);
344
seq[++times] = -1L; /* mark end of sequence list */
346
/*========== Other miscelaneous fields ===================*/
347
outgoing->in_reply_to = reply_in_reply_to(env);
348
outgoing->references = reply_build_refs(env);
349
outgoing->message_id = generate_message_id();
354
!outgoing->newsgroups)
355
q_status_message(SM_ORDER | SM_DING, 3, 6,
356
_("Warning: no valid addresses to reply to!"));
359
/*==================== Now fix up the message body ====================*/
362
* create storage object to be used for message text
364
if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
365
q_status_message(SM_ORDER | SM_DING, 3, 4,
366
_("Error allocating message text"));
370
gf_set_so_writec(&pc, (STORE_S *) msgtext);
372
/*---- Include the original text if requested ----*/
373
if(include_text && totalm > 1L){
375
int impl, template_len = 0, leave_cursor_at_top = 0;
379
if(role && role->template){
383
filtered = detoken(role, env, 0,
384
F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
385
0, &redraft_pos, &impl);
388
so_puts((STORE_S *)msgtext, filtered);
390
template_len = strlen(filtered);
392
leave_cursor_at_top++;
395
fs_give((void **)&filtered);
403
if((sig = reply_signature(role, env, &redraft_pos, &impl)) &&
404
F_OFF(F_SIG_AT_BOTTOM, ps_global)){
407
* If CURSORPOS was set explicitly in sig_file, and there was a
408
* template file before that, we need to adjust the offset by the
409
* length of the template file. However, if the template had
410
* a set CURSORPOS in it then impl was 2 before getting to the
411
* signature, so offset wouldn't have been reset by the signature
412
* CURSORPOS and offset would already be correct. That case will
413
* be ok here because template_len will be 0 and adding it does
414
* nothing. If template
415
* didn't have CURSORPOS in it, then impl was 1 and got set to 2
416
* by the CURSORPOS in the sig. In that case we have to adjust the
417
* offset. That's what the next line does. It adjusts it if
418
* template_len is nonzero and if CURSORPOS was set in sig_file.
421
redraft_pos->offset += template_len;
424
so_puts((STORE_S *)msgtext, sig);
427
* Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
428
* is set, we won't have used it yet and want it to be non-NULL.
430
fs_give((void **)&sig);
434
* Only put cursor in sig if there is a cursorpos there but not
435
* one in the template, and sig-at-bottom.
437
if(!(sig && impl == 2 && !leave_cursor_at_top))
438
leave_cursor_at_top++;
440
body = mail_newbody();
441
body->type = TYPETEXT;
442
body->contents.text.data = msgtext;
444
for(msgno = mn_first_cur(pine_state->msgmap);
446
msgno = mn_next_cur(pine_state->msgmap)){
448
if(env){ /* put 2 between messages */
449
gf_puts(NEWLINE, pc);
450
gf_puts(NEWLINE, pc);
453
/*--- Grab current envelope ---*/
454
env = pine_mail_fetchstructure(pine_state->mail_stream,
455
mn_m2raw(pine_state->msgmap, msgno),
458
q_status_message1(SM_ORDER,3,4,
459
_("Error fetching message %s. Can't reply to it."),
460
long2string(mn_get_cur(pine_state->msgmap)));
464
if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) {
465
reply_delimiter(env, role, pc);
466
if(F_ON(F_INCLUDE_HEADER, pine_state))
467
reply_forward_header(pine_state->mail_stream,
468
mn_m2raw(pine_state->msgmap,msgno),
469
NULL, env, pc, prefix);
471
get_body_part_text(pine_state->mail_stream, reply_raw_body ? NULL : orig_body,
472
mn_m2raw(pine_state->msgmap, msgno),
473
reply_raw_body ? NULL : "1", 0L, pc, prefix, NULL);
475
else if(orig_body->type == TYPEMULTIPART) {
477
q_status_message(SM_ORDER,3,7,
478
_("WARNING! Attachments not included in multiple reply."));
480
if(orig_body->nested.part
481
&& orig_body->nested.part->body.type == TYPETEXT) {
482
/*---- First part of the message is text -----*/
483
reply_delimiter(env, role, pc);
484
if(F_ON(F_INCLUDE_HEADER, pine_state))
485
reply_forward_header(pine_state->mail_stream,
486
mn_m2raw(pine_state->msgmap,
488
NULL, env, pc, prefix);
490
get_body_part_text(pine_state->mail_stream,
491
&orig_body->nested.part->body,
492
mn_m2raw(pine_state->msgmap, msgno),
493
"1", 0L, pc, prefix, NULL);
496
q_status_message(SM_ORDER,0,3,
497
_("Multipart with no leading text part."));
501
/*---- Single non-text message of some sort ----*/
502
q_status_message(SM_ORDER,3,3,
503
_("Non-text message not included."));
507
if(!leave_cursor_at_top){
511
/* rewind and count chars to start of sig file */
512
so_seek((STORE_S *)msgtext, 0L, 0);
513
while(so_readc(&c, (STORE_S *)msgtext))
517
redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(*redraft_pos));
518
memset((void *)redraft_pos, 0,sizeof(*redraft_pos));
519
redraft_pos->hdrname = cpystr(":");
523
* If explicit cursor positioning in sig file,
524
* add offset to start of sig file plus offset into sig file.
525
* Else, just offset to start of sig file.
527
redraft_pos->offset += cnt;
532
so_puts((STORE_S *)msgtext, sig);
534
fs_give((void **)&sig);
538
msgno = mn_m2raw(pine_state->msgmap,
539
mn_get_cur(pine_state->msgmap));
541
/*--- Grab current envelope ---*/
542
env = pine_mail_fetchstructure(pine_state->mail_stream, msgno,
546
* If the charset of the body part is different from ascii and
547
* charset conversion is _not_ happening, then preserve the original
548
* charset from the message so that if we don't enter any new
549
* chars with the hibit set we can use the original charset.
550
* If not all those things, then don't try to preserve it.
555
charset = rfc2231_get_param(orig_body->parameter,
556
"charset", NULL, NULL);
557
if(charset && strucmp(charset, "us-ascii") != 0){
561
* There is a non-ascii charset, is there conversion happening?
563
if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
564
reply.orig_charset = charset;
570
fs_give((void **) &charset);
574
if(!(body = reply_body(pine_state->mail_stream, env, orig_body,
575
msgno, NULL, msgtext, prefix,
576
include_text, role, 1, &redraft_pos)))
580
q_status_message1(SM_ORDER,3,4,
581
_("Error fetching message %s. Can't reply to it."),
582
long2string(mn_get_cur(pine_state->msgmap)));
587
/* fill in reply structure */
588
reply.prefix = prefix;
589
reply.mailbox = cpystr(pine_state->mail_stream->mailbox);
590
reply.origmbox = cpystr(pine_state->mail_stream->original_mailbox
591
? pine_state->mail_stream->original_mailbox
592
: pine_state->mail_stream->mailbox);
593
reply.data.uid.msgs = (imapuid_t *) fs_get((times + 1) * sizeof(imapuid_t));
594
if(reply.data.uid.validity = pine_state->mail_stream->uid_validity){
595
reply.flags = REPLY_UID;
596
for(i = 0; i < times ; i++)
597
reply.data.uid.msgs[i] = mail_uid(pine_state->mail_stream, seq[i]);
600
reply.flags = REPLY_MSGNO;
601
for(i = 0; i < times ; i++)
602
reply.data.uid.msgs[i] = seq[i];
605
reply.data.uid.msgs[i] = 0; /* tie off list */
607
#if defined(DOS) && !defined(_WINDOWS)
608
free((void *)reserve);
611
/* partially formatted outgoing message */
612
pine_send(outgoing, &body, _("COMPOSE MESSAGE REPLY"),
613
role, fcc, &reply, redraft_pos, NULL, NULL, 0);
616
pine_free_body(&body);
618
fs_give((void **) &reply.mailbox);
620
fs_give((void **) &reply.origmbox);
621
if(reply.orig_charset)
622
fs_give((void **) &reply.orig_charset);
623
fs_give((void **) &reply.data.uid.msgs);
625
if((STORE_S *) msgtext)
626
gf_clear_so_writec((STORE_S *) msgtext);
628
mail_free_envelope(&outgoing);
629
mail_free_address(&saved_from);
630
mail_free_address(&saved_to);
631
mail_free_address(&saved_cc);
632
mail_free_address(&saved_resent);
634
fs_give((void **)&seq);
637
fs_give((void **)&prefix);
640
fs_give((void **) &fcc);
642
free_redraft_pos(&redraft_pos);
649
* Ask user to confirm role choice, or choose another role.
651
* Args role -- A pointer into the pattern_h space at the default
652
* role to use. This can't be a copy, the comparison
653
* relies on it pointing at the actual role.
654
* This arg is also used to return a pointer to the
657
* Returns 1 -- Yes, use role which is now in *role. This may not be
658
* the same as the role passed in and it may even be NULL.
662
confirm_role(long int rflags, ACTION_S **role)
664
ACTION_S *role_p = NULL;
665
char prompt[80], *prompt_fodder;
666
int cmd, done, ret = 1;
667
void (*prev_screen)(struct pine *) = ps_global->prev_screen,
668
(*redraw)(void) = ps_global->redrawer;
674
if(!nonempty_patterns(ROLE_DO_ROLES, &pstate) || !role)
678
* If this is a reply or forward and the role doesn't require confirmation,
679
* then we just return with what was passed in.
681
if(((rflags & ROLE_REPLY) &&
682
*role && (*role)->repl_type == ROLE_REPL_NOCONF) ||
683
((rflags & ROLE_FORWARD) &&
684
*role && (*role)->forw_type == ROLE_FORW_NOCONF) ||
685
((rflags & ROLE_COMPOSE) &&
686
*role && (*role)->comp_type == ROLE_COMP_NOCONF) ||
687
(!*role && F_OFF(F_ROLE_CONFIRM_DEFAULT, ps_global) &&
688
!(rflags & ROLE_DEFAULTOK)))
692
* Check that there is at least one role available. This is among all
693
* roles, not just the reply roles or just the forward roles. That's
694
* because we have ^T take us to all the roles, not the category-specific
697
if(!(pat = last_pattern(&pstate)))
706
ekey[2].ch = ctrl('T');
712
/* check for more than one role available (or no role set) */
713
if(pat == first_pattern(&pstate) && *role) /* no ^T */
719
for(pat = first_pattern(&pstate);
721
pat = next_pattern(&pstate)){
722
if(pat->action == *role){
729
if(rflags & ROLE_REPLY)
730
prompt_fodder = _("Reply");
731
else if(rflags & ROLE_FORWARD)
732
prompt_fodder = _("Forward");
734
prompt_fodder = _("Compose");
741
help = h_role_confirm;
743
ekey[0].label = N_("Yes");
745
ekey[1].label = N_("No, use default settings");
746
ekey[2].label = N_("To Select Alternate Role");
747
if(curpat->patgrp && curpat->patgrp->nick)
748
/* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
749
snprintf(prompt, sizeof(prompt), _("Use role \"%s\" for %s? "),
750
short_str(curpat->patgrp->nick, buf, sizeof(buf), 50, MidDots),
753
snprintf(prompt, sizeof(prompt),
754
_("Use role \"<a role without a nickname>\" for %s? "),
758
help = h_norole_confirm;
759
ekey[0].name = "Ret";
760
ekey[0].label = prompt_fodder;
763
ekey[2].label = N_("To Select Role");
764
snprintf(prompt, sizeof(prompt),
765
_("Press Return to %s using no role, or ^T to select a role "),
769
prompt[sizeof(prompt)-1] = '\0';
771
cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
772
'y', 'x', help, RB_NORM);
775
case 'y': /* Accept */
777
*role = curpat ? curpat->action : NULL;
780
case 'x': /* Cancel */
784
case 'n': /* NoRole */
790
if(role_select_screen(ps_global, &role_p, 0) >= 0){
792
for(pat = first_pattern(&pstate);
794
pat = next_pattern(&pstate)){
795
if(pat->action == role_p){
806
ps_global->mangled_body = 1;
807
ps_global->prev_screen = prev_screen;
808
ps_global->redrawer = redraw;
818
* reply_to_all_query - Ask user about replying to all recipients
820
* Returns: -1 if cancel, 0 otherwise
821
* by reference: flagp
824
reply_to_all_query(int *flagp)
826
switch(want_to("Reply to all recipients",
827
'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)){
831
case 'y' : /* set reply-all bit */
832
(*flagp) |= RSF_FORCE_REPLY_ALL;
835
case 'n' : /* clear reply-all bit */
836
(*flagp) &= ~RSF_FORCE_REPLY_ALL;
845
* reply_using_replyto_query - Ask user about replying with reply-to value
847
* Returns: 'y' if yes
851
reply_using_replyto_query(void)
853
return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
854
'y', 'x', NO_HELP,WT_SEQ_SENSITIVE));
859
* reply_text_query - Ask user about replying with text...
861
* Returns: 1 if include the text
862
* 0 if we're NOT to include the text
863
* -1 on cancel or error
866
reply_text_query(struct pine *ps, long int many, char **prefix)
869
static ESCKEY_S rtq_opts[] = {
870
{'y', 'y', "Y", N_("Yes")},
871
{'n', 'n', "N", N_("No")},
872
{-1, 0, NULL, NULL}, /* may be overridden below */
876
if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)
877
&& F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps))
882
/* TRANSLATORS: The final three %s's can probably be safely ignored */
883
snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "),
885
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
886
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
887
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
889
snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "),
890
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
891
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
892
F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
894
if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
895
rtq_opts[2].ch = ctrl('R');
896
rtq_opts[2].rval = 'r';
897
rtq_opts[2].name = "^R";
898
rtq_opts[2].label = N_("Edit Indent String");
903
switch(ret = radio_buttons(tmp_20k_buf,
904
ps->ttyo->screen_rows > 4
905
? -FOOTER_ROWS(ps_global) : -1,
907
(edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps))
909
'x', NO_HELP, RB_SEQ_SENSITIVE)){
911
cmd_cancelled("Reply");
915
if(prefix && *prefix){
921
strncpy(buf, *prefix, sizeof(buf)-1);
922
buf[sizeof(buf)-1] = '\0';
924
flags = OE_APPEND_CURRENT |
925
OE_KEEP_TRAILING_SPACE |
929
switch(optionally_enter(buf, ps->ttyo->screen_rows > 4
930
? -FOOTER_ROWS(ps_global) : -1,
931
0, sizeof(buf), "Reply prefix : ",
932
NULL, NO_HELP, &flags)){
933
case 0: /* entry successful, continue */
934
if(flags & OE_USER_MODIFIED){
935
fs_give((void **)prefix);
936
*prefix = removing_quotes(cpystr(buf));
944
cmd_cancelled("Reply");
953
if(ps_global->redrawer != NULL)
954
(*ps_global->redrawer)();
964
q_status_message(SM_ORDER, 3, 4,
965
"Programmer botch in reply_text_query()");
980
q_status_message1(SM_ORDER, 3, 4,
981
"Invalid rval \'%s\'", pretty_command(ret));
989
* reply_poster_followup - return TRUE if "followup-to" set to "poster"
991
* NOTE: queues status message indicating such
994
reply_poster_followup(ENVELOPE *e)
996
if(e && e->followup_to && !strucmp(e->followup_to, "poster")){
997
q_status_message(SM_ORDER, 2, 3,
998
_("Replying to Poster as specified in \"Followup-To\""));
1007
* reply_news_test - Test given envelope for newsgroup data and copy
1008
* it at the users request
1010
* 0 if error or cancel
1012
* 2 follow-up via news
1016
reply_news_test(ENVELOPE *env, ENVELOPE *outgoing)
1019
static ESCKEY_S news_opt[] = { {'f', 'f', "F", N_("Follow-up")},
1020
{'r', 'r', "R", N_("Reply")},
1021
{'b', 'b', "B", N_("Both")},
1022
{-1, 0, NULL, NULL} };
1024
if(env->newsgroups && *env->newsgroups && !reply_poster_followup(env))
1026
* Now that we know a newsgroups field is present,
1027
* ask if the user is posting a follow-up article...
1029
switch(radio_buttons(
1030
_("Follow-up to news group(s), Reply via email to author or Both? "),
1031
-FOOTER_ROWS(ps_global), news_opt, 'r', 'x',
1033
case 'r' : /* Reply */
1037
case 'f' : /* Follow-Up via news ONLY! */
1041
case 'b' : /* BOTH */
1045
case 'x' : /* cancel or unknown response */
1047
cmd_cancelled("Reply");
1053
if(env->followup_to){
1054
q_status_message(SM_ORDER, 2, 3,
1055
_("Posting to specified Followup-To groups"));
1056
outgoing->newsgroups = cpystr(env->followup_to);
1058
else if(!outgoing->newsgroups)
1059
outgoing->newsgroups = cpystr(env->newsgroups);
1060
if(!IS_NEWS(ps_global->mail_stream))
1061
q_status_message(SM_ORDER, 2, 3,
1062
_("Replying to message that MAY or MAY NOT have been posted to newsgroup"));
1069
/*----------------------------------------------------------------------
1070
Acquire the pinerc defined signature file
1071
It is allocated here and freed by the caller.
1073
file -- use this file
1074
prenewlines -- prefix the file contents with this many newlines
1075
postnewlines -- postfix the file contents with this many newlines
1076
is_sig -- this is a signature (not a template)
1079
get_signature_file(char *file, int prenewlines, int postnewlines, int is_sig)
1081
char *sig, *tmp_sig = NULL, sig_path[MAXPATH+1];
1082
int len, do_the_pipe_thang = 0;
1083
long sigsize = 0L, cntdown;
1086
if(!signature_path(file, sig_path, MAXPATH))
1089
dprint((5, "get_signature(%s)\n", sig_path));
1091
if(sig_path[(len=strlen(sig_path))-1] == '|'){
1092
if(is_sig && F_ON(F_DISABLE_PIPES_IN_SIGS, ps_global)){
1093
q_status_message(SM_ORDER | SM_DING, 3, 4,
1094
_("Pipes for signatures are administratively disabled"));
1097
else if(!is_sig && F_ON(F_DISABLE_PIPES_IN_TEMPLATES, ps_global)){
1098
q_status_message(SM_ORDER | SM_DING, 3, 4,
1099
_("Pipes for templates are administratively disabled"));
1103
sig_path[len-1] = '\0';
1104
removing_trailing_white_space(sig_path);
1105
do_the_pipe_thang++;
1108
if(!IS_REMOTE(sig_path) && ps_global->VAR_OPER_DIR &&
1109
!in_dir(ps_global->VAR_OPER_DIR, sig_path)){
1110
q_status_message2(SM_ORDER | SM_DING, 3, 4,
1111
/* TRANSLATORS: First arg is the directory name, second is
1112
the file user wants to read but can't. */
1113
_("Can't read file outside %s: %s"),
1114
ps_global->VAR_OPER_DIR, file);
1119
if(IS_REMOTE(sig_path) || can_access(sig_path, ACCESS_EXISTS) == 0){
1120
if(do_the_pipe_thang){
1121
if(can_access(sig_path, EXECUTE_ACCESS) == 0){
1128
if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1130
flags = PIPE_READ | PIPE_STDERR | PIPE_NOSHELL;
1134
if(syspipe = open_system_pipe(sig_path, NULL, NULL, flags, 5,
1135
pipe_callback, pipe_report_error)){
1139
gf_set_so_writec(&pc, store);
1140
gf_set_readc(&gc, (void *)syspipe, 0, PipeStar);
1143
if((error = gf_pipe(gc, pc)) != NULL){
1144
(void)close_system_pipe(&syspipe, NULL, NULL);
1145
gf_clear_so_writec(store);
1147
q_status_message1(SM_ORDER | SM_DING, 3, 4,
1148
_("Can't get file: %s"), error);
1152
if(close_system_pipe(&syspipe, NULL, NULL)){
1156
q_status_message2(SM_ORDER, 3, 4,
1157
_("Error running program \"%s\"%s"),
1159
(now - start > 4) ? ": timed out" : "");
1162
gf_clear_so_writec(store);
1164
/* rewind and count chars */
1165
so_seek(store, 0L, 0);
1166
while(so_readc(&c, store) && sigsize < 100000L)
1169
/* allocate space */
1170
tmp_sig = fs_get((sigsize + 1) * sizeof(char));
1174
/* rewind and copy chars, no prenewlines... */
1175
so_seek(store, 0L, 0);
1177
while(so_readc(&c, store) && cntdown-- > 0L)
1185
q_status_message1(SM_ORDER | SM_DING, 3, 4,
1186
_("Error running program \"%s\""),
1191
q_status_message(SM_ORDER | SM_DING, 3, 4,
1192
"Error allocating space for sig or template program");
1195
q_status_message1(SM_ORDER | SM_DING, 3, 4,
1196
/* TRANSLATORS: Arg is a program name */
1197
_("Can't execute \"%s\": Permission denied"),
1200
else if((IS_REMOTE(sig_path) &&
1201
(tmp_sig = read_remote_sigfile(sig_path))) ||
1202
(tmp_sig = read_file(sig_path, READ_FROM_LOCALE)))
1203
sigsize = strlen(tmp_sig);
1205
q_status_message2(SM_ORDER | SM_DING, 3, 4,
1206
/* TRANSLATORS: First arg is error description, 2nd is
1208
_("Error \"%s\" reading file \"%s\""),
1209
error_description(errno), sig_path);
1212
sig = get_signature_lit(tmp_sig, prenewlines, postnewlines, is_sig, 0);
1214
fs_give((void **)&tmp_sig);
1221
/*----------------------------------------------------------------------
1222
Partially set up message to forward and pass off to composer/mailer
1224
Args: pine_state -- The usual pine structure
1226
Result: outgoing envelope and body created and passed off to composer/mailer
1228
Create the outgoing envelope for the mail being forwarded, which is
1229
not much more than filling in the subject, and create the message body
1230
of the outgoing message which requires formatting the header from the
1231
envelope of the original messasge.
1232
----------------------------------------------------------------------*/
1234
forward(struct pine *ps, ACTION_S *role_arg)
1237
int ret, forward_raw_body = 0, rv = 0;
1238
long msgno, j, totalmsgs, rflags;
1239
ENVELOPE *env, *outgoing;
1240
BODY *orig_body, *body = NULL;
1242
void *msgtext = NULL;
1244
int impl, template_len = 0;
1246
REDRAFT_POS_S *redraft_pos = NULL;
1247
ACTION_S *role = NULL, *nrole;
1248
#if defined(DOS) && !defined(_WINDOWS)
1252
dprint((4, "\n - forward -\n"));
1254
memset((void *)&reply, 0, sizeof(reply));
1255
outgoing = mail_newenvelope();
1256
outgoing->message_id = generate_message_id();
1258
if(ps_global->full_header == 2
1259
&& F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
1260
forward_raw_body = 1;
1262
if((totalmsgs = mn_total_cur(ps->msgmap)) > 1L){
1263
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s forwarded messages...", comatose(totalmsgs));
1264
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1265
outgoing->subject = cpystr(tmp_20k_buf);
1268
/*---------- Get the envelope of message we're forwarding ------*/
1269
msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
1270
if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL))
1271
&& (outgoing->subject = forward_subject(env, 0)))){
1272
q_status_message1(SM_ORDER,3,4,
1273
_("Error fetching message %s. Can't forward it."),
1274
long2string(msgno));
1280
* as with all text bound for the composer, build it in
1281
* a storage object of the type it understands...
1283
if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
1284
q_status_message(SM_ORDER | SM_DING, 3, 4,
1285
_("Error allocating message text"));
1289
ret = (totalmsgs > 1L)
1290
? want_to(_("Forward messages as a MIME digest"), 'y', 'x',
1291
NO_HELP, WT_SEQ_SENSITIVE)
1292
: (ps->full_header == 2)
1293
? want_to(_("Forward message as an attachment"), 'n', 'x',
1294
NO_HELP, WT_SEQ_SENSITIVE)
1298
cmd_cancelled("Forward");
1299
so_give((STORE_S **)&msgtext);
1303
/* Setup possible role */
1305
role = copy_action(role_arg);
1308
rflags = ROLE_FORWARD;
1309
if(nonempty_patterns(rflags, &dummy)){
1310
/* setup default role */
1312
j = mn_first_cur(ps->msgmap);
1315
nrole = set_role_from_msg(ps, rflags,
1316
mn_m2raw(ps->msgmap, j), NULL);
1317
} while(nrole && (!role || nrole == role)
1318
&& (j=mn_next_cur(ps->msgmap)) > 0L);
1320
if(!role || nrole == role)
1325
if(confirm_role(rflags, &role))
1326
role = combine_inherited_role(role);
1327
else{ /* cancel reply */
1329
cmd_cancelled("Forward");
1330
so_give((STORE_S **)&msgtext);
1337
q_status_message1(SM_ORDER, 3, 4,
1338
_("Forwarding using role \"%s\""), role->nick);
1340
if(role && role->template){
1344
filtered = detoken(role, (totalmsgs == 1L) ? env : NULL,
1345
0, 0, 0, &redraft_pos, &impl);
1348
so_puts((STORE_S *)msgtext, filtered);
1350
template_len = strlen(filtered);
1353
fs_give((void **)&filtered);
1359
if(sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)){
1361
redraft_pos->offset += template_len;
1363
so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
1365
fs_give((void **)&sig);
1368
so_puts((STORE_S *)msgtext, NEWLINE);
1370
gf_set_so_writec(&pc, (STORE_S *)msgtext);
1372
#if defined(DOS) && !defined(_WINDOWS)
1373
#if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1374
#define IN_RESERVE 8192
1376
#define IN_RESERVE 16384
1378
if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
1379
gf_clear_so_writec((STORE_S *) msgtext);
1380
so_give((STORE_S **)&msgtext);
1381
q_status_message(SM_ORDER | SM_DING, 3, 4,
1382
_("Insufficient memory for message text"));
1388
* If we're forwarding multiple messages *or* the forward-as-mime
1389
* is turned on and the users wants it done that way, package things
1392
if(ret == 'y'){ /* attach message[s]!!! */
1394
long totalsize = 0L;
1396
/*---- New Body to start with ----*/
1397
body = mail_newbody();
1398
body->type = TYPEMULTIPART;
1400
/*---- The TEXT part/body ----*/
1401
body->nested.part = mail_newbody_part();
1402
body->nested.part->body.type = TYPETEXT;
1403
body->nested.part->body.contents.text.data = msgtext;
1406
/*---- The MULTIPART/DIGEST part ----*/
1407
body->nested.part->next = mail_newbody_part();
1408
body->nested.part->next->body.type = TYPEMULTIPART;
1409
body->nested.part->next->body.subtype = cpystr("Digest");
1410
snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Digest of %s messages", comatose(totalmsgs));
1411
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1412
body->nested.part->next->body.description = cpystr(tmp_20k_buf);
1413
pp = &(body->nested.part->next->body.nested.part);
1416
pp = &(body->nested.part->next);
1418
/*---- The Message body subparts ----*/
1419
for(msgno = mn_first_cur(ps->msgmap);
1421
msgno = mn_next_cur(ps->msgmap)){
1423
msgno = mn_m2raw(ps->msgmap, msgno);
1424
env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL);
1426
if(forward_mime_msg(ps->mail_stream,msgno,NULL,env,pp,msgtext)){
1427
totalsize += (*pp)->body.size.bytes;
1428
pp = &((*pp)->next);
1435
body->nested.part->next->body.size.bytes = totalsize;
1437
else if(totalmsgs > 1L){
1439
body = mail_newbody();
1440
body->type = TYPETEXT;
1441
body->contents.text.data = msgtext;
1444
for(msgno = mn_first_cur(ps->msgmap);
1446
msgno = mn_next_cur(ps->msgmap)){
1448
if(env){ /* put 2 between messages */
1449
gf_puts(NEWLINE, pc);
1450
gf_puts(NEWLINE, pc);
1453
/*--- Grab current envelope ---*/
1454
env = pine_mail_fetchstructure(ps->mail_stream,
1455
mn_m2raw(ps->msgmap, msgno),
1457
if(!env || !orig_body){
1458
q_status_message1(SM_ORDER,3,4,
1459
_("Error fetching message %s. Can't forward it."),
1460
long2string(msgno));
1464
if(orig_body == NULL || orig_body->type == TYPETEXT || forward_raw_body) {
1465
forward_delimiter(pc);
1466
reply_forward_header(ps->mail_stream,
1467
mn_m2raw(ps->msgmap, msgno),
1470
if(!get_body_part_text(ps->mail_stream, forward_raw_body ? NULL : orig_body,
1471
mn_m2raw(ps->msgmap, msgno),
1472
forward_raw_body ? NULL : "1", 0L, pc, NULL, NULL))
1474
} else if(orig_body->type == TYPEMULTIPART) {
1476
q_status_message(SM_ORDER,3,7,
1477
_("WARNING! Attachments not included in multiple forward."));
1479
if(orig_body->nested.part &&
1480
orig_body->nested.part->body.type == TYPETEXT) {
1481
/*---- First part of the message is text -----*/
1482
forward_delimiter(pc);
1483
reply_forward_header(ps->mail_stream,
1484
mn_m2raw(ps->msgmap,msgno),
1487
if(!get_body_part_text(ps->mail_stream,
1488
&orig_body->nested.part->body,
1489
mn_m2raw(ps->msgmap, msgno),
1490
"1", 0L, pc, NULL, NULL))
1493
q_status_message(SM_ORDER,0,3,
1494
_("Multipart with no leading text part!"));
1497
/*---- Single non-text message of some sort ----*/
1498
q_status_message(SM_ORDER,0,3,
1499
_("Non-text message not included!"));
1503
else if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno,
1505
&& (body = forward_body(ps->mail_stream, env, orig_body, msgno,
1508
q_status_message1(SM_ORDER,3,4,
1509
_("Error fetching message %s. Can't forward it."),
1510
long2string(msgno));
1514
if(ret != 'y' && totalmsgs == 1L && orig_body){
1517
charset = rfc2231_get_param(orig_body->parameter,
1518
"charset", NULL, NULL);
1519
if(charset && strucmp(charset, "us-ascii") != 0){
1523
* There is a non-ascii charset, is there conversion happening?
1525
if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
1526
reply.orig_charset = charset;
1532
fs_give((void **) &charset);
1534
if(reply.orig_charset)
1535
reply.flags = REPLY_FORW;
1538
#if defined(DOS) && !defined(_WINDOWS)
1539
free((void *)reserve);
1541
pine_send(outgoing, &body, "FORWARD MESSAGE",
1542
role, NULL, reply.flags ? &reply : NULL, redraft_pos,
1548
pine_free_body(&body);
1550
if((STORE_S *) msgtext)
1551
gf_clear_so_writec((STORE_S *) msgtext);
1553
mail_free_envelope(&outgoing);
1554
free_redraft_pos(&redraft_pos);
1557
if(reply.orig_charset)
1558
fs_give((void **)&reply.orig_charset);
1563
#if defined(DOS) && !defined(WIN32)
1564
mail_parameters(ps->mail_stream, SET_GETS, (void *) NULL);
1566
mail_gc(ps->mail_stream, GC_TEXTS);
1568
q_status_message(SM_ORDER | SM_DING, 4, 5,
1569
_("Error fetching message contents. Can't forward message."));
1574
/*----------------------------------------------------------------------
1575
Partially set up message to forward and pass off to composer/mailer
1577
Args: pine_state -- The usual pine structure
1578
message -- The MESSAGECACHE of entry to reply to
1580
Result: outgoing envelope and body created and passed off to composer/mailer
1582
Create the outgoing envelope for the mail being forwarded, which is
1583
not much more than filling in the subject, and create the message body
1584
of the outgoing message which requires formatting the header from the
1585
envelope of the original messasge.
1586
----------------------------------------------------------------------*/
1588
forward_text(struct pine *pine_state, void *text, SourceType source)
1594
char *enc_error, *sig;
1595
ACTION_S *role = NULL;
1597
long rflags = ROLE_COMPOSE;
1599
if(msgtext = so_get(PicoText, NULL, EDIT_ACCESS)){
1600
env = mail_newenvelope();
1601
env->message_id = generate_message_id();
1602
body = mail_newbody();
1603
body->type = TYPETEXT;
1604
body->contents.text.data = (void *) msgtext;
1606
if(nonempty_patterns(rflags, &dummy)){
1608
* This is really more like Compose, even though it
1609
* is called Forward.
1611
if(confirm_role(rflags, &role))
1612
role = combine_inherited_role(role);
1614
cmd_cancelled("Composition");
1615
display_message('x');
1616
mail_free_envelope(&env);
1617
pine_free_body(&body);
1623
q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
1626
sig = detoken(role, NULL, 2, 0, 1, NULL, NULL);
1627
so_puts(msgtext, (sig && *sig) ? sig : NEWLINE);
1628
so_puts(msgtext, NEWLINE);
1629
so_puts(msgtext, "----- Included text -----");
1630
so_puts(msgtext, NEWLINE);
1632
fs_give((void **)&sig);
1635
gf_set_so_writec(&pc, msgtext);
1636
gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
1639
if((enc_error = gf_pipe(gc, pc)) == NULL){
1640
pine_send(env, &body, "SEND MESSAGE", role, NULL, NULL, NULL,
1642
pine_state->mangled_screen = 1;
1645
q_status_message1(SM_ORDER | SM_DING, 3, 5,
1646
_("Error reading text \"%s\""),enc_error);
1647
display_message('x');
1650
gf_clear_so_writec(msgtext);
1651
mail_free_envelope(&env);
1652
pine_free_body(&body);
1655
q_status_message(SM_ORDER | SM_DING, 3, 4,
1656
_("Error allocating message text"));
1657
display_message('x');
1664
/*----------------------------------------------------------------------
1665
Partially set up message to resend and pass off to mailer
1667
Args: pine_state -- The usual pine structure
1669
Result: outgoing envelope and body created and passed off to mailer
1671
Create the outgoing envelope for the mail being resent, which is
1672
not much more than filling in the subject, and create the message body
1673
of the outgoing message which requires formatting the header from the
1674
envelope of the original messasge.
1675
----------------------------------------------------------------------*/
1677
bounce(struct pine *pine_state, ACTION_S *role)
1681
char *save_to = NULL, **save_toptr = NULL, *errstr = NULL,
1682
*prmpt_who = NULL, *prmpt_cnf = NULL;
1684
dprint((4, "\n - bounce -\n"));
1686
if(mn_total_cur(pine_state->msgmap) > 1L){
1687
save_toptr = &save_to;
1688
snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %d messages to : "),
1689
mn_total_cur(pine_state->msgmap));
1690
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1691
prmpt_who = cpystr(tmp_20k_buf);
1692
snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %d messages "),
1693
mn_total_cur(pine_state->msgmap));
1694
tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1695
prmpt_cnf = cpystr(tmp_20k_buf);
1698
for(msgno = mn_first_cur(pine_state->msgmap);
1700
msgno = mn_next_cur(pine_state->msgmap)){
1702
rawno = mn_m2raw(pine_state->msgmap, msgno);
1703
if(env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL))
1704
errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role,
1705
save_toptr, env->subject, prmpt_who, prmpt_cnf);
1707
errstr = _("Can't fetch Subject for Bounce");
1712
q_status_message(SM_ORDER | SM_DING, 4, 7, errstr);
1719
fs_give((void **)&save_to);
1722
fs_give((void **) &prmpt_who);
1725
fs_give((void **) &prmpt_cnf);
1727
return(errstr ? 0 : 1);
1733
bounce_msg(MAILSTREAM *stream,
1742
char *errstr = NULL;
1748
if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){
1749
if(pine_simple_send(outgoing, &body, role, pmt_who, pmt_cnf, to,
1750
!(to && *to) ? SS_PROMPTFORTO : 0) < 0){
1751
errstr = ""; /* p_s_s() better have explained! */
1752
/* clear seen flag */
1753
if(was_seen == 0 && rawno > 0L
1754
&& stream && rawno <= stream->nmsgs
1755
&& (mc = mail_elt(stream, rawno)) && mc->seen)
1756
mail_flag(stream, long2string(rawno), "\\SEEN", 0);
1760
/* Just for good measure... */
1761
mail_free_envelope(&outgoing);
1762
pine_free_body(&body);
1764
return(errstr); /* no problem-o */
1768
/*----------------------------------------------------------------------
1769
Serve up the current signature within pico for editing
1773
Result: signature changed or not.
1776
signature_edit(char *sigfile, char *title)
1779
char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
1781
STORE_S *msgso, *tmpso = NULL;
1784
struct variable *vars = ps_global->vars;
1785
REMDATA_S *rd = NULL;
1787
if(!signature_path(sigfile, sig_path, MAXPATH))
1788
return(cpystr(_("No signature file defined.")));
1790
if(IS_REMOTE(sigfile)){
1791
rd = rd_create_remote(RemImap, sig_path, (void *)REMOTE_SIG_SUBTYPE,
1793
_("Can't access remote configuration."));
1795
return(cpystr(_("Error attempting to edit remote configuration")));
1797
(void)rd_read_metadata(rd);
1799
if(rd->access == MaybeRorW){
1800
if(rd->read_status == 'R')
1801
rd->access = ReadOnly;
1803
rd->access = ReadWrite;
1806
if(rd->access != NoExists){
1808
rd_check_remvalid(rd, 1L);
1811
* If the cached info says it is readonly but
1812
* it looks like it's been fixed now, change it to readwrite.
1814
if(rd->read_status == 'R'){
1815
rd_check_readonly_access(rd);
1816
if(rd->read_status == 'W'){
1817
rd->access = ReadWrite;
1818
rd->flags |= REM_OUTOFDATE;
1821
rd->access = ReadOnly;
1825
if(rd->flags & REM_OUTOFDATE){
1826
if(rd_update_local(rd) != 0){
1829
"signature_edit: rd_update_local failed\n"));
1830
rd_close_remdata(&rd);
1831
return(cpystr(_("Can't access remote sig")));
1837
if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1838
rd_close_remdata(&rd);
1839
return(cpystr(_("Can't get write permission for remote sig")));
1842
rd->flags |= DO_REMTRIM;
1844
strncpy(sig_path, rd->lf, sizeof(sig_path)-1);
1845
sig_path[sizeof(sig_path)-1] = '\0';
1848
standard_picobuf_setup(&pbf);
1849
pbf.tty_fix = PineRaw;
1850
pbf.composer_help = h_composer_sigedit;
1851
pbf.exittest = sigedit_exit_for_pico;
1852
pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
1853
? upload_msg_to_pico : NULL;
1854
pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
1855
? VAR_EDITOR : NULL;
1856
pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
1857
pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
1858
pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
1859
pbf.allow_flowed_text = 0;
1861
pbf.pine_anchor = set_titlebar(title,
1862
ps_global->mail_stream,
1863
ps_global->context_current,
1864
ps_global->cur_folder,
1866
0, FolderName, 0, 0, NULL);
1868
/* NOTE: at this point, alot of pico struct fields are null'd out
1869
* thanks to the leading memset; in particular "headents" which tells
1870
* pico to behave like a normal editor (though modified slightly to
1871
* let the caller dictate the file to edit and such)...
1874
if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
1877
l = strlen(VAR_OPER_DIR) + 100;
1878
ret = (char *) fs_get((l+1) * sizeof(char));
1879
snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR);
1885
* Now alloc and init the text to pass pico
1887
if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
1888
ret = cpystr(_("Error allocating space for file"));
1889
dprint((1, "Can't alloc space for signature_edit"));
1893
pbf.msgtext = so_text(msgso);
1895
if(can_access(sig_path, READ_ACCESS) == 0
1896
&& !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){
1897
char *problem = error_description(errno);
1899
snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"),
1900
sig_path, problem ? problem : "<NULL>");
1901
errbuf[sizeof(errbuf)-1] = '\0';
1902
ret = cpystr(errbuf);
1904
dprint((1, "signature_edit: can't open %s: %s", sig_path,
1905
problem ? problem : "<NULL>"));
1908
else if(tmpso){ /* else, fill pico's edit buffer */
1909
gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */
1910
gf_set_so_writec(&pc, msgso);
1911
gf_filter_init(); /* no filters needed */
1912
if(errstr = gf_pipe(gc, pc)){
1913
snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr);
1914
errbuf[sizeof(errbuf)-1] = '\0';
1915
ret = cpystr(errbuf);
1918
gf_clear_so_readc(tmpso);
1919
gf_clear_so_writec(msgso);
1925
mswin_setwindowmenu (MENU_COMPOSER);
1928
/*------ OK, Go edit the signature ------*/
1929
editor_result = pico(&pbf);
1932
mswin_setwindowmenu (MENU_DEFAULT);
1934
if(editor_result & COMP_GOTHUP){
1935
hup_signal(); /* do what's normal for a hup */
1938
fix_windsize(ps_global);
1942
if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
1945
/*------ Must have an edited buffer, write it to .sig -----*/
1946
our_unlink(sig_path); /* blast old copy */
1947
if(tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)){
1948
so_seek(msgso, 0L, 0);
1949
gf_set_so_readc(&gc, msgso); /* read from pico buf */
1950
gf_set_so_writec(&pc, tmpso); /* write sig file */
1951
gf_filter_init(); /* no filters needed */
1952
if(errstr = gf_pipe(gc, pc)){
1953
snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
1955
errbuf[sizeof(errbuf)-1] = '\0';
1956
ret = cpystr(errbuf);
1959
gf_clear_so_readc(msgso);
1960
gf_clear_so_writec(tmpso);
1961
if(so_give(&tmpso)){
1962
errstr = error_description(errno);
1963
snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
1965
errbuf[sizeof(errbuf)-1] = '\0';
1966
ret = cpystr(errbuf);
1969
if(IS_REMOTE(sigfile)){
1975
we_cancel = busy_cue("Copying to remote sig", NULL, 1);
1976
if((e = rd_update_remote(rd, datebuf)) != 0){
1978
q_status_message2(SM_ORDER | SM_DING, 3, 5,
1979
_("Error opening temporary sig file %s: %s"),
1980
rd->lf, error_description(errno));
1982
"write_remote_sig: error opening temp file %s\n",
1983
rd->lf ? rd->lf : "?"));
1986
q_status_message2(SM_ORDER | SM_DING, 3, 5,
1987
_("Error copying to %s: %s"),
1988
rd->rn, error_description(errno));
1990
"write_remote_sig: error copying from %s to %s\n",
1991
rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1994
q_status_message(SM_ORDER | SM_DING, 5, 5,
1995
_("Copy of sig to remote folder failed, changes NOT saved remotely"));
1998
rd_update_metadata(rd, datebuf);
1999
rd->read_status = 'W';
2002
rd_close_remdata(&rd);
2005
cancel_busy_cue(-1);
2009
snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path);
2010
errbuf[sizeof(errbuf)-1] = '\0';
2011
ret = cpystr(errbuf);
2012
dprint((1, "signature_edit: can't write %s",
2018
standard_picobuf_teardown(&pbf);
2024
/*----------------------------------------------------------------------
2025
Serve up the current signature within pico for editing
2027
Args: literal signature to edit
2029
Result: raw edited signature is returned in result arg
2032
signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help)
2035
char *errstr = NULL;
2039
struct variable *vars = ps_global->vars;
2041
standard_picobuf_setup(&pbf);
2042
pbf.tty_fix = PineRaw;
2043
pbf.search_help = h_sigedit_search;
2044
pbf.composer_help = composer_help;
2045
pbf.exittest = sigedit_exit_for_pico;
2046
pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2047
? upload_msg_to_pico : NULL;
2048
pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2049
? VAR_EDITOR : NULL;
2050
pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2051
pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2052
pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2053
pbf.allow_flowed_text = 0;
2055
pbf.pine_anchor = set_titlebar(title,
2056
ps_global->mail_stream,
2057
ps_global->context_current,
2058
ps_global->cur_folder,
2060
0, FolderName, 0, 0, NULL);
2062
/* NOTE: at this point, alot of pico struct fields are null'd out
2063
* thanks to the leading memset; in particular "headents" which tells
2064
* pico to behave like a normal editor (though modified slightly to
2065
* let the caller dictate the file to edit and such)...
2069
* Now alloc and init the text to pass pico
2071
if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2072
ret = cpystr(_("Error allocating space"));
2073
dprint((1, "Can't alloc space for signature_edit_lit"));
2077
pbf.msgtext = so_text(msgso);
2079
so_puts(msgso, litsig ? litsig : "");
2084
mswin_setwindowmenu (MENU_COMPOSER);
2087
/*------ OK, Go edit the signature ------*/
2088
editor_result = pico(&pbf);
2091
mswin_setwindowmenu (MENU_DEFAULT);
2093
if(editor_result & COMP_GOTHUP){
2094
hup_signal(); /* do what's normal for a hup */
2097
fix_windsize(ps_global);
2101
if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2102
ret = cpystr(_("Signature Edit Cancelled"));
2105
/*------ Must have an edited buffer, write it to .sig -----*/
2110
so_seek(msgso, 0L, 0);
2111
while(so_readc(&c, msgso))
2114
*result = (char *)fs_get((cnt+1) * sizeof(char));
2116
so_seek(msgso, 0L, 0);
2117
while(so_readc(&c, msgso))
2124
standard_picobuf_teardown(&pbf);
2134
sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed)
2138
void (*redraw)(void) = ps_global->redrawer;
2139
static ESCKEY_S opts[] = {
2140
{'y', 'y', "Y", N_("Yes")},
2141
{'n', 'n', "N", N_("No")},
2145
ps_global->redrawer = redraw_pico;
2146
fix_windsize(ps_global);
2149
rv = radio_buttons(_("Exit editor and apply changes? "),
2150
-FOOTER_ROWS(ps_global), opts,
2151
'y', 'x', NO_HELP, RB_NORM);
2152
if(rv == 'y'){ /* user ACCEPTS! */
2155
else if(rv == 'n'){ /* Declined! */
2156
rstr = _("No Changes Saved");
2159
else if(rv == 'x'){ /* Cancelled! */
2160
rstr = _("Exit Cancelled");
2165
ps_global->redrawer = redraw;
2171
* Common stuff we almost always want to set when calling pico.
2174
standard_picobuf_setup(PICO *pbf)
2176
memset(pbf, 0, sizeof(*pbf));
2178
pbf->pine_version = ALPINE_VERSION;
2179
pbf->fillcolumn = ps_global->composer_fillcol;
2180
pbf->menu_rows = FOOTER_ROWS(ps_global) - 1;
2181
pbf->colors = colors_for_pico();
2182
pbf->helper = helper;
2183
pbf->showmsg = display_message_for_pico;
2184
pbf->suspend = do_suspend;
2185
pbf->keybinput = cmd_input_for_pico;
2186
pbf->tty_fix = ttyfix; /* watch out for this one */
2187
pbf->newmail = new_mail_for_pico;
2188
pbf->ckptdir = checkpoint_dir_for_pico;
2189
pbf->resize = resize_for_pico;
2190
pbf->input_cs = ps_global->input_cs;
2191
pbf->winch_cleanup = winch_cleanup;
2192
pbf->search_help = h_composer_search;
2193
pbf->ins_help = h_composer_ins;
2194
pbf->ins_m_help = h_composer_ins_m;
2195
pbf->composer_help = h_composer;
2196
pbf->browse_help = h_composer_browse;
2197
pbf->attach_help = h_composer_ctrl_j;
2200
( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L)
2201
| (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L)
2202
| (ps_global->restricted ? P_SECURE : 0L)
2203
| (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L)
2204
| (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L)
2205
| (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L)
2206
| (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L)
2207
| (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L)
2208
| (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L)
2209
| (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L)
2210
| (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L)
2211
| (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L)
2212
| (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L)
2213
| (!ps_global->pass_ctrl_chars
2214
&& !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L)
2215
| ((F_ON(F_ENABLE_ALT_ED,ps_global)
2216
|| F_ON(F_ALT_ED_NOW,ps_global)
2217
|| (ps_global->VAR_EDITOR
2218
&& ps_global->VAR_EDITOR[0]
2219
&& ps_global->VAR_EDITOR[0][0]))
2221
| ((!ps_global->keyboard_charmap
2222
|| !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
2223
? P_HIBITIGN : 0L));
2225
if(ps_global->VAR_OPER_DIR){
2226
pbf->oper_dir = ps_global->VAR_OPER_DIR;
2227
pbf->pine_flags |= P_TREE;
2229
pbf->home_dir = ps_global->home_dir;
2234
standard_picobuf_teardown(PICO *pbf)
2238
free_pcolors(&pbf->colors);
2243
/*----------------------------------------------------------------------
2244
Call back for pico to use to check for new mail.
2246
Args: cursor -- pointer to in to tell caller if cursor location changed
2247
if NULL, turn off cursor positioning.
2248
timing -- whether or not it's a good time to check
2251
Returns: returns 1 on success, zero on error.
2254
new_mail_for_pico(int timing, int status)
2257
* If we're not interested in the status, don't display the busy
2260
/* don't know where the cursor's been, reset it */
2262
return(new_mail(0, timing,
2263
(status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT
2264
| NM_FROM_COMPOSER));
2269
cmd_input_for_pico(void)
2271
zero_new_mail_count();
2275
/*----------------------------------------------------------------------
2276
Call back for pico to get newmail status messages displayed
2278
Args: x -- char processed
2283
display_message_for_pico(int x)
2287
clear_cursor_pos(); /* can't know where cursor is */
2288
mark_status_dirty(); /* don't count on cached text */
2289
fix_windsize(ps_global);
2292
rv = ps_global->mangled_screen;
2293
ps_global->mangled_screen = 0;
2298
/*----------------------------------------------------------------------
2299
Call back for pico to get desired directory for its check point file
2301
Args: s -- buffer to write directory name
2302
n -- length of that buffer
2304
Returns: pointer to static buffer
2307
checkpoint_dir_for_pico(char *s, size_t n)
2309
#if defined(DOS) || defined(OS2)
2311
* we can't assume anything about root or home dirs, so
2312
* just plunk it down in the same place as the pinerc
2314
if(!getenv("HOME")){
2315
char *lc = last_cmpnt(ps_global->pinerc);
2318
strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc));
2319
s[MIN(n-1,lc-ps_global->pinerc)] = '\0';
2322
strncpy(s, ".\\", n-1);
2328
strncpy(s, ps_global->home_dir, n-1);
2335
/*----------------------------------------------------------------------
2336
Call back for pico to tell us the window size's changed
2340
Returns: none (but pine's ttyo structure may have been updated)
2343
resize_for_pico(void)
2345
fix_windsize(ps_global);
2350
colors_for_pico(void)
2352
PCOLORS *colors = NULL;
2353
struct variable *vars = ps_global->vars;
2355
if (pico_usingcolor()){
2356
colors = (PCOLORS *)fs_get(sizeof(PCOLORS));
2357
if (VAR_TITLE_FORE_COLOR && VAR_TITLE_BACK_COLOR){
2358
colors->tbcp = new_color_pair(VAR_TITLE_FORE_COLOR,
2359
VAR_TITLE_BACK_COLOR);
2361
else colors->tbcp = NULL;
2363
if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
2364
colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
2365
VAR_KEYLABEL_BACK_COLOR);
2366
if (!pico_is_good_colorpair(colors->klcp))
2367
free_color_pair(&colors->klcp);
2369
else colors->klcp = NULL;
2371
if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
2372
colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR,
2373
VAR_KEYNAME_BACK_COLOR);
2375
else colors->kncp = NULL;
2376
if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){
2377
colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR,
2378
VAR_STATUS_BACK_COLOR);
2380
else colors->stcp = NULL;
2388
free_pcolors(PCOLORS **colors)
2391
if ((*colors)->tbcp)
2392
free_color_pair(&(*colors)->tbcp);
2393
if ((*colors)->kncp)
2394
free_color_pair(&(*colors)->kncp);
2395
if ((*colors)->klcp)
2396
free_color_pair(&(*colors)->klcp);
2397
if ((*colors)->stcp)
2398
free_color_pair(&(*colors)->stcp);
2399
fs_give((void **)colors);