* Correction to URL for getting access code from refresh code for Yandex.com.
[alpine.git] / alpine / reply.c
blobd7875a9239e7508ad19c7ad38ae789ad93ad5ea5
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: reply.c 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 Code here for forward and reply to mail
21 A few support routines as well
23 This code will forward and reply to MIME messages. The Alpine composer
24 at this time will only support non-text segments at the end of a
25 message so, things don't always come out as one would like. If you
26 always forward a message in MIME format, all will be correct. Forwarding
27 of nested MULTIPART messages will work. There's still a problem with
28 MULTIPART/ALTERNATIVE as the "first text part" rule doesn't allow modifying
29 the equivalent parts. Ideally, we should probably such segments as a
30 single attachment when forwarding/replying. It would also be real nice to
31 flatten out the nesting in the composer so pieces inside can get snipped.
33 The evolution continues...
34 =====*/
37 #include "headers.h"
38 #include "reply.h"
39 #include "status.h"
40 #include "radio.h"
41 #include "send.h"
42 #include "titlebar.h"
43 #include "mailindx.h"
44 #include "help.h"
45 #include "signal.h"
46 #include "mailcmd.h"
47 #include "alpine.h"
48 #include "roleconf.h"
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"
68 * Internal Prototypes
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
88 Reply
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()
98 ---*/
99 int
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;
106 REPLY_S reply;
107 void *msgtext = NULL;
108 char *tmpfix = NULL, *prefix = NULL, *fcc = NULL, *errmsg = NULL;
109 char *hostpart;
110 long msgno, j, totalm, rflags, *seq = NULL;
111 int i, include_text = 0, times = -1, warned = 0, rv = 0,
112 flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
113 int rolemsg = 0, copytomsg = 0;
114 gf_io_t pc;
115 PAT_STATE dummy;
116 REDRAFT_POS_S *redraft_pos = NULL;
117 ACTION_S *role = NULL, *nrole;
118 #if defined(DOS) && !defined(_WINDOWS)
119 char *reserve;
120 #endif
122 outgoing = mail_newenvelope();
123 totalm = mn_total_cur(pine_state->msgmap);
124 seq = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));
126 dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm)));
128 saved_from = (ADDRESS *) NULL;
129 saved_to = (ADDRESS *) NULL;
130 saved_cc = (ADDRESS *) NULL;
131 saved_resent = (ADDRESS *) NULL;
133 us_in_to_and_cc = (ADDRESS *) NULL;
135 outgoing->subject = NULL;
137 memset((void *)&reply, 0, sizeof(reply));
139 if(ps_global->full_header == 2
140 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
141 reply_raw_body = 1;
144 * We may have to loop through first to figure out what default
145 * reply-indent-string to offer...
147 if(mn_total_cur(pine_state->msgmap) > 1 &&
148 (F_ON(F_ALT_REPLY_MENU, pine_state)
149 || F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state)) &&
150 reply_quote_str_contains_tokens()){
151 for(msgno = mn_first_cur(pine_state->msgmap);
152 msgno > 0L && !tmpfix;
153 msgno = mn_next_cur(pine_state->msgmap)){
155 env = pine_mail_fetchstructure(pine_state->mail_stream,
156 mn_m2raw(pine_state->msgmap, msgno),
157 NULL);
158 if(!env) {
159 q_status_message1(SM_ORDER,3,4,
160 _("Error fetching message %s. Can't reply to it."),
161 long2string(msgno));
162 goto done_early;
165 if(!tmpfix){ /* look for prefix? */
166 tmpfix = reply_quote_str(env);
167 if(prefix){
168 i = strcmp(tmpfix, prefix);
169 fs_give((void **) &tmpfix);
170 if(i){ /* don't check back if dissimilar */
171 fs_give((void **) &prefix);
173 * We free prefix, not tmpfix. We set tmpfix to prefix
174 * so that we won't come check again.
176 tmpfix = prefix = cpystr("> ");
179 else{
180 prefix = tmpfix;
181 tmpfix = NULL; /* check back later? */
186 tmpfix = prefix;
190 * Loop thru the selected messages building the
191 * outgoing envelope's destinations...
193 for(msgno = mn_first_cur(pine_state->msgmap);
194 msgno > 0L;
195 msgno = mn_next_cur(pine_state->msgmap)){
197 /*--- Grab current envelope ---*/
198 env = pine_mail_fetchstructure(pine_state->mail_stream,
199 seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
200 NULL);
201 if(!env) {
202 q_status_message1(SM_ORDER,3,4,
203 _("Error fetching message %s. Can't reply to it."),
204 long2string(msgno));
205 goto done_early;
209 * We check for the prefix here if we didn't do it in the first
210 * loop above. This is just to save having to go through the loop
211 * twice in the cases where we don't need to.
213 if(!tmpfix){
214 tmpfix = reply_quote_str(env);
215 if(prefix){
216 i = strcmp(tmpfix, prefix);
217 fs_give((void **) &tmpfix);
218 if(i){ /* don't check back if dissimilar */
219 fs_give((void **) &prefix);
220 tmpfix = prefix = cpystr("> ");
223 else{
224 prefix = tmpfix;
225 tmpfix = NULL; /* check back later? */
230 * For consistency, the first question is always "include text?"
232 if(!times){ /* only first time */
233 char *p = cpystr(prefix);
235 if((include_text=reply_text_query(pine_state,totalm,env,&prefix)) < 0)
236 goto done_early;
238 /* edited prefix? */
239 if(strcmp(p, prefix))
240 tmpfix = prefix; /* stop looking */
242 fs_give((void **)&p);
246 * If we're agg-replying or there's a newsgroup and the user wants
247 * to post to news *and* via email, add relevant addresses to the
248 * outgoing envelope...
250 * The single message case gets us around the aggregate reply
251 * to messages in a mixed mail-news archive where some might
252 * have newsgroups and others not or whatever.
254 if(totalm > 1L || ((i = reply_news_test(env, outgoing)) & 1)){
255 if(totalm > 1)
256 flags |= RSF_FORCE_REPLY_TO;
258 if(!reply_harvest(pine_state, seq[times], NULL, env,
259 &saved_from, &saved_to, &saved_cc,
260 &saved_resent, &flags))
261 goto done_early;
263 else if(i == 0)
264 goto done_early;
266 /* collect a list of addresses that are us in to and cc fields */
267 if(env->to)
268 if((ap=reply_cp_addr(pine_state, 0L, NULL,
269 NULL, us_in_to_and_cc, NULL,
270 env->to, RCA_ONLY_US)) != NULL)
271 reply_append_addr(&us_in_to_and_cc, ap);
273 if(env->cc)
274 if((ap=reply_cp_addr(pine_state, 0L, NULL,
275 NULL, us_in_to_and_cc, NULL,
276 env->cc, RCA_ONLY_US)) != NULL)
277 reply_append_addr(&us_in_to_and_cc, ap);
279 /*------------ Format the subject line ---------------*/
280 if(outgoing->subject){
282 * if reply to more than one message, and all subjects
283 * match, so be it. otherwise set it to something generic...
285 if(!same_subject(outgoing->subject,
286 reply_subject(env->subject,tmp_20k_buf,SIZEOF_20KBUF))){
287 fs_give((void **)&outgoing->subject);
288 outgoing->subject = cpystr("Re: several messages");
291 else
292 outgoing->subject = reply_subject(env->subject, NULL, 0);
295 /* fill reply header */
296 reply_seed(pine_state, outgoing, env, saved_from,
297 saved_to, saved_cc, saved_resent,
298 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
299 if(errmsg){
300 if(*errmsg){
301 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
302 display_message(NO_OP_COMMAND);
305 fs_give((void **)&errmsg);
308 if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */
309 goto done_early;
311 /* Setup possible role */
312 if (ps_global->reply.role_chosen)
313 role = ps_global->reply.role_chosen;
314 else if(role_arg)
315 role = copy_action(role_arg);
317 if(!role){
318 rflags = ROLE_REPLY;
319 if(!ps_global->reply.role_chosen && nonempty_patterns(rflags, &dummy)){
320 /* setup default role */
321 nrole = NULL;
322 j = mn_first_cur(pine_state->msgmap);
323 do {
324 role = nrole;
325 nrole = set_role_from_msg(pine_state, rflags,
326 mn_m2raw(pine_state->msgmap, j),
327 NULL);
328 } while(nrole && (!role || nrole == role)
329 && (j=mn_next_cur(pine_state->msgmap)) > 0L);
331 if(!role || nrole == role)
332 role = nrole;
333 else
334 role = NULL;
336 if(confirm_role(rflags, &role))
337 role = combine_inherited_role(role);
338 else{ /* cancel reply */
339 role = NULL;
340 cmd_cancelled("Reply");
341 goto done_early;
347 * Reply_seed may call c-client in get_fcc_based_on_to, so env may
348 * no longer be valid. Get it again.
349 * Similarly for set_role_from_message.
351 env = pine_mail_fetchstructure(pine_state->mail_stream, seq[times], NULL);
353 if(role){
354 rolemsg++;
355 /* override fcc gotten in reply_seed */
356 if(role->fcc && fcc)
357 fs_give((void **) &fcc);
360 if(F_ON(F_COPY_TO_TO_FROM, pine_state) && !(role && role->from)){
362 * A list of all of our addresses that appear in the To
363 * and cc fields is in us_in_to_and_cc.
364 * If there is exactly one address in that list then
365 * use it for the outgoing From.
367 if(us_in_to_and_cc && !us_in_to_and_cc->next){
368 PINEFIELD *custom, *pf;
369 ADDRESS *a = NULL;
370 char *addr = NULL;
373 * Check to see if this address is different from what
374 * we would have used anyway. If it is, notify the user
375 * with a status message. This is pretty hokey because we're
376 * mimicking how pine_send would set the From address and
377 * there is no coordination between the two.
380 /* in case user has a custom From value */
381 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
383 pf = (PINEFIELD *) fs_get(sizeof(*pf));
384 memset((void *) pf, 0, sizeof(*pf));
385 pf->name = cpystr("From");
386 pf->addr = &a;
387 if(set_default_hdrval(pf, custom) >= UseAsDef
388 && pf->textbuf && pf->textbuf[0]){
389 removing_trailing_white_space(pf->textbuf);
390 (void)removing_double_quotes(pf->textbuf);
391 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
392 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
393 if(addr)
394 fs_give((void **) &addr);
397 if(!*pf->addr)
398 *pf->addr = generate_from();
400 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
401 copytomsg++;
402 if(!role){
403 role = (ACTION_S *) fs_get(sizeof(*role));
404 memset((void *) role, 0, sizeof(*role));
405 role->is_a_role = 1;
408 role->from = us_in_to_and_cc;
409 us_in_to_and_cc = NULL;
412 free_customs(custom);
413 free_customs(pf);
417 if(role){
418 if(rolemsg && copytomsg)
419 q_status_message1(SM_ORDER, 3, 4,
420 _("Replying using role \"%s\" and To as From"), role->nick);
421 else if(rolemsg)
422 q_status_message1(SM_ORDER, 3, 4,
423 _("Replying using role \"%s\""), role->nick);
424 else if(copytomsg)
425 q_status_message(SM_ORDER, 3, 4,
426 _("Replying using incoming To as outgoing From"));
429 if(us_in_to_and_cc)
430 mail_free_address(&us_in_to_and_cc);
433 seq[++times] = -1L; /* mark end of sequence list */
435 /*========== Other miscellaneous fields ===================*/
436 outgoing->in_reply_to = reply_in_reply_to(env);
437 outgoing->references = reply_build_refs(env);
438 outgoing->message_id = generate_message_id(role);
440 if(!outgoing->to &&
441 !outgoing->cc &&
442 !outgoing->bcc &&
443 !outgoing->newsgroups)
444 q_status_message(SM_ORDER | SM_DING, 3, 6,
445 _("Warning: no valid addresses to reply to!"));
448 /*==================== Now fix up the message body ====================*/
451 * create storage object to be used for message text
453 if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
454 q_status_message(SM_ORDER | SM_DING, 3, 4,
455 _("Error allocating message text"));
456 goto done_early;
459 gf_set_so_writec(&pc, (STORE_S *) msgtext);
461 /*---- Include the original text if requested ----*/
462 if(include_text && totalm > 1L){
463 char *sig;
464 int impl, template_len = 0, leave_cursor_at_top = 0;
467 env = NULL;
468 if(role && role->template){
469 char *filtered;
471 impl = 0;
472 filtered = detoken(role, env, 0,
473 ps_global->reply.signature_bottom,
474 0, &redraft_pos, &impl);
475 if(filtered){
476 if(*filtered){
477 so_puts((STORE_S *)msgtext, filtered);
478 if(impl == 1)
479 template_len = strlen(filtered);
480 else if(impl == 2)
481 leave_cursor_at_top++;
484 fs_give((void **)&filtered);
486 else
487 impl = 1;
489 else
490 impl = 1;
492 if((sig = reply_signature(role, env, &redraft_pos, &impl)) &&
493 !ps_global->reply.signature_bottom){
496 * If CURSORPOS was set explicitly in sig_file, and there was a
497 * template file before that, we need to adjust the offset by the
498 * length of the template file. However, if the template had
499 * a set CURSORPOS in it then impl was 2 before getting to the
500 * signature, so offset wouldn't have been reset by the signature
501 * CURSORPOS and offset would already be correct. That case will
502 * be ok here because template_len will be 0 and adding it does
503 * nothing. If template
504 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
505 * by the CURSORPOS in the sig. In that case we have to adjust the
506 * offset. That's what the next line does. It adjusts it if
507 * template_len is nonzero and if CURSORPOS was set in sig_file.
509 if(impl == 2)
510 redraft_pos->offset += template_len;
512 if(*sig)
513 so_puts((STORE_S *)msgtext, sig);
516 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
517 * is set, we won't have used it yet and want it to be non-NULL.
519 fs_give((void **)&sig);
523 * Only put cursor in sig if there is a cursorpos there but not
524 * one in the template, and sig-at-bottom.
526 if(!(sig && impl == 2 && !leave_cursor_at_top))
527 leave_cursor_at_top++;
529 body = mail_newbody();
530 body->type = TYPETEXT;
531 body->contents.text.data = msgtext;
533 for(msgno = mn_first_cur(pine_state->msgmap);
534 msgno > 0L;
535 msgno = mn_next_cur(pine_state->msgmap)){
537 if(env){ /* put 2 between messages */
538 gf_puts(NEWLINE, pc);
539 gf_puts(NEWLINE, pc);
542 /*--- Grab current envelope ---*/
543 env = pine_mail_fetchstructure(pine_state->mail_stream,
544 mn_m2raw(pine_state->msgmap, msgno),
545 &orig_body);
546 if(!env){
547 q_status_message1(SM_ORDER,3,4,
548 _("Error fetching message %s. Can't reply to it."),
549 long2string(mn_get_cur(pine_state->msgmap)));
550 goto done_early;
553 if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) {
554 reply_delimiter(env, role, pc);
555 if(ps_global->reply.include_header)
556 reply_forward_header(pine_state->mail_stream,
557 mn_m2raw(pine_state->msgmap,msgno),
558 NULL, env, pc, prefix);
560 get_body_part_text(pine_state->mail_stream, reply_raw_body ? NULL : orig_body,
561 mn_m2raw(pine_state->msgmap, msgno),
562 reply_raw_body ? NULL : "1", 0L, pc, prefix,
563 NULL, GBPT_NONE);
565 else if(orig_body->type == TYPEMULTIPART) {
566 if(!warned++)
567 q_status_message(SM_ORDER,3,7,
568 _("WARNING! Attachments not included in multiple reply."));
570 if(orig_body->nested.part
571 && orig_body->nested.part->body.type == TYPETEXT) {
572 /*---- First part of the message is text -----*/
573 reply_delimiter(env, role, pc);
574 if(ps_global->reply.include_header)
575 reply_forward_header(pine_state->mail_stream,
576 mn_m2raw(pine_state->msgmap,
577 msgno),
578 NULL, env, pc, prefix);
580 get_body_part_text(pine_state->mail_stream,
581 &orig_body->nested.part->body,
582 mn_m2raw(pine_state->msgmap, msgno),
583 "1", 0L, pc, prefix, NULL, GBPT_NONE);
585 else{
586 q_status_message(SM_ORDER,0,3,
587 _("Multipart with no leading text part."));
590 else{
591 /*---- Single non-text message of some sort ----*/
592 q_status_message(SM_ORDER,3,3,
593 _("Non-text message not included."));
597 if(!leave_cursor_at_top){
598 long cnt = 0L;
599 unsigned char c;
601 /* rewind and count chars to start of sig file */
602 so_seek((STORE_S *)msgtext, 0L, 0);
603 while(so_readc(&c, (STORE_S *)msgtext))
604 cnt++;
606 if(!redraft_pos){
607 redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(*redraft_pos));
608 memset((void *)redraft_pos, 0,sizeof(*redraft_pos));
609 redraft_pos->hdrname = cpystr(":");
613 * If explicit cursor positioning in sig file,
614 * add offset to start of sig file plus offset into sig file.
615 * Else, just offset to start of sig file.
617 redraft_pos->offset += cnt;
620 if(sig){
621 if(*sig)
622 so_puts((STORE_S *)msgtext, sig);
624 fs_give((void **)&sig);
627 else{
628 msgno = mn_m2raw(pine_state->msgmap,
629 mn_get_cur(pine_state->msgmap));
631 /*--- Grab current envelope ---*/
632 env = pine_mail_fetchstructure(pine_state->mail_stream, msgno,
633 &orig_body);
636 * If the charset of the body part is different from ascii and
637 * charset conversion is _not_ happening, then preserve the original
638 * charset from the message so that if we don't enter any new
639 * chars with the hibit set we can use the original charset.
640 * If not all those things, then don't try to preserve it.
642 if(orig_body){
643 char *charset;
645 charset = parameter_val(orig_body->parameter, "charset");
646 if(charset && strucmp(charset, "us-ascii") != 0){
647 CONV_TABLE *ct;
650 * There is a non-ascii charset, is there conversion happening?
652 if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
653 reply.orig_charset = charset;
654 charset = NULL;
658 if(charset)
659 fs_give((void **) &charset);
662 if(env) {
663 if(!(body = reply_body(pine_state->mail_stream, env, orig_body,
664 msgno, NULL, msgtext, prefix,
665 include_text, role, 1, &redraft_pos)))
666 goto done_early;
668 else{
669 q_status_message1(SM_ORDER,3,4,
670 _("Error fetching message %s. Can't reply to it."),
671 long2string(mn_get_cur(pine_state->msgmap)));
672 goto done_early;
676 /* fill in reply structure */
677 reply.prefix = prefix;
678 reply.mailbox = cpystr(pine_state->mail_stream->mailbox);
679 reply.origmbox = cpystr(pine_state->mail_stream->original_mailbox
680 ? pine_state->mail_stream->original_mailbox
681 : pine_state->mail_stream->mailbox);
682 reply.data.uid.msgs = (imapuid_t *) fs_get((times + 1) * sizeof(imapuid_t));
683 if((reply.data.uid.validity = pine_state->mail_stream->uid_validity) != 0){
684 reply.uid = 1;
685 for(i = 0; i < times ; i++)
686 reply.data.uid.msgs[i] = mail_uid(pine_state->mail_stream, seq[i]);
688 else{
689 reply.msgno = 1;
690 for(i = 0; i < times ; i++)
691 reply.data.uid.msgs[i] = seq[i];
694 reply.data.uid.msgs[i] = 0; /* tie off list */
696 #if defined(DOS) && !defined(_WINDOWS)
697 free((void *)reserve);
698 #endif
700 /* partially formatted outgoing message */
701 pine_send(outgoing, &body, _("COMPOSE MESSAGE REPLY"),
702 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
704 rv++;
705 pine_free_body(&body);
706 if(reply.mailbox)
707 fs_give((void **) &reply.mailbox);
708 if(reply.origmbox)
709 fs_give((void **) &reply.origmbox);
710 if(reply.orig_charset)
711 fs_give((void **) &reply.orig_charset);
712 fs_give((void **) &reply.data.uid.msgs);
713 done_early:
714 if((STORE_S *) msgtext)
715 gf_clear_so_writec((STORE_S *) msgtext);
717 mail_free_envelope(&outgoing);
718 mail_free_address(&saved_from);
719 mail_free_address(&saved_to);
720 mail_free_address(&saved_cc);
721 mail_free_address(&saved_resent);
723 fs_give((void **)&seq);
725 if(prefix)
726 fs_give((void **)&prefix);
728 if(fcc)
729 fs_give((void **) &fcc);
731 free_redraft_pos(&redraft_pos);
732 free_action(&role);
733 return rv;
738 * Ask user to confirm role choice, or choose another role.
740 * Args role -- A pointer into the pattern_h space at the default
741 * role to use. This can't be a copy, the comparison
742 * relies on it pointing at the actual role.
743 * This arg is also used to return a pointer to the
744 * chosen role.
746 * Returns 1 -- Yes, use role which is now in *role. This may not be
747 * the same as the role passed in and it may even be NULL.
748 * 0 -- Cancel reply.
751 confirm_role(long int rflags, ACTION_S **role)
753 ACTION_S *role_p = NULL;
754 ACTION_S *default_role = NULL;
755 char prompt[80], *prompt_fodder;
756 int cmd, done, ret = 1;
757 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
758 (*redraw)(void) = ps_global->redrawer;
759 PAT_S *curpat, *pat;
760 PAT_STATE pstate;
761 HelpType help;
762 ESCKEY_S ekey[4];
764 if(!nonempty_patterns(ROLE_DO_ROLES, &pstate) || !role)
765 return(ret);
768 * If this is a reply or forward and the role doesn't require confirmation,
769 * then we just return with what was passed in.
771 if(((rflags & ROLE_REPLY) &&
772 *role && (*role)->repl_type == ROLE_REPL_NOCONF) ||
773 ((rflags & ROLE_FORWARD) &&
774 *role && (*role)->forw_type == ROLE_FORW_NOCONF) ||
775 ((rflags & ROLE_COMPOSE) &&
776 *role && (*role)->comp_type == ROLE_COMP_NOCONF) ||
777 (!*role && F_OFF(F_ROLE_CONFIRM_DEFAULT, ps_global)
778 && !ps_global->default_role))
779 return(ret);
782 * Check that there is at least one role available. This is among all
783 * roles, not just the reply roles or just the forward roles. That's
784 * because we have ^T take us to all the roles, not the category-specific
785 * roles.
787 if(!(pat = last_pattern(&pstate)))
788 return(ret);
790 ekey[0].ch = 'y';
791 ekey[0].rval = 'y';
793 ekey[1].ch = 'n';
794 ekey[1].rval = 'n';
796 ekey[2].ch = ctrl('T');
797 ekey[2].rval = 2;
798 ekey[2].name = "^T";
800 ekey[3].ch = -1;
802 /* check for more than one role available (or no role set) */
803 if(pat == first_pattern(&pstate) && *role) /* no ^T */
804 ekey[2].ch = -1;
807 * Setup default role
808 * Go through the loop just in case default_role doesn't point
809 * to a real current role.
811 if(ps_global->default_role){
812 for(pat = first_pattern(&pstate);
813 pat;
814 pat = next_pattern(&pstate)){
815 if(pat->action == ps_global->default_role){
816 default_role = ps_global->default_role;
817 break;
822 curpat = NULL;
824 /* override default */
825 if(*role){
826 for(pat = first_pattern(&pstate);
827 pat;
828 pat = next_pattern(&pstate)){
829 if(pat->action == *role){
830 curpat = pat;
831 break;
836 if(rflags & ROLE_REPLY)
837 prompt_fodder = _("Reply");
838 else if(rflags & ROLE_FORWARD)
839 prompt_fodder = _("Forward");
840 else
841 prompt_fodder = _("Compose");
843 done = 0;
844 while(!done){
845 if(curpat){
846 char buf[100];
848 help = h_role_confirm;
849 ekey[0].name = "Y";
850 ekey[0].label = N_("Yes");
851 ekey[1].name = "N";
852 if(default_role)
853 ekey[1].label = N_("No, use default role");
854 else
855 ekey[1].label = N_("No, use default settings");
857 ekey[2].label = N_("To Select Alternate Role");
859 if(curpat->patgrp && curpat->patgrp->nick)
860 /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
861 snprintf(prompt, sizeof(prompt), _("Use role \"%s\" for %s? "),
862 short_str(curpat->patgrp->nick, buf, sizeof(buf), 50, MidDots),
863 prompt_fodder);
864 else
865 snprintf(prompt, sizeof(prompt),
866 _("Use role \"<a role without a nickname>\" for %s? "),
867 prompt_fodder);
869 else{
870 help = h_norole_confirm;
871 ekey[0].name = "Ret";
872 ekey[0].label = prompt_fodder;
873 ekey[1].name = "";
874 ekey[1].label = "";
875 ekey[2].label = N_("To Select Role");
876 snprintf(prompt, sizeof(prompt),
877 _("Press Return to %s using %s role, or ^T to select a role "),
878 prompt_fodder, default_role ? _("default") : _("no"));
881 prompt[sizeof(prompt)-1] = '\0';
883 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
884 'y', 'x', help, RB_NORM);
886 switch(cmd){
887 case 'y': /* Accept */
888 done++;
889 *role = curpat ? curpat->action : default_role;
890 break;
892 case 'x': /* Cancel */
893 ret = 0;
894 /* fall through */
896 case 'n': /* NoRole */
897 done++;
898 *role = default_role;
899 break;
901 case 2: /* ^T */
902 if(role_select_screen(ps_global, &role_p, 0) >= 0){
903 if(role_p){
904 for(pat = first_pattern(&pstate);
905 pat;
906 pat = next_pattern(&pstate)){
907 if(pat->action == role_p){
908 curpat = pat;
909 break;
913 else
914 curpat = NULL;
917 ClearBody();
918 ps_global->mangled_body = 1;
919 ps_global->prev_screen = prev_screen;
920 ps_global->redrawer = redraw;
921 break;
925 return(ret);
930 * reply_to_all_query - Ask user about replying to all recipients
932 * Returns: -1 if cancel, 0 otherwise
933 * by reference: flagp
936 reply_to_all_query(int *flagp)
938 char prompt[80];
939 ESCKEY_S ekey[4];
940 char cmd;
942 ekey[0].name = "Y";
943 ekey[0].ch = 'y';
944 ekey[0].rval = 'y';
945 ekey[0].label = N_("Yes");
946 ekey[1].name = "N";
947 ekey[1].ch = 'n';
948 ekey[1].rval = 'n';
949 ekey[1].label = N_("No");
950 ekey[2].name = "P";
951 ekey[2].ch = 'p';
952 ekey[2].rval = 'p';
953 ekey[3].ch = -1;
955 ps_global->reply.preserve_fields = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps_global);
956 loop:
957 ekey[2].label = ps_global->reply.preserve_fields ? N_("Not Preserve") : N_("Preserve");
958 snprintf(prompt, sizeof(prompt), _("Reply to all recipients%s"),
959 ps_global->reply.preserve_fields ? _(" (preserving fields)? ") : "? ");
961 prompt[sizeof(prompt)-1] = '\0';
963 switch(cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
964 'n', 'x', h_preserve_field, RB_NORM)){
966 case 'x' :
967 return(-1);
969 case 'y' : /* set reply-all bit */
970 (*flagp) |= RSF_FORCE_REPLY_ALL;
971 break;
973 case 'n' : /* clear reply-all bit */
974 (*flagp) &= ~RSF_FORCE_REPLY_ALL;
975 break;
977 case 'p' :
978 ps_global->reply.preserve_fields =
979 (ps_global->reply.preserve_fields + 1) % 2;
980 goto loop; /* ugly, but saves me a variable */
981 break;
984 return(0);
989 * reply_using_replyto_query - Ask user about replying with reply-to value
991 * Returns: 'y' if yes
992 * 'x' if cancel
995 reply_using_replyto_query(void)
997 return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
998 'y', 'x', NO_HELP,WT_SEQ_SENSITIVE));
1003 * reply_text_query - Ask user about replying with text, or in the case
1004 * of alternate reply menu, set values to the answer to all questions
1005 * asked during reply.
1007 * Returns: 1 if include the text
1008 * 0 if we're NOT to include the text
1009 * -1 on cancel or error
1011 #define MAX_REPLY_OPTIONS 10
1013 reply_text_query(struct pine *ps, long int many, ENVELOPE *env, char **prefix)
1015 int ret, edited = 0, headers = 0;
1016 static ESCKEY_S compose_style[MAX_REPLY_OPTIONS];
1017 int ekey_num;
1018 int orig_sf;
1020 orig_sf = ps->reply.use_flowed = *prefix && **prefix ? (F_OFF(F_QUELL_FLOWED_TEXT, ps)
1021 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps)
1022 && (strcmp(*prefix, "> ") == 0
1023 || strcmp(*prefix, ">") == 0)) : 0;
1024 ps->reply.strip_signature = ps->full_header == 0
1025 && (F_ON(F_ENABLE_STRIP_SIGDASHES, ps)
1026 || F_ON(F_ENABLE_SIGDASHES, ps));
1027 ps->reply.keep_attach = F_ON(F_ATTACHMENTS_IN_REPLY, ps);
1028 ps->reply.include_header = F_ON(F_INCLUDE_HEADER, ps);
1029 ps->reply.preserve_fields = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps);
1030 ps->reply.signature_bottom = F_ON(F_SIG_AT_BOTTOM, ps);
1031 ps->reply.role_chosen = NULL;
1033 if(F_OFF(F_ALT_REPLY_MENU, ps)
1034 && F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)
1035 && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps)
1036 && F_OFF(F_ALT_REPLY_MENU,ps))
1037 return(1);
1039 while(1){
1040 compose_style[ekey_num = 0].ch = 'y';
1041 compose_style[ekey_num].rval = 'y';
1042 compose_style[ekey_num].name = "Y";
1043 compose_style[ekey_num++].label = N_("Yes");
1045 compose_style[ekey_num].ch = 'n';
1046 compose_style[ekey_num].rval = 'n';
1047 compose_style[ekey_num].name = "N";
1048 compose_style[ekey_num++].label = N_("No");
1050 if (F_OFF(F_ALT_REPLY_MENU, ps)){ /**** Standard menu ****/
1051 /* TRANSLATORS: The final five %s's can probably be safely ignored */
1052 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s%soriginal message%s in Reply%s%s%s%s%s%s? "),
1053 (many > 1L) ? comatose(many) : "",
1054 (many > 1L) ? " " : "",
1055 (many > 1L) ? "s" : "",
1056 (many > 1L) ? "s" : "",
1057 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
1058 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
1059 ps->reply.role_chosen ? "\" and role \"" : "",
1060 ps->reply.role_chosen ? ps->reply.role_chosen->nick : "",
1061 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
1062 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1064 if (F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
1065 compose_style[ekey_num].ch = ctrl('R');
1066 compose_style[ekey_num].rval = 'r';
1067 compose_style[ekey_num].name = "^R";
1068 compose_style[ekey_num++].label = N_("Edit Indent String");
1070 } else { /***** Alternate Reply Menu ********/
1071 unsigned which_help;
1073 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s%soriginal message%s in Reply (using \"%s%s%s\")? "),
1074 (many > 1L) ? comatose(many) : "",
1075 (many > 1L) ? " " : "",
1076 (many > 1L) ? "s" : "",
1077 *prefix,
1078 ps->reply.role_chosen ? "\" and role \"" : "",
1079 ps->reply.role_chosen ? ps->reply.role_chosen->nick : "");
1080 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1082 compose_style[ekey_num].ch = 'h';
1083 compose_style[ekey_num].rval = 'H';
1084 compose_style[ekey_num].name = "H";
1085 compose_style[ekey_num++].label = ps->reply.include_header
1086 ? N_("No Header") : N_("Inc Headr");
1088 compose_style[ekey_num].ch = 's';
1089 compose_style[ekey_num].rval = 'S';
1090 compose_style[ekey_num].name = "S";
1091 compose_style[ekey_num++].label = ps->reply.strip_signature
1092 ? N_("No Strip"): N_("Strip Sig");
1094 compose_style[ekey_num].ch = 'a';
1095 compose_style[ekey_num].rval = 'A';
1096 compose_style[ekey_num].name = "A";
1097 compose_style[ekey_num++].label = ps->reply.keep_attach
1098 ? N_("No Attach"): N_("Inc Attach");
1100 compose_style[ekey_num].ch = 'b';
1101 compose_style[ekey_num].rval = 'B';
1102 compose_style[ekey_num].name = "B";
1103 compose_style[ekey_num++].label = ps->reply.signature_bottom
1104 ? N_("Sig Top") : N_("Sig Bottom");
1106 compose_style[ekey_num].ch = 'r';
1107 compose_style[ekey_num].rval = 'R';
1108 compose_style[ekey_num].name = "R";
1109 compose_style[ekey_num++].label = N_("Set Role");
1111 compose_style[ekey_num].ch = ctrl('R');
1112 compose_style[ekey_num].rval = 'r';
1113 compose_style[ekey_num].name = "^R";
1114 compose_style[ekey_num++].label = N_("Edit Indent String");
1116 /***** End Alt Reply Menu *********/
1119 compose_style[ekey_num].ch = -1;
1120 compose_style[ekey_num].name = NULL;
1121 compose_style[ekey_num].label = NULL;
1123 switch(ret = radio_buttons(tmp_20k_buf,
1124 ps->ttyo->screen_rows > 4
1125 ? -FOOTER_ROWS(ps) : -1,
1126 compose_style,
1127 (edited || headers || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps))
1128 ? 'y' : 'n',
1129 'x', NO_HELP, RB_SEQ_SENSITIVE)){
1130 case 'x':
1131 cmd_cancelled("Reply");
1132 return(-1);
1134 case 'A':
1135 ps->reply.keep_attach = (ps->reply.keep_attach + 1) % 2;
1136 break;
1138 case 'B':
1139 ps->reply.signature_bottom = (ps->reply.signature_bottom + 1) % 2;
1140 break;
1142 case 'F':
1143 ps->reply.use_flowed = (ps->reply.use_flowed + 1) % 2;
1144 break;
1146 case 'S':
1147 ps->reply.strip_signature = (ps->reply.strip_signature + 1) % 2;
1148 break;
1150 case 'H':
1151 ps->reply.include_header = (ps->reply.include_header + 1) % 2;
1152 headers = ps->reply.include_header;
1153 break;
1155 case 'R':
1157 void (*prev_screen)(struct pine *) = ps->prev_screen,
1158 (*redraw)(void) = ps->redrawer;
1159 ps->redrawer = NULL;
1160 ps->next_screen = SCREEN_FUN_NULL;
1161 if(role_select_screen(ps, &ps->reply.role_chosen, 1) < 0){
1162 cmd_cancelled("Reply");
1163 ps->next_screen = prev_screen;
1164 ps->redrawer = redraw;
1165 if (ps->redrawer)
1166 (*ps->redrawer)();
1167 continue;
1169 ps->next_screen = prev_screen;
1170 ps->redrawer = redraw;
1171 if(ps->reply.role_chosen)
1172 ps->reply.role_chosen = combine_inherited_role(ps->reply.role_chosen);
1174 if (ps->redrawer)
1175 (*ps->redrawer)();
1176 break;
1178 case 'r':
1179 if(prefix && *prefix){
1180 int done = 0;
1181 char buf[64];
1182 int flags;
1184 while(!done){
1185 strncpy(buf, *prefix, sizeof(buf)-1);
1186 buf[sizeof(buf)-1] = '\0';
1188 flags = OE_APPEND_CURRENT |
1189 OE_KEEP_TRAILING_SPACE |
1190 OE_DISALLOW_HELP |
1191 OE_SEQ_SENSITIVE;
1193 switch(optionally_enter(buf, ps->ttyo->screen_rows > 4
1194 ? -FOOTER_ROWS(ps) : -1,
1195 0, sizeof(buf), "Reply prefix : ",
1196 NULL, NO_HELP, &flags)){
1197 case 0: /* entry successful, continue */
1198 if(flags & OE_USER_MODIFIED){
1199 fs_give((void **)prefix);
1200 *prefix = removing_quotes(cpystr(buf));
1201 orig_sf = ps->reply.use_flowed = *prefix && **prefix ?
1202 (F_OFF(F_QUELL_FLOWED_TEXT, ps)
1203 && F_OFF(F_STRIP_WS_BEFORE_SEND, ps)
1204 && (strcmp(*prefix, "> ") == 0
1205 || strcmp(*prefix, ">") == 0)) : 0;
1206 edited = 1;
1209 done++;
1210 break;
1212 case 1:
1213 cmd_cancelled("Reply");
1215 case -1:
1216 return(-1);
1218 case 4:
1219 EndInverse();
1220 ClearScreen();
1221 redraw_titlebar();
1222 if(ps_global->redrawer != NULL)
1223 (*ps->redrawer)();
1225 redraw_keymenu();
1226 break;
1228 case 3:
1229 break;
1231 case 2:
1232 default:
1233 q_status_message(SM_ORDER, 3, 4,
1234 "Programmer botch in reply_text_query()");
1235 return(-1);
1240 break;
1242 case 'y':
1243 return(1);
1245 case 'n':
1246 return(0);
1248 default:
1249 q_status_message1(SM_ORDER, 3, 4,
1250 "Invalid rval \'%s\'", pretty_command(ret));
1251 return(-1);
1258 * reply_poster_followup - return TRUE if "followup-to" set to "poster"
1260 * NOTE: queues status message indicating such
1263 reply_poster_followup(ENVELOPE *e)
1265 if(e && e->followup_to && !strucmp(e->followup_to, "poster")){
1266 q_status_message(SM_ORDER, 2, 3,
1267 _("Replying to Poster as specified in \"Followup-To\""));
1268 return(1);
1271 return(0);
1276 * reply_news_test - Test given envelope for newsgroup data and copy
1277 * it at the users request
1278 * RETURNS:
1279 * 0 if error or cancel
1280 * 1 reply via email
1281 * 2 follow-up via news
1282 * 3 do both
1285 reply_news_test(ENVELOPE *env, ENVELOPE *outgoing)
1287 int ret = 1;
1288 static ESCKEY_S news_opt[] = { {'f', 'f', "F", N_("Follow-up")},
1289 {'r', 'r', "R", N_("Reply")},
1290 {'b', 'b', "B", N_("Both")},
1291 {-1, 0, NULL, NULL} };
1293 if(env->newsgroups && *env->newsgroups && !reply_poster_followup(env))
1295 * Now that we know a newsgroups field is present,
1296 * ask if the user is posting a follow-up article...
1298 switch(radio_buttons(
1299 _("Follow-up to news group(s), Reply via email to author or Both? "),
1300 -FOOTER_ROWS(ps_global), news_opt, 'r', 'x',
1301 NO_HELP, RB_NORM)){
1302 case 'r' : /* Reply */
1303 ret = 1;
1304 break;
1306 case 'f' : /* Follow-Up via news ONLY! */
1307 ret = 2;
1308 break;
1310 case 'b' : /* BOTH */
1311 ret = 3;
1312 break;
1314 case 'x' : /* cancel or unknown response */
1315 default :
1316 cmd_cancelled("Reply");
1317 ret = 0;
1318 break;
1321 if(ret > 1){
1322 if(env->followup_to){
1323 q_status_message(SM_ORDER, 2, 3,
1324 _("Posting to specified Followup-To groups"));
1325 outgoing->newsgroups = cpystr(env->followup_to);
1327 else if(!outgoing->newsgroups)
1328 outgoing->newsgroups = cpystr(env->newsgroups);
1331 return(ret);
1335 /*----------------------------------------------------------------------
1336 Acquire the pinerc defined signature file
1337 It is allocated here and freed by the caller.
1339 file -- use this file
1340 prenewlines -- prefix the file contents with this many newlines
1341 postnewlines -- postfix the file contents with this many newlines
1342 is_sig -- this is a signature (not a template)
1343 ----*/
1344 char *
1345 get_signature_file(char *file, int prenewlines, int postnewlines, int is_sig)
1347 char *sig, *tmp_sig = NULL, sig_path[MAXPATH+1];
1348 int len, do_the_pipe_thang = 0;
1349 long sigsize = 0L, cntdown;
1351 sig_path[0] = '\0';
1352 if(!signature_path(file, sig_path, MAXPATH))
1353 return(NULL);
1355 dprint((5, "get_signature(%s)\n", sig_path));
1357 if(sig_path[(len=strlen(sig_path))-1] == '|'){
1358 if(is_sig && F_ON(F_DISABLE_PIPES_IN_SIGS, ps_global)){
1359 q_status_message(SM_ORDER | SM_DING, 3, 4,
1360 _("Pipes for signatures are administratively disabled"));
1361 return(NULL);
1363 else if(!is_sig && F_ON(F_DISABLE_PIPES_IN_TEMPLATES, ps_global)){
1364 q_status_message(SM_ORDER | SM_DING, 3, 4,
1365 _("Pipes for templates are administratively disabled"));
1366 return(NULL);
1369 sig_path[len-1] = '\0';
1370 removing_trailing_white_space(sig_path);
1371 do_the_pipe_thang++;
1374 if(!IS_REMOTE(sig_path) && ps_global->VAR_OPER_DIR &&
1375 !in_dir(ps_global->VAR_OPER_DIR, sig_path)){
1376 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1377 /* TRANSLATORS: First arg is the directory name, second is
1378 the file user wants to read but can't. */
1379 _("Can't read file outside %s: %s"),
1380 ps_global->VAR_OPER_DIR, file);
1382 return(NULL);
1385 if(IS_REMOTE(sig_path) || can_access(sig_path, ACCESS_EXISTS) == 0){
1386 if(do_the_pipe_thang){
1387 if(can_access(sig_path, EXECUTE_ACCESS) == 0){
1388 STORE_S *store;
1389 int flags;
1390 PIPE_S *syspipe;
1391 gf_io_t pc, gc;
1392 long start;
1394 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1396 flags = PIPE_READ | PIPE_STDERR | PIPE_NOSHELL;
1398 start = time(0);
1400 if((syspipe = open_system_pipe(sig_path, NULL, NULL, flags, 5,
1401 pipe_callback, pipe_report_error)) != NULL){
1402 unsigned char c;
1403 char *error, *q;
1405 gf_set_so_writec(&pc, store);
1406 gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, READ_FROM_LOCALE);
1407 gf_filter_init();
1409 if((error = gf_pipe(gc, pc)) != NULL){
1410 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1411 gf_clear_so_writec(store);
1412 so_give(&store);
1413 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1414 _("Can't get file: %s"), error);
1415 return(NULL);
1418 if(close_system_pipe(&syspipe, NULL, pipe_callback)){
1419 long now;
1421 now = time(0);
1422 q_status_message2(SM_ORDER, 3, 4,
1423 _("Error running program \"%s\"%s"),
1424 file,
1425 (now - start > 4) ? ": timed out" : "");
1428 gf_clear_so_writec(store);
1430 /* rewind and count chars */
1431 so_seek(store, 0L, 0);
1432 while(so_readc(&c, store) && sigsize < 100000L)
1433 sigsize++;
1435 /* allocate space */
1436 tmp_sig = fs_get((sigsize + 1) * sizeof(char));
1437 tmp_sig[0] = '\0';
1438 q = tmp_sig;
1440 /* rewind and copy chars, no prenewlines... */
1441 so_seek(store, 0L, 0);
1442 cntdown = sigsize;
1443 while(so_readc(&c, store) && cntdown-- > 0L)
1444 *q++ = c;
1446 *q = '\0';
1447 so_give(&store);
1449 else{
1450 so_give(&store);
1451 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1452 _("Error running program \"%s\""),
1453 file);
1456 else
1457 q_status_message(SM_ORDER | SM_DING, 3, 4,
1458 "Error allocating space for sig or template program");
1460 else
1461 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1462 /* TRANSLATORS: Arg is a program name */
1463 _("Can't execute \"%s\": Permission denied"),
1464 sig_path);
1466 else if((IS_REMOTE(sig_path) &&
1467 (tmp_sig = simple_read_remote_file(sig_path, REMOTE_SIG_SUBTYPE))) ||
1468 (tmp_sig = read_file(sig_path, READ_FROM_LOCALE)))
1469 sigsize = strlen(tmp_sig);
1470 else
1471 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1472 /* TRANSLATORS: First arg is error description, 2nd is
1473 filename */
1474 _("Error \"%s\" reading file \"%s\""),
1475 error_description(errno), sig_path);
1478 sig = get_signature_lit(tmp_sig, prenewlines, postnewlines, is_sig, 0);
1479 if(tmp_sig)
1480 fs_give((void **)&tmp_sig);
1482 return(sig);
1487 /*----------------------------------------------------------------------
1488 Partially set up message to forward and pass off to composer/mailer
1490 Args: pine_state -- The usual pine structure
1492 Result: outgoing envelope and body created and passed off to composer/mailer
1494 Create the outgoing envelope for the mail being forwarded, which is
1495 not much more than filling in the subject, and create the message body
1496 of the outgoing message which requires formatting the header from the
1497 envelope of the original messasge.
1498 ----------------------------------------------------------------------*/
1500 forward(struct pine *ps, ACTION_S *role_arg)
1502 char *sig;
1503 int ret, forward_raw_body = 0, rv = 0, i;
1504 long msgno, j, totalmsgs, rflags;
1505 ENVELOPE *env, *outgoing;
1506 BODY *orig_body, *body = NULL;
1507 REPLY_S reply;
1508 void *msgtext = NULL;
1509 gf_io_t pc;
1510 int impl, template_len = 0;
1511 PAT_STATE dummy;
1512 REDRAFT_POS_S *redraft_pos = NULL;
1513 ACTION_S *role = NULL, *nrole;
1514 #if defined(DOS) && !defined(_WINDOWS)
1515 char *reserve;
1516 #endif
1518 dprint((4, "\n - forward -\n"));
1520 memset((void *)&reply, 0, sizeof(reply));
1521 outgoing = mail_newenvelope();
1523 if(ps_global->full_header == 2
1524 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
1525 forward_raw_body = 1;
1527 if((totalmsgs = mn_total_cur(ps->msgmap)) > 1L){
1528 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s forwarded messages...", comatose(totalmsgs));
1529 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1530 outgoing->subject = cpystr(tmp_20k_buf);
1532 else{
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));
1540 goto clean;
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"));
1551 goto clean;
1554 ret = (F_ON(F_FORWARD_AS_ATTACHMENT, ps_global))
1555 ? 'y'
1556 : (totalmsgs > 1L)
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)
1560 : 0;
1562 if(ret == 'x'){
1563 cmd_cancelled("Forward");
1564 so_give((STORE_S **)&msgtext);
1565 goto clean;
1568 /* Setup possible role */
1569 if(role_arg)
1570 role = copy_action(role_arg);
1572 if(!role){
1573 rflags = ROLE_FORWARD;
1574 if(nonempty_patterns(rflags, &dummy)){
1575 /* setup default role */
1576 nrole = NULL;
1577 j = mn_first_cur(ps->msgmap);
1578 do {
1579 role = nrole;
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)
1586 role = nrole;
1587 else
1588 role = NULL;
1590 if(confirm_role(rflags, &role))
1591 role = combine_inherited_role(role);
1592 else{ /* cancel reply */
1593 role = NULL;
1594 cmd_cancelled("Forward");
1595 so_give((STORE_S **)&msgtext);
1596 goto clean;
1601 if(role)
1602 q_status_message1(SM_ORDER, 3, 4,
1603 _("Forwarding using role \"%s\""), role->nick);
1605 outgoing->message_id = generate_message_id(role);
1607 if(role && role->template){
1608 char *filtered;
1610 impl = 1;
1611 filtered = detoken(role, (totalmsgs == 1L) ? env : NULL,
1612 0, 0, 0, &redraft_pos, &impl);
1613 if(filtered){
1614 if(*filtered){
1615 so_puts((STORE_S *)msgtext, filtered);
1616 if(impl == 1)
1617 template_len = strlen(filtered);
1620 fs_give((void **)&filtered);
1623 else
1624 impl = 1;
1626 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
1627 if(impl == 2)
1628 redraft_pos->offset += template_len;
1630 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
1632 fs_give((void **)&sig);
1634 else
1635 so_puts((STORE_S *)msgtext, NEWLINE);
1637 gf_set_so_writec(&pc, (STORE_S *)msgtext);
1639 #if defined(DOS) && !defined(_WINDOWS)
1640 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1641 #define IN_RESERVE 8192
1642 #else
1643 #define IN_RESERVE 16384
1644 #endif
1645 if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
1646 gf_clear_so_writec((STORE_S *) msgtext);
1647 so_give((STORE_S **)&msgtext);
1648 q_status_message(SM_ORDER | SM_DING, 3, 4,
1649 _("Insufficient memory for message text"));
1650 goto clean;
1652 #endif
1655 * If we're forwarding multiple messages *or* the forward-as-mime
1656 * is turned on and the users wants it done that way, package things
1657 * up...
1659 if(ret == 'y'){ /* attach message[s]!!! */
1660 PART **pp;
1661 long totalsize = 0L;
1663 /*---- New Body to start with ----*/
1664 body = mail_newbody();
1665 body->type = TYPEMULTIPART;
1667 /*---- The TEXT part/body ----*/
1668 body->nested.part = mail_newbody_part();
1669 body->nested.part->body.type = TYPETEXT;
1670 body->nested.part->body.contents.text.data = msgtext;
1672 if(totalmsgs > 1L){
1673 /*---- The MULTIPART/DIGEST part ----*/
1674 body->nested.part->next = mail_newbody_part();
1675 body->nested.part->next->body.type = TYPEMULTIPART;
1676 body->nested.part->next->body.subtype = cpystr("Digest");
1677 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Digest of %s messages", comatose(totalmsgs));
1678 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1679 body->nested.part->next->body.description = cpystr(tmp_20k_buf);
1680 pp = &(body->nested.part->next->body.nested.part);
1682 else
1683 pp = &(body->nested.part->next);
1685 /*---- The Message body subparts ----*/
1686 for(msgno = mn_first_cur(ps->msgmap);
1687 msgno > 0L;
1688 msgno = mn_next_cur(ps->msgmap)){
1690 msgno = mn_m2raw(ps->msgmap, msgno);
1691 env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL);
1693 if(forward_mime_msg(ps->mail_stream,msgno,NULL,env,pp,msgtext)){
1694 totalsize += (*pp)->body.size.bytes;
1695 pp = &((*pp)->next);
1697 else
1698 goto bomb;
1701 if(totalmsgs > 1L)
1702 body->nested.part->next->body.size.bytes = totalsize;
1704 else if(totalmsgs > 1L){
1705 int warned = 0;
1706 body = mail_newbody();
1707 body->type = TYPETEXT;
1708 body->contents.text.data = msgtext;
1709 env = NULL;
1711 for(msgno = mn_first_cur(ps->msgmap);
1712 msgno > 0L;
1713 msgno = mn_next_cur(ps->msgmap)){
1715 if(env){ /* put 2 between messages */
1716 gf_puts(NEWLINE, pc);
1717 gf_puts(NEWLINE, pc);
1720 /*--- Grab current envelope ---*/
1721 env = pine_mail_fetchstructure(ps->mail_stream,
1722 mn_m2raw(ps->msgmap, msgno),
1723 &orig_body);
1724 if(!env || !orig_body){
1725 q_status_message1(SM_ORDER,3,4,
1726 _("Error fetching message %s. Can't forward it."),
1727 long2string(msgno));
1728 goto bomb;
1731 if(orig_body == NULL || orig_body->type == TYPETEXT || forward_raw_body) {
1732 forward_delimiter(pc);
1733 reply_forward_header(ps->mail_stream,
1734 mn_m2raw(ps->msgmap, msgno),
1735 NULL, env, pc, "");
1737 if(!get_body_part_text(ps->mail_stream, forward_raw_body ? NULL : orig_body,
1738 mn_m2raw(ps->msgmap, msgno),
1739 forward_raw_body ? NULL : "1", 0L, pc,
1740 NULL, NULL, GBPT_NONE))
1741 goto bomb;
1742 } else if(orig_body->type == TYPEMULTIPART) {
1743 if(!warned++)
1744 q_status_message(SM_ORDER,3,7,
1745 _("WARNING! Attachments not included in multiple forward."));
1747 if(orig_body->nested.part &&
1748 orig_body->nested.part->body.type == TYPETEXT) {
1749 /*---- First part of the message is text -----*/
1750 forward_delimiter(pc);
1751 reply_forward_header(ps->mail_stream,
1752 mn_m2raw(ps->msgmap,msgno),
1753 NULL, env, pc, "");
1755 if(!get_body_part_text(ps->mail_stream,
1756 &orig_body->nested.part->body,
1757 mn_m2raw(ps->msgmap, msgno),
1758 "1", 0L, pc,
1759 NULL, NULL, GBPT_NONE))
1760 goto bomb;
1761 } else {
1762 q_status_message(SM_ORDER,0,3,
1763 _("Multipart with no leading text part!"));
1765 } else {
1766 /*---- Single non-text message of some sort ----*/
1767 q_status_message(SM_ORDER,0,3,
1768 _("Non-text message not included!"));
1772 else if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno,
1773 &orig_body))
1774 && (body = forward_body(ps->mail_stream, env, orig_body, msgno,
1775 NULL, msgtext,
1776 FWD_NONE)))){
1777 q_status_message1(SM_ORDER,3,4,
1778 _("Error fetching message %s. Can't forward it."),
1779 long2string(msgno));
1780 goto clean;
1783 if(ret != 'y' && totalmsgs == 1L && orig_body){
1784 char *charset;
1786 charset = parameter_val(orig_body->parameter, "charset");
1787 if(charset && strucmp(charset, "us-ascii") != 0){
1788 CONV_TABLE *ct;
1791 * There is a non-ascii charset, is there conversion happening?
1793 if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
1794 reply.orig_charset = charset;
1795 charset = NULL;
1799 if(charset)
1800 fs_give((void **) &charset);
1803 * I don't think orig_charset is ever used except possibly
1804 * right here. Hubert 2008-01-15.
1806 if(reply.orig_charset)
1807 reply.forw = 1;
1810 /* fill in reply structure */
1811 reply.forwarded = 1;
1812 reply.mailbox = cpystr(ps->mail_stream->mailbox);
1813 reply.origmbox = cpystr(ps->mail_stream->original_mailbox
1814 ? ps->mail_stream->original_mailbox
1815 : ps->mail_stream->mailbox);
1816 reply.data.uid.msgs = (imapuid_t *) fs_get((totalmsgs + 1) * sizeof(imapuid_t));
1817 if((reply.data.uid.validity = ps->mail_stream->uid_validity) != 0){
1818 reply.uid = 1;
1819 for(msgno = mn_first_cur(ps->msgmap), i = 0;
1820 msgno > 0L;
1821 msgno = mn_next_cur(ps->msgmap), i++)
1822 reply.data.uid.msgs[i] = mail_uid(ps->mail_stream, mn_m2raw(ps->msgmap, msgno));
1824 else{
1825 reply.msgno = 1;
1826 for(msgno = mn_first_cur(ps->msgmap), i = 0;
1827 msgno > 0L;
1828 msgno = mn_next_cur(ps->msgmap), i++)
1829 reply.data.uid.msgs[i] = mn_m2raw(ps->msgmap, msgno);
1832 reply.data.uid.msgs[i] = 0; /* tie off list */
1834 #if defined(DOS) && !defined(_WINDOWS)
1835 free((void *)reserve);
1836 #endif
1837 pine_send(outgoing, &body, "FORWARD MESSAGE",
1838 role, NULL, &reply, redraft_pos,
1839 NULL, NULL, 0);
1840 rv++;
1842 clean:
1843 if(body)
1844 pine_free_body(&body);
1846 if((STORE_S *) msgtext)
1847 gf_clear_so_writec((STORE_S *) msgtext);
1849 mail_free_envelope(&outgoing);
1850 free_redraft_pos(&redraft_pos);
1851 free_action(&role);
1853 if(reply.orig_charset)
1854 fs_give((void **)&reply.orig_charset);
1856 return rv;
1858 bomb:
1859 q_status_message(SM_ORDER | SM_DING, 4, 5,
1860 _("Error fetching message contents. Can't forward message."));
1861 goto clean;
1865 /*----------------------------------------------------------------------
1866 Partially set up message to forward and pass off to composer/mailer
1868 Args: pine_state -- The usual pine structure
1869 message -- The MESSAGECACHE of entry to reply to
1871 Result: outgoing envelope and body created and passed off to composer/mailer
1873 Create the outgoing envelope for the mail being forwarded, which is
1874 not much more than filling in the subject, and create the message body
1875 of the outgoing message which requires formatting the header from the
1876 envelope of the original messasge.
1877 ----------------------------------------------------------------------*/
1878 void
1879 forward_text(struct pine *pine_state, void *text, SourceType source)
1881 ENVELOPE *env;
1882 BODY *body;
1883 gf_io_t pc, gc;
1884 STORE_S *msgtext;
1885 char *enc_error, *sig;
1886 ACTION_S *role = NULL;
1887 PAT_STATE dummy;
1888 long rflags = ROLE_COMPOSE;
1890 if((msgtext = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1891 env = mail_newenvelope();
1892 body = mail_newbody();
1893 body->type = TYPETEXT;
1894 body->contents.text.data = (void *) msgtext;
1896 if(nonempty_patterns(rflags, &dummy)){
1898 * This is really more like Compose, even though it
1899 * is called Forward.
1901 if(confirm_role(rflags, &role))
1902 role = combine_inherited_role(role);
1903 else{ /* cancel */
1904 cmd_cancelled("Composition");
1905 display_message('x');
1906 mail_free_envelope(&env);
1907 pine_free_body(&body);
1908 return;
1912 if(role)
1913 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
1914 role->nick);
1916 env->message_id = generate_message_id(role);
1918 sig = detoken(role, NULL, 2, 0, 1, NULL, NULL);
1919 so_puts(msgtext, (sig && *sig) ? sig : NEWLINE);
1920 so_puts(msgtext, NEWLINE);
1921 so_puts(msgtext, "----- Included text -----");
1922 so_puts(msgtext, NEWLINE);
1923 if(sig)
1924 fs_give((void **)&sig);
1926 gf_filter_init();
1927 gf_set_so_writec(&pc, msgtext);
1928 gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
1929 source, 0);
1931 if((enc_error = gf_pipe(gc, pc)) == NULL){
1932 pine_send(env, &body, "SEND MESSAGE", role, NULL, NULL, NULL,
1933 NULL, NULL, 0);
1934 pine_state->mangled_screen = 1;
1936 else{
1937 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1938 _("Error reading text \"%s\""),enc_error);
1939 display_message('x');
1942 gf_clear_so_writec(msgtext);
1943 mail_free_envelope(&env);
1944 pine_free_body(&body);
1946 else {
1947 q_status_message(SM_ORDER | SM_DING, 3, 4,
1948 _("Error allocating message text"));
1949 display_message('x');
1952 free_action(&role);
1956 /*----------------------------------------------------------------------
1957 Partially set up message to resend and pass off to mailer
1959 Args: pine_state -- The usual pine structure
1961 Result: outgoing envelope and body created and passed off to mailer
1963 Create the outgoing envelope for the mail being resent, which is
1964 not much more than filling in the subject, and create the message body
1965 of the outgoing message which requires formatting the header from the
1966 envelope of the original messasge.
1967 ----------------------------------------------------------------------*/
1969 bounce(struct pine *pine_state, ACTION_S *role)
1971 ENVELOPE *env;
1972 long msgno, rawno;
1973 char *save_to = NULL, **save_toptr = NULL, *errstr = NULL,
1974 *prmpt_who = NULL, *prmpt_cnf = NULL;
1976 dprint((4, "\n - bounce -\n"));
1978 if(mn_total_cur(pine_state->msgmap) > 1L){
1979 save_toptr = &save_to;
1980 if(role)
1981 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1982 mn_total_cur(pine_state->msgmap), role->nick);
1983 else
1984 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages to : "),
1985 mn_total_cur(pine_state->msgmap));
1986 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1987 prmpt_who = cpystr(tmp_20k_buf);
1988 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %ld messages "),
1989 mn_total_cur(pine_state->msgmap));
1990 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1991 prmpt_cnf = cpystr(tmp_20k_buf);
1994 for(msgno = mn_first_cur(pine_state->msgmap);
1995 msgno > 0L;
1996 msgno = mn_next_cur(pine_state->msgmap)){
1998 rawno = mn_m2raw(pine_state->msgmap, msgno);
1999 if((env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL)) != NULL)
2000 errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role,
2001 save_toptr, env->subject, prmpt_who, prmpt_cnf);
2002 else
2003 errstr = _("Can't fetch Subject for Bounce");
2006 if(errstr){
2007 if(*errstr)
2008 q_status_message(SM_ORDER | SM_DING, 4, 7, errstr);
2010 break;
2014 if(save_to)
2015 fs_give((void **)&save_to);
2017 if(prmpt_who)
2018 fs_give((void **) &prmpt_who);
2020 if(prmpt_cnf)
2021 fs_give((void **) &prmpt_cnf);
2023 return(errstr ? 0 : 1);
2028 char *
2029 bounce_msg(MAILSTREAM *stream,
2030 long int rawno,
2031 char *part,
2032 ACTION_S *role,
2033 char **to,
2034 char *subject,
2035 char *pmt_who,
2036 char *pmt_cnf)
2038 char *errstr = NULL;
2039 int was_seen = -1;
2040 ENVELOPE *outgoing;
2041 BODY *body = NULL;
2042 MESSAGECACHE *mc;
2044 #ifdef SMIME
2045 /* When we bounce a message, we will leave the original message
2046 * intact, which means that it will not be signed or encrypted,
2047 * so we turn off signing and encrypting now. It will be turned
2048 * on again in send_exit_for_pico().
2050 if(ps_global->smime)
2051 ps_global->smime->do_sign = ps_global->smime->do_encrypt = 0;
2052 #endif /* SMIME */
2054 if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){
2055 if(pine_simple_send(outgoing, &body, &role, pmt_who, pmt_cnf, to,
2056 !(to && *to) ? SS_PROMPTFORTO : 0) < 0){
2057 errstr = ""; /* p_s_s() better have explained! */
2058 /* clear seen flag */
2059 if(was_seen == 0 && rawno > 0L
2060 && stream && rawno <= stream->nmsgs
2061 && (mc = mail_elt(stream, rawno)) && mc->seen)
2062 mail_flag(stream, long2string(rawno), "\\SEEN", 0);
2066 /* Just for good measure... */
2067 mail_free_envelope(&outgoing);
2068 pine_free_body(&body);
2070 return(errstr); /* no problem-o */
2074 /*----------------------------------------------------------------------
2075 Serve up the current signature within pico for editing
2077 Args: file to edit
2079 Result: signature changed or not.
2080 ---*/
2081 char *
2082 signature_edit(char *sigfile, char *title)
2084 int editor_result;
2085 char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
2086 char *ret = NULL;
2087 STORE_S *msgso, *tmpso = NULL;
2088 gf_io_t gc, pc;
2089 PICO pbf;
2090 struct variable *vars = ps_global->vars;
2091 REMDATA_S *rd = NULL;
2093 if(!signature_path(sigfile, sig_path, MAXPATH))
2094 return(cpystr(_("No signature file defined.")));
2096 if(IS_REMOTE(sigfile)){
2097 rd = rd_create_remote(RemImap, sig_path, REMOTE_SIG_SUBTYPE,
2098 NULL, "Error: ",
2099 _("Can't access remote configuration."));
2100 if(!rd)
2101 return(cpystr(_("Error attempting to edit remote configuration")));
2103 (void)rd_read_metadata(rd);
2105 if(rd->access == MaybeRorW){
2106 if(rd->read_status == 'R')
2107 rd->access = ReadOnly;
2108 else
2109 rd->access = ReadWrite;
2112 if(rd->access != NoExists){
2114 rd_check_remvalid(rd, 1L);
2117 * If the cached info says it is readonly but
2118 * it looks like it's been fixed now, change it to readwrite.
2120 if(rd->read_status == 'R'){
2121 rd_check_readonly_access(rd);
2122 if(rd->read_status == 'W'){
2123 rd->access = ReadWrite;
2124 rd->flags |= REM_OUTOFDATE;
2126 else
2127 rd->access = ReadOnly;
2131 if(rd->flags & REM_OUTOFDATE){
2132 if(rd_update_local(rd) != 0){
2134 dprint((1,
2135 "signature_edit: rd_update_local failed\n"));
2136 rd_close_remdata(&rd);
2137 return(cpystr(_("Can't access remote sig")));
2140 else
2141 rd_open_remote(rd);
2143 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
2144 rd_close_remdata(&rd);
2145 return(cpystr(_("Can't get write permission for remote sig")));
2148 rd->flags |= DO_REMTRIM;
2150 strncpy(sig_path, rd->lf, sizeof(sig_path)-1);
2151 sig_path[sizeof(sig_path)-1] = '\0';
2154 standard_picobuf_setup(&pbf);
2155 pbf.tty_fix = PineRaw;
2156 pbf.composer_help = h_composer_sigedit;
2157 pbf.exittest = sigedit_exit_for_pico;
2158 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2159 ? upload_msg_to_pico : NULL;
2160 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2161 ? VAR_EDITOR : NULL;
2162 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2163 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2164 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2165 pbf.allow_flowed_text = 0;
2167 pbf.pine_anchor = set_titlebar(title,
2168 ps_global->mail_stream,
2169 ps_global->context_current,
2170 ps_global->cur_folder,
2171 ps_global->msgmap,
2172 0, FolderName, 0, 0, NULL);
2174 /* NOTE: at this point, a lot of pico struct fields are null'd out
2175 * thanks to the leading memset; in particular "headents" which tells
2176 * pico to behave like a normal editor (though modified slightly to
2177 * let the caller dictate the file to edit and such)...
2180 if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
2181 size_t l;
2183 l = strlen(VAR_OPER_DIR) + 100;
2184 ret = (char *) fs_get((l+1) * sizeof(char));
2185 snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR);
2186 ret[l] = '\0';
2187 return(ret);
2191 * Now alloc and init the text to pass pico
2193 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2194 ret = cpystr(_("Error allocating space for file"));
2195 dprint((1, "Can't alloc space for signature_edit"));
2196 return(ret);
2198 else
2199 pbf.msgtext = so_text(msgso);
2201 if(can_access(sig_path, READ_ACCESS) == 0
2202 && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){
2203 char *problem = error_description(errno);
2205 snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"),
2206 sig_path, problem ? problem : "<NULL>");
2207 errbuf[sizeof(errbuf)-1] = '\0';
2208 ret = cpystr(errbuf);
2210 dprint((1, "signature_edit: can't open %s: %s", sig_path,
2211 problem ? problem : "<NULL>"));
2212 return(ret);
2214 else if(tmpso){ /* else, fill pico's edit buffer */
2215 gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */
2216 gf_set_so_writec(&pc, msgso);
2217 gf_filter_init(); /* no filters needed */
2218 if((errstr = gf_pipe(gc, pc)) != NULL){
2219 snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr);
2220 errbuf[sizeof(errbuf)-1] = '\0';
2221 ret = cpystr(errbuf);
2224 gf_clear_so_readc(tmpso);
2225 gf_clear_so_writec(msgso);
2226 so_give(&tmpso);
2229 if(!errstr){
2230 #ifdef _WINDOWS
2231 mswin_setwindowmenu (MENU_COMPOSER);
2232 #endif
2234 /*------ OK, Go edit the signature ------*/
2235 editor_result = pico(&pbf);
2237 #ifdef _WINDOWS
2238 mswin_setwindowmenu (MENU_DEFAULT);
2239 #endif
2240 if(editor_result & COMP_GOTHUP){
2241 hup_signal(); /* do what's normal for a hup */
2243 else{
2244 fix_windsize(ps_global);
2245 init_signals();
2248 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2250 else{
2251 /*------ Must have an edited buffer, write it to .sig -----*/
2252 our_unlink(sig_path); /* blast old copy */
2253 if((tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){
2254 so_seek(msgso, 0L, 0);
2255 gf_set_so_readc(&gc, msgso); /* read from pico buf */
2256 gf_set_so_writec(&pc, tmpso); /* write sig file */
2257 gf_filter_init(); /* no filters needed */
2258 if((errstr = gf_pipe(gc, pc)) != NULL){
2259 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2260 errstr);
2261 errbuf[sizeof(errbuf)-1] = '\0';
2262 ret = cpystr(errbuf);
2265 gf_clear_so_readc(msgso);
2266 gf_clear_so_writec(tmpso);
2267 if(so_give(&tmpso)){
2268 errstr = error_description(errno);
2269 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2270 errstr);
2271 errbuf[sizeof(errbuf)-1] = '\0';
2272 ret = cpystr(errbuf);
2275 if(IS_REMOTE(sigfile)){
2276 int e, we_cancel;
2277 char datebuf[200];
2279 datebuf[0] = '\0';
2281 we_cancel = busy_cue("Copying to remote sig", NULL, 1);
2282 if((e = rd_update_remote(rd, datebuf)) != 0){
2283 if(e == -1){
2284 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2285 _("Error opening temporary sig file %s: %s"),
2286 rd->lf, error_description(errno));
2287 dprint((1,
2288 "write_remote_sig: error opening temp file %s\n",
2289 rd->lf ? rd->lf : "?"));
2291 else{
2292 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2293 _("Error copying to %s: %s"),
2294 rd->rn, error_description(errno));
2295 dprint((1,
2296 "write_remote_sig: error copying from %s to %s\n",
2297 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
2300 q_status_message(SM_ORDER | SM_DING, 5, 5,
2301 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2303 else{
2304 rd_update_metadata(rd, datebuf);
2305 rd->read_status = 'W';
2308 rd_close_remdata(&rd);
2310 if(we_cancel)
2311 cancel_busy_cue(-1);
2314 else{
2315 snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path);
2316 errbuf[sizeof(errbuf)-1] = '\0';
2317 ret = cpystr(errbuf);
2318 dprint((1, "signature_edit: can't write %s",
2319 sig_path));
2324 standard_picobuf_teardown(&pbf);
2325 so_give(&msgso);
2326 return(ret);
2330 /*----------------------------------------------------------------------
2331 Serve up the current signature within pico for editing
2333 Args: literal signature to edit
2335 Result: raw edited signature is returned in result arg
2336 ---*/
2337 char *
2338 signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help)
2340 int editor_result;
2341 char *errstr = NULL;
2342 char *ret = NULL;
2343 STORE_S *msgso;
2344 PICO pbf;
2345 struct variable *vars = ps_global->vars;
2347 standard_picobuf_setup(&pbf);
2348 pbf.tty_fix = PineRaw;
2349 pbf.search_help = h_sigedit_search;
2350 pbf.composer_help = composer_help;
2351 pbf.exittest = sigedit_exit_for_pico;
2352 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2353 ? upload_msg_to_pico : NULL;
2354 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2355 ? VAR_EDITOR : NULL;
2356 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2357 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2358 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2359 pbf.allow_flowed_text = 0;
2361 pbf.pine_anchor = set_titlebar(title,
2362 ps_global->mail_stream,
2363 ps_global->context_current,
2364 ps_global->cur_folder,
2365 ps_global->msgmap,
2366 0, FolderName, 0, 0, NULL);
2368 /* NOTE: at this point, a lot of pico struct fields are null'd out
2369 * thanks to the leading memset; in particular "headents" which tells
2370 * pico to behave like a normal editor (though modified slightly to
2371 * let the caller dictate the file to edit and such)...
2375 * Now alloc and init the text to pass pico
2377 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2378 ret = cpystr(_("Error allocating space"));
2379 dprint((1, "Can't alloc space for signature_edit_lit"));
2380 return(ret);
2382 else
2383 pbf.msgtext = so_text(msgso);
2385 so_puts(msgso, litsig ? litsig : "");
2388 if(!errstr){
2389 #ifdef _WINDOWS
2390 mswin_setwindowmenu (MENU_COMPOSER);
2391 #endif
2393 /*------ OK, Go edit the signature ------*/
2394 editor_result = pico(&pbf);
2396 #ifdef _WINDOWS
2397 mswin_setwindowmenu (MENU_DEFAULT);
2398 #endif
2399 if(editor_result & COMP_GOTHUP){
2400 hup_signal(); /* do what's normal for a hup */
2402 else{
2403 fix_windsize(ps_global);
2404 init_signals();
2407 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2408 ret = cpystr(_("Edit Cancelled"));
2410 else{
2411 /*------ Must have an edited buffer, write it to .sig -----*/
2412 unsigned char c;
2413 int cnt = 0;
2414 char *p;
2416 so_seek(msgso, 0L, 0);
2417 while(so_readc(&c, msgso))
2418 cnt++;
2420 *result = (char *)fs_get((cnt+1) * sizeof(char));
2421 p = *result;
2422 so_seek(msgso, 0L, 0);
2423 while(so_readc(&c, msgso))
2424 *p++ = c;
2426 *p = '\0';
2430 standard_picobuf_teardown(&pbf);
2431 so_give(&msgso);
2432 return(ret);
2437 * Returns 0 for Save Changes and exit
2438 * 1 for Cancel Exit
2439 * -1 exit but Dont Save Changes
2442 sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
2443 char **result)
2445 int rv;
2446 char *rstr = NULL;
2447 void (*redraw)(void) = ps_global->redrawer;
2448 static ESCKEY_S opts[] = {
2449 {'s', 's', "S", N_("Save changes")},
2450 {'d', 'd', "D", N_("Don't save changes")},
2451 {-1, 0, NULL, NULL}
2454 ps_global->redrawer = redraw_pico;
2455 fix_windsize(ps_global);
2457 while(1){
2458 rv = radio_buttons(_("Exit editor? "),
2459 -FOOTER_ROWS(ps_global), opts,
2460 's', 'x', h_exit_editor, RB_NORM);
2461 if(rv == 's'){ /* user ACCEPTS! */
2462 break;
2464 else if(rv == 'd'){ /* Declined! */
2465 rstr = _("No Changes Saved");
2466 break;
2468 else if(rv == 'x'){ /* Cancelled! */
2469 rstr = _("Exit Cancelled");
2470 break;
2474 if(result)
2475 *result = rstr;
2477 ps_global->redrawer = redraw;
2478 return((rv == 's') ? 0 : (rv == 'd') ? -1 : 1);
2483 * Common stuff we almost always want to set when calling pico.
2485 void
2486 standard_picobuf_setup(PICO *pbf)
2488 memset(pbf, 0, sizeof(*pbf));
2490 pbf->pine_version = ALPINE_VERSION;
2491 pbf->fillcolumn = ps_global->composer_fillcol;
2492 pbf->menu_rows = FOOTER_ROWS(ps_global) - 1;
2493 pbf->colors = colors_for_pico();
2494 pbf->wordseps = user_wordseps(ps_global->VAR_WORDSEPS);
2495 pbf->helper = helper;
2496 pbf->showmsg = display_message_for_pico;
2497 pbf->suspend = do_suspend;
2498 pbf->keybinput = cmd_input_for_pico;
2499 pbf->tty_fix = ttyfix; /* watch out for this one */
2500 pbf->newmail = new_mail_for_pico;
2501 pbf->ckptdir = checkpoint_dir_for_pico;
2502 pbf->resize = resize_for_pico;
2503 pbf->input_cs = ps_global->input_cs;
2504 pbf->winch_cleanup = winch_cleanup;
2505 pbf->search_help = h_composer_search;
2506 pbf->ins_help = h_composer_ins;
2507 pbf->ins_m_help = h_composer_ins_m;
2508 pbf->composer_help = h_composer;
2509 pbf->browse_help = h_composer_browse;
2510 pbf->attach_help = h_composer_ctrl_j;
2512 pbf->pine_flags =
2513 ( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L)
2514 | (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L)
2515 | (ps_global->restricted ? P_SECURE : 0L)
2516 | (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L)
2517 | (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L)
2518 | (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L)
2519 | (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L)
2520 | (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L)
2521 | (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L)
2522 | (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L)
2523 | (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L)
2524 | (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L)
2525 | (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L)
2526 | (!ps_global->pass_ctrl_chars
2527 && !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L)
2528 | ((F_ON(F_ENABLE_ALT_ED,ps_global)
2529 || F_ON(F_ALT_ED_NOW,ps_global)
2530 || (ps_global->VAR_EDITOR
2531 && ps_global->VAR_EDITOR[0]
2532 && ps_global->VAR_EDITOR[0][0]))
2533 ? P_ADVANCED : 0L)
2534 | ((!ps_global->keyboard_charmap
2535 || !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
2536 ? P_HIBITIGN : 0L));
2538 if(ps_global->VAR_OPER_DIR){
2539 pbf->oper_dir = ps_global->VAR_OPER_DIR;
2540 pbf->pine_flags |= P_TREE;
2543 pbf->home_dir = ps_global->home_dir;
2547 void
2548 standard_picobuf_teardown(PICO *pbf)
2550 if(pbf){
2551 if(pbf->colors)
2552 free_pcolors(&pbf->colors);
2554 if(pbf->wordseps)
2555 fs_give((void **) &pbf->wordseps);
2560 /*----------------------------------------------------------------------
2561 Call back for pico to use to check for new mail.
2563 Args: cursor -- pointer to in to tell caller if cursor location changed
2564 if NULL, turn off cursor positioning.
2565 timing -- whether or not it's a good time to check
2568 Returns: returns 1 on success, zero on error.
2569 ----*/
2570 long
2571 new_mail_for_pico(int timing, int status)
2574 * If we're not interested in the status, don't display the busy
2575 * cue either...
2577 /* don't know where the cursor's been, reset it */
2578 clear_cursor_pos();
2579 return(new_mail(0, timing,
2580 (status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT
2581 | NM_FROM_COMPOSER));
2585 void
2586 cmd_input_for_pico(void)
2588 zero_new_mail_count();
2592 /*----------------------------------------------------------------------
2593 Call back for pico to get newmail status messages displayed
2595 Args: x -- char processed
2597 Returns:
2598 ----*/
2600 display_message_for_pico(int x)
2602 int rv;
2604 clear_cursor_pos(); /* can't know where cursor is */
2605 mark_status_dirty(); /* don't count on cached text */
2606 fix_windsize(ps_global);
2607 init_sigwinch();
2608 display_message(x);
2609 rv = ps_global->mangled_screen;
2610 ps_global->mangled_screen = 0;
2611 return(rv);
2615 /*----------------------------------------------------------------------
2616 Call back for pico to get desired directory for its check point file
2618 Args: s -- buffer to write directory name
2619 n -- length of that buffer
2621 Returns: pointer to static buffer
2622 ----*/
2623 char *
2624 checkpoint_dir_for_pico(char *s, size_t n)
2626 #if defined(DOS) || defined(OS2)
2628 * we can't assume anything about root or home dirs, so
2629 * just plunk it down in the same place as the pinerc
2631 if(!getenv("HOME")){
2632 char *lc = last_cmpnt(ps_global->pinerc);
2634 if(lc != NULL){
2635 strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc));
2636 s[MIN(n-1,lc-ps_global->pinerc)] = '\0';
2638 else{
2639 strncpy(s, ".\\", n-1);
2640 s[n-1] = '\0';
2643 else
2644 #endif
2645 strncpy(s, ps_global->home_dir, n-1);
2646 s[n-1] = '\0';
2648 return(s);
2652 /*----------------------------------------------------------------------
2653 Call back for pico to tell us the window size's changed
2655 Args: none
2657 Returns: none (but pine's ttyo structure may have been updated)
2658 ----*/
2659 void
2660 resize_for_pico(void)
2662 fix_windsize(ps_global);
2666 PCOLORS *
2667 colors_for_pico(void)
2669 PCOLORS *colors = NULL;
2670 struct variable *vars = ps_global->vars;
2672 if (pico_usingcolor()){
2673 colors = (PCOLORS *)fs_get(sizeof(PCOLORS));
2675 colors->tbcp = current_titlebar_color();
2677 if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
2678 colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
2679 VAR_KEYLABEL_BACK_COLOR);
2680 if (!pico_is_good_colorpair(colors->klcp))
2681 free_color_pair(&colors->klcp);
2683 else colors->klcp = NULL;
2685 if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
2686 colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR,
2687 VAR_KEYNAME_BACK_COLOR);
2689 else colors->kncp = NULL;
2691 if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){
2692 colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR,
2693 VAR_STATUS_BACK_COLOR);
2695 else colors->stcp = NULL;
2697 if (VAR_PROMPT_FORE_COLOR && VAR_PROMPT_BACK_COLOR){
2698 colors->prcp = new_color_pair(VAR_PROMPT_FORE_COLOR,
2699 VAR_PROMPT_BACK_COLOR);
2701 else colors->prcp = NULL;
2703 if (VAR_QUOTE1_FORE_COLOR && VAR_QUOTE1_BACK_COLOR){
2704 colors->qlcp = new_color_pair(VAR_QUOTE1_FORE_COLOR,
2705 VAR_QUOTE1_BACK_COLOR);
2707 else colors->qlcp = NULL;
2709 if (VAR_QUOTE2_FORE_COLOR && VAR_QUOTE2_BACK_COLOR){
2710 colors->qllcp = new_color_pair(VAR_QUOTE2_FORE_COLOR,
2711 VAR_QUOTE2_BACK_COLOR);
2713 else colors->qllcp = NULL;
2715 if (VAR_QUOTE3_FORE_COLOR && VAR_QUOTE3_BACK_COLOR){
2716 colors->qlllcp = new_color_pair(VAR_QUOTE3_FORE_COLOR,
2717 VAR_QUOTE3_BACK_COLOR);
2719 else colors->qlllcp = NULL;
2721 if (VAR_NORM_FORE_COLOR && VAR_NORM_BACK_COLOR){
2722 colors->ntcp = new_color_pair(VAR_NORM_FORE_COLOR,
2723 VAR_NORM_BACK_COLOR);
2725 else colors->ntcp = NULL;
2727 if (VAR_REV_FORE_COLOR && VAR_REV_BACK_COLOR){
2728 colors->rtcp = new_color_pair(VAR_REV_FORE_COLOR,
2729 VAR_REV_BACK_COLOR);
2731 else colors->rtcp = NULL;
2733 if (VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR){
2734 colors->sbcp = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
2735 VAR_SIGNATURE_BACK_COLOR);
2737 else colors->sbcp = NULL;
2740 return colors;
2744 void
2745 free_pcolors(PCOLORS **colors)
2747 if (*colors){
2748 if ((*colors)->tbcp)
2749 free_color_pair(&(*colors)->tbcp);
2750 if ((*colors)->kncp)
2751 free_color_pair(&(*colors)->kncp);
2752 if ((*colors)->klcp)
2753 free_color_pair(&(*colors)->klcp);
2754 if ((*colors)->stcp)
2755 free_color_pair(&(*colors)->stcp);
2756 if ((*colors)->prcp)
2757 free_color_pair(&(*colors)->prcp);
2758 if ((*colors)->qlcp)
2759 free_color_pair(&(*colors)->qlcp);
2760 if ((*colors)->qllcp)
2761 free_color_pair(&(*colors)->qllcp);
2762 if ((*colors)->qlllcp)
2763 free_color_pair(&(*colors)->qlllcp);
2764 if ((*colors)->ntcp)
2765 free_color_pair(&(*colors)->ntcp);
2766 if ((*colors)->rtcp)
2767 free_color_pair(&(*colors)->rtcp);
2768 if ((*colors)->sbcp)
2769 free_color_pair(&(*colors)->sbcp);
2770 fs_give((void **)colors);
2771 *colors = NULL;