* new version 2.20.8.
[alpine.git] / alpine / alpine.c
blobc7542da8c4b368605aba25774d94119715852605
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: alpine.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2015 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
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 #include "headers.h"
21 #include "../pith/newmail.h"
22 #include "../pith/init.h"
23 #include "../pith/sort.h"
24 #include "../pith/options.h"
25 #include "../pith/list.h"
26 #include "../pith/conf.h"
28 #include "osdep/debuging.h"
29 #include "osdep/termout.gen.h"
30 #include "osdep/chnge_pw.h"
32 #include "alpine.h"
33 #include "mailindx.h"
34 #include "mailcmd.h"
35 #include "addrbook.h"
36 #include "reply.h"
37 #include "arg.h"
38 #include "keymenu.h"
39 #include "status.h"
40 #include "context.h"
41 #include "mailview.h"
42 #include "imap.h"
43 #include "radio.h"
44 #include "folder.h"
45 #include "send.h"
46 #include "help.h"
47 #include "titlebar.h"
48 #include "takeaddr.h"
49 #include "dispfilt.h"
50 #include "init.h"
51 #include "remote.h"
52 #include "pattern.h"
53 #include "setup.h"
54 #include "newuser.h"
55 #include "adrbkcmd.h"
56 #include "signal.h"
57 #include "kblock.h"
58 #include "ldapconf.h"
59 #include "roleconf.h"
60 #include "colorconf.h"
61 #include "print.h"
62 #include "after.h"
63 #include "smime.h"
64 #include "newmail.h"
65 #ifndef _WINDOWS
66 #include "../pico/osdep/raw.h" /* for STD*_FD */
67 #endif
70 #define PIPED_FD 5 /* Some innocuous desc */
73 /* look for my_timer_period in pico directory for an explanation */
74 int my_timer_period = ((IDLE_TIMEOUT + 1)*1000);
76 /* byte count used by our gets routine to keep track */
77 static unsigned long gets_bytes;
81 * Internal prototypes
83 void convert_args_to_utf8(struct pine *, ARGDATA_S *);
84 void preopen_stayopen_folders(void);
85 int read_stdin_char(char *);
86 void main_redrawer(void);
87 void show_main_screen(struct pine *, int, OtherMenu, struct key_menu *, int, Pos *);
88 void do_menu(int, Pos *, struct key_menu *);
89 int choose_setup_cmd(int, MSGNO_S *, SCROLL_S *);
90 int setup_menu(struct pine *);
91 void do_setup_task(int);
92 void queue_init_errors(struct pine *);
93 void process_init_cmds(struct pine *, char **);
94 void goodnight_gracey(struct pine *, int);
95 void pine_read_progress(GETS_DATA *, unsigned long);
96 int remote_pinerc_failure(void);
97 void dump_supported_options(void);
98 int prune_folders_ok(void);
99 #ifdef WIN32
100 char *pine_user_callback(void);
101 #endif
102 #ifdef _WINDOWS
103 int fkey_mode_callback(int, long);
104 void imap_telemetry_on(void);
105 void imap_telemetry_off(void);
106 char *pcpine_help_main(char *);
107 int pcpine_main_cursor(int, long);
108 #define main app_main
109 #endif
112 typedef struct setup_return_val {
113 int cmd;
114 int exc;
115 }SRV_S;
119 * strlen of longest label from keymenu, of labels corresponding to
120 * commands in the middle of the screen. 9 is length of ListFldrs
122 #define LONGEST_LABEL 9 /* length of longest label from keymenu */
124 #define EDIT_EXCEPTION (0x100)
127 static int in_panic = 0;
130 /*----------------------------------------------------------------------
131 main routine -- entry point
133 Args: argv, argc -- The command line arguments
136 Initialize pine, parse arguments and so on
138 If there is a user address on the command line go into send mode and exit,
139 otherwise loop executing the various screens in Alpine.
141 NOTE: The Windows port def's this to "app_main"
142 ----*/
145 main(int argc, char **argv)
147 ARGDATA_S args;
148 int rv;
149 long rvl;
150 struct pine *pine_state;
151 gf_io_t stdin_getc = NULL;
152 char *args_for_debug = NULL, *init_pinerc_debugging = NULL;
154 /*----------------------------------------------------------------------
155 Set up buffering and some data structures
156 ----------------------------------------------------------------------*/
158 pine_state = new_pine_struct();
159 ps_global = pine_state;
162 * fill in optional pith-offered behavior hooks
164 pith_opt_read_msg_prompt = read_msg_prompt;
165 pith_opt_paint_index_hline = paint_index_hline;
166 pith_opt_rfc2369_editorial = rfc2369_editorial;
167 pith_opt_condense_thread_cue = condensed_thread_cue;
168 pith_opt_truncate_sfstr = truncate_subj_and_from_strings;
169 pith_opt_save_and_restore = save_and_restore;
170 pith_opt_newmail_announce = newmail_status_message;
171 pith_opt_newmail_check_cue = newmail_check_cue;
172 pith_opt_checkpoint_cue = newmail_check_point_cue;
173 pith_opt_icon_text = icon_text;
174 pith_opt_rd_metadata_name = rd_metadata_name;
175 pith_opt_remote_pinerc_failure = remote_pinerc_failure;
176 pith_opt_reopen_folder = ask_mailbox_reopen;
177 pith_opt_expunge_prompt = expunge_prompt;
178 pith_opt_begin_closing = expunge_and_close_begins;
179 pith_opt_replyto_prompt = reply_using_replyto_query;
180 pith_opt_reply_to_all_prompt = reply_to_all_query;
181 pith_opt_save_create_prompt = create_for_save_prompt;
182 pith_opt_daemon_confirm = confirm_daemon_send;
183 pith_opt_save_size_changed_prompt = save_size_changed_prompt;
184 pith_opt_save_index_state = setup_index_state;
185 pith_opt_filter_pattern_cmd = pattern_filter_command;
186 pith_opt_get_signature_file = get_signature_file;
187 pith_opt_pretty_var_name = pretty_var_name;
188 pith_opt_pretty_feature_name = pretty_feature_name;
189 pith_opt_closing_stream = titlebar_stream_closing;
190 pith_opt_current_expunged = mm_expunged_current;
191 #ifdef SMIME
192 pith_opt_smime_get_passphrase = smime_get_passphrase;
193 pith_smime_import_certificate = smime_import_certificate;
194 pith_smime_enter_password = alpine_get_password;
195 #endif
196 #ifdef ENABLE_LDAP
197 pith_opt_save_ldap_entry = save_ldap_entry;
198 #endif
200 status_message_lock_init();
201 inverse_itokens();
203 #if HAVE_SRANDOM
205 * Seed the random number generator with the date & pid. Random
206 * numbers are used for new mail notification and bug report id's
208 srandom(getpid() + time(0));
209 #endif
211 /* need home directory early */
212 get_user_info(&ps_global->ui);
214 if(!(pine_state->home_dir = our_getenv("HOME")))
215 pine_state->home_dir = cpystr(ps_global->ui.homedir);
217 #ifdef _WINDOWS
219 char *p;
221 /* normalize path delimiters */
222 for(p = pine_state->home_dir; p = strchr(p, '/'); p++)
223 *p='\\';
225 #endif /* _WINDOWS */
227 #ifdef DEBUG
228 { size_t len = 0;
229 int i;
230 char *p;
231 char *no_args = " <no args>";
233 for(i = 0; i < argc; i++)
234 len += (strlen(argv[i] ? argv[i] : "")+3);
236 if(argc == 1)
237 len += strlen(no_args);
239 p = args_for_debug = (char *)fs_get((len+2) * sizeof(char));
240 *p++ = '\n';
241 *p = '\0';
243 for(i = 0; i < argc; i++){
244 snprintf(p, len+2-(p-args_for_debug), "%s\"%s\"", i ? " " : "", argv[i] ? argv[i] : "");
245 args_for_debug[len+2-1] = '\0';
246 p += strlen(p);
249 if(argc == 1){
250 strncat(args_for_debug, no_args, len+2-strlen(args_for_debug)-1);
251 args_for_debug[len+2-1] = '\0';
254 #endif
256 /*----------------------------------------------------------------------
257 Parse arguments and initialize debugging
258 ----------------------------------------------------------------------*/
259 pine_args(pine_state, argc, argv, &args);
261 #ifndef _WINDOWS
262 if(!isatty(0)){
264 * monkey with descriptors so our normal tty i/o routines don't
265 * choke...
267 dup2(STDIN_FD, PIPED_FD); /* redirected stdin to new desc */
268 dup2(STDERR_FD, STDIN_FD); /* rebind stdin to the tty */
269 stdin_getc = read_stdin_char;
270 if(stdin_getc && args.action == aaURL){
271 display_args_err(
272 "Cannot read stdin when using -url\nFor mailto URLs, use \'body=\' instead",
273 NULL, 1);
274 args_help();
275 exit(-1);
279 #else /* _WINDOWS */
281 * We now have enough information to do some of the basic registry settings.
283 if(ps_global->update_registry != UREG_NEVER_SET){
284 mswin_reg(MSWR_OP_SET
285 | ((ps_global->update_registry == UREG_ALWAYS_SET)
286 ? MSWR_OP_FORCE : 0),
287 MSWR_PINE_DIR, ps_global->pine_dir, (size_t)NULL);
288 mswin_reg(MSWR_OP_SET
289 | ((ps_global->update_registry == UREG_ALWAYS_SET)
290 ? MSWR_OP_FORCE : 0),
291 MSWR_PINE_EXE, ps_global->pine_name, (size_t)NULL);
294 #endif /* _WINDOWS */
296 if(ps_global->convert_sigs &&
297 (!ps_global->pinerc || !ps_global->pinerc[0])){
298 fprintf(stderr, "Use -p <pinerc> with -convert_sigs\n");
299 exit(-1);
302 /* Windows has its own functions to determine width of a character
303 * in the screen, so this is not necessary to do in Window, and
304 * using pith_ucs4width does not produce the correct result
306 #ifndef _WINDOWS
307 mail_parameters(NULL, SET_UCS4WIDTH, (void *) pith_ucs4width);
308 #endif /* _WINDOWS */
309 mail_parameters(NULL, SET_QUOTA, (void *) pine_parse_quota);
310 /* set some default timeouts in case pinerc is remote */
311 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)30);
312 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)15);
313 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
314 /* could be TO_BAIL_THRESHOLD, 15 seems more appropriate for now */
315 pine_state->tcp_query_timeout = 15;
317 mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
318 mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
319 mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
320 #ifdef SMIME
321 mail_parameters(NULL, SET_FREEBODYSPAREP, (void *) free_smime_body_sparep);
322 #endif
324 init_pinerc(pine_state, &init_pinerc_debugging);
326 #ifdef DEBUG
327 /* Since this is specific debugging we don't mind if the
328 ifdef is the type of system.
330 #if defined(HAVE_SMALLOC) || defined(NXT)
331 if(ps_global->debug_malloc)
332 malloc_debug(ps_global->debug_malloc);
333 #endif
334 #ifdef CSRIMALLOC
335 if(ps_global->debug_malloc)
336 mal_debug(ps_global->debug_malloc);
337 #endif
339 if(!ps_global->convert_sigs
340 #ifdef _WINDOWS
341 && !ps_global->install_flag
342 #endif /* _WINDOWS */
344 init_debug();
346 if(args_for_debug){
347 dprint((0, " %s (PID=%ld)\n\n", args_for_debug,
348 (long) getpid()));
349 fs_give((void **)&args_for_debug);
353 char *env_to_free;
354 if((env_to_free = our_getenv("HOME")) != NULL){
355 dprint((2, "Setting home dir from $HOME: \"%s\"\n",
356 env_to_free));
357 fs_give((void **)&env_to_free);
359 else{
360 dprint((2, "Setting home dir: \"%s\"\n",
361 pine_state->home_dir ? pine_state->home_dir : "<?>"));
365 /* Watch out. Sensitive information in debug file. */
366 if(ps_global->debug_imap > 4)
367 mail_parameters(NULL, SET_DEBUGSENSITIVE, (void *) TRUE);
369 #ifndef DEBUGJOURNAL
370 if(ps_global->debug_tcp)
371 #endif
372 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
374 #ifdef _WINDOWS
375 mswin_setdebug(debug, debugfile);
376 mswin_setdebugoncallback (imap_telemetry_on);
377 mswin_setdebugoffcallback (imap_telemetry_off);
378 mswin_enableimaptelemetry(ps_global->debug_imap != 0);
379 #endif
380 #endif /* DEBUG */
382 #ifdef _WINDOWS
383 mswin_setsortcallback(index_sort_callback);
384 mswin_setflagcallback(flag_callback);
385 mswin_sethdrmodecallback(header_mode_callback);
386 mswin_setselectedcallback(any_selected_callback);
387 mswin_setzoomodecallback(zoom_mode_callback);
388 mswin_setfkeymodecallback(fkey_mode_callback);
389 #endif
391 /*------- Set up c-client drivers -------*/
392 #include "../c-client/linkage.c"
394 /*------- ... then tune the drivers just installed -------*/
395 #ifdef _WINDOWS
396 if(_tgetenv(TEXT("HOME")))
397 mail_parameters(NULL, SET_HOMEDIR, (void *) pine_state->home_dir);
399 mail_parameters(NULL, SET_USERPROMPT, (void *) pine_user_callback);
402 * Sniff the environment for timezone offset. We need to do this
403 * here since Windows needs help figuring out UTC, and will adjust
404 * what time() returns based on TZ. THIS WILL SCREW US because
405 * we use time() differences to manage status messages. So, if
406 * rfc822_date, which calls localtime() and thus needs tzset(),
407 * is called while a status message is displayed, it's possible
408 * for time() to return a time *before* what we remember as the
409 * time we put the status message on the display. Sheesh.
411 tzset();
412 #else /* !_WINDOWS */
414 * We used to let c-client do this for us automatically, but it declines
415 * to do so for root. This forces c-client to establish an environment,
416 * even if the uid is 0.
418 env_init(ps_global->ui.login, ps_global->ui.homedir);
421 * Install callback to let us know the progress of network reads...
423 (void) mail_parameters(NULL, SET_READPROGRESS, (void *)pine_read_progress);
424 #endif /* !_WINDOWS */
427 * Install callback to handle certificate validation failures,
428 * allowing the user to continue if they wish.
430 mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) pine_sslcertquery);
431 mail_parameters(NULL, SET_SSLFAILURE, (void *) pine_sslfailure);
433 if(init_pinerc_debugging){
434 dprint((2, init_pinerc_debugging));
435 fs_give((void **)&init_pinerc_debugging);
439 * Initial allocation of array of stream pool pointers.
440 * We do this before init_vars so that we can re-use streams used for
441 * remote config files. These sizes may get changed later.
443 ps_global->s_pool.max_remstream = 2;
444 dprint((9,
445 "Setting initial max_remstream to %d for remote config re-use\n",
446 ps_global->s_pool.max_remstream));
448 init_vars(pine_state, process_init_cmds);
450 #ifdef SMIME
451 if(F_ON(F_DONT_DO_SMIME, ps_global))
452 smime_deinit();
453 #endif /* SMIME */
455 #ifdef ENABLE_NLS
457 * LC_CTYPE is already set from the set_collation call above.
459 * We can't use gettext calls before we do this stuff so it doesn't
460 * help to translate strings that come before this in the program.
461 * Maybe we could rearrange things to accomodate that.
463 setlocale(LC_MESSAGES, "");
464 bindtextdomain(PACKAGE, LOCALEDIR);
465 bind_textdomain_codeset(PACKAGE, "UTF-8");
466 textdomain(PACKAGE);
467 #endif /* ENABLE_NLS */
469 convert_args_to_utf8(pine_state, &args);
471 if(args.action == aaFolder){
472 pine_state->beginning_of_month = first_run_of_month();
473 pine_state->beginning_of_year = first_run_of_year();
476 /* Set up optional for user-defined display filtering */
477 pine_state->tools.display_filter = dfilter;
478 pine_state->tools.display_filter_trigger = dfilter_trigger;
480 #ifdef _WINDOWS
481 if(ps_global->install_flag){
482 init_install_get_vars();
484 if(ps_global->prc)
485 free_pinerc_s(&ps_global->prc);
487 exit(0);
489 #endif
491 if(ps_global->convert_sigs){
492 if(convert_sigs_to_literal(ps_global, 0) == -1){
493 /* TRANSLATORS: sigs refers to signatures, which the user was trying to convert */
494 fprintf(stderr, _("trouble converting sigs\n"));
495 exit(-1);
498 if(ps_global->prc){
499 if(ps_global->prc->outstanding_pinerc_changes)
500 write_pinerc(ps_global, Main, WRP_NONE);
502 free_pinerc_s(&pine_state->prc);
505 exit(0);
509 * Set up a c-client read timeout and timeout handler. In general,
510 * it shouldn't happen, but a server crash or dead link can cause
511 * pine to appear wedged if we don't set this up...
513 rv = 30;
514 if(pine_state->VAR_TCPOPENTIMEO)
515 (void)SVAR_TCP_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
516 mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long)rv);
518 rv = 15;
519 if(pine_state->VAR_TCPREADWARNTIMEO)
520 (void)SVAR_TCP_READWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF);
521 mail_parameters(NULL, SET_READTIMEOUT, (void *)(long)rv);
523 rv = 0;
524 if(pine_state->VAR_TCPWRITEWARNTIMEO){
525 if(!SVAR_TCP_WRITEWARN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
526 if(rv == 0 || rv > 4) /* making sure */
527 mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long)rv);
530 mail_parameters(NULL, SET_TIMEOUT, (void *) pine_tcptimeout);
532 rv = 15;
533 if(pine_state->VAR_RSHOPENTIMEO){
534 if(!SVAR_RSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
535 if(rv == 0 || rv > 4) /* making sure */
536 mail_parameters(NULL, SET_RSHTIMEOUT, (void *)(long)rv);
539 rv = 15;
540 if(pine_state->VAR_SSHOPENTIMEO){
541 if(!SVAR_SSH_OPEN(pine_state, rv, tmp_20k_buf, SIZEOF_20KBUF))
542 if(rv == 0 || rv > 4) /* making sure */
543 mail_parameters(NULL, SET_SSHTIMEOUT, (void *)(long)rv);
546 rvl = 60L;
547 if(pine_state->VAR_MAILDROPCHECK){
548 if(!SVAR_MAILDCHK(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF)){
549 if(rvl == 0L)
550 rvl = (60L * 60L * 24L * 100L); /* 100 days */
552 if(rvl >= 60L) /* making sure */
553 mail_parameters(NULL, SET_SNARFINTERVAL, (void *) rvl);
558 * Lookups of long login names which don't exist are very slow in aix.
559 * This would normally get set in system-wide config if not needed.
561 if(F_ON(F_DISABLE_SHARED_NAMESPACES, ps_global))
562 mail_parameters(NULL, SET_DISABLEAUTOSHAREDNS, (void *) TRUE);
564 if(F_ON(F_HIDE_NNTP_PATH, ps_global))
565 mail_parameters(NULL, SET_NNTPHIDEPATH, (void *) TRUE);
567 if(F_ON(F_MAILDROPS_PRESERVE_STATE, ps_global))
568 mail_parameters(NULL, SET_SNARFPRESERVE, (void *) TRUE);
570 rvl = 0L;
571 if(pine_state->VAR_NNTPRANGE){
572 if(!SVAR_NNTPRANGE(pine_state, rvl, tmp_20k_buf, SIZEOF_20KBUF))
573 if(rvl > 0L)
574 mail_parameters(NULL, SET_NNTPRANGE, (void *) rvl);
578 * Tell c-client not to be so aggressive about uid mappings
580 mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
583 * Setup referral handling
585 mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
586 mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
589 * Setup multiple newsrc transition
591 mail_parameters(NULL, SET_NEWSRCQUERY, (void *) pine_newsrcquery);
594 * Disable some drivers if requested.
596 if(ps_global->VAR_DISABLE_DRIVERS &&
597 ps_global->VAR_DISABLE_DRIVERS[0] &&
598 ps_global->VAR_DISABLE_DRIVERS[0][0]){
599 char **t;
601 for(t = ps_global->VAR_DISABLE_DRIVERS; t[0] && t[0][0]; t++)
602 if(mail_parameters(NULL, DISABLE_DRIVER, (void *)(*t))){
603 dprint((2, "Disabled mail driver \"%s\"\n", *t));
605 else{
606 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
607 _("Failed to disable mail driver \"%s\": name not found"),
608 *t);
609 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
610 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
615 * Disable some authenticators if requested.
617 if(ps_global->VAR_DISABLE_AUTHS &&
618 ps_global->VAR_DISABLE_AUTHS[0] &&
619 ps_global->VAR_DISABLE_AUTHS[0][0]){
620 char **t;
622 for(t = ps_global->VAR_DISABLE_AUTHS; t[0] && t[0][0]; t++)
623 if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *)(*t))){
624 dprint((2,"Disabled SASL authenticator \"%s\"\n", *t));
626 else{
627 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
628 _("Failed to disable SASL authenticator \"%s\": name not found"),
629 *t);
630 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
631 init_error(ps_global, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
636 * setup alternative authentication driver preference for IMAP opens
638 if(F_ON(F_PREFER_ALT_AUTH, ps_global))
639 mail_parameters(NULL, SET_IMAPTRYALT, (void *) TRUE);
642 * Install handler to let us know about potential delays
644 (void) mail_parameters(NULL, SET_BLOCKNOTIFY, (void *) pine_block_notify);
646 if(ps_global->dump_supported_options){
647 dump_supported_options();
648 exit(0);
652 * Install extra headers to fetch along with all the other stuff
653 * mail_fetch_structure and mail_fetch_overview requests.
655 calc_extra_hdrs();
656 if(get_extra_hdrs())
657 (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS,
658 (void *) get_extra_hdrs());
660 if(init_username(pine_state) < 0){
661 fprintf(stderr, _("Who are you? (Unable to look up login name)\n"));
662 exit(-1);
665 if(init_userdir(pine_state) < 0)
666 exit(-1);
668 if(init_hostname(pine_state) < 0)
669 exit(-1);
672 * Verify mail dir if we're not in send only mode...
674 if(args.action == aaFolder && init_mail_dir(pine_state) < 0)
675 exit(-1);
677 init_signals();
679 /*--- input side ---*/
680 if(init_tty_driver(pine_state)){
681 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
682 fprintf(stderr, _("Can't access terminal or input is not a terminal. Redirection of\nstandard input is not allowed. For example \"pine < file\" doesn't work.\n%c"), BELL);
683 exit(-1);
684 #endif /* !_WINDOWS */
688 /*--- output side ---*/
689 rv = config_screen(&(pine_state->ttyo));
690 #ifndef _WINDOWS /* always succeeds under _WINDOWS */
691 if(rv){
692 switch(rv){
693 case -1:
694 printf(_("Terminal type (environment variable TERM) not set.\n"));
695 break;
696 case -2:
697 printf(_("Terminal type \"%s\" is unknown.\n"), getenv("TERM"));
698 break;
699 case -3:
700 printf(_("Can't open terminal capabilities database.\n"));
701 break;
702 case -4:
703 printf(_("Your terminal, of type \"%s\", is lacking functions needed to run alpine.\n"), getenv("TERM"));
704 break;
707 printf("\r");
708 end_tty_driver(pine_state);
709 exit(-1);
711 #endif /* !_WINDOWS */
713 if(F_ON(F_BLANK_KEYMENU,ps_global))
714 FOOTER_ROWS(ps_global) = 1;
716 init_screen();
717 init_keyboard(pine_state->orig_use_fkeys);
718 strncpy(pine_state->inbox_name, INBOX_NAME,
719 sizeof(pine_state->inbox_name)-1);
720 init_folders(pine_state); /* digest folder spec's */
722 pine_state->in_init_seq = 0; /* so output (& ClearScreen) show up */
723 pine_state->dont_use_init_cmds = 1; /* don't use up initial_commands yet */
724 ClearScreen();
726 /* initialize titlebar in case we use it */
727 set_titlebar("", NULL, NULL, NULL, NULL, 0, FolderName, 0, 0, NULL);
730 * Prep storage object driver for PicoText
732 so_register_external_driver(pine_pico_get, pine_pico_give, pine_pico_writec, pine_pico_readc,
733 pine_pico_puts, pine_pico_seek, NULL, NULL);
735 #ifdef DEBUG
736 if(ps_global->debug_imap > 4 || debug > 9){
737 q_status_message(SM_ORDER | SM_DING, 5, 9,
738 _("Warning: sensitive authentication data included in debug file"));
739 flush_status_messages(0);
741 #endif
743 if(args.action == aaPrcCopy || args.action == aaAbookCopy){
744 int exit_val = -1;
745 char *err_msg = NULL;
748 * Don't translate these into UTF-8 because we'll be using them
749 * before we translate next time. User should use ascii.
751 if(args.data.copy.local && args.data.copy.remote){
752 switch(args.action){
753 case aaPrcCopy:
754 exit_val = copy_pinerc(args.data.copy.local,
755 args.data.copy.remote, &err_msg);
756 break;
758 case aaAbookCopy:
759 exit_val = copy_abook(args.data.copy.local,
760 args.data.copy.remote, &err_msg);
761 break;
763 default:
764 break;
767 if(err_msg){
768 q_status_message(SM_ORDER | SM_DING, 3, 4, err_msg);
769 fs_give((void **)&err_msg);
771 goodnight_gracey(pine_state, exit_val);
774 if(args.action == aaFolder
775 && (pine_state->first_time_user || pine_state->show_new_version)){
776 pine_state->mangled_header = 1;
777 show_main_screen(pine_state, 0, FirstMenu, &main_keymenu, 0,
778 (Pos *) NULL);
779 new_user_or_version(pine_state);
780 ClearScreen();
783 /* put back in case we need to suppress output */
784 pine_state->in_init_seq = pine_state->save_in_init_seq;
786 /* queue any init errors so they get displayed in a screen below */
787 queue_init_errors(ps_global);
789 /* "Page" the given file? */
790 if(args.action == aaMore){
791 int dice = 1, redir = 0;
793 if(pine_state->in_init_seq){
794 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
795 clear_cursor_pos();
796 if(pine_state->free_initial_cmds)
797 fs_give((void **)&(pine_state->free_initial_cmds));
799 pine_state->initial_cmds = NULL;
802 /*======= Requested that we simply page the given file =======*/
803 if(args.data.file){ /* Open the requested file... */
804 SourceType src;
805 STORE_S *store = NULL;
806 char *decode_error = NULL;
807 char filename[MAILTMPLEN];
809 if(args.data.file[0] == '\0'){
810 HelpType help = NO_HELP;
812 pine_state->mangled_footer = 1;
813 filename[0] = '\0';
814 while(1){
815 int flags = OE_APPEND_CURRENT;
817 rv = optionally_enter(filename, -FOOTER_ROWS(pine_state),
818 0, sizeof(filename),
819 /* TRANSLATORS: file is computer data */
820 _("File to open : "),
821 NULL, help, &flags);
822 if(rv == 3){
823 help = (help == NO_HELP) ? h_no_F_arg : NO_HELP;
824 continue;
827 if(rv != 4)
828 break;
831 if(rv == 1){
832 q_status_message(SM_ORDER, 0, 2, _("Cancelled"));
833 goodnight_gracey(pine_state, -1);
836 if(*filename){
837 removing_trailing_white_space(filename);
838 removing_leading_white_space(filename);
839 if(is_absolute_path(filename))
840 fnexpand(filename, sizeof(filename));
842 args.data.file = filename;
845 if(!*filename){
846 /* TRANSLATORS: file is computer data */
847 q_status_message(SM_ORDER, 0, 2 ,_("No file to open"));
848 goodnight_gracey(pine_state, -1);
852 if(stdin_getc){
853 redir++;
854 src = CharStar;
855 if(isatty(0) && (store = so_get(src, NULL, EDIT_ACCESS))){
856 gf_io_t pc;
858 gf_set_so_writec(&pc, store);
859 gf_filter_init();
860 if((decode_error = gf_pipe(stdin_getc, pc)) != NULL){
861 dice = 0;
862 q_status_message1(SM_ORDER, 3, 4,
863 _("Problem reading standard input: %s"),
864 decode_error);
867 gf_clear_so_writec(store);
869 else
870 dice = 0;
872 else{
873 src = FileStar;
874 strncpy(ps_global->cur_folder, args.data.file,
875 sizeof(ps_global->cur_folder)-1);
876 ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
877 if(!(store = so_get(src, args.data.file, READ_ACCESS|READ_FROM_LOCALE)))
878 dice = 0;
881 if(dice){
882 SCROLL_S sargs;
884 memset(&sargs, 0, sizeof(SCROLL_S));
885 sargs.text.text = so_text(store);
886 sargs.text.src = src;
887 /* TRANSLATORS: file is computer file being read by user */
888 sargs.text.desc = _("file");
889 /* TRANSLATORS: this is in the title bar at top of screen */
890 sargs.bar.title = _("FILE VIEW");
891 sargs.bar.style = FileTextPercent;
892 sargs.keys.menu = &simple_file_keymenu;
893 setbitmap(sargs.keys.bitmap);
894 scrolltool(&sargs);
896 printf("\n\n");
897 so_give(&store);
901 if(!dice){
902 q_status_message2(SM_ORDER, 3, 4,
903 _("Can't display \"%s\": %s"),
904 (redir) ? _("Standard Input")
905 : args.data.file ? args.data.file : "NULL",
906 error_description(errno));
909 goodnight_gracey(pine_state, 0);
911 else if(args.action == aaMail || (stdin_getc && (args.action != aaURL))){
912 /*======= address on command line/send one message mode ============*/
913 char *to = NULL, *error = NULL, *addr = NULL;
914 int len, good_addr = 1;
915 int exit_val = 0;
916 BUILDER_ARG fcc;
918 if(pine_state->in_init_seq){
919 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
920 clear_cursor_pos();
921 if(pine_state->free_initial_cmds)
922 fs_give((void **) &(pine_state->free_initial_cmds));
924 pine_state->initial_cmds = NULL;
927 /*----- Format the To: line with commas for the composer ---*/
928 if(args.data.mail.addrlist){
929 STRLIST_S *p;
931 for(p = args.data.mail.addrlist, len = 0; p; p = p->next)
932 len += strlen(p->name) + 2;
934 to = (char *) fs_get((len + 5) * sizeof(char));
935 for(p = args.data.mail.addrlist, *to = '\0'; p; p = p->next){
936 if(*to){
937 strncat(to, ", ", len+5-strlen(to)-1);
938 to[len+5-1] = '\0';
941 strncat(to, p->name, len+5-strlen(to)-1);
942 to[len+5-1] = '\0';
945 memset((void *)&fcc, 0, sizeof(BUILDER_ARG));
946 dprint((2, "building addr: -->%s<--\n", to ? to : "?"));
947 good_addr = (build_address(to, &addr, &error, &fcc, NULL) >= 0);
948 dprint((2, "mailing to: -->%s<--\n", addr ? addr : "?"));
949 free_strlist(&args.data.mail.addrlist);
951 else
952 memset(&fcc, 0, sizeof(fcc));
954 if(good_addr){
955 compose_mail(addr, fcc.tptr, NULL,
956 args.data.mail.attachlist, stdin_getc);
958 else{
959 /* TRANSLATORS: refers to bad email address */
960 q_status_message1(SM_ORDER, 3, 4, _("Bad address: %s"), error);
961 exit_val = -1;
964 if(addr)
965 fs_give((void **) &addr);
967 if(fcc.tptr)
968 fs_give((void **) &fcc.tptr);
970 if(args.data.mail.attachlist)
971 free_attachment_list(&args.data.mail.attachlist);
973 if(to)
974 fs_give((void **) &to);
976 if(error)
977 fs_give((void **) &error);
979 goodnight_gracey(pine_state, exit_val);
981 else{
982 char int_mail[MAXPATH+1];
983 struct key_menu *km = &main_keymenu;
985 /*========== Normal pine mail reading mode ==========*/
987 pine_state->mail_stream = NULL;
988 pine_state->mangled_screen = 1;
990 if(args.action == aaURL){
991 url_tool_t f;
993 if(pine_state->in_init_seq){
994 pine_state->in_init_seq = pine_state->save_in_init_seq = 0;
995 clear_cursor_pos();
996 if(pine_state->free_initial_cmds)
997 fs_give((void **) &(pine_state->free_initial_cmds));
998 pine_state->initial_cmds = NULL;
1000 if((f = url_local_handler(args.url)) != NULL){
1001 if(args.data.mail.attachlist){
1002 if(f == url_local_mailto){
1003 if(!(url_local_mailto_and_atts(args.url,
1004 args.data.mail.attachlist)
1005 && pine_state->next_screen))
1006 free_attachment_list(&args.data.mail.attachlist);
1007 goodnight_gracey(pine_state, 0);
1009 else {
1010 q_status_message(SM_ORDER | SM_DING, 3, 4,
1011 _("Only mailto URLs are allowed with file attachments"));
1012 goodnight_gracey(pine_state, -1); /* no return */
1015 else if(!((*f)(args.url) && pine_state->next_screen))
1016 goodnight_gracey(pine_state, 0); /* no return */
1018 else{
1019 q_status_message1(SM_ORDER | SM_DING, 3, 4,
1020 _("Unrecognized URL \"%s\""), args.url);
1021 goodnight_gracey(pine_state, -1); /* no return */
1024 else if(!pine_state->start_in_index){
1025 /* flash message about executing initial commands */
1026 if(pine_state->in_init_seq){
1027 pine_state->in_init_seq = 0;
1028 clear_cursor_pos();
1029 pine_state->mangled_header = 1;
1030 pine_state->mangled_footer = 1;
1031 pine_state->mangled_screen = 0;
1032 /* show that this is Alpine */
1033 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1034 pine_state->mangled_screen = 1;
1035 pine_state->painted_footer_on_startup = 1;
1036 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1037 char buf1[6*MAX_SCREEN_COLS+1];
1038 char buf2[6*MAX_SCREEN_COLS+1];
1039 int wid;
1041 /* TRANSLATORS: Initial Keystroke List is the literal name of an option */
1042 strncpy(buf1, _("Executing Initial Keystroke List......"), sizeof(buf1));
1043 buf1[sizeof(buf1)-1] = '\0';
1044 wid = utf8_width(buf1);
1045 if(wid > ps_global->ttyo->screen_cols){
1046 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1047 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1049 else{
1050 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1051 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1055 pine_state->in_init_seq = 1;
1057 else{
1058 show_main_screen(pine_state, 0, FirstMenu, km, 0, (Pos *)NULL);
1059 pine_state->painted_body_on_startup = 1;
1060 pine_state->painted_footer_on_startup = 1;
1063 else{
1064 /* cancel any initial commands, overridden by cmd line */
1065 if(pine_state->in_init_seq){
1066 pine_state->in_init_seq = 0;
1067 pine_state->save_in_init_seq = 0;
1068 clear_cursor_pos();
1069 if(pine_state->initial_cmds){
1070 if(pine_state->free_initial_cmds)
1071 fs_give((void **)&(pine_state->free_initial_cmds));
1073 pine_state->initial_cmds = NULL;
1076 F_SET(F_USE_FK,pine_state, pine_state->orig_use_fkeys);
1079 (void) do_index_border(pine_state->context_current,
1080 pine_state->cur_folder,
1081 pine_state->mail_stream,
1082 pine_state->msgmap, MsgIndex, NULL,
1083 INDX_CLEAR|INDX_HEADER|INDX_FOOTER);
1084 pine_state->painted_footer_on_startup = 1;
1085 if(MIN(4, pine_state->ttyo->screen_rows - 4) > 1){
1086 char buf1[6*MAX_SCREEN_COLS+1];
1087 char buf2[6*MAX_SCREEN_COLS+1];
1088 int wid;
1090 strncpy(buf1, _("Please wait, opening mail folder......"), sizeof(buf1));
1091 buf1[sizeof(buf1)-1] = '\0';
1092 wid = utf8_width(buf1);
1093 if(wid > ps_global->ttyo->screen_cols){
1094 utf8_pad_to_width(buf2, buf1, sizeof(buf2), ps_global->ttyo->screen_cols, 1);
1095 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4), 0, buf2);
1097 else{
1098 PutLine0(MIN(4, pine_state->ttyo->screen_rows - 4),
1099 MAX(MIN(11, pine_state->ttyo->screen_cols - wid), 0), buf1);
1104 fflush(stdout);
1106 #if !defined(_WINDOWS) && !defined(LEAVEOUTFIFO)
1107 if(ps_global->VAR_FIFOPATH && ps_global->VAR_FIFOPATH[0])
1108 init_newmailfifo(ps_global->VAR_FIFOPATH);
1109 #endif
1111 if(pine_state->in_init_seq){
1112 pine_state->in_init_seq = 0;
1113 clear_cursor_pos();
1116 if(args.action == aaFolder && args.data.folder){
1117 CONTEXT_S *cntxt = NULL, *tc = NULL;
1118 char foldername[MAILTMPLEN];
1119 int notrealinbox = 0;
1121 if(args.data.folder[0] == '\0'){
1122 char *fldr;
1123 unsigned save_def_goto_rule;
1125 foldername[0] = '\0';
1126 save_def_goto_rule = pine_state->goto_default_rule;
1127 pine_state->goto_default_rule = GOTO_FIRST_CLCTN;
1128 tc = default_save_context(pine_state->context_list);
1129 fldr = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1130 pine_state->goto_default_rule = save_def_goto_rule;
1131 if(fldr){
1132 strncpy(foldername, fldr, sizeof(foldername)-1);
1133 foldername[sizeof(foldername)-1] = '\0';
1136 if(*foldername){
1137 removing_trailing_white_space(foldername);
1138 removing_leading_white_space(foldername);
1139 args.data.folder = cpystr(foldername);
1142 if(!*foldername){
1143 q_status_message(SM_ORDER, 0, 2 ,_("No folder to open"));
1144 goodnight_gracey(pine_state, -1);
1148 if(tc)
1149 cntxt = tc;
1150 else if((rv = pine_state->init_context) < 0)
1152 * As with almost all the folder vars in the pinerc,
1153 * we subvert the collection "breakout" here if the
1154 * folder name given looks like an asolute path on
1155 * this system...
1157 cntxt = (is_absolute_path(args.data.folder))
1158 ? NULL : pine_state->context_current;
1159 else if(rv == 0)
1160 cntxt = NULL;
1161 else
1162 for(cntxt = pine_state->context_list;
1163 rv > 1 && cntxt->next;
1164 rv--, cntxt = cntxt->next)
1167 if(pine_state && pine_state->ttyo){
1168 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1169 pine_state->painted_footer_on_startup = 0;
1170 pine_state->mangled_footer = 1;
1173 if(do_broach_folder(args.data.folder, cntxt, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT) <= 0){
1174 q_status_message1(SM_ORDER, 3, 4,
1175 _("Unable to open folder \"%s\""), args.data.folder);
1177 goodnight_gracey(pine_state, -1);
1180 else if(args.action == aaFolder){
1181 #ifdef _WINDOWS
1183 * need to ask for the inbox name if no default under DOS
1184 * since there is no "inbox"
1187 if(!pine_state->VAR_INBOX_PATH || !pine_state->VAR_INBOX_PATH[0]
1188 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0){
1189 HelpType help = NO_HELP;
1190 static ESCKEY_S ekey[] = {{ctrl(T), 2, "^T", "To Fldrs"},
1191 {-1, 0, NULL, NULL}};
1193 pine_state->mangled_footer = 1;
1194 int_mail[0] = '\0';
1195 while(1){
1196 int flags = OE_APPEND_CURRENT;
1198 rv = optionally_enter(int_mail, -FOOTER_ROWS(pine_state),
1199 0, sizeof(int_mail),
1200 _("No inbox! Folder to open as inbox : "),
1201 /* ekey */ NULL, help, &flags);
1202 if(rv == 3){
1203 help = (help == NO_HELP) ? h_sticky_inbox : NO_HELP;
1204 continue;
1207 if(rv != 4)
1208 break;
1211 if(rv == 1){
1212 q_status_message(SM_ORDER, 0, 2 ,_("Folder open cancelled"));
1213 rv = 0; /* reset rv */
1215 else if(rv == 2){
1216 show_main_screen(pine_state,0,FirstMenu,km,0,(Pos *)NULL);
1219 if(*int_mail){
1220 removing_trailing_white_space(int_mail);
1221 removing_leading_white_space(int_mail);
1222 if((!pine_state->VAR_INBOX_PATH
1223 || strucmp(pine_state->VAR_INBOX_PATH, "inbox") == 0)
1224 /* TRANSLATORS: Inbox-Path and PINERC are literal, not to be translated */
1225 && want_to(_("Preserve folder as \"Inbox-Path\" in PINERC"),
1226 'y', 'n', NO_HELP, WT_NORM) == 'y'){
1227 set_variable(V_INBOX_PATH, int_mail, 1, 1, Main);
1229 else{
1230 if(pine_state->VAR_INBOX_PATH)
1231 fs_give((void **)&pine_state->VAR_INBOX_PATH);
1233 pine_state->VAR_INBOX_PATH = cpystr(int_mail);
1236 if(pine_state && pine_state->ttyo){
1237 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1238 pine_state->painted_footer_on_startup = 0;
1239 pine_state->mangled_footer = 1;
1242 do_broach_folder(pine_state->inbox_name,
1243 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1245 else
1246 q_status_message(SM_ORDER, 0, 2 ,_("No folder opened"));
1249 else
1251 #endif /* _WINDOWS */
1252 if(F_ON(F_PREOPEN_STAYOPENS, ps_global))
1253 preopen_stayopen_folders();
1255 if(pine_state && pine_state->ttyo){
1256 blank_keymenu(pine_state->ttyo->screen_rows - 2, 0);
1257 pine_state->painted_footer_on_startup = 0;
1258 pine_state->mangled_footer = 1;
1261 /* open inbox */
1262 do_broach_folder(pine_state->inbox_name,
1263 pine_state->context_list, NULL, DB_INBOXWOCNTXT);
1266 if(pine_state->mangled_footer)
1267 pine_state->painted_footer_on_startup = 0;
1269 if(args.action == aaFolder
1270 && pine_state->mail_stream
1271 && expire_sent_mail())
1272 pine_state->painted_footer_on_startup = 0;
1275 * Initialize the defaults. Initializing here means that
1276 * if they're remote, the user isn't prompted for an imap login
1277 * before the display's drawn, AND there's the chance that
1278 * we can climb onto the already opened folder's stream...
1280 if(ps_global->first_time_user)
1281 init_save_defaults(); /* initialize default save folders */
1283 build_path(int_mail,
1284 ps_global->VAR_OPER_DIR ? ps_global->VAR_OPER_DIR
1285 : pine_state->home_dir,
1286 INTERRUPTED_MAIL, sizeof(int_mail));
1287 if(args.action == aaFolder
1288 && (folder_exists(NULL, int_mail) & FEX_ISFILE))
1289 q_status_message(SM_ORDER | SM_DING, 4, 5,
1290 _("Use Compose command to continue interrupted message."));
1292 #if defined(USE_QUOTAS)
1294 long q;
1295 int over;
1296 q = disk_quota(pine_state->home_dir, &over);
1297 if(q > 0 && over){
1298 q_status_message2(SM_ASYNC | SM_DING, 4, 5,
1299 _("WARNING! Over your disk quota by %s bytes (%s)"),
1300 comatose(q),byte_string(q));
1303 #endif
1305 pine_state->in_init_seq = pine_state->save_in_init_seq;
1306 pine_state->dont_use_init_cmds = 0;
1307 clear_cursor_pos();
1309 if(pine_state->give_fixed_warning)
1310 q_status_message(SM_ASYNC, 0, 10,
1311 /* TRANSLATORS: config is an abbreviation for configuration */
1312 _("Note: some of your config options conflict with site policy and are ignored"));
1314 if(!prune_folders_ok())
1315 q_status_message(SM_ASYNC, 0, 10,
1316 /* TRANSLATORS: Pruned-Folders is literal */
1317 _("Note: ignoring Pruned-Folders outside of default collection for saves"));
1319 if(get_input_timeout() == 0 &&
1320 ps_global->VAR_INBOX_PATH &&
1321 ps_global->VAR_INBOX_PATH[0] == '{')
1322 q_status_message(SM_ASYNC, 0, 10,
1323 _("Note: Mail-Check-Interval=0 may cause IMAP server connection to time out"));
1325 #ifdef _WINDOWS
1326 mswin_setnewmailwidth(ps_global->nmw_width);
1327 #endif
1330 /*-------------------------------------------------------------------
1331 Loop executing the commands
1333 This is done like this so that one command screen can cause
1334 another one to execute it with out going through the main menu.
1335 ------------------------------------------------------------------*/
1336 if(!pine_state->next_screen)
1337 pine_state->next_screen = pine_state->start_in_index
1338 ? mail_index_screen : main_menu_screen;
1339 while(1){
1340 if(pine_state->next_screen == SCREEN_FUN_NULL)
1341 pine_state->next_screen = main_menu_screen;
1343 (*(pine_state->next_screen))(pine_state);
1347 exit(0);
1352 * The arguments need to be converted to UTF-8 for our internal use.
1353 * Not all arguments are converted because some are used before we
1354 * are able to do the conversion, like the pinerc name.
1356 void
1357 convert_args_to_utf8(struct pine *ps, ARGDATA_S *args)
1359 char *fromcharset = NULL;
1360 char *conv;
1362 if(args){
1363 if(ps->keyboard_charmap && strucmp(ps->keyboard_charmap, "UTF-8")
1364 && strucmp(ps->keyboard_charmap, "US-ASCII"))
1365 fromcharset = ps->keyboard_charmap;
1366 else if(ps->display_charmap && strucmp(ps->display_charmap, "UTF-8")
1367 && strucmp(ps->display_charmap, "US-ASCII"))
1368 fromcharset = ps->display_charmap;
1369 #ifndef _WINDOWS
1370 else if(ps->VAR_OLD_CHAR_SET && strucmp(ps->VAR_OLD_CHAR_SET, "UTF-8")
1371 && strucmp(ps->VAR_OLD_CHAR_SET, "US-ASCII"))
1372 fromcharset = ps->VAR_OLD_CHAR_SET;
1373 #endif /* ! _WINDOWS */
1375 if(args->action == aaURL && args->url){
1376 conv = convert_to_utf8(args->url, fromcharset, 0);
1377 if(conv){
1378 fs_give((void **) &args->url);
1379 args->url = conv;
1383 if(args->action == aaFolder && args->data.folder){
1384 conv = convert_to_utf8(args->data.folder, fromcharset, 0);
1385 if(conv){
1386 fs_give((void **) &args->data.folder);
1387 args->data.folder = conv;
1391 if(args->action == aaMore && args->data.file){
1392 conv = convert_to_utf8(args->data.file, fromcharset, 0);
1393 if(conv){
1394 fs_give((void **) &args->data.file);
1395 args->data.file = conv;
1399 if(args->action == aaURL || args->action == aaMail){
1400 if(args->data.mail.addrlist){
1401 STRLIST_S *p;
1403 for(p = args->data.mail.addrlist; p; p=p->next){
1404 if(p->name){
1405 conv = convert_to_utf8(p->name, fromcharset, 0);
1406 if(conv){
1407 fs_give((void **) &p->name);
1408 p->name = conv;
1414 if(args->data.mail.attachlist){
1415 PATMT *p;
1417 for(p = args->data.mail.attachlist; p; p=p->next){
1418 if(p->filename){
1419 conv = convert_to_utf8(p->filename, fromcharset, 0);
1420 if(conv){
1421 fs_give((void **) &p->filename);
1422 p->filename = conv;
1432 void
1433 preopen_stayopen_folders(void)
1435 char **open_these;
1437 for(open_these = ps_global->VAR_PERMLOCKED;
1438 open_these && *open_these; open_these++)
1439 (void) do_broach_folder(*open_these, ps_global->context_list,
1440 NULL, DB_NOVISIT);
1445 * read_stdin_char - simple function to return a character from
1446 * redirected stdin
1449 read_stdin_char(char *c)
1451 int rv;
1453 /* it'd probably be a good idea to fix this to pre-read blocks */
1454 while(1){
1455 rv = read(PIPED_FD, c, 1);
1456 if(rv < 0){
1457 if(errno == EINTR){
1458 dprint((2, "read_stdin_char: read interrupted, restarting\n"));
1459 continue;
1461 else
1462 dprint((1, "read_stdin_char: read FAILED: %s\n",
1463 error_description(errno)));
1465 break;
1467 return(rv);
1471 /* this default is from the array of structs below */
1472 #define DEFAULT_MENU_ITEM ((unsigned) 3) /* LIST FOLDERS */
1473 #define ABOOK_MENU_ITEM ((unsigned) 4) /* ADDRESS BOOK */
1474 #define MAX_MENU_ITEM ((unsigned) 6)
1476 * Skip this many spaces between rows of main menu screen.
1477 * We have MAX_MENU_ITEM+1 = # of commands in menu
1478 * 1 = copyright line
1479 * MAX_MENU_ITEM = rows between commands
1480 * 1 = extra row above commands
1481 * 1 = row between commands and copyright
1483 * To make it simple, if there is enough room for all of that include all the
1484 * extra space, if not, cut it all out.
1486 #define MNSKIP(X) (((HEADER_ROWS(X)+FOOTER_ROWS(X)+(MAX_MENU_ITEM+1)+1+MAX_MENU_ITEM+1+1) <= (X)->ttyo->screen_rows) ? 1 : 0)
1488 static unsigned menu_index = DEFAULT_MENU_ITEM;
1491 * One of these for each line that gets printed in the middle of the
1492 * screen in the main menu.
1494 static struct menu_key {
1495 char *key_and_name,
1496 *news_addition;
1497 int key_index; /* index into keymenu array for this cmd */
1498 } mkeys[] = {
1500 * TRANSLATORS: These next few are headings on the Main alpine menu.
1501 * It's nice if the dashes can be made to line up vertically.
1503 {N_(" %s HELP - Get help using Alpine"),
1504 NULL, MAIN_HELP_KEY},
1505 {N_(" %s COMPOSE MESSAGE - Compose and send%s a message"),
1506 /* TRANSLATORS: We think of sending an email message or posting a news message.
1507 The message is shown as Compose and send/post a message */
1508 N_("/post"), MAIN_COMPOSE_KEY},
1509 {N_(" %s MESSAGE INDEX - View messages in current folder"),
1510 NULL, MAIN_INDEX_KEY},
1511 {N_(" %s FOLDER LIST - Select a folder%s to view"),
1512 /* TRANSLATORS: When news is supported the message above becomes
1513 Select a folder OR news group to view */
1514 N_(" OR news group"), MAIN_FOLDER_KEY},
1515 {N_(" %s ADDRESS BOOK - Update address book"),
1516 NULL, MAIN_ADDRESS_KEY},
1517 {N_(" %s SETUP - Configure Alpine Options"),
1518 NULL, MAIN_SETUP_KEY},
1519 /* TRANSLATORS: final Main menu line */
1520 {N_(" %s QUIT - Leave the Alpine program"),
1521 NULL, MAIN_QUIT_KEY}
1526 /*----------------------------------------------------------------------
1527 display main menu and execute main menu commands
1529 Args: The usual pine structure
1531 Result: main menu commands are executed
1534 M A I N M E N U S C R E E N
1536 Paint the main menu on the screen, get the commands and either execute
1537 the function or pass back the name of the function to execute for the menu
1538 selection. Only simple functions that always return here can be executed
1539 here.
1541 This functions handling of new mail, redrawing, errors and such can
1542 serve as a template for the other screen that do much the same thing.
1544 There is a loop that fetchs and executes commands until a command to leave
1545 this screen is given. Then the name of the next screen to display is
1546 stored in next_screen member of the structure and this function is exited
1547 with a return.
1549 First a check for new mail is performed. This might involve reading the new
1550 mail into the inbox which might then cause the screen to be repainted.
1552 Then the general screen painting is done. This is usually controlled
1553 by a few flags and some other position variables. If they change they
1554 tell this part of the code what to repaint. This will include cursor
1555 motion and so on.
1556 ----*/
1557 void
1558 main_menu_screen(struct pine *pine_state)
1560 UCS ch;
1561 int cmd, just_a_navigate_cmd, setup_command, km_popped;
1562 int notrealinbox;
1563 char *new_folder, *utf8str;
1564 CONTEXT_S *tc;
1565 struct key_menu *km;
1566 OtherMenu what;
1567 Pos curs_pos;
1569 ps_global = pine_state;
1570 just_a_navigate_cmd = 0;
1571 km_popped = 0;
1572 menu_index = DEFAULT_MENU_ITEM;
1573 what = FirstMenu; /* which keymenu to display */
1574 ch = 'x'; /* For display_message 1st time through */
1575 pine_state->next_screen = SCREEN_FUN_NULL;
1576 pine_state->prev_screen = main_menu_screen;
1577 curs_pos.row = pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1578 curs_pos.col = 0;
1579 km = &main_keymenu;
1581 mailcap_free(); /* free resources we won't be using for a while */
1583 if(!pine_state->painted_body_on_startup
1584 && !pine_state->painted_footer_on_startup){
1585 pine_state->mangled_screen = 1;
1588 dprint((1, "\n\n ---- MAIN_MENU_SCREEN ----\n"));
1590 while(1){
1591 if(km_popped){
1592 km_popped--;
1593 if(km_popped == 0){
1594 clearfooter(pine_state);
1595 pine_state->mangled_body = 1;
1600 * fix up redrawer just in case some submenu caused it to get
1601 * reassigned...
1603 pine_state->redrawer = main_redrawer;
1605 /*----------- Check for new mail -----------*/
1606 if(new_mail(0, NM_TIMING(ch), NM_STATUS_MSG | NM_DEFER_SORT) >= 0)
1607 pine_state->mangled_header = 1;
1609 if(streams_died())
1610 pine_state->mangled_header = 1;
1612 show_main_screen(pine_state, just_a_navigate_cmd, what, km,
1613 km_popped, &curs_pos);
1614 just_a_navigate_cmd = 0;
1615 what = SameMenu;
1617 /*---- This displays new mail notification, or errors ---*/
1618 if(km_popped){
1619 FOOTER_ROWS(pine_state) = 3;
1620 mark_status_dirty();
1623 display_message(ch);
1624 if(km_popped){
1625 FOOTER_ROWS(pine_state) = 1;
1626 mark_status_dirty();
1629 if(F_OFF(F_SHOW_CURSOR, ps_global)){
1630 curs_pos.row =pine_state->ttyo->screen_rows-FOOTER_ROWS(pine_state);
1631 curs_pos.col =0;
1634 MoveCursor(curs_pos.row, curs_pos.col);
1636 /*------ Read the command from the keyboard ----*/
1637 #ifdef MOUSE
1638 mouse_in_content(KEY_MOUSE, -1, -1, 0, 0);
1639 register_mfunc(mouse_in_content, HEADER_ROWS(pine_state), 0,
1640 pine_state->ttyo->screen_rows-(FOOTER_ROWS(pine_state)+1),
1641 pine_state->ttyo->screen_cols);
1642 #endif
1643 #if defined(DOS) || defined(OS2)
1645 * AND pre-build header lines. This works just fine under
1646 * DOS since we wait for characters in a loop. Something will
1647 * will have to change under UNIX if we want to do the same.
1649 /* while_waiting = build_header_cache; */
1650 #ifdef _WINDOWS
1651 mswin_sethelptextcallback(pcpine_help_main);
1652 mswin_mousetrackcallback(pcpine_main_cursor);
1653 #endif
1654 #endif
1655 ch = READ_COMMAND(&utf8str);
1656 #ifdef MOUSE
1657 clear_mfunc(mouse_in_content);
1658 #endif
1659 #if defined(DOS) || defined(OS2)
1660 /* while_waiting = NULL; */
1661 #ifdef _WINDOWS
1662 mswin_sethelptextcallback(NULL);
1663 mswin_mousetrackcallback(NULL);
1664 #endif
1665 #endif
1667 /* No matter what, Quit here always works */
1668 if(ch == 'q' || ch == 'Q'){
1669 cmd = MC_QUIT;
1671 #ifdef DEBUG
1672 else if(debug && ch && ch < 0x80 && strchr("123456789", ch)){
1673 int olddebug;
1675 olddebug = debug;
1676 debug = ch - '0';
1677 if(debug > 7)
1678 ps_global->debug_timestamp = 1;
1679 else
1680 ps_global->debug_timestamp = 0;
1682 if(debug > 7)
1683 ps_global->debug_imap = 4;
1684 else if(debug > 6)
1685 ps_global->debug_imap = 3;
1686 else if(debug > 4)
1687 ps_global->debug_imap = 2;
1688 else if(debug > 2)
1689 ps_global->debug_imap = 1;
1690 else
1691 ps_global->debug_imap = 0;
1693 if(ps_global->mail_stream){
1694 if(ps_global->debug_imap > 0){
1695 mail_debug(ps_global->mail_stream);
1696 #ifdef _WINDOWS
1697 mswin_enableimaptelemetry(TRUE);
1698 #endif
1700 else{
1701 mail_nodebug(ps_global->mail_stream);
1702 #ifdef _WINDOWS
1703 mswin_enableimaptelemetry(FALSE);
1704 #endif
1708 if(debug > 7 && olddebug <= 7)
1709 mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
1710 else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
1711 mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
1713 dprint((1, "*** Debug level set to %d ***\n", debug));
1714 if(debugfile)
1715 fflush(debugfile);
1717 q_status_message1(SM_ORDER, 0, 1, _("Debug level set to %s"),
1718 int2string(debug));
1719 continue;
1721 #endif /* DEBUG */
1722 else{
1723 cmd = menu_command(ch, km);
1725 if(km_popped)
1726 switch(cmd){
1727 case MC_NONE :
1728 case MC_OTHER :
1729 case MC_RESIZE :
1730 case MC_REPAINT :
1731 km_popped++;
1732 break;
1734 default:
1735 clearfooter(pine_state);
1736 break;
1740 /*------ Execute the command ------*/
1741 switch (cmd){
1742 help_case :
1743 /*------ HELP ------*/
1744 case MC_HELP :
1746 if(FOOTER_ROWS(pine_state) == 1 && km_popped == 0){
1747 km_popped = 2;
1748 pine_state->mangled_footer = 1;
1750 else{
1751 /* TRANSLATORS: This is a screen title */
1752 helper(main_menu_tx, _("HELP FOR MAIN MENU"), 0);
1753 pine_state->mangled_screen = 1;
1756 break;
1759 /*---------- display other key bindings ------*/
1760 case MC_OTHER :
1761 if(ch == 'o')
1762 warn_other_cmds();
1764 what = NextMenu;
1765 pine_state->mangled_footer = 1;
1766 break;
1769 /*---------- Previous item in menu ----------*/
1770 case MC_PREVITEM :
1771 if(menu_index > 0) {
1772 menu_index--;
1773 pine_state->mangled_body = 1;
1774 if(km->which == 0)
1775 pine_state->mangled_footer = 1;
1777 just_a_navigate_cmd++;
1779 else
1780 /* TRANSLATORS: list refers to list of commands in main menu */
1781 q_status_message(SM_ORDER, 0, 2, _("Already at top of list"));
1783 break;
1786 /*---------- Next item in menu ----------*/
1787 case MC_NEXTITEM :
1788 if(menu_index < MAX_MENU_ITEM){
1789 menu_index++;
1790 pine_state->mangled_body = 1;
1791 if(km->which == 0)
1792 pine_state->mangled_footer = 1;
1794 just_a_navigate_cmd++;
1796 else
1797 q_status_message(SM_ORDER, 0, 2, _("Already at bottom of list"));
1799 break;
1802 /*---------- Release Notes ----------*/
1803 case MC_RELNOTES :
1804 /* TRANSLATORS: This is a screen title */
1805 helper(h_news, _("ALPINE RELEASE NOTES"), 0);
1806 pine_state->mangled_screen = 1;
1807 break;
1810 #ifdef KEYBOARD_LOCK
1811 /*---------- Keyboard lock ----------*/
1812 case MC_KBLOCK :
1813 (void) lock_keyboard();
1814 pine_state->mangled_screen = 1;
1815 break;
1816 #endif /* KEYBOARD_LOCK */
1819 /*---------- Quit pine ----------*/
1820 case MC_QUIT :
1821 pine_state->next_screen = quit_screen;
1822 return;
1825 /*---------- Go to composer ----------*/
1826 case MC_COMPOSE :
1827 pine_state->next_screen = compose_screen;
1828 return;
1831 /*---- Go to alternate composer ------*/
1832 case MC_ROLE :
1833 pine_state->next_screen = alt_compose_screen;
1834 return;
1837 /*---------- Top of Folder list ----------*/
1838 case MC_COLLECTIONS :
1839 pine_state->next_screen = folder_screen;
1840 return;
1843 /*---------- Goto new folder ----------*/
1844 case MC_GOTO :
1845 tc = ps_global->context_current;
1846 new_folder = broach_folder(-FOOTER_ROWS(pine_state), 1, &notrealinbox, &tc);
1847 if(new_folder)
1848 visit_folder(ps_global, new_folder, tc, NULL, notrealinbox ? 0L : DB_INBOXWOCNTXT);
1850 return;
1853 /*---------- Go to index ----------*/
1854 case MC_INDEX :
1855 if(THREADING()
1856 && sp_viewing_a_thread(pine_state->mail_stream)
1857 && unview_thread(pine_state, pine_state->mail_stream,
1858 pine_state->msgmap)){
1859 pine_state->view_skipped_index = 0;
1860 pine_state->mangled_screen = 1;
1863 pine_state->next_screen = mail_index_screen;
1864 return;
1867 /*---------- Review Status Messages ----------*/
1868 case MC_JOURNAL :
1869 review_messages();
1870 pine_state->mangled_screen = 1;
1871 break;
1874 /*---------- Setup mini menu ----------*/
1875 case MC_SETUP :
1876 setup_case :
1877 setup_command = setup_menu(pine_state);
1878 pine_state->mangled_footer = 1;
1879 do_setup_task(setup_command);
1880 if(ps_global->next_screen != main_menu_screen)
1881 return;
1883 break;
1886 /*---------- Go to address book ----------*/
1887 case MC_ADDRBOOK :
1888 pine_state->next_screen = addr_book_screen;
1889 return;
1892 /*------ Repaint the works -------*/
1893 case MC_RESIZE :
1894 case MC_REPAINT :
1895 ClearScreen();
1896 pine_state->mangled_screen = 1;
1897 break;
1900 #ifdef MOUSE
1901 /*------- Mouse event ------*/
1902 case MC_MOUSE :
1904 MOUSEPRESS mp;
1905 unsigned ndmi;
1906 struct pine *ps = pine_state;
1908 mouse_get_last (NULL, &mp);
1910 #ifdef _WINDOWS
1911 if(mp.button == M_BUTTON_RIGHT){
1912 if(!mp.doubleclick){
1913 static MPopup main_popup[] = {
1914 {tQueue, {"Folder List", lNormal}, {'L'}},
1915 {tQueue, {"Message Index", lNormal}, {'I'}},
1916 {tSeparator},
1917 {tQueue, {"Address Book", lNormal}, {'A'}},
1918 {tQueue, {"Setup Options", lNormal}, {'S'}},
1919 {tTail}
1922 mswin_popup(main_popup);
1925 else {
1926 #endif
1927 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps)))
1928 ndmi = (mp.row+1 - HEADER_ROWS(ps) - (MNSKIP(ps)+1))/(MNSKIP(ps)+1);
1930 if (mp.row >= (HEADER_ROWS(ps) + MNSKIP(ps))
1931 && !(MNSKIP(ps) && (mp.row+1) & 0x01)
1932 && ndmi <= MAX_MENU_ITEM
1933 && FOOTER_ROWS(ps) + (ndmi+1)*(MNSKIP(ps)+1)
1934 + MNSKIP(ps) + FOOTER_ROWS(ps) <= ps->ttyo->screen_rows){
1935 if(mp.doubleclick){
1936 switch(ndmi){ /* fake main_screen request */
1937 case 0 :
1938 goto help_case;
1940 case 1 :
1941 pine_state->next_screen = compose_screen;
1942 return;
1944 case 2 :
1945 pine_state->next_screen = mail_index_screen;
1946 return;
1948 case 3 :
1949 pine_state->next_screen = folder_screen;
1950 return;
1952 case 4 :
1953 pine_state->next_screen = addr_book_screen;
1954 return;
1956 case 5 :
1957 goto setup_case;
1959 case 6 :
1960 pine_state->next_screen = quit_screen;
1961 return;
1963 default: /* no op */
1964 break;
1967 else{
1968 menu_index = ndmi;
1969 pine_state->mangled_body = 1;
1970 if(km->which == 0)
1971 pine_state->mangled_footer = 1;
1973 just_a_navigate_cmd++;
1976 #ifdef _WINDOWS
1978 #endif
1981 break;
1982 #endif
1985 /*------ Input timeout ------*/
1986 case MC_NONE :
1987 break; /* noop for timeout loop mail check */
1990 /*------ Bogus Input ------*/
1991 case MC_UNKNOWN :
1992 if(ch == 'm' || ch == 'M'){
1993 q_status_message(SM_ORDER, 0, 1, "Already in Main Menu");
1994 break;
1997 default:
1998 bogus_command(ch, F_ON(F_USE_FK,pine_state) ? "F1" : "?");
1999 break;
2001 case MC_UTF8:
2002 bogus_utf8_command(utf8str, F_ON(F_USE_FK, pine_state) ? "F1" : "?");
2003 break;
2004 } /* the switch */
2005 } /* the BIG while loop! */
2009 /*----------------------------------------------------------------------
2010 Re-Draw the main menu
2012 Args: none.
2014 Result: main menu is re-displayed
2015 ----*/
2016 void
2017 main_redrawer(void)
2019 struct key_menu *km = &main_keymenu;
2021 ps_global->mangled_screen = 1;
2022 show_main_screen(ps_global, 0, FirstMenu, km, 0, (Pos *)NULL);
2026 /*----------------------------------------------------------------------
2027 Draw the main menu
2029 Args: pine_state - the usual struct
2030 quick_draw - tells do_menu() it can skip some drawing
2031 what - tells which section of keymenu to draw
2032 km - the keymenu
2033 cursor_pos - returns a good position for the cursor to be located
2035 Result: main menu is displayed
2036 ----*/
2037 void
2038 show_main_screen(struct pine *ps, int quick_draw, OtherMenu what,
2039 struct key_menu *km, int km_popped, Pos *cursor_pos)
2041 if(ps->painted_body_on_startup || ps->painted_footer_on_startup){
2042 ps->mangled_screen = 0; /* only worry about it here */
2043 ps->mangled_header = 1; /* we have to redo header */
2044 if(!ps->painted_body_on_startup)
2045 ps->mangled_body = 1; /* make sure to paint body*/
2047 if(!ps->painted_footer_on_startup)
2048 ps->mangled_footer = 1; /* make sure to paint footer*/
2050 ps->painted_body_on_startup = 0;
2051 ps->painted_footer_on_startup = 0;
2054 if(ps->mangled_screen){
2055 ps->mangled_header = 1;
2056 ps->mangled_body = 1;
2057 ps->mangled_footer = 1;
2058 ps->mangled_screen = 0;
2061 #ifdef _WINDOWS
2062 /* Reset the scroll range. Main screen never scrolls. */
2063 scroll_setrange (0L, 0L);
2064 mswin_beginupdate();
2065 #endif
2067 /* paint the titlebar if needed */
2068 if(ps->mangled_header){
2069 /* TRANSLATORS: screen title */
2070 set_titlebar(_("MAIN MENU"), ps->mail_stream, ps->context_current,
2071 ps->cur_folder, ps->msgmap, 1, FolderName, 0, 0, NULL);
2072 ps->mangled_header = 0;
2075 /* paint the body if needed */
2076 if(ps->mangled_body){
2077 if(!quick_draw)
2078 ClearBody();
2080 do_menu(quick_draw, cursor_pos, km);
2081 ps->mangled_body = 0;
2084 /* paint the keymenu if needed */
2085 if(km && ps->mangled_footer){
2086 static char label[LONGEST_LABEL + 2 + 1], /* label + brackets + \0 */
2087 name[8];
2088 bitmap_t bitmap;
2090 setbitmap(bitmap);
2092 #ifdef KEYBOARD_LOCK
2093 if(ps_global->restricted || F_ON(F_DISABLE_KBLOCK_CMD,ps_global))
2094 #endif
2095 clrbitn(MAIN_KBLOCK_KEY, bitmap);
2097 menu_clear_binding(km, '>');
2098 menu_clear_binding(km, '.');
2099 menu_clear_binding(km, KEY_RIGHT);
2100 menu_clear_binding(km, ctrl('M'));
2101 menu_clear_binding(km, ctrl('J'));
2102 km->keys[MAIN_DEFAULT_KEY].bind
2103 = km->keys[mkeys[menu_index].key_index].bind;
2104 km->keys[MAIN_DEFAULT_KEY].label
2105 = km->keys[mkeys[menu_index].key_index].label;
2107 /* put brackets around the default action */
2108 snprintf(label, sizeof(label), "[%s]", km->keys[mkeys[menu_index].key_index].label);
2109 label[sizeof(label)-1] = '\0';
2110 strncpy(name, ">", sizeof(name));
2111 name[sizeof(name)-1] = '\0';
2112 km->keys[MAIN_DEFAULT_KEY].label = label;
2113 km->keys[MAIN_DEFAULT_KEY].name = name;
2114 menu_add_binding(km, '>', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2115 menu_add_binding(km, '.', km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2116 menu_add_binding(km, ctrl('M'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2117 menu_add_binding(km, ctrl('J'), km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2119 if(F_ON(F_ARROW_NAV,ps_global))
2120 menu_add_binding(km, KEY_RIGHT, km->keys[MAIN_DEFAULT_KEY].bind.cmd);
2122 if(km_popped){
2123 FOOTER_ROWS(ps) = 3;
2124 clearfooter(ps);
2127 draw_keymenu(km, bitmap, ps_global->ttyo->screen_cols,
2128 1-FOOTER_ROWS(ps_global), 0, what);
2129 ps->mangled_footer = 0;
2130 if(km_popped){
2131 FOOTER_ROWS(ps) = 1;
2132 mark_keymenu_dirty();
2136 #ifdef _WINDOWS
2137 mswin_endupdate();
2138 #endif
2142 /*----------------------------------------------------------------------
2143 Actually display the main menu
2145 Args: quick_draw - just a next or prev command was typed so we only have
2146 to redraw the highlighting
2147 cursor_pos - a place to return a good value for cursor location
2149 Result: Main menu is displayed
2150 ---*/
2151 void
2152 do_menu(int quick_draw, Pos *cursor_pos, struct key_menu *km)
2154 struct pine *ps = ps_global;
2155 int dline, indent, longest = 0, cmd;
2156 char buf[4*MAX_SCREEN_COLS+1];
2157 char buf2[4*MAX_SCREEN_COLS+1];
2158 static int last_inverse = -1;
2159 Pos pos;
2161 /* find the longest command */
2162 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2163 memset((void *) buf, ' ', sizeof(buf));
2164 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2165 (F_OFF(F_USE_FK,ps)
2166 && km->keys[mkeys[cmd].key_index].name)
2167 ? km->keys[mkeys[cmd].key_index].name : "",
2168 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2169 ? _(mkeys[cmd].news_addition) : "");
2170 buf[sizeof(buf)-1] = '\0';
2172 if(longest < (indent = utf8_width(buf)))
2173 longest = indent;
2176 indent = MAX(((ps->ttyo->screen_cols - longest)/2) - 1, 0);
2178 dline = HEADER_ROWS(ps) + MNSKIP(ps);
2179 for(cmd = 0; cmd < sizeof(mkeys)/(sizeof(mkeys[1])); cmd++){
2180 /* leave room for copyright and footer */
2181 if(dline + MNSKIP(ps) + 1 + FOOTER_ROWS(ps) >= ps->ttyo->screen_rows)
2182 break;
2184 if(quick_draw && !(cmd == last_inverse || cmd == menu_index)){
2185 dline += (1 + MNSKIP(ps));
2186 continue;
2189 if(cmd == menu_index)
2190 StartInverse();
2192 memset((void *) buf, ' ', sizeof(buf));
2193 snprintf(buf, sizeof(buf), mkeys[cmd].key_and_name[0] ? _(mkeys[cmd].key_and_name) : "",
2194 (F_OFF(F_USE_FK,ps)
2195 && km->keys[mkeys[cmd].key_index].name)
2196 ? km->keys[mkeys[cmd].key_index].name : "",
2197 (ps->VAR_NEWS_SPEC && mkeys[cmd].news_addition && mkeys[cmd].news_addition[0])
2198 ? _(mkeys[cmd].news_addition) : "");
2199 buf[sizeof(buf)-1] = '\0';
2201 utf8_pad_to_width(buf2, buf, sizeof(buf2),
2202 MIN(ps->ttyo->screen_cols-indent,longest+1), 1);
2203 pos.row = dline++;
2204 pos.col = indent;
2205 PutLine0(pos.row, pos.col, buf2);
2207 if(MNSKIP(ps))
2208 dline++;
2210 if(cmd == menu_index){
2211 if(cursor_pos){
2212 cursor_pos->row = pos.row;
2213 /* 6 is 1 for the letter plus 5 spaces */
2214 cursor_pos->col = pos.col + 6;
2215 if(F_OFF(F_USE_FK,ps))
2216 cursor_pos->col++;
2218 cursor_pos->col = MIN(cursor_pos->col, ps->ttyo->screen_cols);
2221 EndInverse();
2226 last_inverse = menu_index;
2228 if(!quick_draw && FOOTER_ROWS(ps)+1 < ps->ttyo->screen_rows){
2229 utf8_to_width(buf2, LEGAL_NOTICE, sizeof(buf2),
2230 ps->ttyo->screen_cols-3, NULL);
2231 PutLine0(ps->ttyo->screen_rows - (FOOTER_ROWS(ps)+1),
2232 MAX(0, ((ps->ttyo->screen_cols-utf8_width(buf2))/2)),
2233 buf2);
2236 fflush(stdout);
2241 choose_setup_cmd(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
2243 int rv = 1;
2244 SRV_S *srv;
2246 if(!(srv = (SRV_S *)sparms->proc.data.p)){
2247 sparms->proc.data.p = (SRV_S *)fs_get(sizeof(*srv));
2248 srv = (SRV_S *)sparms->proc.data.p;
2249 memset(srv, 0, sizeof(*srv));
2252 ps_global->next_screen = SCREEN_FUN_NULL;
2254 switch(cmd){
2255 case MC_PRINTER :
2256 srv->cmd = 'p';
2257 break;
2259 case MC_PASSWD :
2260 srv->cmd = 'n';
2261 break;
2263 case MC_CONFIG :
2264 srv->cmd = 'c';
2265 break;
2267 case MC_SIG :
2268 srv->cmd = 's';
2269 break;
2271 case MC_ABOOKS :
2272 srv->cmd = 'a';
2273 break;
2275 case MC_CLISTS :
2276 srv->cmd = 'l';
2277 break;
2279 case MC_RULES :
2280 srv->cmd = 'r';
2281 break;
2283 case MC_DIRECTORY :
2284 srv->cmd = 'd';
2285 break;
2287 case MC_KOLOR :
2288 srv->cmd = 'k';
2289 break;
2291 case MC_REMOTE :
2292 srv->cmd = 'z';
2293 break;
2295 case MC_SECURITY : /* S/MIME setup screen */
2296 srv->cmd = 'm';
2297 break;
2299 case MC_EXCEPT :
2300 srv->exc = !srv->exc;
2301 menu_clear_binding(sparms->keys.menu, 'x');
2302 if(srv->exc){
2303 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2304 /* TRANSLATORS: screen title */
2305 sparms->bar.title = cpystr(_("SETUP EXCEPTIONS"));
2306 ps_global->mangled_header = 1;
2307 /* TRANSLATORS: The reason the X is upper case in eXceptions
2308 is because the command key is X. It isn't necessary, just
2309 nice if it works. */
2310 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2311 N_("not eXceptions"), SETUP_EXCEPT);
2313 else{
2314 if(sparms->bar.title) fs_give((void **)&sparms->bar.title);
2315 /* TRANSLATORS: screen title */
2316 sparms->bar.title = cpystr(_("SETUP"));
2317 ps_global->mangled_header = 1;
2318 menu_init_binding(sparms->keys.menu, 'x', MC_EXCEPT, "X",
2319 N_("eXceptions"), SETUP_EXCEPT);
2322 if(sparms->keys.menu->which == 1)
2323 ps_global->mangled_footer = 1;
2325 rv = 0;
2326 break;
2328 case MC_NO_EXCEPT :
2329 #if defined(DOS) || defined(OS2)
2330 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \"PINERCEX\" file to use eXceptions"));
2331 #else
2332 q_status_message(SM_ORDER, 0, 2, _("Need argument \"-x <except_config>\" or \".pinercex\" file to use eXceptions"));
2333 #endif
2334 rv = 0;
2335 break;
2337 default:
2338 alpine_panic("Unexpected command in choose_setup_cmd");
2339 break;
2342 return(rv);
2347 setup_menu(struct pine *ps)
2349 int ret = 0, exceptions = 0;
2350 int printer = 0, passwd = 0, config = 0, sig = 0, dir = 0, smime = 0, exc = 0;
2351 SCROLL_S sargs;
2352 SRV_S *srv;
2353 STORE_S *store;
2355 if(!(store = so_get(CharStar, NULL, EDIT_ACCESS))){
2356 q_status_message(SM_ORDER | SM_DING, 3, 3, _("Error allocating space."));
2357 return(ret);
2360 #if !defined(DOS)
2361 if(!ps_global->vars[V_PRINTER].is_fixed) /* printer can be changed */
2362 printer++;
2363 #endif
2365 #ifdef PASSWD_PROG
2366 if(F_OFF(F_DISABLE_PASSWORD_CMD,ps_global)) /* password is allowed */
2367 passwd++;
2368 #endif
2370 if(F_OFF(F_DISABLE_CONFIG_SCREEN,ps_global)) /* config allowed */
2371 config++;
2373 if(F_OFF(F_DISABLE_SIGEDIT_CMD,ps_global)) /* .sig editing is allowed */
2374 sig++;
2376 #ifdef ENABLE_LDAP
2377 dir++;
2378 #endif
2380 #ifdef SMIME
2381 smime++;
2382 #endif
2384 if(ps_global->post_prc)
2385 exc++;
2387 /* TRANSLATORS: starting here we have a whole screen of help text */
2388 so_puts(store, _("This is the Setup screen for Alpine. Choose from the following commands:\n"));
2390 so_puts(store, "\n");
2391 so_puts(store, _("(E) Exit Setup:\n"));
2392 so_puts(store, _(" This puts you back at the Main Menu.\n"));
2394 if(exc){
2395 so_puts(store, "\n");
2396 so_puts(store, _("(X) eXceptions:\n"));
2397 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2398 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2399 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2400 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2401 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2402 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2403 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2404 so_puts(store, _(" on a particular platform.\n"));
2405 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2406 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2407 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2410 if(printer){
2411 so_puts(store, "\n");
2412 so_puts(store, _("(P) Printer:\n"));
2413 so_puts(store, _(" Allows you to set a default printer and to define custom\n"));
2414 so_puts(store, _(" print commands.\n"));
2417 if(passwd){
2418 so_puts(store, "\n");
2419 so_puts(store, _("(N) Newpassword:\n"));
2420 so_puts(store, _(" Change your password.\n"));
2423 if(config){
2424 so_puts(store, "\n");
2425 so_puts(store, _("(C) Config:\n"));
2426 so_puts(store, _(" Allows you to set or unset many features of Alpine.\n"));
2427 so_puts(store, _(" You may also set the values of many options with this command.\n"));
2430 if(sig){
2431 so_puts(store, "\n");
2432 so_puts(store, _("(S) Signature:\n"));
2433 so_puts(store, _(" Enter or edit a custom signature which will\n"));
2434 so_puts(store, _(" be included with each new message you send.\n"));
2437 so_puts(store, "\n");
2438 so_puts(store, _("(A) AddressBooks:\n"));
2439 so_puts(store, _(" Define a non-default address book.\n"));
2441 so_puts(store, "\n");
2442 so_puts(store, _("(L) collectionLists:\n"));
2443 so_puts(store, _(" You may define groups of folders to help you better organize your mail.\n"));
2445 so_puts(store, "\n");
2446 so_puts(store, _("(R) Rules:\n"));
2447 so_puts(store, _(" This has up to six sub-categories: Roles, Index Colors, Filters,\n"));
2448 so_puts(store, _(" SetScores, Search, and Other. If the Index Colors option is\n"));
2449 so_puts(store, _(" missing you may turn it on (if possible) with Setup/Kolor.\n"));
2450 so_puts(store, _(" If Roles is missing it has probably been administratively disabled.\n"));
2452 if(dir){
2453 so_puts(store, "\n");
2454 so_puts(store, _("(D) Directory:\n"));
2455 so_puts(store, _(" Define an LDAP Directory server for Alpine's use. A directory server is\n"));
2456 so_puts(store, _(" similar to an address book, but it is usually maintained by an\n"));
2457 so_puts(store, _(" organization. It is similar to a telephone directory.\n"));
2460 so_puts(store, "\n");
2461 so_puts(store, _("(K) Kolor:\n"));
2462 so_puts(store, _(" Set custom colors for various parts of the Alpine screens. For example, the\n"));
2463 so_puts(store, _(" command key labels, the titlebar at the top of each page, and quoted\n"));
2464 so_puts(store, _(" sections of messages you are viewing.\n"));
2466 if(smime){
2467 so_puts(store, "\n");
2468 so_puts(store, _("(M) S/MIME:\n"));
2469 so_puts(store, _(" Setup for using S/MIME to verify signed messages, decrypt\n"));
2470 so_puts(store, _(" encrypted messages, and to sign or encrypt outgoing messages.\n"));
2473 so_puts(store, "\n");
2474 so_puts(store, _("(Z) RemoteConfigSetup:\n"));
2475 so_puts(store, _(" This is a command you will probably only want to use once, if at all.\n"));
2476 so_puts(store, _(" It helps you transfer your Alpine configuration data to an IMAP server,\n"));
2477 so_puts(store, _(" where it will be accessible from any of the computers you read mail\n"));
2478 so_puts(store, _(" from (using Alpine). The idea behind a remote configuration is that you\n"));
2479 so_puts(store, _(" can change your configuration in one place and have that change show\n"));
2480 so_puts(store, _(" up on all of the computers you use.\n"));
2481 so_puts(store, _(" (Note: this command does not show up on the keymenu at the bottom of\n"));
2482 so_puts(store, _(" the screen unless you press \"O\" for \"Other Commands\" --but you don't\n"));
2483 so_puts(store, _(" need to press the \"O\" in order to invoke the command.)\n"));
2485 /* put this down here for people who don't have exceptions */
2486 if(!exc){
2487 so_puts(store, "\n");
2488 so_puts(store, _("(X) eXceptions:\n"));
2489 so_puts(store, _(" This command is different from the rest. It is not actually a command\n"));
2490 so_puts(store, _(" itself. Instead, it is a toggle which modifies the behavior of the\n"));
2491 so_puts(store, _(" other commands. You toggle Exceptions editing on and off with this\n"));
2492 so_puts(store, _(" command. When it is off you will be editing (changing) your regular\n"));
2493 so_puts(store, _(" configuration file. When it is on you will be editing your exceptions\n"));
2494 so_puts(store, _(" configuration file. For example, you might want to type the command \n"));
2495 so_puts(store, _(" \"eXceptions\" followed by \"Kolor\" to setup different screen colors\n"));
2496 so_puts(store, _(" on a particular platform.\n"));
2497 so_puts(store, _(" (Note: this command does not do anything unless you have a configuration\n"));
2498 so_puts(store, _(" with exceptions enabled (you don't have that). Common ways to enable an\n"));
2499 so_puts(store, _(" exceptions config are the command line argument \"-x <exception_config>\";\n"));
2500 so_puts(store, _(" or the existence of the file \".pinercex\" for Unix Alpine, or \"PINERCEX\")\n"));
2501 so_puts(store, _(" for PC-Alpine.)\n"));
2502 so_puts(store, _(" (Another note: this command does not show up on the keymenu at the bottom\n"));
2503 so_puts(store, _(" of the screen unless you press \"O\" for \"Other Commands\" --but you\n"));
2504 so_puts(store, _(" don't need to press the \"O\" in order to invoke the command.)\n"));
2507 memset(&sargs, 0, sizeof(SCROLL_S));
2508 sargs.text.text = so_text(store);
2509 sargs.text.src = CharStar;
2510 sargs.text.desc = _("Information About Setup Command");
2511 sargs.bar.title = cpystr(_("SETUP"));
2512 sargs.proc.tool = choose_setup_cmd;
2513 sargs.help.text = NO_HELP;
2514 sargs.help.title = NULL;
2515 sargs.keys.menu = &choose_setup_keymenu;
2516 sargs.keys.menu->how_many = 2;
2518 setbitmap(sargs.keys.bitmap);
2519 if(!printer)
2520 clrbitn(SETUP_PRINTER, sargs.keys.bitmap);
2522 if(!passwd)
2523 clrbitn(SETUP_PASSWD, sargs.keys.bitmap);
2525 if(!config)
2526 clrbitn(SETUP_CONFIG, sargs.keys.bitmap);
2528 if(!sig)
2529 clrbitn(SETUP_SIG, sargs.keys.bitmap);
2531 if(!dir)
2532 clrbitn(SETUP_DIRECTORY, sargs.keys.bitmap);
2534 if(!smime)
2535 clrbitn(SETUP_SMIME, sargs.keys.bitmap);
2537 if(exc)
2538 menu_init_binding(sargs.keys.menu, 'x', MC_EXCEPT, "X",
2539 N_("eXceptions"), SETUP_EXCEPT);
2540 else
2541 menu_init_binding(sargs.keys.menu, 'x', MC_NO_EXCEPT, "X",
2542 N_("eXceptions"), SETUP_EXCEPT);
2545 scrolltool(&sargs);
2547 ps->mangled_screen = 1;
2549 srv = (SRV_S *)sargs.proc.data.p;
2551 exceptions = srv ? srv->exc : 0;
2553 so_give(&store);
2555 if(sargs.bar.title) fs_give((void**)&sargs.bar.title);
2556 if(srv){
2557 ret = srv->cmd;
2558 fs_give((void **)&sargs.proc.data.p);
2560 else
2561 ret = 'e';
2563 return(ret | (exceptions ? EDIT_EXCEPTION : 0));
2567 /*----------------------------------------------------------------------
2569 Args: command -- command char to perform
2571 ----*/
2572 void
2573 do_setup_task(int command)
2575 char *err = NULL;
2576 int rtype;
2577 int edit_exceptions = 0;
2578 int do_lit_sig = 0;
2580 if(command & EDIT_EXCEPTION){
2581 edit_exceptions = 1;
2582 command &= ~EDIT_EXCEPTION;
2585 switch(command) {
2586 /*----- EDIT SIGNATURE -----*/
2587 case 's':
2588 if(ps_global->VAR_LITERAL_SIG)
2589 do_lit_sig = 1;
2590 else {
2591 char sig_path[MAXPATH+1];
2593 if(!signature_path(ps_global->VAR_SIGNATURE_FILE, sig_path, MAXPATH))
2594 do_lit_sig = 1;
2595 else if((!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2596 && can_access(sig_path, READ_ACCESS) == 0)
2597 ||(IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)
2598 && (folder_exists(NULL, sig_path) & FEX_ISFILE)))
2599 do_lit_sig = 0;
2600 else if(!ps_global->vars[V_SIGNATURE_FILE].main_user_val.p
2601 && !ps_global->vars[V_SIGNATURE_FILE].cmdline_val.p
2602 && !ps_global->vars[V_SIGNATURE_FILE].fixed_val.p)
2603 do_lit_sig = 1;
2604 else
2605 do_lit_sig = 0;
2608 if(do_lit_sig){
2609 char *result = NULL;
2610 char **apval;
2611 EditWhich ew;
2612 int readonly = 0;
2614 ew = edit_exceptions ? ps_global->ew_for_except_vars : Main;
2616 if(ps_global->restricted)
2617 readonly = 1;
2618 else switch(ew){
2619 case Main:
2620 readonly = ps_global->prc->readonly;
2621 break;
2622 case Post:
2623 readonly = ps_global->post_prc->readonly;
2624 break;
2625 default:
2626 break;
2629 if(readonly)
2630 err = cpystr(ps_global->restricted
2631 ? "Alpine demo can't change config file"
2632 : _("Config file not changeable"));
2634 if(!err){
2635 apval = APVAL(&ps_global->vars[V_LITERAL_SIG], ew);
2636 if(!apval)
2637 err = cpystr(_("Problem accessing configuration"));
2638 else{
2639 char *input;
2641 input = (char *)fs_get((strlen(*apval ? *apval : "")+1) *
2642 sizeof(char));
2643 input[0] = '\0';
2644 cstring_to_string(*apval, input);
2645 err = signature_edit_lit(input, &result,
2646 _("SIGNATURE EDITOR"),
2647 h_composer_sigedit);
2648 fs_give((void **)&input);
2652 if(!err){
2653 char *cstring_version;
2655 cstring_version = string_to_cstring(result);
2657 set_variable(V_LITERAL_SIG, cstring_version, 0, 0, ew);
2659 if(cstring_version)
2660 fs_give((void **)&cstring_version);
2663 if(result)
2664 fs_give((void **)&result);
2666 else
2667 err = signature_edit(ps_global->VAR_SIGNATURE_FILE,
2668 _("SIGNATURE EDITOR"));
2670 if(err){
2671 q_status_message(SM_ORDER, 3, 4, err);
2672 fs_give((void **)&err);
2675 ps_global->mangled_screen = 1;
2676 break;
2678 /*----- ADD ADDRESSBOOK ----*/
2679 case 'a':
2680 addr_book_config(ps_global, edit_exceptions);
2681 menu_index = ABOOK_MENU_ITEM;
2682 ps_global->mangled_screen = 1;
2683 break;
2685 #ifdef ENABLE_LDAP
2686 /*--- ADD DIRECTORY SERVER --*/
2687 case 'd':
2688 directory_config(ps_global, edit_exceptions);
2689 ps_global->mangled_screen = 1;
2690 break;
2691 #endif
2693 #ifdef SMIME
2694 /*--- S/MIME --*/
2695 case 'm':
2696 smime_config_screen(ps_global, edit_exceptions);
2697 ps_global->mangled_screen = 1;
2698 break;
2699 #endif
2701 /*----- CONFIGURE OPTIONS -----*/
2702 case 'c':
2703 option_screen(ps_global, edit_exceptions);
2704 ps_global->mangled_screen = 1;
2705 break;
2707 /*----- COLLECTION LIST -----*/
2708 case 'l':
2709 folder_config_screen(ps_global, edit_exceptions);
2710 ps_global->mangled_screen = 1;
2711 break;
2713 /*----- RULES -----*/
2714 case 'r':
2715 rtype = rule_setup_type(ps_global, RS_RULES | RS_INCFILTNOW,
2716 _("Type of rule setup : "));
2717 switch(rtype){
2718 case 'r':
2719 case 's':
2720 case 'i':
2721 case 'f':
2722 case 'o':
2723 case 'c':
2724 role_config_screen(ps_global, (rtype == 'r') ? ROLE_DO_ROLES :
2725 (rtype == 's') ? ROLE_DO_SCORES :
2726 (rtype == 'o') ? ROLE_DO_OTHER :
2727 (rtype == 'f') ? ROLE_DO_FILTER :
2728 (rtype == 'c') ? ROLE_DO_SRCH :
2729 ROLE_DO_INCOLS,
2730 edit_exceptions);
2731 break;
2733 case 'Z':
2734 q_status_message(SM_ORDER | SM_DING, 3, 5,
2735 _("Try turning on color with the Setup/Kolor command."));
2736 break;
2738 case 'n':
2739 role_process_filters();
2740 break;
2742 default:
2743 cmd_cancelled(NULL);
2744 break;
2747 ps_global->mangled_screen = 1;
2748 break;
2750 /*----- COLOR -----*/
2751 case 'k':
2752 color_config_screen(ps_global, edit_exceptions);
2753 ps_global->mangled_screen = 1;
2754 break;
2756 case 'z':
2757 convert_to_remote_config(ps_global, edit_exceptions);
2758 ps_global->mangled_screen = 1;
2759 break;
2761 /*----- EXIT -----*/
2762 case 'e':
2763 break;
2765 /*----- NEW PASSWORD -----*/
2766 case 'n':
2767 #ifdef PASSWD_PROG
2768 if(ps_global->restricted){
2769 q_status_message(SM_ORDER, 3, 5,
2770 "Password change unavailable in restricted demo version of Alpine.");
2771 }else {
2772 change_passwd();
2773 ClearScreen();
2774 ps_global->mangled_screen = 1;
2776 #else
2777 q_status_message(SM_ORDER, 0, 5,
2778 _("Password changing not configured for this version of Alpine."));
2779 display_message('x');
2780 #endif /* DOS */
2781 break;
2783 #if !defined(DOS)
2784 /*----- CHOOSE PRINTER ------*/
2785 case 'p':
2786 select_printer(ps_global, edit_exceptions);
2787 ps_global->mangled_screen = 1;
2788 break;
2789 #endif
2795 rule_setup_type(struct pine *ps, int flags, char *prompt)
2797 ESCKEY_S opts[9];
2798 int ekey_num = 0, deefault = 0;
2800 if(flags & RS_INCADDR){
2801 deefault = 'a';
2802 opts[ekey_num].ch = 'a';
2803 opts[ekey_num].rval = 'a';
2804 opts[ekey_num].name = "A";
2805 opts[ekey_num++].label = "Addrbook";
2808 if(flags & RS_RULES){
2810 if(F_OFF(F_DISABLE_ROLES_SETUP,ps)){ /* roles are allowed */
2811 if(deefault != 'a')
2812 deefault = 'r';
2814 opts[ekey_num].ch = 'r';
2815 opts[ekey_num].rval = 'r';
2816 opts[ekey_num].name = "R";
2817 opts[ekey_num++].label = "Roles";
2819 else if(deefault != 'a')
2820 deefault = 's';
2822 opts[ekey_num].ch = 's';
2823 opts[ekey_num].rval = 's';
2824 opts[ekey_num].name = "S";
2825 opts[ekey_num++].label = "SetScores";
2827 #ifndef _WINDOWS
2828 if(ps->color_style != COL_NONE && pico_hascolor()){
2829 #endif
2830 if(deefault != 'a')
2831 deefault = 'i';
2833 opts[ekey_num].ch = 'i';
2834 opts[ekey_num].rval = 'i';
2835 opts[ekey_num].name = "I";
2836 opts[ekey_num++].label = "Indexcolor";
2837 #ifndef _WINDOWS
2839 else{
2840 opts[ekey_num].ch = 'i';
2841 opts[ekey_num].rval = 'Z'; /* notice this rval! */
2842 opts[ekey_num].name = "I";
2843 opts[ekey_num++].label = "Indexcolor";
2845 #endif
2847 opts[ekey_num].ch = 'f';
2848 opts[ekey_num].rval = 'f';
2849 opts[ekey_num].name = "F";
2850 opts[ekey_num++].label = "Filters";
2852 opts[ekey_num].ch = 'o';
2853 opts[ekey_num].rval = 'o';
2854 opts[ekey_num].name = "O";
2855 opts[ekey_num++].label = "Other";
2857 opts[ekey_num].ch = 'c';
2858 opts[ekey_num].rval = 'c';
2859 opts[ekey_num].name = "C";
2860 opts[ekey_num++].label = "searCh";
2864 if(flags & RS_INCEXP){
2865 opts[ekey_num].ch = 'e';
2866 opts[ekey_num].rval = 'e';
2867 opts[ekey_num].name = "E";
2868 opts[ekey_num++].label = "Export";
2871 if(flags & RS_INCFILTNOW){
2872 opts[ekey_num].ch = 'n';
2873 opts[ekey_num].rval = 'n';
2874 opts[ekey_num].name = "N";
2875 opts[ekey_num++].label = "filterNow";
2878 opts[ekey_num].ch = -1;
2880 return(radio_buttons(prompt, -FOOTER_ROWS(ps), opts,
2881 deefault, 'x', NO_HELP, RB_NORM));
2887 * Process the command list, changing function key notation into
2888 * lexical equivalents.
2890 void
2891 process_init_cmds(struct pine *ps, char **list)
2893 char **p;
2894 int i = 0;
2895 int j;
2896 int lpm1;
2897 #define MAX_INIT_CMDS 500
2898 /* this is just a temporary stack array, the real one is allocated below */
2899 int i_cmds[MAX_INIT_CMDS];
2900 int fkeys = 0;
2901 int not_fkeys = 0;
2903 if(list){
2904 for(p = list; *p; p++){
2905 if(i >= MAX_INIT_CMDS){
2906 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2907 "Initial keystroke list too long at \"%s\"", *p);
2908 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2909 break;
2913 /* regular character commands */
2914 if(strlen(*p) == 1){
2915 i_cmds[i++] = **p;
2916 not_fkeys++;
2919 /* special commands */
2920 else if(strucmp(*p, "SPACE") == 0)
2921 i_cmds[i++] = ' ';
2922 else if(strucmp(*p, "CR") == 0)
2923 i_cmds[i++] = '\n';
2924 else if(strucmp(*p, "TAB") == 0)
2925 i_cmds[i++] = '\t';
2926 else if(strucmp(*p, "UP") == 0)
2927 i_cmds[i++] = KEY_UP;
2928 else if(strucmp(*p, "DOWN") == 0)
2929 i_cmds[i++] = KEY_DOWN;
2930 else if(strucmp(*p, "LEFT") == 0)
2931 i_cmds[i++] = KEY_LEFT;
2932 else if(strucmp(*p, "RIGHT") == 0)
2933 i_cmds[i++] = KEY_RIGHT;
2935 /* control chars */
2936 else if(strlen(*p) == 2 && **p == '^')
2937 i_cmds[i++] = ctrl(*((*p)+1));
2939 /* function keys */
2940 else if(**p == 'F' || **p == 'f'){
2941 int v;
2943 fkeys++;
2944 v = atoi((*p)+1);
2945 if(v >= 1 && v <= 12)
2946 i_cmds[i++] = PF1 + v - 1;
2947 else
2948 i_cmds[i++] = KEY_JUNK;
2951 /* literal string */
2952 else if(**p == '"' && (*p)[lpm1 = strlen(*p) - 1] == '"'){
2953 if(lpm1 + i - 1 > MAX_INIT_CMDS){
2954 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
2955 "Initial keystroke list too long, truncated at %s\n", *p);
2956 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2957 break; /* Bail out of this loop! */
2958 } else
2959 for(j = 1; j < lpm1; j++)
2960 i_cmds[i++] = (*p)[j];
2962 else {
2963 snprintf(tmp_20k_buf,SIZEOF_20KBUF,
2964 "Bad initial keystroke \"%.500s\" (missing comma?)", *p);
2965 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
2966 break;
2972 * We don't handle the case where function keys are used to specify the
2973 * commands but some non-function key input is also required. For example,
2974 * you might want to jump to a specific message number and view it
2975 * on start up. To do that, you need to use character commands instead
2976 * of function key commands in the initial-keystroke-list.
2978 if(fkeys && not_fkeys){
2979 init_error(ps, SM_ORDER | SM_DING, 3, 5,
2980 "Mixed characters and function keys in \"initial-keystroke-list\", skipping.");
2981 i = 0;
2984 if(fkeys && !not_fkeys)
2985 F_TURN_ON(F_USE_FK,ps);
2986 if(!fkeys && not_fkeys)
2987 F_TURN_OFF(F_USE_FK,ps);
2989 if(i > 0){
2990 ps->initial_cmds = (int *)fs_get((i+1) * sizeof(int));
2991 ps->free_initial_cmds = ps->initial_cmds;
2992 for(j = 0; j < i; j++)
2993 ps->initial_cmds[j] = i_cmds[j];
2995 ps->initial_cmds[i] = 0;
2996 ps->in_init_seq = ps->save_in_init_seq = 1;
3001 UCS *
3002 user_wordseps(char **list)
3004 char **p;
3005 int i = 0;
3006 int j;
3007 #define MAX_SEPARATORS 500
3009 * This is just a temporary stack array, the real one is allocated below.
3010 * This is supposed to be way large enough.
3012 UCS seps[MAX_SEPARATORS+1];
3013 UCS *u;
3014 UCS *return_array = NULL;
3015 size_t l;
3017 seps[0] = '\0';
3019 if(list){
3020 for(p = list; *p; p++){
3021 if(i >= MAX_SEPARATORS){
3022 q_status_message(SM_ORDER | SM_DING, 3, 3,
3023 "Warning: composer-word-separators list is too long");
3024 break;
3027 u = utf8_to_ucs4_cpystr(*p);
3029 if(u){
3030 if(ucs4_strlen(u) == 1)
3031 seps[i++] = *u;
3032 else if(*u == '"' && u[l = ucs4_strlen(u) - 1] == '"'){
3033 if(l + i - 1 > MAX_SEPARATORS){
3034 q_status_message(SM_ORDER | SM_DING, 3, 3,
3035 "Warning: composer-word-separators list is too long");
3036 break; /* Bail out of this loop! */
3038 else{
3039 for(j = 1; j < l; j++)
3040 seps[i++] = u[j];
3043 else{
3044 l = ucs4_strlen(u);
3045 if(l + i > MAX_SEPARATORS){
3046 q_status_message(SM_ORDER | SM_DING, 3, 3,
3047 "Warning: composer-word-separators list is too long");
3048 break; /* Bail out of this loop! */
3050 else{
3051 for(j = 0; j < l; j++)
3052 seps[i++] = u[j];
3056 fs_give((void **) &u);
3061 seps[i] = '\0';
3063 if(i > 0)
3064 return_array = ucs4_cpystr(seps);
3066 return(return_array);
3071 * Make sure any errors during initialization get queued for display
3073 void
3074 queue_init_errors(struct pine *ps)
3076 int i;
3078 if(ps->init_errs){
3079 for(i = 0; (ps->init_errs)[i].message; i++){
3080 q_status_message((ps->init_errs)[i].flags,
3081 (ps->init_errs)[i].min_time,
3082 (ps->init_errs)[i].max_time,
3083 (ps->init_errs)[i].message);
3084 fs_give((void **)&(ps->init_errs)[i].message);
3087 fs_give((void **)&ps->init_errs);
3092 /*----------------------------------------------------------------------
3093 Quit pine if the user wants to
3095 Args: The usual pine structure
3097 Result: User is asked if she wants to quit, if yes then execute quit.
3099 Q U I T S C R E E N
3101 Not really a full screen. Just count up deletions and ask if we really
3102 want to quit.
3103 ----*/
3104 void
3105 quit_screen(struct pine *pine_state)
3107 int quit = 0;
3109 dprint((1, "\n\n ---- QUIT SCREEN ----\n"));
3111 if(F_ON(F_CHECK_MAIL_ONQUIT,ps_global)
3112 && new_mail(1, VeryBadTime, NM_STATUS_MSG | NM_DEFER_SORT) > 0
3113 && (quit = want_to(_("Quit even though new mail just arrived"), 'y', 0,
3114 NO_HELP, WT_NORM)) != 'y'){
3115 refresh_sort(pine_state->mail_stream, pine_state->msgmap, SRT_VRB);
3116 pine_state->next_screen = pine_state->prev_screen;
3117 return;
3120 if(quit != 'y'
3121 && F_OFF(F_QUIT_WO_CONFIRM,pine_state)
3122 && want_to(_("Really quit Alpine"), 'y', 0, NO_HELP, WT_NORM) != 'y'){
3123 pine_state->next_screen = pine_state->prev_screen;
3124 return;
3127 goodnight_gracey(pine_state, 0);
3131 /*----------------------------------------------------------------------
3132 The nuts and bolts of actually cleaning up and exitting pine
3134 Args: ps -- the usual pine structure,
3135 exit_val -- what to tell our parent
3137 Result: This never returns
3139 ----*/
3140 void
3141 goodnight_gracey(struct pine *pine_state, int exit_val)
3143 int i, cnt_user_streams = 0;
3144 char *final_msg = NULL;
3145 char msg[MAX_SCREEN_COLS+1];
3146 char *pf = _("Alpine finished");
3147 MAILSTREAM *m;
3148 extern KBESC_T *kbesc;
3150 dprint((2, "goodnight_gracey:\n"));
3152 /* We want to do this here before we close up the streams */
3153 trim_remote_adrbks();
3155 for(i = 0; i < ps_global->s_pool.nstream; i++){
3156 m = ps_global->s_pool.streams[i];
3157 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR))
3158 cnt_user_streams++;
3161 /* clean up open streams */
3163 if(pine_state->mail_stream
3164 && sp_flagged(pine_state->mail_stream, SP_LOCKED)
3165 && sp_flagged(pine_state->mail_stream, SP_USERFLDR)){
3166 dprint((5, "goodnight_gracey: close current stream\n"));
3167 expunge_and_close(pine_state->mail_stream,
3168 (cnt_user_streams <= 1) ? &final_msg : NULL, EC_NONE);
3169 cnt_user_streams--;
3172 pine_state->mail_stream = NULL;
3173 pine_state->redrawer = (void (*)(void))NULL;
3175 dprint((5,
3176 "goodnight_gracey: close other stream pool streams\n"));
3177 for(i = 0; i < ps_global->s_pool.nstream; i++){
3178 m = ps_global->s_pool.streams[i];
3180 * fix global for functions that depend(ed) on it sort_folder.
3181 * Hopefully those will get phased out.
3183 ps_global->mail_stream = m;
3184 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3185 && !sp_flagged(m, SP_INBOX)){
3186 sp_set_expunge_count(m, 0L);
3187 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3188 EC_NONE);
3189 cnt_user_streams--;
3193 for(i = 0; i < ps_global->s_pool.nstream; i++){
3194 m = ps_global->s_pool.streams[i];
3196 * fix global for functions that depend(ed) on it (sort_folder).
3197 * Hopefully those will get phased out.
3199 ps_global->mail_stream = m;
3200 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
3201 && sp_flagged(m, SP_INBOX)){
3202 dprint((5,
3203 "goodnight_gracey: close inbox stream stream\n"));
3204 sp_set_expunge_count(m, 0L);
3205 expunge_and_close(m, (cnt_user_streams <= 1) ? &final_msg : NULL,
3206 EC_NONE);
3207 cnt_user_streams--;
3211 #ifdef _WINDOWS
3212 if(ps_global->ttyo)
3213 (void)get_windsize(ps_global->ttyo);
3214 #endif
3216 dprint((7, "goodnight_gracey: close config files\n"));
3218 #ifdef SMIME
3219 smime_deinit();
3220 #endif
3222 free_pinerc_strings(&pine_state);
3224 strncpy(msg, pf, sizeof(msg));
3225 msg[sizeof(msg)-1] = '\0';
3226 if(final_msg){
3227 strncat(msg, " -- ", sizeof(msg)-strlen(msg)-1);
3228 msg[sizeof(msg)-1] = '\0';
3229 strncat(msg, final_msg, sizeof(msg)-strlen(msg)-1);
3230 msg[sizeof(msg)-1] = '\0';
3231 fs_give((void **)&final_msg);
3234 dprint((7, "goodnight_gracey: sp_end\n"));
3235 ps_global->noshow_error = 1;
3236 sp_end();
3238 /* after sp_end, which might call a filter */
3239 completely_done_with_adrbks();
3241 dprint((7, "goodnight_gracey: end_screen\n"));
3242 end_screen(msg, exit_val);
3243 dprint((7, "goodnight_gracey: end_titlebar\n"));
3244 end_titlebar();
3245 dprint((7, "goodnight_gracey: end_keymenu\n"));
3246 end_keymenu();
3248 dprint((7, "goodnight_gracey: end_keyboard\n"));
3249 end_keyboard(F_ON(F_USE_FK,pine_state));
3250 dprint((7, "goodnight_gracey: end_ttydriver\n"));
3251 end_tty_driver(pine_state);
3252 #if !defined(DOS) && !defined(OS2)
3253 kbdestroy(kbesc);
3254 #if !defined(LEAVEOUTFIFO)
3255 close_newmailfifo();
3256 #endif
3257 #endif
3258 end_signals(0);
3259 if(filter_data_file(0))
3260 our_unlink(filter_data_file(0));
3262 imap_flush_passwd_cache(TRUE);
3263 free_newsgrp_cache();
3264 mailcap_free();
3265 close_every_pattern();
3266 free_extra_hdrs();
3267 free_contexts(&ps_global->context_list);
3268 free_charsetchecker();
3269 dprint((7, "goodnight_gracey: free more memory\n"));
3270 #ifdef ENABLE_LDAP
3271 free_saved_query_parameters();
3272 #endif
3274 free_pine_struct(&pine_state);
3276 free_histlist();
3278 #ifdef DEBUG
3279 if(debugfile){
3280 if(debug >= 2)
3281 fputs("goodnight_gracey finished\n", debugfile);
3283 fclose(debugfile);
3285 #endif
3287 exit(exit_val);
3291 /*----------------------------------------------------------------------
3292 Call back for c-client to feed us back the progress of network reads
3294 Input:
3296 Result:
3297 ----*/
3298 void
3299 pine_read_progress(GETS_DATA *md, long unsigned int count)
3301 gets_bytes += count; /* update counter */
3305 /*----------------------------------------------------------------------
3306 Function to fish the current byte count from a c-client fetch.
3308 Input: reset -- flag telling us to reset the count
3310 Result: Returns the number of bytes read by the c-client so far
3311 ----*/
3312 unsigned long
3313 pine_gets_bytes(int reset)
3315 if(reset)
3316 gets_bytes = 0L;
3318 return(gets_bytes);
3322 /*----------------------------------------------------------------------
3323 Panic pine - call on detected programmatic errors to exit pine
3325 Args: message -- message to record in debug file and to be printed for user
3327 Result: The various tty modes are restored
3328 If debugging is active a core dump will be generated
3329 Exits Alpine
3331 This is also called from imap routines and fs_get and fs_resize.
3332 ----*/
3333 void
3334 alpine_panic(char *message)
3336 char buf[256];
3338 /* global variable in .../pico/edef.h */
3339 in_panic = 1;
3341 if(ps_global->ttyo){
3342 end_screen(NULL, -1);
3343 end_keyboard(ps_global != NULL ? F_ON(F_USE_FK,ps_global) : 0);
3344 end_tty_driver(ps_global);
3345 end_signals(1);
3347 if(filter_data_file(0))
3348 our_unlink(filter_data_file(0));
3350 dprint((1, "\n===========================================\n\n"));
3351 dprint((1, " Alpine Panic: %s\n\n", message ? message : "?"));
3352 dprint((1, "===========================================\n\n"));
3354 /* intercept c-client "free storage" errors */
3355 if(strstr(message, "free storage"))
3356 snprintf(buf, sizeof(buf), _("No more available memory.\nAlpine Exiting"));
3357 else
3358 snprintf(buf, sizeof(buf), _("Problem detected: \"%s\".\nAlpine Exiting."), message);
3360 buf[sizeof(buf)-1] = '\0';
3362 #ifdef _WINDOWS
3363 /* Put up a message box. */
3364 mswin_messagebox (buf, 1);
3365 #else
3366 fprintf(stderr, "\n\n%s\n", buf);
3367 #endif
3369 #ifdef DEBUG
3370 if(debugfile){
3371 save_debug_on_crash(debugfile, recent_keystroke);
3374 coredump(); /*--- If we're debugging get a core dump --*/
3375 #endif
3377 exit(-1);
3378 fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
3383 * panicking - function to test whether or not we're exiting under stress.
3387 panicking(void)
3389 return(in_panic);
3393 /*----------------------------------------------------------------------
3394 exceptional_exit - called to exit under unusual conditions (with no core)
3396 Args: message -- message to record in debug file and to be printed for user
3397 ev -- exit value
3399 ----*/
3400 void
3401 exceptional_exit(char *message, int ev)
3403 fprintf(stderr, "%s\n", message);
3404 exit(ev);
3409 * PicoText Storage Object Support Routines
3412 STORE_S *
3413 pine_pico_get(void)
3415 return((STORE_S *)pico_get());
3419 pine_pico_give(STORE_S **sop)
3421 pico_give((void *)sop);
3422 return(1);
3426 pine_pico_writec(int c, STORE_S *so)
3428 unsigned char ch = (unsigned char) c;
3430 return(pico_writec(so->txt, ch, PICOREADC_NONE));
3434 pine_pico_writec_noucs(int c, STORE_S *so)
3436 unsigned char ch = (unsigned char) c;
3438 return(pico_writec(so->txt, ch, PICOREADC_NOUCS));
3442 pine_pico_readc(unsigned char *c, STORE_S *so)
3444 return(pico_readc(so->txt, c, PICOREADC_NONE));
3448 pine_pico_readc_noucs(unsigned char *c, STORE_S *so)
3450 return(pico_readc(so->txt, c, PICOREADC_NOUCS));
3454 pine_pico_puts(STORE_S *so, char *s)
3456 return(pico_puts(so->txt, s, PICOREADC_NONE));
3460 pine_pico_puts_noucs(STORE_S *so, char *s)
3462 return(pico_puts(so->txt, s, PICOREADC_NOUCS));
3466 pine_pico_seek(STORE_S *so, long pos, int orig)
3468 return(pico_seek((void *)so, pos, orig));
3473 remote_pinerc_failure(void)
3475 #ifdef _WINDOWS
3476 if(ps_global->install_flag) /* just exit silently */
3477 exit(0);
3478 #endif /* _WINDOWS */
3480 if(ps_global->exit_if_no_pinerc){
3481 exceptional_exit("Exiting because -bail option is set and config file not readable.", -1);
3484 if(want_to("Trouble reading remote configuration! Continue anyway ",
3485 'n', 'n', NO_HELP, WT_FLUSH_IN) != 'y'){
3486 return(0);
3489 return(1);
3493 void
3494 dump_supported_options(void)
3496 char **config;
3498 config = get_supported_options();
3499 if(config){
3500 display_args_err(NULL, config, 0);
3501 free_list_array(&config);
3506 /*----------------------------------------------------------------------
3507 Check pruned-folders for validity, making sure they are in the
3508 same context as sent-mail.
3510 ----*/
3512 prune_folders_ok(void)
3514 char **p;
3516 for(p = ps_global->VAR_PRUNED_FOLDERS; p && *p && **p; p++)
3517 if(!context_isambig(*p))
3518 return(0);
3520 return(1);
3524 #ifdef WIN32
3525 char *
3526 pine_user_callback()
3528 if(ps_global->VAR_USER_ID && ps_global->VAR_USER_ID[0]){
3529 return(ps_global->VAR_USER_ID);
3531 else{
3532 /* SHOULD PROMPT HERE! */
3533 return(NULL);
3536 #endif
3538 #ifdef _WINDOWS
3540 * windows callback to get/set function keys mode state
3543 fkey_mode_callback(set, args)
3544 int set;
3545 long args;
3547 return(F_ON(F_USE_FK, ps_global) != 0);
3551 void
3552 imap_telemetry_on()
3554 if(ps_global->mail_stream)
3555 mail_debug(ps_global->mail_stream);
3559 void
3560 imap_telemetry_off()
3562 if(ps_global->mail_stream)
3563 mail_nodebug(ps_global->mail_stream);
3567 char *
3568 pcpine_help_main(title)
3569 char *title;
3571 if(title)
3572 strncpy(title, _("PC-Alpine MAIN MENU Help"), 256);
3574 return(pcpine_help(main_menu_tx));
3579 pcpine_main_cursor(col, row)
3580 int col;
3581 long row;
3583 unsigned ndmi;
3585 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global)))
3586 ndmi = (row+1 - HEADER_ROWS(ps_global) - (MNSKIP(ps_global)+1))/(MNSKIP(ps_global)+1);
3588 if (row >= (HEADER_ROWS(ps_global) + MNSKIP(ps_global))
3589 && !(MNSKIP(ps_global) && (row+1) & 0x01)
3590 && ndmi <= MAX_MENU_ITEM
3591 && FOOTER_ROWS(ps_global) + (ndmi+1)*(MNSKIP(ps_global)+1)
3592 + MNSKIP(ps_global) + FOOTER_ROWS(ps_global) <= ps_global->ttyo->screen_rows)
3593 return(MSWIN_CURSOR_HAND);
3594 else
3595 return(MSWIN_CURSOR_ARROW);
3597 #endif /* _WINDOWS */