* new version 2.19.9993
[alpine.git] / alpine / reply.c
blob317a793f4632a12df36b76c87285ed1fd344f467
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(!same_subject(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 if(role)
1851 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages (using role %s) to : "),
1852 mn_total_cur(pine_state->msgmap), role->nick);
1853 else
1854 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("BOUNCE (redirect) %ld messages to : "),
1855 mn_total_cur(pine_state->msgmap));
1856 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1857 prmpt_who = cpystr(tmp_20k_buf);
1858 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Send %ld messages "),
1859 mn_total_cur(pine_state->msgmap));
1860 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1861 prmpt_cnf = cpystr(tmp_20k_buf);
1864 for(msgno = mn_first_cur(pine_state->msgmap);
1865 msgno > 0L;
1866 msgno = mn_next_cur(pine_state->msgmap)){
1868 rawno = mn_m2raw(pine_state->msgmap, msgno);
1869 if((env = pine_mail_fetchstructure(pine_state->mail_stream, rawno, NULL)) != NULL)
1870 errstr = bounce_msg(pine_state->mail_stream, rawno, NULL, role,
1871 save_toptr, env->subject, prmpt_who, prmpt_cnf);
1872 else
1873 errstr = _("Can't fetch Subject for Bounce");
1876 if(errstr){
1877 if(*errstr)
1878 q_status_message(SM_ORDER | SM_DING, 4, 7, errstr);
1880 break;
1884 if(save_to)
1885 fs_give((void **)&save_to);
1887 if(prmpt_who)
1888 fs_give((void **) &prmpt_who);
1890 if(prmpt_cnf)
1891 fs_give((void **) &prmpt_cnf);
1893 return(errstr ? 0 : 1);
1898 char *
1899 bounce_msg(MAILSTREAM *stream,
1900 long int rawno,
1901 char *part,
1902 ACTION_S *role,
1903 char **to,
1904 char *subject,
1905 char *pmt_who,
1906 char *pmt_cnf)
1908 char *errstr = NULL;
1909 int was_seen = -1;
1910 ENVELOPE *outgoing;
1911 BODY *body = NULL;
1912 MESSAGECACHE *mc;
1914 if((errstr = bounce_msg_body(stream, rawno, part, to, subject, &outgoing, &body, &was_seen)) == NULL){
1915 if(pine_simple_send(outgoing, &body, role, pmt_who, pmt_cnf, to,
1916 !(to && *to) ? SS_PROMPTFORTO : 0) < 0){
1917 errstr = ""; /* p_s_s() better have explained! */
1918 /* clear seen flag */
1919 if(was_seen == 0 && rawno > 0L
1920 && stream && rawno <= stream->nmsgs
1921 && (mc = mail_elt(stream, rawno)) && mc->seen)
1922 mail_flag(stream, long2string(rawno), "\\SEEN", 0);
1926 /* Just for good measure... */
1927 mail_free_envelope(&outgoing);
1928 pine_free_body(&body);
1930 return(errstr); /* no problem-o */
1934 /*----------------------------------------------------------------------
1935 Serve up the current signature within pico for editing
1937 Args: file to edit
1939 Result: signature changed or not.
1940 ---*/
1941 char *
1942 signature_edit(char *sigfile, char *title)
1944 int editor_result;
1945 char sig_path[MAXPATH+1], errbuf[2000], *errstr = NULL;
1946 char *ret = NULL;
1947 STORE_S *msgso, *tmpso = NULL;
1948 gf_io_t gc, pc;
1949 PICO pbf;
1950 struct variable *vars = ps_global->vars;
1951 REMDATA_S *rd = NULL;
1953 if(!signature_path(sigfile, sig_path, MAXPATH))
1954 return(cpystr(_("No signature file defined.")));
1956 if(IS_REMOTE(sigfile)){
1957 rd = rd_create_remote(RemImap, sig_path, REMOTE_SIG_SUBTYPE,
1958 NULL, "Error: ",
1959 _("Can't access remote configuration."));
1960 if(!rd)
1961 return(cpystr(_("Error attempting to edit remote configuration")));
1963 (void)rd_read_metadata(rd);
1965 if(rd->access == MaybeRorW){
1966 if(rd->read_status == 'R')
1967 rd->access = ReadOnly;
1968 else
1969 rd->access = ReadWrite;
1972 if(rd->access != NoExists){
1974 rd_check_remvalid(rd, 1L);
1977 * If the cached info says it is readonly but
1978 * it looks like it's been fixed now, change it to readwrite.
1980 if(rd->read_status == 'R'){
1981 rd_check_readonly_access(rd);
1982 if(rd->read_status == 'W'){
1983 rd->access = ReadWrite;
1984 rd->flags |= REM_OUTOFDATE;
1986 else
1987 rd->access = ReadOnly;
1991 if(rd->flags & REM_OUTOFDATE){
1992 if(rd_update_local(rd) != 0){
1994 dprint((1,
1995 "signature_edit: rd_update_local failed\n"));
1996 rd_close_remdata(&rd);
1997 return(cpystr(_("Can't access remote sig")));
2000 else
2001 rd_open_remote(rd);
2003 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
2004 rd_close_remdata(&rd);
2005 return(cpystr(_("Can't get write permission for remote sig")));
2008 rd->flags |= DO_REMTRIM;
2010 strncpy(sig_path, rd->lf, sizeof(sig_path)-1);
2011 sig_path[sizeof(sig_path)-1] = '\0';
2014 standard_picobuf_setup(&pbf);
2015 pbf.tty_fix = PineRaw;
2016 pbf.composer_help = h_composer_sigedit;
2017 pbf.exittest = sigedit_exit_for_pico;
2018 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2019 ? upload_msg_to_pico : NULL;
2020 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2021 ? VAR_EDITOR : NULL;
2022 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2023 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2024 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2025 pbf.allow_flowed_text = 0;
2027 pbf.pine_anchor = set_titlebar(title,
2028 ps_global->mail_stream,
2029 ps_global->context_current,
2030 ps_global->cur_folder,
2031 ps_global->msgmap,
2032 0, FolderName, 0, 0, NULL);
2034 /* NOTE: at this point, alot of pico struct fields are null'd out
2035 * thanks to the leading memset; in particular "headents" which tells
2036 * pico to behave like a normal editor (though modified slightly to
2037 * let the caller dictate the file to edit and such)...
2040 if(VAR_OPER_DIR && !in_dir(VAR_OPER_DIR, sig_path)){
2041 size_t l;
2043 l = strlen(VAR_OPER_DIR) + 100;
2044 ret = (char *) fs_get((l+1) * sizeof(char));
2045 snprintf(ret, l+1, _("Can't edit file outside of %s"), VAR_OPER_DIR);
2046 ret[l] = '\0';
2047 return(ret);
2051 * Now alloc and init the text to pass pico
2053 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2054 ret = cpystr(_("Error allocating space for file"));
2055 dprint((1, "Can't alloc space for signature_edit"));
2056 return(ret);
2058 else
2059 pbf.msgtext = so_text(msgso);
2061 if(can_access(sig_path, READ_ACCESS) == 0
2062 && !(tmpso = so_get(FileStar, sig_path, READ_ACCESS|READ_FROM_LOCALE))){
2063 char *problem = error_description(errno);
2065 snprintf(errbuf, sizeof(errbuf), _("Error editing \"%s\": %s"),
2066 sig_path, problem ? problem : "<NULL>");
2067 errbuf[sizeof(errbuf)-1] = '\0';
2068 ret = cpystr(errbuf);
2070 dprint((1, "signature_edit: can't open %s: %s", sig_path,
2071 problem ? problem : "<NULL>"));
2072 return(ret);
2074 else if(tmpso){ /* else, fill pico's edit buffer */
2075 gf_set_so_readc(&gc, tmpso); /* read from file, write pico buf */
2076 gf_set_so_writec(&pc, msgso);
2077 gf_filter_init(); /* no filters needed */
2078 if((errstr = gf_pipe(gc, pc)) != NULL){
2079 snprintf(errbuf, sizeof(errbuf), _("Error reading file: \"%s\""), errstr);
2080 errbuf[sizeof(errbuf)-1] = '\0';
2081 ret = cpystr(errbuf);
2084 gf_clear_so_readc(tmpso);
2085 gf_clear_so_writec(msgso);
2086 so_give(&tmpso);
2089 if(!errstr){
2090 #ifdef _WINDOWS
2091 mswin_setwindowmenu (MENU_COMPOSER);
2092 #endif
2094 /*------ OK, Go edit the signature ------*/
2095 editor_result = pico(&pbf);
2097 #ifdef _WINDOWS
2098 mswin_setwindowmenu (MENU_DEFAULT);
2099 #endif
2100 if(editor_result & COMP_GOTHUP){
2101 hup_signal(); /* do what's normal for a hup */
2103 else{
2104 fix_windsize(ps_global);
2105 init_signals();
2108 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2110 else{
2111 /*------ Must have an edited buffer, write it to .sig -----*/
2112 our_unlink(sig_path); /* blast old copy */
2113 if((tmpso = so_get(FileStar, sig_path, WRITE_ACCESS|WRITE_TO_LOCALE)) != NULL){
2114 so_seek(msgso, 0L, 0);
2115 gf_set_so_readc(&gc, msgso); /* read from pico buf */
2116 gf_set_so_writec(&pc, tmpso); /* write sig file */
2117 gf_filter_init(); /* no filters needed */
2118 if((errstr = gf_pipe(gc, pc)) != NULL){
2119 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2120 errstr);
2121 errbuf[sizeof(errbuf)-1] = '\0';
2122 ret = cpystr(errbuf);
2125 gf_clear_so_readc(msgso);
2126 gf_clear_so_writec(tmpso);
2127 if(so_give(&tmpso)){
2128 errstr = error_description(errno);
2129 snprintf(errbuf, sizeof(errbuf), _("Error writing file: \"%s\""),
2130 errstr);
2131 errbuf[sizeof(errbuf)-1] = '\0';
2132 ret = cpystr(errbuf);
2135 if(IS_REMOTE(sigfile)){
2136 int e, we_cancel;
2137 char datebuf[200];
2139 datebuf[0] = '\0';
2141 we_cancel = busy_cue("Copying to remote sig", NULL, 1);
2142 if((e = rd_update_remote(rd, datebuf)) != 0){
2143 if(e == -1){
2144 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2145 _("Error opening temporary sig file %s: %s"),
2146 rd->lf, error_description(errno));
2147 dprint((1,
2148 "write_remote_sig: error opening temp file %s\n",
2149 rd->lf ? rd->lf : "?"));
2151 else{
2152 q_status_message2(SM_ORDER | SM_DING, 3, 5,
2153 _("Error copying to %s: %s"),
2154 rd->rn, error_description(errno));
2155 dprint((1,
2156 "write_remote_sig: error copying from %s to %s\n",
2157 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
2160 q_status_message(SM_ORDER | SM_DING, 5, 5,
2161 _("Copy of sig to remote folder failed, changes NOT saved remotely"));
2163 else{
2164 rd_update_metadata(rd, datebuf);
2165 rd->read_status = 'W';
2168 rd_close_remdata(&rd);
2170 if(we_cancel)
2171 cancel_busy_cue(-1);
2174 else{
2175 snprintf(errbuf, sizeof(errbuf), _("Error writing \"%s\""), sig_path);
2176 errbuf[sizeof(errbuf)-1] = '\0';
2177 ret = cpystr(errbuf);
2178 dprint((1, "signature_edit: can't write %s",
2179 sig_path));
2184 standard_picobuf_teardown(&pbf);
2185 so_give(&msgso);
2186 return(ret);
2190 /*----------------------------------------------------------------------
2191 Serve up the current signature within pico for editing
2193 Args: literal signature to edit
2195 Result: raw edited signature is returned in result arg
2196 ---*/
2197 char *
2198 signature_edit_lit(char *litsig, char **result, char *title, HelpType composer_help)
2200 int editor_result;
2201 char *errstr = NULL;
2202 char *ret = NULL;
2203 STORE_S *msgso;
2204 PICO pbf;
2205 struct variable *vars = ps_global->vars;
2207 standard_picobuf_setup(&pbf);
2208 pbf.tty_fix = PineRaw;
2209 pbf.search_help = h_sigedit_search;
2210 pbf.composer_help = composer_help;
2211 pbf.exittest = sigedit_exit_for_pico;
2212 pbf.upload = (VAR_UPLOAD_CMD && VAR_UPLOAD_CMD[0])
2213 ? upload_msg_to_pico : NULL;
2214 pbf.alt_ed = (VAR_EDITOR && VAR_EDITOR[0] && VAR_EDITOR[0][0])
2215 ? VAR_EDITOR : NULL;
2216 pbf.alt_spell = (VAR_SPELLER && VAR_SPELLER[0]) ? VAR_SPELLER : NULL;
2217 pbf.always_spell_check = F_ON(F_ALWAYS_SPELL_CHECK, ps_global);
2218 pbf.strip_ws_before_send = F_ON(F_STRIP_WS_BEFORE_SEND, ps_global);
2219 pbf.allow_flowed_text = 0;
2221 pbf.pine_anchor = set_titlebar(title,
2222 ps_global->mail_stream,
2223 ps_global->context_current,
2224 ps_global->cur_folder,
2225 ps_global->msgmap,
2226 0, FolderName, 0, 0, NULL);
2228 /* NOTE: at this point, alot of pico struct fields are null'd out
2229 * thanks to the leading memset; in particular "headents" which tells
2230 * pico to behave like a normal editor (though modified slightly to
2231 * let the caller dictate the file to edit and such)...
2235 * Now alloc and init the text to pass pico
2237 if(!(msgso = so_get(PicoText, NULL, EDIT_ACCESS))){
2238 ret = cpystr(_("Error allocating space"));
2239 dprint((1, "Can't alloc space for signature_edit_lit"));
2240 return(ret);
2242 else
2243 pbf.msgtext = so_text(msgso);
2245 so_puts(msgso, litsig ? litsig : "");
2248 if(!errstr){
2249 #ifdef _WINDOWS
2250 mswin_setwindowmenu (MENU_COMPOSER);
2251 #endif
2253 /*------ OK, Go edit the signature ------*/
2254 editor_result = pico(&pbf);
2256 #ifdef _WINDOWS
2257 mswin_setwindowmenu (MENU_DEFAULT);
2258 #endif
2259 if(editor_result & COMP_GOTHUP){
2260 hup_signal(); /* do what's normal for a hup */
2262 else{
2263 fix_windsize(ps_global);
2264 init_signals();
2267 if(editor_result & (COMP_SUSPEND | COMP_GOTHUP | COMP_CANCEL)){
2268 ret = cpystr(_("Edit Cancelled"));
2270 else{
2271 /*------ Must have an edited buffer, write it to .sig -----*/
2272 unsigned char c;
2273 int cnt = 0;
2274 char *p;
2276 so_seek(msgso, 0L, 0);
2277 while(so_readc(&c, msgso))
2278 cnt++;
2280 *result = (char *)fs_get((cnt+1) * sizeof(char));
2281 p = *result;
2282 so_seek(msgso, 0L, 0);
2283 while(so_readc(&c, msgso))
2284 *p++ = c;
2286 *p = '\0';
2290 standard_picobuf_teardown(&pbf);
2291 so_give(&msgso);
2292 return(ret);
2297 * Returns 0 for Save Changes and exit
2298 * 1 for Cancel Exit
2299 * -1 exit but Dont Save Changes
2302 sigedit_exit_for_pico(struct headerentry *he, void (*redraw_pico)(void), int allow_flowed,
2303 char **result)
2305 int rv;
2306 char *rstr = NULL;
2307 void (*redraw)(void) = ps_global->redrawer;
2308 static ESCKEY_S opts[] = {
2309 {'s', 's', "S", N_("Save changes")},
2310 {'d', 'd', "D", N_("Don't save changes")},
2311 {-1, 0, NULL, NULL}
2314 ps_global->redrawer = redraw_pico;
2315 fix_windsize(ps_global);
2317 while(1){
2318 rv = radio_buttons(_("Exit editor? "),
2319 -FOOTER_ROWS(ps_global), opts,
2320 's', 'x', h_exit_editor, RB_NORM);
2321 if(rv == 's'){ /* user ACCEPTS! */
2322 break;
2324 else if(rv == 'd'){ /* Declined! */
2325 rstr = _("No Changes Saved");
2326 break;
2328 else if(rv == 'x'){ /* Cancelled! */
2329 rstr = _("Exit Cancelled");
2330 break;
2334 if(result)
2335 *result = rstr;
2337 ps_global->redrawer = redraw;
2338 return((rv == 's') ? 0 : (rv == 'd') ? -1 : 1);
2343 * Common stuff we almost always want to set when calling pico.
2345 void
2346 standard_picobuf_setup(PICO *pbf)
2348 memset(pbf, 0, sizeof(*pbf));
2350 pbf->pine_version = ALPINE_VERSION;
2351 pbf->fillcolumn = ps_global->composer_fillcol;
2352 pbf->menu_rows = FOOTER_ROWS(ps_global) - 1;
2353 pbf->colors = colors_for_pico();
2354 pbf->wordseps = user_wordseps(ps_global->VAR_WORDSEPS);
2355 pbf->helper = helper;
2356 pbf->showmsg = display_message_for_pico;
2357 pbf->suspend = do_suspend;
2358 pbf->keybinput = cmd_input_for_pico;
2359 pbf->tty_fix = ttyfix; /* watch out for this one */
2360 pbf->newmail = new_mail_for_pico;
2361 pbf->ckptdir = checkpoint_dir_for_pico;
2362 pbf->resize = resize_for_pico;
2363 pbf->input_cs = ps_global->input_cs;
2364 pbf->winch_cleanup = winch_cleanup;
2365 pbf->search_help = h_composer_search;
2366 pbf->ins_help = h_composer_ins;
2367 pbf->ins_m_help = h_composer_ins_m;
2368 pbf->composer_help = h_composer;
2369 pbf->browse_help = h_composer_browse;
2370 pbf->attach_help = h_composer_ctrl_j;
2372 pbf->pine_flags =
2373 ( (F_ON(F_CAN_SUSPEND,ps_global) ? P_SUSPEND : 0L)
2374 | (F_ON(F_USE_FK,ps_global) ? P_FKEYS : 0L)
2375 | (ps_global->restricted ? P_SECURE : 0L)
2376 | (F_ON(F_ALT_ED_NOW,ps_global) ? P_ALTNOW : 0L)
2377 | (F_ON(F_USE_CURRENT_DIR,ps_global) ? P_CURDIR : 0L)
2378 | (F_ON(F_SUSPEND_SPAWNS,ps_global) ? P_SUBSHELL : 0L)
2379 | (F_ON(F_COMPOSE_MAPS_DEL,ps_global) ? P_DELRUBS : 0L)
2380 | (F_ON(F_ENABLE_TAB_COMPLETE,ps_global) ? P_COMPLETE : 0L)
2381 | (F_ON(F_SHOW_CURSOR,ps_global) ? P_SHOCUR : 0L)
2382 | (F_ON(F_DEL_FROM_DOT,ps_global) ? P_DOTKILL : 0L)
2383 | (F_ON(F_ENABLE_DOT_FILES,ps_global) ? P_DOTFILES : 0L)
2384 | (F_ON(F_ALLOW_GOTO,ps_global) ? P_ALLOW_GOTO : 0L)
2385 | (F_ON(F_ENABLE_SEARCH_AND_REPL,ps_global) ? P_REPLACE : 0L)
2386 | (!ps_global->pass_ctrl_chars
2387 && !ps_global->pass_c1_ctrl_chars ? P_HICTRL : 0L)
2388 | ((F_ON(F_ENABLE_ALT_ED,ps_global)
2389 || F_ON(F_ALT_ED_NOW,ps_global)
2390 || (ps_global->VAR_EDITOR
2391 && ps_global->VAR_EDITOR[0]
2392 && ps_global->VAR_EDITOR[0][0]))
2393 ? P_ADVANCED : 0L)
2394 | ((!ps_global->keyboard_charmap
2395 || !strucmp(ps_global->keyboard_charmap, "US-ASCII"))
2396 ? P_HIBITIGN : 0L));
2398 if(ps_global->VAR_OPER_DIR){
2399 pbf->oper_dir = ps_global->VAR_OPER_DIR;
2400 pbf->pine_flags |= P_TREE;
2403 pbf->home_dir = ps_global->home_dir;
2407 void
2408 standard_picobuf_teardown(PICO *pbf)
2410 if(pbf){
2411 if(pbf->colors)
2412 free_pcolors(&pbf->colors);
2414 if(pbf->wordseps)
2415 fs_give((void **) &pbf->wordseps);
2420 /*----------------------------------------------------------------------
2421 Call back for pico to use to check for new mail.
2423 Args: cursor -- pointer to in to tell caller if cursor location changed
2424 if NULL, turn off cursor positioning.
2425 timing -- whether or not it's a good time to check
2428 Returns: returns 1 on success, zero on error.
2429 ----*/
2430 long
2431 new_mail_for_pico(int timing, int status)
2434 * If we're not interested in the status, don't display the busy
2435 * cue either...
2437 /* don't know where the cursor's been, reset it */
2438 clear_cursor_pos();
2439 return(new_mail(0, timing,
2440 (status ? NM_STATUS_MSG : NM_NONE) | NM_DEFER_SORT
2441 | NM_FROM_COMPOSER));
2445 void
2446 cmd_input_for_pico(void)
2448 zero_new_mail_count();
2452 /*----------------------------------------------------------------------
2453 Call back for pico to get newmail status messages displayed
2455 Args: x -- char processed
2457 Returns:
2458 ----*/
2460 display_message_for_pico(int x)
2462 int rv;
2464 clear_cursor_pos(); /* can't know where cursor is */
2465 mark_status_dirty(); /* don't count on cached text */
2466 fix_windsize(ps_global);
2467 init_sigwinch();
2468 display_message(x);
2469 rv = ps_global->mangled_screen;
2470 ps_global->mangled_screen = 0;
2471 return(rv);
2475 /*----------------------------------------------------------------------
2476 Call back for pico to get desired directory for its check point file
2478 Args: s -- buffer to write directory name
2479 n -- length of that buffer
2481 Returns: pointer to static buffer
2482 ----*/
2483 char *
2484 checkpoint_dir_for_pico(char *s, size_t n)
2486 #if defined(DOS) || defined(OS2)
2488 * we can't assume anything about root or home dirs, so
2489 * just plunk it down in the same place as the pinerc
2491 if(!getenv("HOME")){
2492 char *lc = last_cmpnt(ps_global->pinerc);
2494 if(lc != NULL){
2495 strncpy(s, ps_global->pinerc, MIN(n-1,lc-ps_global->pinerc));
2496 s[MIN(n-1,lc-ps_global->pinerc)] = '\0';
2498 else{
2499 strncpy(s, ".\\", n-1);
2500 s[n-1] = '\0';
2503 else
2504 #endif
2505 strncpy(s, ps_global->home_dir, n-1);
2506 s[n-1] = '\0';
2508 return(s);
2512 /*----------------------------------------------------------------------
2513 Call back for pico to tell us the window size's changed
2515 Args: none
2517 Returns: none (but pine's ttyo structure may have been updated)
2518 ----*/
2519 void
2520 resize_for_pico(void)
2522 fix_windsize(ps_global);
2526 PCOLORS *
2527 colors_for_pico(void)
2529 PCOLORS *colors = NULL;
2530 struct variable *vars = ps_global->vars;
2532 if (pico_usingcolor()){
2533 colors = (PCOLORS *)fs_get(sizeof(PCOLORS));
2535 colors->tbcp = current_titlebar_color();
2537 if (VAR_KEYLABEL_FORE_COLOR && VAR_KEYLABEL_BACK_COLOR){
2538 colors->klcp = new_color_pair(VAR_KEYLABEL_FORE_COLOR,
2539 VAR_KEYLABEL_BACK_COLOR);
2540 if (!pico_is_good_colorpair(colors->klcp))
2541 free_color_pair(&colors->klcp);
2543 else colors->klcp = NULL;
2545 if (colors->klcp && VAR_KEYNAME_FORE_COLOR && VAR_KEYNAME_BACK_COLOR){
2546 colors->kncp = new_color_pair(VAR_KEYNAME_FORE_COLOR,
2547 VAR_KEYNAME_BACK_COLOR);
2549 else colors->kncp = NULL;
2551 if (VAR_STATUS_FORE_COLOR && VAR_STATUS_BACK_COLOR){
2552 colors->stcp = new_color_pair(VAR_STATUS_FORE_COLOR,
2553 VAR_STATUS_BACK_COLOR);
2555 else colors->stcp = NULL;
2557 if (VAR_PROMPT_FORE_COLOR && VAR_PROMPT_BACK_COLOR){
2558 colors->prcp = new_color_pair(VAR_PROMPT_FORE_COLOR,
2559 VAR_PROMPT_BACK_COLOR);
2561 else colors->prcp = NULL;
2564 return colors;
2568 void
2569 free_pcolors(PCOLORS **colors)
2571 if (*colors){
2572 if ((*colors)->tbcp)
2573 free_color_pair(&(*colors)->tbcp);
2574 if ((*colors)->kncp)
2575 free_color_pair(&(*colors)->kncp);
2576 if ((*colors)->klcp)
2577 free_color_pair(&(*colors)->klcp);
2578 if ((*colors)->stcp)
2579 free_color_pair(&(*colors)->stcp);
2580 if ((*colors)->prcp)
2581 free_color_pair(&(*colors)->prcp);
2582 fs_give((void **)colors);
2583 fs_give((void **)colors);
2584 *colors = NULL;