* Update to version 2.19.5
[alpine.git] / alpine / reply.c
blob23d40b1899f2364b2b020cb6675dec3aa0cedd32
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-2014 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 long msgno, j, totalm, rflags, *seq = NULL;
110 int i, include_text = 0, times = -1, warned = 0, rv = 0,
111 flags = RSF_QUERY_REPLY_ALL, reply_raw_body = 0;
112 int rolemsg = 0, copytomsg = 0;
113 gf_io_t pc;
114 PAT_STATE dummy;
115 REDRAFT_POS_S *redraft_pos = NULL;
116 ACTION_S *role = NULL, *nrole;
117 #if defined(DOS) && !defined(_WINDOWS)
118 char *reserve;
119 #endif
121 outgoing = mail_newenvelope();
122 totalm = mn_total_cur(pine_state->msgmap);
123 seq = (long *)fs_get(((size_t)totalm + 1) * sizeof(long));
125 dprint((4,"\n - reply (%s msgs) -\n", comatose(totalm)));
127 saved_from = (ADDRESS *) NULL;
128 saved_to = (ADDRESS *) NULL;
129 saved_cc = (ADDRESS *) NULL;
130 saved_resent = (ADDRESS *) NULL;
132 us_in_to_and_cc = (ADDRESS *) NULL;
134 outgoing->subject = NULL;
136 memset((void *)&reply, 0, sizeof(reply));
138 if(ps_global->full_header == 2
139 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
140 reply_raw_body = 1;
143 * We may have to loop through first to figure out what default
144 * reply-indent-string to offer...
146 if(mn_total_cur(pine_state->msgmap) > 1 &&
147 F_ON(F_ENABLE_EDIT_REPLY_INDENT, pine_state) &&
148 reply_quote_str_contains_tokens()){
149 for(msgno = mn_first_cur(pine_state->msgmap);
150 msgno > 0L && !tmpfix;
151 msgno = mn_next_cur(pine_state->msgmap)){
153 env = pine_mail_fetchstructure(pine_state->mail_stream,
154 mn_m2raw(pine_state->msgmap, msgno),
155 NULL);
156 if(!env) {
157 q_status_message1(SM_ORDER,3,4,
158 _("Error fetching message %s. Can't reply to it."),
159 long2string(msgno));
160 goto done_early;
163 if(!tmpfix){ /* look for prefix? */
164 tmpfix = reply_quote_str(env);
165 if(prefix){
166 i = strcmp(tmpfix, prefix);
167 fs_give((void **) &tmpfix);
168 if(i){ /* don't check back if dissimilar */
169 fs_give((void **) &prefix);
171 * We free prefix, not tmpfix. We set tmpfix to prefix
172 * so that we won't come check again.
174 tmpfix = prefix = cpystr("> ");
177 else{
178 prefix = tmpfix;
179 tmpfix = NULL; /* check back later? */
184 tmpfix = prefix;
188 * Loop thru the selected messages building the
189 * outgoing envelope's destinations...
191 for(msgno = mn_first_cur(pine_state->msgmap);
192 msgno > 0L;
193 msgno = mn_next_cur(pine_state->msgmap)){
195 /*--- Grab current envelope ---*/
196 env = pine_mail_fetchstructure(pine_state->mail_stream,
197 seq[++times] = mn_m2raw(pine_state->msgmap, msgno),
198 NULL);
199 if(!env) {
200 q_status_message1(SM_ORDER,3,4,
201 _("Error fetching message %s. Can't reply to it."),
202 long2string(msgno));
203 goto done_early;
207 * We check for the prefix here if we didn't do it in the first
208 * loop above. This is just to save having to go through the loop
209 * twice in the cases where we don't need to.
211 if(!tmpfix){
212 tmpfix = reply_quote_str(env);
213 if(prefix){
214 i = strcmp(tmpfix, prefix);
215 fs_give((void **) &tmpfix);
216 if(i){ /* don't check back if dissimilar */
217 fs_give((void **) &prefix);
218 tmpfix = prefix = cpystr("> ");
221 else{
222 prefix = tmpfix;
223 tmpfix = NULL; /* check back later? */
228 * For consistency, the first question is always "include text?"
230 if(!times){ /* only first time */
231 char *p = cpystr(prefix);
233 if((include_text=reply_text_query(pine_state,totalm,&prefix)) < 0)
234 goto done_early;
236 /* edited prefix? */
237 if(strcmp(p, prefix))
238 tmpfix = prefix; /* stop looking */
240 fs_give((void **)&p);
244 * If we're agg-replying or there's a newsgroup and the user want's
245 * to post to news *and* via email, add relevant addresses to the
246 * outgoing envelope...
248 * The single message case gets us around the aggregate reply
249 * to messages in a mixed mail-news archive where some might
250 * have newsgroups and others not or whatever.
252 if(totalm > 1L || ((i = reply_news_test(env, outgoing)) & 1)){
253 if(totalm > 1)
254 flags |= RSF_FORCE_REPLY_TO;
256 if(!reply_harvest(pine_state, seq[times], NULL, env,
257 &saved_from, &saved_to, &saved_cc,
258 &saved_resent, &flags))
259 goto done_early;
261 else if(i == 0)
262 goto done_early;
264 /* collect a list of addresses that are us in to and cc fields */
265 if(env->to)
266 if((ap=reply_cp_addr(pine_state, 0L, NULL,
267 NULL, us_in_to_and_cc, NULL,
268 env->to, RCA_ONLY_US)) != NULL)
269 reply_append_addr(&us_in_to_and_cc, ap);
271 if(env->cc)
272 if((ap=reply_cp_addr(pine_state, 0L, NULL,
273 NULL, us_in_to_and_cc, NULL,
274 env->cc, RCA_ONLY_US)) != NULL)
275 reply_append_addr(&us_in_to_and_cc, ap);
277 /*------------ Format the subject line ---------------*/
278 if(outgoing->subject){
280 * if reply to more than one message, and all subjects
281 * match, so be it. otherwise set it to something generic...
283 if(strucmp(outgoing->subject,
284 reply_subject(env->subject,tmp_20k_buf,SIZEOF_20KBUF))){
285 fs_give((void **)&outgoing->subject);
286 outgoing->subject = cpystr("Re: several messages");
289 else
290 outgoing->subject = reply_subject(env->subject, NULL, 0);
293 /* fill reply header */
294 reply_seed(pine_state, outgoing, env, saved_from,
295 saved_to, saved_cc, saved_resent,
296 &fcc, flags & RSF_FORCE_REPLY_ALL, &errmsg);
297 if(errmsg){
298 if(*errmsg){
299 q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
300 display_message(NO_OP_COMMAND);
303 fs_give((void **)&errmsg);
306 if(sp_expunge_count(pine_state->mail_stream)) /* cur msg expunged */
307 goto done_early;
309 /* Setup possible role */
310 if(role_arg)
311 role = copy_action(role_arg);
313 if(!role){
314 rflags = ROLE_REPLY;
315 if(nonempty_patterns(rflags, &dummy)){
316 /* setup default role */
317 nrole = NULL;
318 j = mn_first_cur(pine_state->msgmap);
319 do {
320 role = nrole;
321 nrole = set_role_from_msg(pine_state, rflags,
322 mn_m2raw(pine_state->msgmap, j),
323 NULL);
324 } while(nrole && (!role || nrole == role)
325 && (j=mn_next_cur(pine_state->msgmap)) > 0L);
327 if(!role || nrole == role)
328 role = nrole;
329 else
330 role = NULL;
332 if(confirm_role(rflags, &role))
333 role = combine_inherited_role(role);
334 else{ /* cancel reply */
335 role = NULL;
336 cmd_cancelled("Reply");
337 goto done_early;
343 * Reply_seed may call c-client in get_fcc_based_on_to, so env may
344 * no longer be valid. Get it again.
345 * Similarly for set_role_from_message.
347 env = pine_mail_fetchstructure(pine_state->mail_stream, seq[times], NULL);
349 if(role){
350 rolemsg++;
351 /* override fcc gotten in reply_seed */
352 if(role->fcc && fcc)
353 fs_give((void **) &fcc);
356 if(F_ON(F_COPY_TO_TO_FROM, pine_state) && !(role && role->from)){
358 * A list of all of our addresses that appear in the To
359 * and cc fields is in us_in_to_and_cc.
360 * If there is exactly one address in that list then
361 * use it for the outgoing From.
363 if(us_in_to_and_cc && !us_in_to_and_cc->next){
364 PINEFIELD *custom, *pf;
365 ADDRESS *a = NULL;
366 char *addr = NULL;
369 * Check to see if this address is different from what
370 * we would have used anyway. If it is, notify the user
371 * with a status message. This is pretty hokey because we're
372 * mimicking how pine_send would set the From address and
373 * there is no coordination between the two.
376 /* in case user has a custom From value */
377 custom = parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef);
379 pf = (PINEFIELD *) fs_get(sizeof(*pf));
380 memset((void *) pf, 0, sizeof(*pf));
381 pf->name = cpystr("From");
382 pf->addr = &a;
383 if(set_default_hdrval(pf, custom) >= UseAsDef
384 && pf->textbuf && pf->textbuf[0]){
385 removing_trailing_white_space(pf->textbuf);
386 (void)removing_double_quotes(pf->textbuf);
387 build_address(pf->textbuf, &addr, NULL, NULL, NULL);
388 rfc822_parse_adrlist(pf->addr, addr, ps_global->maildomain);
389 if(addr)
390 fs_give((void **) &addr);
393 if(!*pf->addr)
394 *pf->addr = generate_from();
396 if(*pf->addr && !address_is_same(*pf->addr, us_in_to_and_cc)){
397 copytomsg++;
398 if(!role){
399 role = (ACTION_S *) fs_get(sizeof(*role));
400 memset((void *) role, 0, sizeof(*role));
401 role->is_a_role = 1;
404 role->from = us_in_to_and_cc;
405 us_in_to_and_cc = NULL;
408 free_customs(custom);
409 free_customs(pf);
413 if(role){
414 if(rolemsg && copytomsg)
415 q_status_message1(SM_ORDER, 3, 4,
416 _("Replying using role \"%s\" and To as From"), role->nick);
417 else if(rolemsg)
418 q_status_message1(SM_ORDER, 3, 4,
419 _("Replying using role \"%s\""), role->nick);
420 else if(copytomsg)
421 q_status_message(SM_ORDER, 3, 4,
422 _("Replying using incoming To as outgoing From"));
425 if(us_in_to_and_cc)
426 mail_free_address(&us_in_to_and_cc);
429 seq[++times] = -1L; /* mark end of sequence list */
431 /*========== Other miscelaneous fields ===================*/
432 outgoing->in_reply_to = reply_in_reply_to(env);
433 outgoing->references = reply_build_refs(env);
434 outgoing->message_id = generate_message_id();
436 if(!outgoing->to &&
437 !outgoing->cc &&
438 !outgoing->bcc &&
439 !outgoing->newsgroups)
440 q_status_message(SM_ORDER | SM_DING, 3, 6,
441 _("Warning: no valid addresses to reply to!"));
444 /*==================== Now fix up the message body ====================*/
447 * create storage object to be used for message text
449 if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
450 q_status_message(SM_ORDER | SM_DING, 3, 4,
451 _("Error allocating message text"));
452 goto done_early;
455 gf_set_so_writec(&pc, (STORE_S *) msgtext);
457 /*---- Include the original text if requested ----*/
458 if(include_text && totalm > 1L){
459 char *sig;
460 int impl, template_len = 0, leave_cursor_at_top = 0;
463 env = NULL;
464 if(role && role->template){
465 char *filtered;
467 impl = 0;
468 filtered = detoken(role, env, 0,
469 F_ON(F_SIG_AT_BOTTOM, ps_global) ? 1 : 0,
470 0, &redraft_pos, &impl);
471 if(filtered){
472 if(*filtered){
473 so_puts((STORE_S *)msgtext, filtered);
474 if(impl == 1)
475 template_len = strlen(filtered);
476 else if(impl == 2)
477 leave_cursor_at_top++;
480 fs_give((void **)&filtered);
482 else
483 impl = 1;
485 else
486 impl = 1;
488 if((sig = reply_signature(role, env, &redraft_pos, &impl)) &&
489 F_OFF(F_SIG_AT_BOTTOM, ps_global)){
492 * If CURSORPOS was set explicitly in sig_file, and there was a
493 * template file before that, we need to adjust the offset by the
494 * length of the template file. However, if the template had
495 * a set CURSORPOS in it then impl was 2 before getting to the
496 * signature, so offset wouldn't have been reset by the signature
497 * CURSORPOS and offset would already be correct. That case will
498 * be ok here because template_len will be 0 and adding it does
499 * nothing. If template
500 * didn't have CURSORPOS in it, then impl was 1 and got set to 2
501 * by the CURSORPOS in the sig. In that case we have to adjust the
502 * offset. That's what the next line does. It adjusts it if
503 * template_len is nonzero and if CURSORPOS was set in sig_file.
505 if(impl == 2)
506 redraft_pos->offset += template_len;
508 if(*sig)
509 so_puts((STORE_S *)msgtext, sig);
512 * Set sig to NULL because we've already used it. If SIG_AT_BOTTOM
513 * is set, we won't have used it yet and want it to be non-NULL.
515 fs_give((void **)&sig);
519 * Only put cursor in sig if there is a cursorpos there but not
520 * one in the template, and sig-at-bottom.
522 if(!(sig && impl == 2 && !leave_cursor_at_top))
523 leave_cursor_at_top++;
525 body = mail_newbody();
526 body->type = TYPETEXT;
527 body->contents.text.data = msgtext;
529 for(msgno = mn_first_cur(pine_state->msgmap);
530 msgno > 0L;
531 msgno = mn_next_cur(pine_state->msgmap)){
533 if(env){ /* put 2 between messages */
534 gf_puts(NEWLINE, pc);
535 gf_puts(NEWLINE, pc);
538 /*--- Grab current envelope ---*/
539 env = pine_mail_fetchstructure(pine_state->mail_stream,
540 mn_m2raw(pine_state->msgmap, msgno),
541 &orig_body);
542 if(!env){
543 q_status_message1(SM_ORDER,3,4,
544 _("Error fetching message %s. Can't reply to it."),
545 long2string(mn_get_cur(pine_state->msgmap)));
546 goto done_early;
549 if(orig_body == NULL || orig_body->type == TYPETEXT || reply_raw_body) {
550 reply_delimiter(env, role, pc);
551 if(F_ON(F_INCLUDE_HEADER, pine_state))
552 reply_forward_header(pine_state->mail_stream,
553 mn_m2raw(pine_state->msgmap,msgno),
554 NULL, env, pc, prefix);
556 get_body_part_text(pine_state->mail_stream, reply_raw_body ? NULL : orig_body,
557 mn_m2raw(pine_state->msgmap, msgno),
558 reply_raw_body ? NULL : "1", 0L, pc, prefix,
559 NULL, GBPT_NONE);
561 else if(orig_body->type == TYPEMULTIPART) {
562 if(!warned++)
563 q_status_message(SM_ORDER,3,7,
564 _("WARNING! Attachments not included in multiple reply."));
566 if(orig_body->nested.part
567 && orig_body->nested.part->body.type == TYPETEXT) {
568 /*---- First part of the message is text -----*/
569 reply_delimiter(env, role, pc);
570 if(F_ON(F_INCLUDE_HEADER, pine_state))
571 reply_forward_header(pine_state->mail_stream,
572 mn_m2raw(pine_state->msgmap,
573 msgno),
574 NULL, env, pc, prefix);
576 get_body_part_text(pine_state->mail_stream,
577 &orig_body->nested.part->body,
578 mn_m2raw(pine_state->msgmap, msgno),
579 "1", 0L, pc, prefix, NULL, GBPT_NONE);
581 else{
582 q_status_message(SM_ORDER,0,3,
583 _("Multipart with no leading text part."));
586 else{
587 /*---- Single non-text message of some sort ----*/
588 q_status_message(SM_ORDER,3,3,
589 _("Non-text message not included."));
593 if(!leave_cursor_at_top){
594 long cnt = 0L;
595 unsigned char c;
597 /* rewind and count chars to start of sig file */
598 so_seek((STORE_S *)msgtext, 0L, 0);
599 while(so_readc(&c, (STORE_S *)msgtext))
600 cnt++;
602 if(!redraft_pos){
603 redraft_pos = (REDRAFT_POS_S *)fs_get(sizeof(*redraft_pos));
604 memset((void *)redraft_pos, 0,sizeof(*redraft_pos));
605 redraft_pos->hdrname = cpystr(":");
609 * If explicit cursor positioning in sig file,
610 * add offset to start of sig file plus offset into sig file.
611 * Else, just offset to start of sig file.
613 redraft_pos->offset += cnt;
616 if(sig){
617 if(*sig)
618 so_puts((STORE_S *)msgtext, sig);
620 fs_give((void **)&sig);
623 else{
624 msgno = mn_m2raw(pine_state->msgmap,
625 mn_get_cur(pine_state->msgmap));
627 /*--- Grab current envelope ---*/
628 env = pine_mail_fetchstructure(pine_state->mail_stream, msgno,
629 &orig_body);
632 * If the charset of the body part is different from ascii and
633 * charset conversion is _not_ happening, then preserve the original
634 * charset from the message so that if we don't enter any new
635 * chars with the hibit set we can use the original charset.
636 * If not all those things, then don't try to preserve it.
638 if(orig_body){
639 char *charset;
641 charset = parameter_val(orig_body->parameter, "charset");
642 if(charset && strucmp(charset, "us-ascii") != 0){
643 CONV_TABLE *ct;
646 * There is a non-ascii charset, is there conversion happening?
648 if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
649 reply.orig_charset = charset;
650 charset = NULL;
654 if(charset)
655 fs_give((void **) &charset);
658 if(env) {
659 if(!(body = reply_body(pine_state->mail_stream, env, orig_body,
660 msgno, NULL, msgtext, prefix,
661 include_text, role, 1, &redraft_pos)))
662 goto done_early;
664 else{
665 q_status_message1(SM_ORDER,3,4,
666 _("Error fetching message %s. Can't reply to it."),
667 long2string(mn_get_cur(pine_state->msgmap)));
668 goto done_early;
672 /* fill in reply structure */
673 reply.prefix = prefix;
674 reply.mailbox = cpystr(pine_state->mail_stream->mailbox);
675 reply.origmbox = cpystr(pine_state->mail_stream->original_mailbox
676 ? pine_state->mail_stream->original_mailbox
677 : pine_state->mail_stream->mailbox);
678 reply.data.uid.msgs = (imapuid_t *) fs_get((times + 1) * sizeof(imapuid_t));
679 if((reply.data.uid.validity = pine_state->mail_stream->uid_validity) != 0){
680 reply.uid = 1;
681 for(i = 0; i < times ; i++)
682 reply.data.uid.msgs[i] = mail_uid(pine_state->mail_stream, seq[i]);
684 else{
685 reply.msgno = 1;
686 for(i = 0; i < times ; i++)
687 reply.data.uid.msgs[i] = seq[i];
690 reply.data.uid.msgs[i] = 0; /* tie off list */
692 #if defined(DOS) && !defined(_WINDOWS)
693 free((void *)reserve);
694 #endif
696 /* partially formatted outgoing message */
697 pine_send(outgoing, &body, _("COMPOSE MESSAGE REPLY"),
698 role, fcc, &reply, redraft_pos, NULL, NULL, 0);
700 rv++;
701 pine_free_body(&body);
702 if(reply.mailbox)
703 fs_give((void **) &reply.mailbox);
704 if(reply.origmbox)
705 fs_give((void **) &reply.origmbox);
706 if(reply.orig_charset)
707 fs_give((void **) &reply.orig_charset);
708 fs_give((void **) &reply.data.uid.msgs);
709 done_early:
710 if((STORE_S *) msgtext)
711 gf_clear_so_writec((STORE_S *) msgtext);
713 mail_free_envelope(&outgoing);
714 mail_free_address(&saved_from);
715 mail_free_address(&saved_to);
716 mail_free_address(&saved_cc);
717 mail_free_address(&saved_resent);
719 fs_give((void **)&seq);
721 if(prefix)
722 fs_give((void **)&prefix);
724 if(fcc)
725 fs_give((void **) &fcc);
727 free_redraft_pos(&redraft_pos);
728 free_action(&role);
729 return rv;
734 * Ask user to confirm role choice, or choose another role.
736 * Args role -- A pointer into the pattern_h space at the default
737 * role to use. This can't be a copy, the comparison
738 * relies on it pointing at the actual role.
739 * This arg is also used to return a pointer to the
740 * chosen role.
742 * Returns 1 -- Yes, use role which is now in *role. This may not be
743 * the same as the role passed in and it may even be NULL.
744 * 0 -- Cancel reply.
747 confirm_role(long int rflags, ACTION_S **role)
749 ACTION_S *role_p = NULL;
750 ACTION_S *default_role = NULL;
751 char prompt[80], *prompt_fodder;
752 int cmd, done, ret = 1;
753 void (*prev_screen)(struct pine *) = ps_global->prev_screen,
754 (*redraw)(void) = ps_global->redrawer;
755 PAT_S *curpat, *pat;
756 PAT_STATE pstate;
757 HelpType help;
758 ESCKEY_S ekey[4];
760 if(!nonempty_patterns(ROLE_DO_ROLES, &pstate) || !role)
761 return(ret);
764 * If this is a reply or forward and the role doesn't require confirmation,
765 * then we just return with what was passed in.
767 if(((rflags & ROLE_REPLY) &&
768 *role && (*role)->repl_type == ROLE_REPL_NOCONF) ||
769 ((rflags & ROLE_FORWARD) &&
770 *role && (*role)->forw_type == ROLE_FORW_NOCONF) ||
771 ((rflags & ROLE_COMPOSE) &&
772 *role && (*role)->comp_type == ROLE_COMP_NOCONF) ||
773 (!*role && F_OFF(F_ROLE_CONFIRM_DEFAULT, ps_global)
774 && !ps_global->default_role))
775 return(ret);
778 * Check that there is at least one role available. This is among all
779 * roles, not just the reply roles or just the forward roles. That's
780 * because we have ^T take us to all the roles, not the category-specific
781 * roles.
783 if(!(pat = last_pattern(&pstate)))
784 return(ret);
786 ekey[0].ch = 'y';
787 ekey[0].rval = 'y';
789 ekey[1].ch = 'n';
790 ekey[1].rval = 'n';
792 ekey[2].ch = ctrl('T');
793 ekey[2].rval = 2;
794 ekey[2].name = "^T";
796 ekey[3].ch = -1;
798 /* check for more than one role available (or no role set) */
799 if(pat == first_pattern(&pstate) && *role) /* no ^T */
800 ekey[2].ch = -1;
803 * Setup default role
804 * Go through the loop just in case default_role doesn't point
805 * to a real current role.
807 if(ps_global->default_role){
808 for(pat = first_pattern(&pstate);
809 pat;
810 pat = next_pattern(&pstate)){
811 if(pat->action == ps_global->default_role){
812 default_role = ps_global->default_role;
813 break;
818 curpat = NULL;
820 /* override default */
821 if(*role){
822 for(pat = first_pattern(&pstate);
823 pat;
824 pat = next_pattern(&pstate)){
825 if(pat->action == *role){
826 curpat = pat;
827 break;
832 if(rflags & ROLE_REPLY)
833 prompt_fodder = _("Reply");
834 else if(rflags & ROLE_FORWARD)
835 prompt_fodder = _("Forward");
836 else
837 prompt_fodder = _("Compose");
839 done = 0;
840 while(!done){
841 if(curpat){
842 char buf[100];
844 help = h_role_confirm;
845 ekey[0].name = "Y";
846 ekey[0].label = N_("Yes");
847 ekey[1].name = "N";
848 if(default_role)
849 ekey[1].label = N_("No, use default role");
850 else
851 ekey[1].label = N_("No, use default settings");
853 ekey[2].label = N_("To Select Alternate Role");
855 if(curpat->patgrp && curpat->patgrp->nick)
856 /* TRANSLATORS: This is something like Use role <nickname of role> for Reply? */
857 snprintf(prompt, sizeof(prompt), _("Use role \"%s\" for %s? "),
858 short_str(curpat->patgrp->nick, buf, sizeof(buf), 50, MidDots),
859 prompt_fodder);
860 else
861 snprintf(prompt, sizeof(prompt),
862 _("Use role \"<a role without a nickname>\" for %s? "),
863 prompt_fodder);
865 else{
866 help = h_norole_confirm;
867 ekey[0].name = "Ret";
868 ekey[0].label = prompt_fodder;
869 ekey[1].name = "";
870 ekey[1].label = "";
871 ekey[2].label = N_("To Select Role");
872 snprintf(prompt, sizeof(prompt),
873 _("Press Return to %s using %s role, or ^T to select a role "),
874 prompt_fodder, default_role ? _("default") : _("no"));
877 prompt[sizeof(prompt)-1] = '\0';
879 cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
880 'y', 'x', help, RB_NORM);
882 switch(cmd){
883 case 'y': /* Accept */
884 done++;
885 *role = curpat ? curpat->action : default_role;
886 break;
888 case 'x': /* Cancel */
889 ret = 0;
890 /* fall through */
892 case 'n': /* NoRole */
893 done++;
894 *role = default_role;
895 break;
897 case 2: /* ^T */
898 if(role_select_screen(ps_global, &role_p, 0) >= 0){
899 if(role_p){
900 for(pat = first_pattern(&pstate);
901 pat;
902 pat = next_pattern(&pstate)){
903 if(pat->action == role_p){
904 curpat = pat;
905 break;
909 else
910 curpat = NULL;
913 ClearBody();
914 ps_global->mangled_body = 1;
915 ps_global->prev_screen = prev_screen;
916 ps_global->redrawer = redraw;
917 break;
921 return(ret);
926 * reply_to_all_query - Ask user about replying to all recipients
928 * Returns: -1 if cancel, 0 otherwise
929 * by reference: flagp
932 reply_to_all_query(int *flagp)
934 char prompt[80];
935 ESCKEY_S ekey[4];
936 char cmd;
938 ekey[0].name = "Y";
939 ekey[0].ch = 'y';
940 ekey[0].rval = 'y';
941 ekey[0].label = N_("Yes");
942 ekey[1].name = "N";
943 ekey[1].ch = 'n';
944 ekey[1].rval = 'n';
945 ekey[1].label = N_("No");
946 ekey[2].name = "P";
947 ekey[2].ch = 'p';
948 ekey[2].rval = 'p';
949 ekey[3].ch = -1;
951 ps_global->preserve = F_ON(F_PRESERVE_ORIGINAL_FIELD, ps_global);
954 loop:
955 ekey[2].label = ps_global->preserve ? N_("Not Preserve") : N_("Preserve");
956 snprintf(prompt, sizeof(prompt), _("Reply to all recipients%s"),
957 ps_global->preserve ? _(" (preserving fields)? ") : "? ");
959 prompt[sizeof(prompt)-1] = '\0';
961 switch(cmd = radio_buttons(prompt, -FOOTER_ROWS(ps_global), ekey,
962 'n', 'x', h_preserve_field, RB_NORM)){
964 case 'x' :
965 return(-1);
967 case 'y' : /* set reply-all bit */
968 (*flagp) |= RSF_FORCE_REPLY_ALL;
969 break;
971 case 'n' : /* clear reply-all bit */
972 (*flagp) &= ~RSF_FORCE_REPLY_ALL;
973 break;
975 case 'p' :
976 ps_global->preserve = !ps_global->preserve;
977 goto loop; /* ugly, but saves me a variable */
978 break;
981 return(0);
986 * reply_using_replyto_query - Ask user about replying with reply-to value
988 * Returns: 'y' if yes
989 * 'x' if cancel
992 reply_using_replyto_query(void)
994 return(want_to("Use \"Reply-To:\" address instead of \"From:\" address",
995 'y', 'x', NO_HELP,WT_SEQ_SENSITIVE));
1000 * reply_text_query - Ask user about replying with text...
1002 * Returns: 1 if include the text
1003 * 0 if we're NOT to include the text
1004 * -1 on cancel or error
1007 reply_text_query(struct pine *ps, long int many, char **prefix)
1009 int ret, edited = 0;
1010 static ESCKEY_S rtq_opts[] = {
1011 {'y', 'y', "Y", N_("Yes")},
1012 {'n', 'n', "N", N_("No")},
1013 {-1, 0, NULL, NULL}, /* may be overridden below */
1014 {-1, 0, NULL, NULL}
1017 if(F_ON(F_AUTO_INCLUDE_IN_REPLY, ps)
1018 && F_OFF(F_ENABLE_EDIT_REPLY_INDENT, ps))
1019 return(1);
1021 while(1){
1022 if(many > 1L)
1023 /* TRANSLATORS: The final three %s's can probably be safely ignored */
1024 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include %s original messages in Reply%s%s%s? "),
1025 comatose(many),
1026 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
1027 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
1028 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
1029 else
1030 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Include original message in Reply%s%s%s? "),
1031 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? " (using \"" : "",
1032 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? *prefix : "",
1033 F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps) ? "\")" : "");
1035 if(F_ON(F_ENABLE_EDIT_REPLY_INDENT, ps)){
1036 rtq_opts[2].ch = ctrl('R');
1037 rtq_opts[2].rval = 'r';
1038 rtq_opts[2].name = "^R";
1039 rtq_opts[2].label = N_("Edit Indent String");
1041 else
1042 rtq_opts[2].ch = -1;
1044 switch(ret = radio_buttons(tmp_20k_buf,
1045 ps->ttyo->screen_rows > 4
1046 ? -FOOTER_ROWS(ps_global) : -1,
1047 rtq_opts,
1048 (edited || F_ON(F_AUTO_INCLUDE_IN_REPLY, ps))
1049 ? 'y' : 'n',
1050 'x', NO_HELP, RB_SEQ_SENSITIVE)){
1051 case 'x':
1052 cmd_cancelled("Reply");
1053 return(-1);
1055 case 'r':
1056 if(prefix && *prefix){
1057 int done = 0;
1058 char buf[64];
1059 int flags;
1061 while(!done){
1062 strncpy(buf, *prefix, sizeof(buf)-1);
1063 buf[sizeof(buf)-1] = '\0';
1065 flags = OE_APPEND_CURRENT |
1066 OE_KEEP_TRAILING_SPACE |
1067 OE_DISALLOW_HELP |
1068 OE_SEQ_SENSITIVE;
1070 switch(optionally_enter(buf, ps->ttyo->screen_rows > 4
1071 ? -FOOTER_ROWS(ps_global) : -1,
1072 0, sizeof(buf), "Reply prefix : ",
1073 NULL, NO_HELP, &flags)){
1074 case 0: /* entry successful, continue */
1075 if(flags & OE_USER_MODIFIED){
1076 fs_give((void **)prefix);
1077 *prefix = removing_quotes(cpystr(buf));
1078 edited = 1;
1081 done++;
1082 break;
1084 case 1:
1085 cmd_cancelled("Reply");
1087 case -1:
1088 return(-1);
1090 case 4:
1091 EndInverse();
1092 ClearScreen();
1093 redraw_titlebar();
1094 if(ps_global->redrawer != NULL)
1095 (*ps_global->redrawer)();
1097 redraw_keymenu();
1098 break;
1100 case 3:
1101 break;
1103 case 2:
1104 default:
1105 q_status_message(SM_ORDER, 3, 4,
1106 "Programmer botch in reply_text_query()");
1107 return(-1);
1112 break;
1114 case 'y':
1115 return(1);
1117 case 'n':
1118 return(0);
1120 default:
1121 q_status_message1(SM_ORDER, 3, 4,
1122 "Invalid rval \'%s\'", pretty_command(ret));
1123 return(-1);
1130 * reply_poster_followup - return TRUE if "followup-to" set to "poster"
1132 * NOTE: queues status message indicating such
1135 reply_poster_followup(ENVELOPE *e)
1137 if(e && e->followup_to && !strucmp(e->followup_to, "poster")){
1138 q_status_message(SM_ORDER, 2, 3,
1139 _("Replying to Poster as specified in \"Followup-To\""));
1140 return(1);
1143 return(0);
1148 * reply_news_test - Test given envelope for newsgroup data and copy
1149 * it at the users request
1150 * RETURNS:
1151 * 0 if error or cancel
1152 * 1 reply via email
1153 * 2 follow-up via news
1154 * 3 do both
1157 reply_news_test(ENVELOPE *env, ENVELOPE *outgoing)
1159 int ret = 1;
1160 static ESCKEY_S news_opt[] = { {'f', 'f', "F", N_("Follow-up")},
1161 {'r', 'r', "R", N_("Reply")},
1162 {'b', 'b', "B", N_("Both")},
1163 {-1, 0, NULL, NULL} };
1165 if(env->newsgroups && *env->newsgroups && !reply_poster_followup(env))
1167 * Now that we know a newsgroups field is present,
1168 * ask if the user is posting a follow-up article...
1170 switch(radio_buttons(
1171 _("Follow-up to news group(s), Reply via email to author or Both? "),
1172 -FOOTER_ROWS(ps_global), news_opt, 'r', 'x',
1173 NO_HELP, RB_NORM)){
1174 case 'r' : /* Reply */
1175 ret = 1;
1176 break;
1178 case 'f' : /* Follow-Up via news ONLY! */
1179 ret = 2;
1180 break;
1182 case 'b' : /* BOTH */
1183 ret = 3;
1184 break;
1186 case 'x' : /* cancel or unknown response */
1187 default :
1188 cmd_cancelled("Reply");
1189 ret = 0;
1190 break;
1193 if(ret > 1){
1194 if(env->followup_to){
1195 q_status_message(SM_ORDER, 2, 3,
1196 _("Posting to specified Followup-To groups"));
1197 outgoing->newsgroups = cpystr(env->followup_to);
1199 else if(!outgoing->newsgroups)
1200 outgoing->newsgroups = cpystr(env->newsgroups);
1203 return(ret);
1207 /*----------------------------------------------------------------------
1208 Acquire the pinerc defined signature file
1209 It is allocated here and freed by the caller.
1211 file -- use this file
1212 prenewlines -- prefix the file contents with this many newlines
1213 postnewlines -- postfix the file contents with this many newlines
1214 is_sig -- this is a signature (not a template)
1215 ----*/
1216 char *
1217 get_signature_file(char *file, int prenewlines, int postnewlines, int is_sig)
1219 char *sig, *tmp_sig = NULL, sig_path[MAXPATH+1];
1220 int len, do_the_pipe_thang = 0;
1221 long sigsize = 0L, cntdown;
1223 sig_path[0] = '\0';
1224 if(!signature_path(file, sig_path, MAXPATH))
1225 return(NULL);
1227 dprint((5, "get_signature(%s)\n", sig_path));
1229 if(sig_path[(len=strlen(sig_path))-1] == '|'){
1230 if(is_sig && F_ON(F_DISABLE_PIPES_IN_SIGS, ps_global)){
1231 q_status_message(SM_ORDER | SM_DING, 3, 4,
1232 _("Pipes for signatures are administratively disabled"));
1233 return(NULL);
1235 else if(!is_sig && F_ON(F_DISABLE_PIPES_IN_TEMPLATES, ps_global)){
1236 q_status_message(SM_ORDER | SM_DING, 3, 4,
1237 _("Pipes for templates are administratively disabled"));
1238 return(NULL);
1241 sig_path[len-1] = '\0';
1242 removing_trailing_white_space(sig_path);
1243 do_the_pipe_thang++;
1246 if(!IS_REMOTE(sig_path) && ps_global->VAR_OPER_DIR &&
1247 !in_dir(ps_global->VAR_OPER_DIR, sig_path)){
1248 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1249 /* TRANSLATORS: First arg is the directory name, second is
1250 the file user wants to read but can't. */
1251 _("Can't read file outside %s: %s"),
1252 ps_global->VAR_OPER_DIR, file);
1254 return(NULL);
1257 if(IS_REMOTE(sig_path) || can_access(sig_path, ACCESS_EXISTS) == 0){
1258 if(do_the_pipe_thang){
1259 if(can_access(sig_path, EXECUTE_ACCESS) == 0){
1260 STORE_S *store;
1261 int flags;
1262 PIPE_S *syspipe;
1263 gf_io_t pc, gc;
1264 long start;
1266 if((store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
1268 flags = PIPE_READ | PIPE_STDERR | PIPE_NOSHELL;
1270 start = time(0);
1272 if((syspipe = open_system_pipe(sig_path, NULL, NULL, flags, 5,
1273 pipe_callback, pipe_report_error)) != NULL){
1274 unsigned char c;
1275 char *error, *q;
1277 gf_set_so_writec(&pc, store);
1278 gf_set_readc(&gc, (void *)syspipe, 0, PipeStar, READ_FROM_LOCALE);
1279 gf_filter_init();
1281 if((error = gf_pipe(gc, pc)) != NULL){
1282 (void)close_system_pipe(&syspipe, NULL, pipe_callback);
1283 gf_clear_so_writec(store);
1284 so_give(&store);
1285 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1286 _("Can't get file: %s"), error);
1287 return(NULL);
1290 if(close_system_pipe(&syspipe, NULL, pipe_callback)){
1291 long now;
1293 now = time(0);
1294 q_status_message2(SM_ORDER, 3, 4,
1295 _("Error running program \"%s\"%s"),
1296 file,
1297 (now - start > 4) ? ": timed out" : "");
1300 gf_clear_so_writec(store);
1302 /* rewind and count chars */
1303 so_seek(store, 0L, 0);
1304 while(so_readc(&c, store) && sigsize < 100000L)
1305 sigsize++;
1307 /* allocate space */
1308 tmp_sig = fs_get((sigsize + 1) * sizeof(char));
1309 tmp_sig[0] = '\0';
1310 q = tmp_sig;
1312 /* rewind and copy chars, no prenewlines... */
1313 so_seek(store, 0L, 0);
1314 cntdown = sigsize;
1315 while(so_readc(&c, store) && cntdown-- > 0L)
1316 *q++ = c;
1318 *q = '\0';
1319 so_give(&store);
1321 else{
1322 so_give(&store);
1323 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1324 _("Error running program \"%s\""),
1325 file);
1328 else
1329 q_status_message(SM_ORDER | SM_DING, 3, 4,
1330 "Error allocating space for sig or template program");
1332 else
1333 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1334 /* TRANSLATORS: Arg is a program name */
1335 _("Can't execute \"%s\": Permission denied"),
1336 sig_path);
1338 else if((IS_REMOTE(sig_path) &&
1339 (tmp_sig = simple_read_remote_file(sig_path, REMOTE_SIG_SUBTYPE))) ||
1340 (tmp_sig = read_file(sig_path, READ_FROM_LOCALE)))
1341 sigsize = strlen(tmp_sig);
1342 else
1343 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1344 /* TRANSLATORS: First arg is error description, 2nd is
1345 filename */
1346 _("Error \"%s\" reading file \"%s\""),
1347 error_description(errno), sig_path);
1350 sig = get_signature_lit(tmp_sig, prenewlines, postnewlines, is_sig, 0);
1351 if(tmp_sig)
1352 fs_give((void **)&tmp_sig);
1354 return(sig);
1359 /*----------------------------------------------------------------------
1360 Partially set up message to forward and pass off to composer/mailer
1362 Args: pine_state -- The usual pine structure
1364 Result: outgoing envelope and body created and passed off to composer/mailer
1366 Create the outgoing envelope for the mail being forwarded, which is
1367 not much more than filling in the subject, and create the message body
1368 of the outgoing message which requires formatting the header from the
1369 envelope of the original messasge.
1370 ----------------------------------------------------------------------*/
1372 forward(struct pine *ps, ACTION_S *role_arg)
1374 char *sig;
1375 int ret, forward_raw_body = 0, rv = 0, i;
1376 long msgno, j, totalmsgs, rflags;
1377 ENVELOPE *env, *outgoing;
1378 BODY *orig_body, *body = NULL;
1379 REPLY_S reply;
1380 void *msgtext = NULL;
1381 gf_io_t pc;
1382 int impl, template_len = 0;
1383 PAT_STATE dummy;
1384 REDRAFT_POS_S *redraft_pos = NULL;
1385 ACTION_S *role = NULL, *nrole;
1386 #if defined(DOS) && !defined(_WINDOWS)
1387 char *reserve;
1388 #endif
1390 dprint((4, "\n - forward -\n"));
1392 memset((void *)&reply, 0, sizeof(reply));
1393 outgoing = mail_newenvelope();
1394 outgoing->message_id = generate_message_id();
1396 if(ps_global->full_header == 2
1397 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))
1398 forward_raw_body = 1;
1400 if((totalmsgs = mn_total_cur(ps->msgmap)) > 1L){
1401 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s forwarded messages...", comatose(totalmsgs));
1402 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1403 outgoing->subject = cpystr(tmp_20k_buf);
1405 else{
1406 /*---------- Get the envelope of message we're forwarding ------*/
1407 msgno = mn_m2raw(ps->msgmap, mn_get_cur(ps->msgmap));
1408 if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL))
1409 && (outgoing->subject = forward_subject(env, 0)))){
1410 q_status_message1(SM_ORDER,3,4,
1411 _("Error fetching message %s. Can't forward it."),
1412 long2string(msgno));
1413 goto clean;
1418 * as with all text bound for the composer, build it in
1419 * a storage object of the type it understands...
1421 if((msgtext = (void *)so_get(PicoText, NULL, EDIT_ACCESS)) == NULL){
1422 q_status_message(SM_ORDER | SM_DING, 3, 4,
1423 _("Error allocating message text"));
1424 goto clean;
1427 ret = (F_ON(F_FORWARD_AS_ATTACHMENT, ps_global))
1428 ? 'y'
1429 : (totalmsgs > 1L)
1430 ? want_to(_("Forward messages as a MIME digest"), 'y', 'x', NO_HELP, WT_SEQ_SENSITIVE)
1431 : (ps->full_header == 2)
1432 ? want_to(_("Forward message as an attachment"), 'n', 'x', NO_HELP, WT_SEQ_SENSITIVE)
1433 : 0;
1435 if(ret == 'x'){
1436 cmd_cancelled("Forward");
1437 so_give((STORE_S **)&msgtext);
1438 goto clean;
1441 /* Setup possible role */
1442 if(role_arg)
1443 role = copy_action(role_arg);
1445 if(!role){
1446 rflags = ROLE_FORWARD;
1447 if(nonempty_patterns(rflags, &dummy)){
1448 /* setup default role */
1449 nrole = NULL;
1450 j = mn_first_cur(ps->msgmap);
1451 do {
1452 role = nrole;
1453 nrole = set_role_from_msg(ps, rflags,
1454 mn_m2raw(ps->msgmap, j), NULL);
1455 } while(nrole && (!role || nrole == role)
1456 && (j=mn_next_cur(ps->msgmap)) > 0L);
1458 if(!role || nrole == role)
1459 role = nrole;
1460 else
1461 role = NULL;
1463 if(confirm_role(rflags, &role))
1464 role = combine_inherited_role(role);
1465 else{ /* cancel reply */
1466 role = NULL;
1467 cmd_cancelled("Forward");
1468 so_give((STORE_S **)&msgtext);
1469 goto clean;
1474 if(role)
1475 q_status_message1(SM_ORDER, 3, 4,
1476 _("Forwarding using role \"%s\""), role->nick);
1478 if(role && role->template){
1479 char *filtered;
1481 impl = 1;
1482 filtered = detoken(role, (totalmsgs == 1L) ? env : NULL,
1483 0, 0, 0, &redraft_pos, &impl);
1484 if(filtered){
1485 if(*filtered){
1486 so_puts((STORE_S *)msgtext, filtered);
1487 if(impl == 1)
1488 template_len = strlen(filtered);
1491 fs_give((void **)&filtered);
1494 else
1495 impl = 1;
1497 if((sig = detoken(role, NULL, 2, 0, 1, &redraft_pos, &impl)) != NULL){
1498 if(impl == 2)
1499 redraft_pos->offset += template_len;
1501 so_puts((STORE_S *)msgtext, *sig ? sig : NEWLINE);
1503 fs_give((void **)&sig);
1505 else
1506 so_puts((STORE_S *)msgtext, NEWLINE);
1508 gf_set_so_writec(&pc, (STORE_S *)msgtext);
1510 #if defined(DOS) && !defined(_WINDOWS)
1511 #if defined(LWP) || defined(PCTCP) || defined(PCNFS)
1512 #define IN_RESERVE 8192
1513 #else
1514 #define IN_RESERVE 16384
1515 #endif
1516 if((reserve=(char *)malloc(IN_RESERVE)) == NULL){
1517 gf_clear_so_writec((STORE_S *) msgtext);
1518 so_give((STORE_S **)&msgtext);
1519 q_status_message(SM_ORDER | SM_DING, 3, 4,
1520 _("Insufficient memory for message text"));
1521 goto clean;
1523 #endif
1526 * If we're forwarding multiple messages *or* the forward-as-mime
1527 * is turned on and the users wants it done that way, package things
1528 * up...
1530 if(ret == 'y'){ /* attach message[s]!!! */
1531 PART **pp;
1532 long totalsize = 0L;
1534 /*---- New Body to start with ----*/
1535 body = mail_newbody();
1536 body->type = TYPEMULTIPART;
1538 /*---- The TEXT part/body ----*/
1539 body->nested.part = mail_newbody_part();
1540 body->nested.part->body.type = TYPETEXT;
1541 body->nested.part->body.contents.text.data = msgtext;
1543 if(totalmsgs > 1L){
1544 /*---- The MULTIPART/DIGEST part ----*/
1545 body->nested.part->next = mail_newbody_part();
1546 body->nested.part->next->body.type = TYPEMULTIPART;
1547 body->nested.part->next->body.subtype = cpystr("Digest");
1548 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Digest of %s messages", comatose(totalmsgs));
1549 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1550 body->nested.part->next->body.description = cpystr(tmp_20k_buf);
1551 pp = &(body->nested.part->next->body.nested.part);
1553 else
1554 pp = &(body->nested.part->next);
1556 /*---- The Message body subparts ----*/
1557 for(msgno = mn_first_cur(ps->msgmap);
1558 msgno > 0L;
1559 msgno = mn_next_cur(ps->msgmap)){
1561 msgno = mn_m2raw(ps->msgmap, msgno);
1562 env = pine_mail_fetchstructure(ps->mail_stream, msgno, NULL);
1564 if(forward_mime_msg(ps->mail_stream,msgno,NULL,env,pp,msgtext)){
1565 totalsize += (*pp)->body.size.bytes;
1566 pp = &((*pp)->next);
1568 else
1569 goto bomb;
1572 if(totalmsgs > 1L)
1573 body->nested.part->next->body.size.bytes = totalsize;
1575 else if(totalmsgs > 1L){
1576 int warned = 0;
1577 body = mail_newbody();
1578 body->type = TYPETEXT;
1579 body->contents.text.data = msgtext;
1580 env = NULL;
1582 for(msgno = mn_first_cur(ps->msgmap);
1583 msgno > 0L;
1584 msgno = mn_next_cur(ps->msgmap)){
1586 if(env){ /* put 2 between messages */
1587 gf_puts(NEWLINE, pc);
1588 gf_puts(NEWLINE, pc);
1591 /*--- Grab current envelope ---*/
1592 env = pine_mail_fetchstructure(ps->mail_stream,
1593 mn_m2raw(ps->msgmap, msgno),
1594 &orig_body);
1595 if(!env || !orig_body){
1596 q_status_message1(SM_ORDER,3,4,
1597 _("Error fetching message %s. Can't forward it."),
1598 long2string(msgno));
1599 goto bomb;
1602 if(orig_body == NULL || orig_body->type == TYPETEXT || forward_raw_body) {
1603 forward_delimiter(pc);
1604 reply_forward_header(ps->mail_stream,
1605 mn_m2raw(ps->msgmap, msgno),
1606 NULL, env, pc, "");
1608 if(!get_body_part_text(ps->mail_stream, forward_raw_body ? NULL : orig_body,
1609 mn_m2raw(ps->msgmap, msgno),
1610 forward_raw_body ? NULL : "1", 0L, pc,
1611 NULL, NULL, GBPT_NONE))
1612 goto bomb;
1613 } else if(orig_body->type == TYPEMULTIPART) {
1614 if(!warned++)
1615 q_status_message(SM_ORDER,3,7,
1616 _("WARNING! Attachments not included in multiple forward."));
1618 if(orig_body->nested.part &&
1619 orig_body->nested.part->body.type == TYPETEXT) {
1620 /*---- First part of the message is text -----*/
1621 forward_delimiter(pc);
1622 reply_forward_header(ps->mail_stream,
1623 mn_m2raw(ps->msgmap,msgno),
1624 NULL, env, pc, "");
1626 if(!get_body_part_text(ps->mail_stream,
1627 &orig_body->nested.part->body,
1628 mn_m2raw(ps->msgmap, msgno),
1629 "1", 0L, pc,
1630 NULL, NULL, GBPT_NONE))
1631 goto bomb;
1632 } else {
1633 q_status_message(SM_ORDER,0,3,
1634 _("Multipart with no leading text part!"));
1636 } else {
1637 /*---- Single non-text message of some sort ----*/
1638 q_status_message(SM_ORDER,0,3,
1639 _("Non-text message not included!"));
1643 else if(!((env = pine_mail_fetchstructure(ps->mail_stream, msgno,
1644 &orig_body))
1645 && (body = forward_body(ps->mail_stream, env, orig_body, msgno,
1646 NULL, msgtext,
1647 FWD_NONE)))){
1648 q_status_message1(SM_ORDER,3,4,
1649 _("Error fetching message %s. Can't forward it."),
1650 long2string(msgno));
1651 goto clean;
1654 if(ret != 'y' && totalmsgs == 1L && orig_body){
1655 char *charset;
1657 charset = parameter_val(orig_body->parameter, "charset");
1658 if(charset && strucmp(charset, "us-ascii") != 0){
1659 CONV_TABLE *ct;
1662 * There is a non-ascii charset, is there conversion happening?
1664 if(!(ct=conversion_table(charset, ps_global->posting_charmap)) || !ct->table){
1665 reply.orig_charset = charset;
1666 charset = NULL;
1670 if(charset)
1671 fs_give((void **) &charset);
1674 * I don't think orig_charset is ever used except possibly
1675 * right here. Hubert 2008-01-15.
1677 if(reply.orig_charset)
1678 reply.forw = 1;
1681 /* fill in reply structure */
1682 reply.forwarded = 1;
1683 reply.mailbox = cpystr(ps->mail_stream->mailbox);
1684 reply.origmbox = cpystr(ps->mail_stream->original_mailbox
1685 ? ps->mail_stream->original_mailbox
1686 : ps->mail_stream->mailbox);
1687 reply.data.uid.msgs = (imapuid_t *) fs_get((totalmsgs + 1) * sizeof(imapuid_t));
1688 if((reply.data.uid.validity = ps->mail_stream->uid_validity) != 0){
1689 reply.uid = 1;
1690 for(msgno = mn_first_cur(ps->msgmap), i = 0;
1691 msgno > 0L;
1692 msgno = mn_next_cur(ps->msgmap), i++)
1693 reply.data.uid.msgs[i] = mail_uid(ps->mail_stream, mn_m2raw(ps->msgmap, msgno));
1695 else{
1696 reply.msgno = 1;
1697 for(msgno = mn_first_cur(ps->msgmap), i = 0;
1698 msgno > 0L;
1699 msgno = mn_next_cur(ps->msgmap), i++)
1700 reply.data.uid.msgs[i] = mn_m2raw(ps->msgmap, msgno);
1703 reply.data.uid.msgs[i] = 0; /* tie off list */
1705 #if defined(DOS) && !defined(_WINDOWS)
1706 free((void *)reserve);
1707 #endif
1708 pine_send(outgoing, &body, "FORWARD MESSAGE",
1709 role, NULL, &reply, redraft_pos,
1710 NULL, NULL, 0);
1711 rv++;
1713 clean:
1714 if(body)
1715 pine_free_body(&body);
1717 if((STORE_S *) msgtext)
1718 gf_clear_so_writec((STORE_S *) msgtext);
1720 mail_free_envelope(&outgoing);
1721 free_redraft_pos(&redraft_pos);
1722 free_action(&role);
1724 if(reply.orig_charset)
1725 fs_give((void **)&reply.orig_charset);
1727 return rv;
1729 bomb:
1730 q_status_message(SM_ORDER | SM_DING, 4, 5,
1731 _("Error fetching message contents. Can't forward message."));
1732 goto clean;
1736 /*----------------------------------------------------------------------
1737 Partially set up message to forward and pass off to composer/mailer
1739 Args: pine_state -- The usual pine structure
1740 message -- The MESSAGECACHE of entry to reply to
1742 Result: outgoing envelope and body created and passed off to composer/mailer
1744 Create the outgoing envelope for the mail being forwarded, which is
1745 not much more than filling in the subject, and create the message body
1746 of the outgoing message which requires formatting the header from the
1747 envelope of the original messasge.
1748 ----------------------------------------------------------------------*/
1749 void
1750 forward_text(struct pine *pine_state, void *text, SourceType source)
1752 ENVELOPE *env;
1753 BODY *body;
1754 gf_io_t pc, gc;
1755 STORE_S *msgtext;
1756 char *enc_error, *sig;
1757 ACTION_S *role = NULL;
1758 PAT_STATE dummy;
1759 long rflags = ROLE_COMPOSE;
1761 if((msgtext = so_get(PicoText, NULL, EDIT_ACCESS)) != NULL){
1762 env = mail_newenvelope();
1763 env->message_id = generate_message_id();
1764 body = mail_newbody();
1765 body->type = TYPETEXT;
1766 body->contents.text.data = (void *) msgtext;
1768 if(nonempty_patterns(rflags, &dummy)){
1770 * This is really more like Compose, even though it
1771 * is called Forward.
1773 if(confirm_role(rflags, &role))
1774 role = combine_inherited_role(role);
1775 else{ /* cancel */
1776 cmd_cancelled("Composition");
1777 display_message('x');
1778 mail_free_envelope(&env);
1779 pine_free_body(&body);
1780 return;
1784 if(role)
1785 q_status_message1(SM_ORDER, 3, 4, _("Composing using role \"%s\""),
1786 role->nick);
1788 sig = detoken(role, NULL, 2, 0, 1, NULL, NULL);
1789 so_puts(msgtext, (sig && *sig) ? sig : NEWLINE);
1790 so_puts(msgtext, NEWLINE);
1791 so_puts(msgtext, "----- Included text -----");
1792 so_puts(msgtext, NEWLINE);
1793 if(sig)
1794 fs_give((void **)&sig);
1796 gf_filter_init();
1797 gf_set_so_writec(&pc, msgtext);
1798 gf_set_readc(&gc,text,(source == CharStar) ? strlen((char *)text) : 0L,
1799 source, 0);
1801 if((enc_error = gf_pipe(gc, pc)) == NULL){
1802 pine_send(env, &body, "SEND MESSAGE", role, NULL, NULL, NULL,
1803 NULL, NULL, 0);
1804 pine_state->mangled_screen = 1;
1806 else{
1807 q_status_message1(SM_ORDER | SM_DING, 3, 5,
1808 _("Error reading text \"%s\""),enc_error);
1809 display_message('x');
1812 gf_clear_so_writec(msgtext);
1813 mail_free_envelope(&env);
1814 pine_free_body(&body);
1816 else {
1817 q_status_message(SM_ORDER | SM_DING, 3, 4,
1818 _("Error allocating message text"));
1819 display_message('x');
1822 free_action(&role);
1826 /*----------------------------------------------------------------------
1827 Partially set up message to resend and pass off to mailer
1829 Args: pine_state -- The usual pine structure
1831 Result: outgoing envelope and body created and passed off to mailer
1833 Create the outgoing envelope for the mail being resent, which is
1834 not much more than filling in the subject, and create the message body
1835 of the outgoing message which requires formatting the header from the
1836 envelope of the original messasge.
1837 ----------------------------------------------------------------------*/
1839 bounce(struct pine *pine_state, ACTION_S *role)
1841 ENVELOPE *env;
1842 long msgno, rawno;
1843 char *save_to = NULL, **save_toptr = NULL, *errstr = NULL,
1844 *prmpt_who = NULL, *prmpt_cnf = NULL;
1846 dprint((4, "\n - bounce -\n"));
1848 if(mn_total_cur(pine_state->msgmap) > 1L){
1849 save_toptr = &save_to;
1850 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages to : "),
1851 mn_total_cur(pine_state->msgmap));
1852 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1853 prmpt_who = cpystr(tmp_20k_buf);
1854 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %ld messages "),
1855 mn_total_cur(pine_state->msgmap));
1856 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1857 prmpt_cnf = cpystr(tmp_20k_buf);
1860 for(msgno = mn_first_cur(pine_state->msgmap);
1861 msgno > 0L;
1862 msgno = mn_next_cur(pine_state->msgmap)){
1864 rawno = mn_m2raw(pine_state->msgmap, msgno);
1865 if((env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL)) != NULL)
1866 errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role,
1867 save_toptr, env->subject, prmpt_who, prmpt_cnf);
1868 else
1869 errstr = _("Can't fetch Subject for Bounce");
1872 if(errstr){
1873 if(*errstr)
1874 q_status_message(SM_ORDER | SM_DING, 4, 7, errstr);
1876 break;
1880 if(save_to)
1881 fs_give((void **)&save_to);
1883 if(prmpt_who)
1884 fs_give((void **) &prmpt_who);
1886 if(prmpt_cnf)
1887 fs_give((void **) &prmpt_cnf);
1889 return(errstr ? 0 : 1);
1894 char *
1895 bounce_msg(MAILSTREAM *stream,
1896 long int rawno,
1897 char *part,
1898 ACTION_S *role,
1899 char **to,
1900 char *subject,
1901 char *pmt_who,
1902 char *pmt_cnf)
1904 char *errstr = NULL;
1905 int was_seen = -1;
1906 ENVELOPE *outgoing;
1907 BODY *body = NULL;
1908 MESSAGECACHE *mc;
1910 if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){
1911 if(pine_simple_send(outgoing, &body, role, pmt_who, pmt_cnf, to,
1912 !(to && *to) ? SS_PROMPTFORTO : 0) < 0){
1913 errstr = ""; /* p_s_s() better have explained! */
1914 /* clear seen flag */
1915 if(was_seen == 0 && rawno > 0L
1916 && stream && rawno <= stream->nmsgs
1917 && (mc = mail_elt(stream, rawno)) && mc->seen)
1918 mail_flag(stream, long2string(rawno), "\\SEEN", 0);
1922 /* Just for good measure... */
1923 mail_free_envelope(&outgoing);
1924 pine_free_body(&body);
1926 return(errstr); /* no problem-o */
1930 /*----------------------------------------------------------------------
1931 Serve up the current signature within pico for editing
1933 Args: file to edit
1935 Result: signature changed or not.
1936 ---*/
1937 char *
1938 signature_edit(char *sigfile, char *title)
1940 int editor_result;
1941 char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
1942 char *ret = NULL;
1943 STORE_S *msgso, *tmpso = NULL;
1944 gf_io_t gc, pc;
1945 PICO pbf;
1946 struct variable *vars = ps_global->vars;
1947 REMDATA_S *rd = NULL;
1949 if(!signature_path(sigfile, sig_path, MAXPATH))
1950 return(cpystr(_("No signature file defined.")));
1952 if(IS_REMOTE(sigfile)){
1953 rd = rd_create_remote(RemImap, sig_path, REMOTE_SIG_SUBTYPE,
1954 NULL, "Error: ",
1955 _("Can't access remote configuration."));
1956 if(!rd)
1957 return(cpystr(_("Error attempting to edit remote configuration")));
1959 (void)rd_read_metadata(rd);
1961 if(rd->access == MaybeRorW){
1962 if(rd->read_status == 'R')
1963 rd->access = ReadOnly;
1964 else
1965 rd->access = ReadWrite;
1968 if(rd->access != NoExists){
1970 rd_check_remvalid(rd, 1L);
1973 * If the cached info says it is readonly but
1974 * it looks like it's been fixed now, change it to readwrite.
1976 if(rd->read_status == 'R'){
1977 rd_check_readonly_access(rd);
1978 if(rd->read_status == 'W'){
1979 rd->access = ReadWrite;
1980 rd->flags |= REM_OUTOFDATE;
1982 else
1983 rd->access = ReadOnly;
1987 if(rd->flags & REM_OUTOFDATE){
1988 if(rd_update_local(rd) != 0){
1990 dprint((1,
1991 "signature_edit: rd_update_local failed\n"));
1992 rd_close_remdata(&rd);
1993 return(cpystr(_("Can't access remote sig")));
1996 else
1997 rd_open_remote(rd);
1999 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
2000 rd_close_remdata(&rd);
2001 return(cpystr(_("Can't get write permission for remote sig")));
2004 rd->flags |= DO_REMTRIM;
2006 strncpy(sig_path, rd->lf, sizeof(sig_path)-1);
2007 sig_path[sizeof(sig_path)-1] = '\0';
2010 standard_picobuf_setup(&pbf);
2011 pbf.tty_fix = PineRaw;
2012 pbf.composer_help = h_composer_sigedit;
2013 pbf.exittest = sigedit_exit_for_pico;
2014 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2015 ? upload_msg_to_pico : NULL;
2016 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2017 ? VAR_EDITOR : NULL;
2018 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2019 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2020 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2021 pbf.allow_flowed_text = 0;
2023 pbf.pine_anchor = set_titlebar(title,
2024 ps_global->mail_stream,
2025 ps_global->context_current,
2026 ps_global->cur_folder,
2027 ps_global->msgmap,
2028 0, FolderName, 0, 0, NULL);
2030 /* NOTE: at this point, alot of pico struct fields are null'd out
2031 * thanks to the leading memset; in particular "headents" which tells
2032 * pico to behave like a normal editor (though modified slightly to
2033 * let the caller dictate the file to edit and such)...
2036 if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
2037 size_t l;
2039 l = strlen(VAR_OPER_DIR) + 100;
2040 ret = (char *) fs_get((l+1) * sizeof(char));
2041 snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR);
2042 ret[l] = '\0';
2043 return(ret);
2047 * Now alloc and init the text to pass pico
2049 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2050 ret = cpystr(_("Error allocating space for file"));
2051 dprint((1, "Can't alloc space for signature_edit"));
2052 return(ret);
2054 else
2055 pbf.msgtext = so_text(msgso);
2057 if(can_access(sig_path, READ_ACCESS) == 0
2058 && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){
2059 char *problem = error_description(errno);
2061 snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"),
2062 sig_path, problem ? problem : "<NULL>");
2063 errbuf[sizeof(errbuf)-1] = '\0';
2064 ret = cpystr(errbuf);
2066 dprint((1, "signature_edit: can't open %s: %s", sig_path,
2067 problem ? problem : "<NULL>"));
2068 return(ret);
2070 else if(tmpso){ /* else, fill pico's edit buffer */
2071 gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */
2072 gf_set_so_writec(&pc, msgso);
2073 gf_filter_init(); /* no filters needed */
2074 if((errstr = gf_pipe(gc, pc)) != NULL){
2075 snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr);
2076 errbuf[sizeof(errbuf)-1] = '\0';
2077 ret = cpystr(errbuf);
2080 gf_clear_so_readc(tmpso);
2081 gf_clear_so_writec(msgso);
2082 so_give(&tmpso);
2085 if(!errstr){
2086 #ifdef _WINDOWS
2087 mswin_setwindowmenu (MENU_COMPOSER);
2088 #endif
2090 /*------ OK, Go edit the signature ------*/
2091 editor_result = pico(&pbf);
2093 #ifdef _WINDOWS
2094 mswin_setwindowmenu (MENU_DEFAULT);
2095 #endif
2096 if(editor_result & COMP_GOTHUP){
2097 hup_signal(); /* do what's normal for a hup */
2099 else{
2100 fix_windsize(ps_global);
2101 init_signals();
2104 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2106 else{
2107 /*------ Must have an edited buffer, write it to .sig -----*/
2108 our_unlink(sig_path); /* blast old copy */
2109 if((tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){
2110 so_seek(msgso, 0L, 0);
2111 gf_set_so_readc(&gc, msgso); /* read from pico buf */
2112 gf_set_so_writec(&pc, tmpso); /* write sig file */
2113 gf_filter_init(); /* no filters needed */
2114 if((errstr = gf_pipe(gc, pc)) != NULL){
2115 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2116 errstr);
2117 errbuf[sizeof(errbuf)-1] = '\0';
2118 ret = cpystr(errbuf);
2121 gf_clear_so_readc(msgso);
2122 gf_clear_so_writec(tmpso);
2123 if(so_give(&tmpso)){
2124 errstr = error_description(errno);
2125 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2126 errstr);
2127 errbuf[sizeof(errbuf)-1] = '\0';
2128 ret = cpystr(errbuf);
2131 if(IS_REMOTE(sigfile)){
2132 int e, we_cancel;
2133 char datebuf[200];
2135 datebuf[0] = '\0';
2137 we_cancel = busy_cue("Copying to remote sig", NULL, 1);
2138 if((e = rd_update_remote(rd, datebuf)) != 0){
2139 if(e == -1){
2140 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2141 _("Error opening temporary sig file %s: %s"),
2142 rd->lf, error_description(errno));
2143 dprint((1,
2144 "write_remote_sig: error opening temp file %s\n",
2145 rd->lf ? rd->lf : "?"));
2147 else{
2148 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2149 _("Error copying to %s: %s"),
2150 rd->rn, error_description(errno));
2151 dprint((1,
2152 "write_remote_sig: error copying from %s to %s\n",
2153 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
2156 q_status_message(SM_ORDER | SM_DING, 5, 5,
2157 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2159 else{
2160 rd_update_metadata(rd, datebuf);
2161 rd->read_status = 'W';
2164 rd_close_remdata(&rd);
2166 if(we_cancel)
2167 cancel_busy_cue(-1);
2170 else{
2171 snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path);
2172 errbuf[sizeof(errbuf)-1] = '\0';
2173 ret = cpystr(errbuf);
2174 dprint((1, "signature_edit: can't write %s",
2175 sig_path));
2180 standard_picobuf_teardown(&pbf);
2181 so_give(&msgso);
2182 return(ret);
2186 /*----------------------------------------------------------------------
2187 Serve up the current signature within pico for editing
2189 Args: literal signature to edit
2191 Result: raw edited signature is returned in result arg
2192 ---*/
2193 char *
2194 signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help)
2196 int editor_result;
2197 char *errstr = NULL;
2198 char *ret = NULL;
2199 STORE_S *msgso;
2200 PICO pbf;
2201 struct variable *vars = ps_global->vars;
2203 standard_picobuf_setup(&pbf);
2204 pbf.tty_fix = PineRaw;
2205 pbf.search_help = h_sigedit_search;
2206 pbf.composer_help = composer_help;
2207 pbf.exittest = sigedit_exit_for_pico;
2208 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2209 ? upload_msg_to_pico : NULL;
2210 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2211 ? VAR_EDITOR : NULL;
2212 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2213 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2214 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2215 pbf.allow_flowed_text = 0;
2217 pbf.pine_anchor = set_titlebar(title,
2218 ps_global->mail_stream,
2219 ps_global->context_current,
2220 ps_global->cur_folder,
2221 ps_global->msgmap,
2222 0, FolderName, 0, 0, NULL);
2224 /* NOTE: at this point, alot of pico struct fields are null'd out
2225 * thanks to the leading memset; in particular "headents" which tells
2226 * pico to behave like a normal editor (though modified slightly to
2227 * let the caller dictate the file to edit and such)...
2231 * Now alloc and init the text to pass pico
2233 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2234 ret = cpystr(_("Error allocating space"));
2235 dprint((1, "Can't alloc space for signature_edit_lit"));
2236 return(ret);
2238 else
2239 pbf.msgtext = so_text(msgso);
2241 so_puts(msgso, litsig ? litsig : "");
2244 if(!errstr){
2245 #ifdef _WINDOWS
2246 mswin_setwindowmenu (MENU_COMPOSER);
2247 #endif
2249 /*------ OK, Go edit the signature ------*/
2250 editor_result = pico(&pbf);
2252 #ifdef _WINDOWS
2253 mswin_setwindowmenu (MENU_DEFAULT);
2254 #endif
2255 if(editor_result & COMP_GOTHUP){
2256 hup_signal(); /* do what's normal for a hup */
2258 else{
2259 fix_windsize(ps_global);
2260 init_signals();
2263 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2264 ret = cpystr(_("Edit Cancelled"));
2266 else{
2267 /*------ Must have an edited buffer, write it to .sig -----*/
2268 unsigned char c;
2269 int cnt = 0;
2270 char *p;
2272 so_seek(msgso, 0L, 0);
2273 while(so_readc(&c, msgso))
2274 cnt++;
2276 *result = (char *)fs_get((cnt+1) * sizeof(char));
2277 p = *result;
2278 so_seek(msgso, 0L, 0);
2279 while(so_readc(&c, msgso))
2280 *p++ = c;
2282 *p = '\0';
2286 standard_picobuf_teardown(&pbf);
2287 so_give(&msgso);
2288 return(ret);
2293 * Returns 0 for Save Changes and exit
2294 * 1 for Cancel Exit
2295 * -1 exit but Dont Save Changes
2298 sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
2299 char **result)
2301 int rv;
2302 char *rstr = NULL;
2303 void (*redraw)(void) = ps_global->redrawer;
2304 static ESCKEY_S opts[] = {
2305 {'s', 's', "S", N_("Save changes")},
2306 {'d', 'd', "D", N_("Don't save changes")},
2307 {-1, 0, NULL, NULL}
2310 ps_global->redrawer = redraw_pico;
2311 fix_windsize(ps_global);
2313 while(1){
2314 rv = radio_buttons(_("Exit editor? "),
2315 -FOOTER_ROWS(ps_global), opts,
2316 's', 'x', h_exit_editor, RB_NORM);
2317 if(rv == 's'){ /* user ACCEPTS! */
2318 break;
2320 else if(rv == 'd'){ /* Declined! */
2321 rstr = _("No Changes Saved");
2322 break;
2324 else if(rv == 'x'){ /* Cancelled! */
2325 rstr = _("Exit Cancelled");
2326 break;
2330 if(result)
2331 *result = rstr;
2333 ps_global->redrawer = redraw;
2334 return((rv == 's') ? 0 : (rv == 'd') ? -1 : 1);
2339 * Common stuff we almost always want to set when calling pico.
2341 void
2342 standard_picobuf_setup(PICO *pbf)
2344 memset(pbf, 0, sizeof(*pbf));
2346 pbf->pine_version = ALPINE_VERSION;
2347 pbf->fillcolumn = ps_global->composer_fillcol;
2348 pbf->menu_rows = FOOTER_ROWS(ps_global) - 1;
2349 pbf->colors = colors_for_pico();
2350 pbf->wordseps = user_wordseps(ps_global->VAR_WORDSEPS);
2351 pbf->helper = helper;
2352 pbf->showmsg = display_message_for_pico;
2353 pbf->suspend = do_suspend;
2354 pbf->keybinput = cmd_input_for_pico;
2355 pbf->tty_fix = ttyfix; /* watch out for this one */
2356 pbf->newmail = new_mail_for_pico;
2357 pbf->ckptdir = checkpoint_dir_for_pico;
2358 pbf->resize = resize_for_pico;
2359 pbf->input_cs = ps_global->input_cs;
2360 pbf->winch_cleanup = winch_cleanup;
2361 pbf->search_help = h_composer_search;
2362 pbf->ins_help = h_composer_ins;
2363 pbf->ins_m_help = h_composer_ins_m;
2364 pbf->composer_help = h_composer;
2365 pbf->browse_help = h_composer_browse;
2366 pbf->attach_help = h_composer_ctrl_j;
2368 pbf->pine_flags =
2369 ( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L)
2370 | (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L)
2371 | (ps_global->restricted ? P_SECURE : 0L)
2372 | (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L)
2373 | (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L)
2374 | (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L)
2375 | (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L)
2376 | (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L)
2377 | (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L)
2378 | (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L)
2379 | (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L)
2380 | (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L)
2381 | (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L)
2382 | (!ps_global->pass_ctrl_chars
2383 && !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L)
2384 | ((F_ON(F_ENABLE_ALT_ED,ps_global)
2385 || F_ON(F_ALT_ED_NOW,ps_global)
2386 || (ps_global->VAR_EDITOR
2387 && ps_global->VAR_EDITOR[0]
2388 && ps_global->VAR_EDITOR[0][0]))
2389 ? P_ADVANCED : 0L)
2390 | ((!ps_global->keyboard_charmap
2391 || !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
2392 ? P_HIBITIGN : 0L));
2394 if(ps_global->VAR_OPER_DIR){
2395 pbf->oper_dir = ps_global->VAR_OPER_DIR;
2396 pbf->pine_flags |= P_TREE;
2399 pbf->home_dir = ps_global->home_dir;
2403 void
2404 standard_picobuf_teardown(PICO *pbf)
2406 if(pbf){
2407 if(pbf->colors)
2408 free_pcolors(&pbf->colors);
2410 if(pbf->wordseps)
2411 fs_give((void **) &pbf->wordseps);
2416 /*----------------------------------------------------------------------
2417 Call back for pico to use to check for new mail.
2419 Args: cursor -- pointer to in to tell caller if cursor location changed
2420 if NULL, turn off cursor positioning.
2421 timing -- whether or not it's a good time to check
2424 Returns: returns 1 on success, zero on error.
2425 ----*/
2426 long
2427 new_mail_for_pico(int timing, int status)
2430 * If we're not interested in the status, don't display the busy
2431 * cue either...
2433 /* don't know where the cursor's been, reset it */
2434 clear_cursor_pos();
2435 return(new_mail(0, timing,
2436 (status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT
2437 | NM_FROM_COMPOSER));
2441 void
2442 cmd_input_for_pico(void)
2444 zero_new_mail_count();
2448 /*----------------------------------------------------------------------
2449 Call back for pico to get newmail status messages displayed
2451 Args: x -- char processed
2453 Returns:
2454 ----*/
2456 display_message_for_pico(int x)
2458 int rv;
2460 clear_cursor_pos(); /* can't know where cursor is */
2461 mark_status_dirty(); /* don't count on cached text */
2462 fix_windsize(ps_global);
2463 init_sigwinch();
2464 display_message(x);
2465 rv = ps_global->mangled_screen;
2466 ps_global->mangled_screen = 0;
2467 return(rv);
2471 /*----------------------------------------------------------------------
2472 Call back for pico to get desired directory for its check point file
2474 Args: s -- buffer to write directory name
2475 n -- length of that buffer
2477 Returns: pointer to static buffer
2478 ----*/
2479 char *
2480 checkpoint_dir_for_pico(char *s, size_t n)
2482 #if defined(DOS) || defined(OS2)
2484 * we can't assume anything about root or home dirs, so
2485 * just plunk it down in the same place as the pinerc
2487 if(!getenv("HOME")){
2488 char *lc = last_cmpnt(ps_global->pinerc);
2490 if(lc != NULL){
2491 strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc));
2492 s[MIN(n-1,lc-ps_global->pinerc)] = '\0';
2494 else{
2495 strncpy(s, ".\\", n-1);
2496 s[n-1] = '\0';
2499 else
2500 #endif
2501 strncpy(s, ps_global->home_dir, n-1);
2502 s[n-1] = '\0';
2504 return(s);
2508 /*----------------------------------------------------------------------
2509 Call back for pico to tell us the window size's changed
2511 Args: none
2513 Returns: none (but pine's ttyo structure may have been updated)
2514 ----*/
2515 void
2516 resize_for_pico(void)
2518 fix_windsize(ps_global);
2522 PCOLORS *
2523 colors_for_pico(void)
2525 PCOLORS *colors = NULL;
2526 struct variable *vars = ps_global->vars;
2528 if (pico_usingcolor()){
2529 colors = (PCOLORS *)fs_get(sizeof(PCOLORS));
2531 colors->tbcp = current_titlebar_color();
2533 if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
2534 colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
2535 VAR_KEYLABEL_BACK_COLOR);
2536 if (!pico_is_good_colorpair(colors->klcp))
2537 free_color_pair(&colors->klcp);
2539 else colors->klcp = NULL;
2541 if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
2542 colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR,
2543 VAR_KEYNAME_BACK_COLOR);
2545 else colors->kncp = NULL;
2547 if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){
2548 colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR,
2549 VAR_STATUS_BACK_COLOR);
2551 else colors->stcp = NULL;
2553 if (VAR_PROMPT_FORE_COLOR && VAR_PROMPT_BACK_COLOR){
2554 colors->prcp = new_color_pair(VAR_PROMPT_FORE_COLOR,
2555 VAR_PROMPT_BACK_COLOR);
2557 else colors->prcp = NULL;
2560 return colors;
2564 void
2565 free_pcolors(PCOLORS **colors)
2567 if (*colors){
2568 if ((*colors)->tbcp)
2569 free_color_pair(&(*colors)->tbcp);
2570 if ((*colors)->kncp)
2571 free_color_pair(&(*colors)->kncp);
2572 if ((*colors)->klcp)
2573 free_color_pair(&(*colors)->klcp);
2574 if ((*colors)->stcp)
2575 free_color_pair(&(*colors)->stcp);
2576 if ((*colors)->prcp)
2577 free_color_pair(&(*colors)->prcp);
2578 fs_give((void **)colors);
2579 fs_give((void **)colors);
2580 *colors = NULL;