2 * ========================================================================
3 * Copyright 2006-2008 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 #include <system.h> /* os-dep defs/includes */
15 #include <general.h> /* generally useful definitions */
17 #include "../../c-client/mail.h" /* for MAILSTREAM and friends */
18 #include "../../c-client/osdep.h"
19 #include "../../c-client/rfc822.h" /* for soutr_t and such */
20 #include "../../c-client/misc.h" /* for cpystr proto */
21 #include "../../c-client/utf8.h" /* for CHARSET and such*/
22 #include "../../c-client/imap4r1.h"
24 #include "../../pith/osdep/canaccess.h"
26 #include "../../pith/debug.h"
27 #include "../../pith/osdep/debugtime.h"
28 #include "../../pith/osdep/color.h"
29 #include "../../pith/osdep/bldpath.h"
30 #include "../../pith/osdep/rename.h"
31 #include "../../pith/osdep/filesize.h"
32 #include "../../pith/init.h"
33 #include "../../pith/status.h"
34 #include "../../pith/sort.h"
35 #include "../../pith/state.h"
36 #include "../../pith/msgno.h"
37 #include "../../pith/conf.h"
38 #include "../../pith/flag.h"
39 #include "../../pith/foldertype.h"
40 #include "../../pith/folder.h"
41 #include "../../pith/charconv/utf8.h"
42 #include "../../pith/charconv/filesys.h"
53 FILE *debugfile
= NULL
;
56 /*----------------------------------------------------------------------
57 Initialize debugging - open the debug log file
61 Result: opens the debug logfile for dprints
63 Opens the file "~/.pine-debug1. Also maintains .pine-debug[2-4]
64 by renaming them each time so the last 4 sessions are saved.
70 char newfname
[MAXPATH
+1], filename
[MAXPATH
+1], *dfile
= NULL
;
71 char basename
[MAXPATH
+1];
74 #endif /* DEBUGFILEEXT */
77 if(!((debug
|| ps_global
->debug_imap
|| ps_global
->debug_tcp
|| ps_global
->debug_http
) && ps_global
->write_debug_file
))
80 build_path(basename
, ps_global
->home_dir
, DEBUGFILE
, sizeof(basename
));
82 if (ps_global
->debug_nfiles
> 0) {
83 strncpy(filename
, basename
, sizeof(filename
) - 1);
84 filename
[sizeof(filename
) - 1] = '\0';
85 strncat(filename
, DEBUGFILEEXT
, sizeof(filename
) - 1 - strlen(filename
));
86 snprintf(nbuf
, sizeof(nbuf
), "%d", ps_global
->debug_nfiles
);
87 strncat(filename
, nbuf
, sizeof(filename
) - 1 - strlen(filename
));
90 #endif /* DEBUGFILEEXT */
92 for(i
= ps_global
->debug_nfiles
- 1; i
> 0; i
--){
93 strncpy(filename
, basename
, sizeof(filename
)-1);
94 filename
[sizeof(filename
)-1] = '\0';
95 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
96 strncat(filename
, nbuf
, sizeof(filename
)-1-strlen(filename
));
98 strncat(filename
, DEBUGFILEEXT
, sizeof(filename
)-1-strlen(filename
));
99 if (our_stat(filename
, &sbuf
) < 0){
100 strncpy(filename
, basename
, sizeof(filename
)-1);
101 filename
[sizeof(filename
)-1] = '\0';
102 strncat(filename
, DEBUGFILEEXT
, sizeof(filename
)-1-strlen(filename
));
103 strncat(filename
, nbuf
, sizeof(filename
)-1-strlen(filename
));
105 #endif /* DEBUGFILEEXT */
106 strncpy(newfname
, basename
, sizeof(newfname
)-1);
107 newfname
[sizeof(newfname
)-1] = '\0';
108 snprintf(nbuf
, sizeof(nbuf
), "%d", i
+1);
109 strncat(newfname
, nbuf
, sizeof(newfname
)-1-strlen(newfname
));
111 strncat(newfname
, DEBUGFILEEXT
, sizeof(newfname
)-1-strlen(newfname
));
112 #endif /* DEBUGFILEEXT */
113 (void)rename_file(filename
, newfname
);
116 strncpy(filename
, basename
, sizeof(filename
) - 1);
117 filename
[sizeof(filename
) - 1] = '\0';
118 strncat(filename
, "1", sizeof(filename
)-1-strlen(filename
));
119 filename
[sizeof(filename
)-1] = '\0';
121 strncat(filename
, DEBUGFILEEXT
, sizeof(filename
) - 1 - strlen(filename
));
122 #endif /* DEBUGFILEEXT */
127 * We were doing our_open so _WINDOWS would open it in widechar mode, but
128 * that means we have to print in widechar mode. For now just do open, since
129 * we it's a debug file and still readable. The alternative is copying all
130 * debug text to a widechar buffer, which would be a drag.
132 if((fd
= open(filename
, O_TRUNC
|O_RDWR
|O_CREAT
, 0600)) >= 0)
133 debugfile
= fdopen(fd
, "w+");
135 if(debugfile
!= NULL
){
137 time_t now
= time((time_t *)0);
138 if(ps_global
->debug_flush
)
139 setvbuf(debugfile
, (char *)NULL
, _IOLBF
, BUFSIZ
);
141 if(ps_global
->debug_nfiles
== 0){
143 * If no debug files are asked for, make filename a tempfile
144 * to be used for a record should pine later crash...
146 if(debug
< 9 && !ps_global
->debug_flush
&& ps_global
->debug_imap
<4){
147 our_unlink(filename
);
152 dprint((0, "Debug output of the Alpine program (debug=%d debug_imap=%d). Version %s (%s %s)\n%s\n",
153 debug
, ps_global
->debug_imap
,
155 SYSTYPE
? SYSTYPE
: "?",
156 get_alpine_revision_string(rev
, sizeof(rev
)),
159 dprint((0, "Starting after the reading_pinerc calls, the data in this file should\nbe encoded as UTF-8. Before that it will be in the user's native charset.\n"));
160 if(dfile
&& (debug
> DEFAULT_DEBUG
||
161 ps_global
->debug_imap
> 0 ||
162 ps_global
->debug_tcp
> 0 ||
163 ps_global
->debug_http
> 0)){
164 snprintf(newfname
, sizeof(newfname
), "Debug file: %s (level=%d imap=%d)", dfile
,
165 debug
, ps_global
->debug_imap
);
166 init_error(ps_global
, SM_ORDER
, 3, 5, newfname
);
172 /*----------------------------------------------------------------------
173 Try to save the debug file if we crash in a controlled way
175 Args: dfile: pointer to open debug file
177 Result: tries to move the appropriate .pine-debugx file to .pine-crash
179 Looks through the four .pine-debug files hunting for the one that is
180 associated with this pine, and then renames it.
183 save_debug_on_crash(FILE *dfile
, int (*keystrokes
) (int *, char *, size_t))
185 char nbuf
[11], crashfile
[MAXPATH
+1], filename
[MAXPATH
+1];
187 struct stat dbuf
, tbuf
;
188 time_t now
= time((time_t *)0);
190 if(!(dfile
&& fstat(fileno(dfile
), &dbuf
) == 0))
193 fprintf(dfile
, "save_debug_on_crash: Version %s: debug level %d",
194 ALPINE_VERSION
, debug
);
195 fprintf(dfile
, "\n : %s\n", ctime(&now
));
197 build_path(crashfile
, ps_global
->home_dir
, ".pine-crash",sizeof(crashfile
));
199 fprintf(dfile
, "Attempting to save debug file to %s\n\n", crashfile
);
201 "\n\n Attempting to save debug file to %s\n\n", crashfile
);
203 /* Blat out last n keystrokes */
208 fputs("========== Latest Keystrokes =========================\n", dfile
);
210 while((*keystrokes
)(&cval
, cstr
, sizeof(cstr
)) != -1){
211 fprintf(dfile
, "\t%s\t(0x%4.4x)\n", cstr
, cval
);
215 fputs("========== Latest Keystrokes End =====================\n\n", dfile
);
218 fputs("========== Append DebugJournal =======================\n", dfile
);
219 #else /* DEBUGJOURNAL */
220 fputs("========== Append Journal =======================\n", dfile
);
221 #endif /* DEBUGJOURNAL */
222 debugjournal_to_file(dfile
);
224 fputs("========== Append DebugJournal End ===================\n", dfile
);
225 #else /* DEBUGJOURNAL */
226 fputs("========== Append Journal End ===================\n", dfile
);
227 #endif /* DEBUGJOURNAL */
229 /* look for existing debug file */
230 for(i
= 1; i
<= ps_global
->debug_nfiles
; i
++){
231 build_path(filename
, ps_global
->home_dir
, DEBUGFILE
, sizeof(filename
));
232 snprintf(nbuf
, sizeof(nbuf
), "%d", i
);
233 strncat(filename
, nbuf
, sizeof(filename
)-1-strlen(filename
));
234 if(our_stat(filename
, &tbuf
) != 0)
237 /* This must be the current debug file */
238 if(tbuf
.st_dev
== dbuf
.st_dev
&& tbuf
.st_ino
== dbuf
.st_ino
){
239 rename_file(filename
, crashfile
);
244 /* if current debug file name not found, write it by hand */
245 if(i
> ps_global
->debug_nfiles
){
250 * Copy the debug temp file into the
252 if((cfp
= our_fopen(crashfile
, "wb")) != NULL
){
253 buf
[sizeof(buf
)-1] = '\0';
255 while(fgets(buf
, sizeof(buf
)-1, dfile
) && fputs(buf
, cfp
) != EOF
)
267 * functions required by pith library
269 #define CHECK_EVERY_N_TIMES 100
270 #define MAX_DEBUG_FILE_SIZE 200000L
272 * This is just to catch runaway Pines that are looping spewing out
273 * debugging (and filling up a file system). The stop doesn't have to be
274 * at all precise, just soon enough to hopefully prevent filling the
275 * file system. If the debugging level is high (9 for now), then we're
276 * presumably looking for some problem, so don't truncate.
279 do_debug(FILE *debug_fp
)
281 static int counter
= CHECK_EVERY_N_TIMES
;
288 if(debug
<= DEFAULT_DEBUG
289 && !ps_global
->debug_flush
290 && !ps_global
->debug_tcp
291 && !ps_global
->debug_http
292 && !ps_global
->debug_timestamp
293 && !ps_global
->debug_imap
296 if((filesize
= fp_file_size(debug_fp
)) != -1L)
297 ok
= (unsigned long)filesize
< (unsigned long)MAX_DEBUG_FILE_SIZE
;
299 counter
= CHECK_EVERY_N_TIMES
;
301 fprintf(debug_fp
, "\n\n --- No more debugging ---\n");
303 " (debug file growing too large - over %ld bytes)\n\n",
304 MAX_DEBUG_FILE_SIZE
);
309 if(ok
&& ps_global
->debug_timestamp
)
310 fprintf(debug_fp
, "\n%s\n", debug_time(0, ps_global
->debug_timestamp
, ps_global
->signal_in_progress
));
316 output_debug_msg(int dlevel
, char *fmt
, ...)
320 if(debugfile
&& debug
>= dlevel
&& do_debug(debugfile
)){
324 vfprintf(debugfile
, fmt
, args
);
327 if((l
= strlen(fmt
)) > 2 && fmt
[l
-1] != '\n')
328 fputc('\n', debugfile
);
330 if(ps_global
->debug_flush
)
339 if(dlevel
<= 9 || dlevel
<= debug
){
341 * Make this static in order to move it off of
342 * the stack. We were getting "random" crashes
343 * on some systems when this size interacted with
344 * pthread stack size somehow. Taking it off of
345 * the stack ought to fix that without us having to
346 * understand how it all works.
348 static char b
[64000];
351 vsnprintf(b
, sizeof(b
), fmt
, args
);
354 b
[sizeof(b
)-1] = '\0';
355 add_review_message(b
, dlevel
);
358 #endif /* DEBUGJOURNAL */
363 /*----------------------------------------------------------------------
364 Dump the whole config enchilada using the given function
366 Args: ps -- pine struct containing vars to dump
367 pc -- function to use to write the config data
368 brief -- brief dump, only dump user_vals
369 Result: command line, global, user var's written with given function
373 dump_configuration(int brief
)
377 if(!do_debug(debugfile
))
380 gf_set_writec(&pc
, debugfile
, 0L, FileStar
, 0);
382 dump_config(ps_global
, pc
, brief
);
387 dump_config(struct pine
*ps
, gf_o_t pc
, int brief
)
390 char quotes
[3], tmp
[MAILTMPLEN
];
391 register struct variable
*vars
;
394 quotes
[0] = '"'; quotes
[1] = '"'; quotes
[2] = '\0';
396 for(i
= (brief
? 2 : 0); i
< (brief
? 4 : 6); i
++){
397 snprintf(tmp
, sizeof(tmp
), "======= %.20s_val options set",
398 (i
== 0) ? "Current" :
399 (i
== 1) ? "Command_line" :
401 (i
== 3) ? "PostloadUser" :
406 snprintf(tmp
, sizeof(tmp
), " (%.100s)",
407 (i
== 2) ? ((ps
->prc
) ? ps
->prc
->name
: ".pinerc") :
408 (i
== 3) ? ((ps
->post_prc
) ? ps
->post_prc
->name
: "postload") :
409 (i
== 4) ? ((ps
->pconf
) ? ps
->pconf
->name
411 #if defined(DOS) || defined(OS2)
414 ((can_access(SYSTEM_PINERC_FIXED
, ACCESS_EXISTS
) == 0)
415 ? SYSTEM_PINERC_FIXED
: "NO pine.conf.fixed")
421 gf_puts(" =======\n", pc
);
422 for(vars
= ps
->vars
; vars
->name
; vars
++){
425 t
= (i
== 0) ? vars
->current_val
.l
:
426 (i
== 1) ? vars
->cmdline_val
.l
:
427 (i
== 2) ? vars
->main_user_val
.l
:
428 (i
== 3) ? vars
->post_user_val
.l
:
429 (i
== 4) ? vars
->global_val
.l
431 if(t
&& *t
){ /* MAILTMPLEN = sizeof(tmp) */
432 snprintf(tmp
, sizeof(tmp
), " %20.20s : %.*s\n", vars
->name
,
433 MAILTMPLEN
-26, **t
? *t
: quotes
);
436 snprintf(tmp
, sizeof(tmp
)," %20.20s : %.*s\n","",
437 MAILTMPLEN
-26, **t
? *t
: quotes
);
444 t
= (i
== 0) ? vars
->current_val
.p
:
445 (i
== 1) ? vars
->cmdline_val
.p
:
446 (i
== 2) ? vars
->main_user_val
.p
:
447 (i
== 3) ? vars
->post_user_val
.p
:
448 (i
== 4) ? vars
->global_val
.p
450 if(t
){ /* MAILTMPLEN = sizeof(tmp) */
451 snprintf(tmp
, sizeof(tmp
), " %20.20s : %.*s\n", vars
->name
,
452 MAILTMPLEN
-26, *t
? t
: quotes
);
460 gf_puts("========== Feature settings ==========\n", pc
);
461 for(i
= 0; (feat
= feature_list(i
)); i
++)
462 if(feat
->id
!= F_OLD_GROWTH
){
463 snprintf(tmp
, sizeof(tmp
),
464 " %.50s%.100s\n", F_ON(feat
->id
, ps
) ? " " : "no-",
472 /*----------------------------------------------------------------------
473 Dump interesting variables from the given pine struct
475 Args: ps -- pine struct to dump
476 pc -- function to use to write the config data
478 Result: various important pine struct data written
482 dump_pine_struct(struct pine
*ps
, gf_o_t pc
)
485 extern char term_name
[];
490 gf_puts("========== struct pine * ==========\n", pc
);
492 gf_puts("!No struct!\n", pc
);
496 gf_puts("ui:\tlogin = ", pc
);
497 gf_puts((ps
->ui
.login
) ? ps
->ui
.login
: "NULL", pc
);
498 gf_puts(", full = ", pc
);
499 gf_puts((ps
->ui
.fullname
) ? ps
->ui
.fullname
: "NULL", pc
);
500 gf_puts("\n\thome = ", pc
);
501 gf_puts((ps
->ui
.homedir
) ? ps
->ui
.homedir
: "NULL", pc
);
503 gf_puts("\nhome_dir=\t", pc
);
504 gf_puts(ps
->home_dir
? ps
->home_dir
: "NULL", pc
);
505 gf_puts("\nhostname=\t", pc
);
506 gf_puts(ps
->hostname
? ps
->hostname
: "NULL", pc
);
507 gf_puts("\nlocaldom=\t", pc
);
508 gf_puts(ps
->localdomain
? ps
->localdomain
: "NULL", pc
);
509 gf_puts("\nuserdom=\t", pc
);
510 gf_puts(ps
->userdomain
? ps
->userdomain
: "NULL", pc
);
511 gf_puts("\nmaildom=\t", pc
);
512 gf_puts(ps
->maildomain
? ps
->maildomain
: "NULL", pc
);
514 gf_puts("\ncur_cntxt=\t", pc
);
515 gf_puts((ps
->context_current
&& ps
->context_current
->context
)
516 ? ps
->context_current
->context
: "None", pc
);
517 gf_puts("\ncur_fldr=\t", pc
);
518 gf_puts(ps
->cur_folder
, pc
);
520 gf_puts("\nnstream=\t", pc
);
521 gf_puts(long2string((long) ps
->s_pool
.nstream
), pc
);
523 for(i
= 0; i
< ps
->s_pool
.nstream
; i
++){
524 m
= ps
->s_pool
.streams
[i
];
525 gf_puts("\nstream slot ", pc
);
526 gf_puts(long2string((long) i
), pc
);
528 gf_puts(" empty", pc
);
532 if(ps
->mail_stream
== m
)
533 gf_puts("\nThis is the current mailstream", pc
);
534 if(sp_flagged(m
, SP_INBOX
))
535 gf_puts("\nThis is the inbox stream", pc
);
537 gf_puts("\nactual mbox=\t", pc
);
538 gf_puts(m
->mailbox
? m
->mailbox
: "no mailbox!", pc
);
540 gf_puts("\nflags=", pc
);
541 gf_puts(long2string((long) sp_flags(m
)), pc
);
542 gf_puts("\nnew_mail_count=", pc
);
543 gf_puts(long2string((long) sp_new_mail_count(m
)), pc
);
544 gf_puts("\nmail_since_cmd=", pc
);
545 gf_puts(long2string((long) sp_mail_since_cmd(m
)), pc
);
546 gf_puts("\nmail_box_changed=", pc
);
547 gf_puts(long2string((long) sp_mail_box_changed(m
)), pc
);
548 gf_puts("\nunsorted_newmail=", pc
);
549 gf_puts(long2string((long) sp_unsorted_newmail(m
)), pc
);
550 gf_puts("\nneed_to_rethread=", pc
);
551 gf_puts(long2string((long) sp_need_to_rethread(m
)), pc
);
552 gf_puts("\nviewing_a_thread=", pc
);
553 gf_puts(long2string((long) sp_viewing_a_thread(m
)), pc
);
554 gf_puts("\ndead_stream=", pc
);
555 gf_puts(long2string((long) sp_dead_stream(m
)), pc
);
556 gf_puts("\nnoticed_dead_stream=", pc
);
557 gf_puts(long2string((long) sp_noticed_dead_stream(m
)), pc
);
558 gf_puts("\nio_error_on_stream=", pc
);
559 gf_puts(long2string((long) sp_io_error_on_stream(m
)), pc
);
561 msgmap
= sp_msgmap(m
);
563 gf_puts("\nmsgmap: tot=", pc
);
564 gf_puts(long2string(mn_get_total(msgmap
)), pc
);
565 gf_puts(", cur=", pc
);
566 gf_puts(long2string(mn_get_cur(msgmap
)), pc
);
567 gf_puts(", del=", pc
);
568 gf_puts(long2string(count_flagged(m
, F_DEL
)),pc
);
569 gf_puts(", hid=", pc
);
570 gf_puts(long2string(any_lflagged(msgmap
, MN_HIDE
)), pc
);
571 gf_puts(", exld=", pc
);
572 gf_puts(long2string(any_lflagged(msgmap
, MN_EXLD
)), pc
);
573 gf_puts(", slct=", pc
);
574 gf_puts(long2string(any_lflagged(msgmap
, MN_SLCT
)), pc
);
575 gf_puts(", chid=", pc
);
576 gf_puts(long2string(any_lflagged(msgmap
, MN_CHID
)), pc
);
577 gf_puts(", coll=", pc
);
578 gf_puts(long2string(any_lflagged(msgmap
, MN_COLL
)), pc
);
579 gf_puts(", usor=", pc
);
580 gf_puts(long2string(any_lflagged(msgmap
, MN_USOR
)), pc
);
581 gf_puts(", stmp=", pc
);
582 gf_puts(long2string(any_lflagged(msgmap
, MN_STMP
)), pc
);
583 gf_puts(", sort=", pc
);
584 if(mn_get_revsort(msgmap
))
587 gf_puts(sort_name(mn_get_sort(msgmap
)), pc
);
590 gf_puts("\nNo msgmap", pc
);
594 gf_puts("\nterm ", pc
);
595 #if !defined(DOS) && !defined(OS2)
596 gf_puts("type=", pc
);
597 gf_puts(term_name
, pc
);
598 gf_puts(", ttyname=", pc
);
599 gf_puts((p
= (char *)ttyname(0)) ? p
: "NONE", pc
);
601 gf_puts(", size=", pc
);
602 gf_puts(int2string(ps
->ttyo
->screen_rows
), pc
);
604 gf_puts(int2string(ps
->ttyo
->screen_cols
), pc
);
605 gf_puts(", speed=", pc
);
606 gf_puts((ps
->low_speed
) ? "slow" : "normal", pc
);
615 CONTEXT_S
*c
= ps_global
->context_list
;
617 if(!(debugfile
&& debug
> 7 && do_debug(debugfile
)))
620 while(debugfile
&& c
!= NULL
){
621 fprintf(debugfile
, "\n***** context %s\n", c
->context
);
623 fprintf(debugfile
,"LABEL: %s\n", c
->label
);
626 fprintf(debugfile
,"COMMENT: %s\n", c
->comment
);
628 for(i
= 0; i
< folder_total(FOLDERS(c
)); i
++)
629 fprintf(debugfile
, " %d) %s\n", i
+ 1, folder_entry(i
,FOLDERS(c
))->name
);