* Fix some compiler warnings for bad casting in some functions in the file
[alpine.git] / pith / init.c
blobc38ae41a3b4404d79e834d7d4aacc13a64a90430
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2007 University of Washington
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 * ========================================================================
15 /*======================================================================
16 init.c
17 Routines for pine start up and initialization
18 ====*/
21 #include "../pith/headers.h"
22 #include "../pith/init.h"
23 #include "../pith/conf.h"
24 #include "../pith/status.h"
25 #include "../pith/folder.h"
29 * Internal prototypes
31 int compare_sm_files(const qsort_t *, const qsort_t *);
35 /*----------------------------------------------------------------------
36 Sets login, full_username and home_dir
38 Args: ps -- The Pine structure to put the user name, etc in
40 Result: sets the fullname, login and home_dir field of the pine structure
41 returns 0 on success, -1 if not.
42 ----*/
44 int
45 init_username(struct pine *ps)
47 char *expanded;
48 int rv;
50 rv = 0;
51 expanded = NULL;
52 #if defined(DOS) || defined(OS2)
53 if(ps->COM_USER_ID)
54 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
55 ps->COM_USER_ID, 0);
57 if(!expanded && ps->vars[V_USER_ID].post_user_val.p)
58 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
59 ps->vars[V_USER_ID].post_user_val.p, 0);
61 if(!expanded && ps->vars[V_USER_ID].main_user_val.p)
62 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
63 ps->vars[V_USER_ID].main_user_val.p, 0);
65 if(!expanded)
66 ps->blank_user_id = 1;
68 ps->VAR_USER_ID = cpystr(expanded ? expanded : "");
69 #else
70 ps->VAR_USER_ID = cpystr(ps->ui.login);
71 if(!ps->VAR_USER_ID[0]){
72 fprintf(stderr, "Who are you? (Unable to look up login name)\n");
73 rv = -1;
75 #endif
77 expanded = NULL;
78 if(ps->vars[V_PERSONAL_NAME].is_fixed){
79 if(ps->FIX_PERSONAL_NAME){
80 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
81 ps->FIX_PERSONAL_NAME, 0);
83 if(ps->vars[V_PERSONAL_NAME].main_user_val.p ||
84 ps->vars[V_PERSONAL_NAME].post_user_val.p){
85 ps_global->give_fixed_warning = 1;
86 ps_global->fix_fixed_warning = 1;
88 else if(ps->COM_PERSONAL_NAME)
89 ps_global->give_fixed_warning = 1;
91 else{
92 if(ps->COM_PERSONAL_NAME)
93 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
94 ps->COM_PERSONAL_NAME, 0);
96 if(!expanded && ps->vars[V_PERSONAL_NAME].post_user_val.p)
97 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
98 ps->vars[V_PERSONAL_NAME].post_user_val.p, 0);
100 if(!expanded && ps->vars[V_PERSONAL_NAME].main_user_val.p)
101 expanded = expand_variables(tmp_20k_buf, SIZEOF_20KBUF,
102 ps->vars[V_PERSONAL_NAME].main_user_val.p, 0);
105 if(!expanded){
106 expanded = ps->ui.fullname;
107 #if defined(DOS) || defined(OS2)
108 ps->blank_personal_name = 1;
109 #endif
112 ps->VAR_PERSONAL_NAME = cpystr(expanded ? expanded : "");
114 dprint((1, "Userid: %s\nFullname: \"%s\"\n",
115 ps->VAR_USER_ID, ps->VAR_PERSONAL_NAME));
116 return(rv);
120 /*----------------------------------------------------------------------
121 Sets home_dir
123 Args: ps -- The Pine structure to put the user name, etc in
125 Result: sets the home_dir field of the pine structure
126 returns 0 on success, -1 if not.
127 ----*/
130 init_userdir(struct pine *ps)
132 char fld_dir[MAXPATH+1];
134 if(strlen(ps->home_dir) + strlen(ps->VAR_MAIL_DIRECTORY)+2 > MAXPATH){
135 printf(_("Folders directory name is longer than %d\n"), MAXPATH);
136 printf(_("Directory name: \"%s/%s\"\n"),ps->home_dir,
137 ps->VAR_MAIL_DIRECTORY);
138 return(-1);
140 #if defined(DOS) || defined(OS2)
141 if(ps->VAR_MAIL_DIRECTORY[1] == ':'){
142 strncpy(fld_dir, ps->VAR_MAIL_DIRECTORY, sizeof(fld_dir)-1);
143 fld_dir[sizeof(fld_dir)-1] = '\0';
145 else
146 #endif
147 build_path(fld_dir, ps->home_dir, ps->VAR_MAIL_DIRECTORY, sizeof(fld_dir));
148 ps->folders_dir = cpystr(fld_dir);
150 return(0);
154 /*----------------------------------------------------------------------
155 Fetch the hostname of the current system and put it in pine struct
157 Args: ps -- The pine structure to put the hostname, etc in
159 Result: hostname, localdomain, userdomain and maildomain are set
162 ** Pine uses the following set of names:
163 hostname - The fully-qualified hostname. Obtained with
164 gethostbyname() which reads /etc/hosts or does a DNS
165 lookup. This may be blank.
166 localdomain - The domain name without the host. Obtained from the
167 above hostname if it has a "." in it. Removes first
168 segment. If hostname has no "." in it then the hostname
169 is used. This may be blank.
170 userdomain - Explicitly configured domainname. This is read out of the
171 global pine.conf or user's .pinerc. The user's entry in the
172 .pinerc overrides.
174 ** Pine has the following uses for such names:
176 1. On outgoing messages in the From: line
177 Uses userdomain if there is one. If not uses, uses
178 hostname unless Pine has been configured to use localdomain.
180 2. When expanding/fully-qualifying unqualified addresses during
181 composition
182 (same as 1)
184 3. When expanding/fully-qualifying unqualified addresses during
185 composition when a local entry in the password file exists for
186 name.
187 If no userdomain is given, then this lookup is always done
188 and the hostname is used unless Pine's been configured to
189 use the localdomain. If userdomain is defined, it is used,
190 but no local lookup is done. We can't assume users on the
191 local host are valid in the given domain (and, for simplicity,
192 have chosen to ignore the cases userdomain matches localdomain
193 or localhost). Setting user-lookup-even-if-domain-mismatch
194 feature will tell pine to override this behavior and perform
195 the local lookup anyway. The problem of a global "even-if"
196 set and a .pinerc-defined user-domain of something odd causing
197 the local lookup, but this will only effect the personal name,
198 and is not judged to be a significant problem.
200 4. In determining if an address is that of the current pine user for
201 formatting index and filtering addresses when replying
202 If a userdomain is specified the address must match the
203 userdomain exactly. If a userdomain is not specified or the
204 userdomain is the same as the hostname or domainname, then
205 an address will be considered the users if it matches either
206 the domainname or the hostname. Of course, the userid must
207 match too.
209 5. In Message ID's
210 The fully-qualified hostname is always users here.
213 ** Setting the domain names
214 To set the domain name for all Pine users on the system to be
215 different from what Pine figures out from DNS, set the domain name in
216 the "user-domain" variable in pine.conf. To set the domain name for an
217 individual user, set the "user-domain" variable in his .pinerc.
218 The .pinerc setting overrides any other setting.
219 ----*/
221 init_hostname(struct pine *ps)
223 char hostname[MAX_ADDRESS+1], domainname[MAX_ADDRESS+1];
225 getdomainnames(hostname, sizeof(hostname)-1,
226 domainname, sizeof(domainname)-1);
228 if(ps->hostname)
229 fs_give((void **)&ps->hostname);
231 ps->hostname = cpystr(hostname);
233 if(ps->localdomain)
234 fs_give((void **)&ps->localdomain);
236 ps->localdomain = cpystr(domainname);
237 ps->userdomain = NULL;
239 if(ps->VAR_USER_DOMAIN && ps->VAR_USER_DOMAIN[0]){
240 ps->maildomain = ps->userdomain = ps->VAR_USER_DOMAIN;
241 }else{
242 #if defined(DOS) || defined(OS2)
243 if(ps->VAR_USER_DOMAIN)
244 ps->blank_user_domain = 1; /* user domain set to null string! */
246 ps->maildomain = ps->localdomain[0] ? ps->localdomain : ps->hostname;
247 #else
248 ps->maildomain = strucmp(ps->VAR_USE_ONLY_DOMAIN_NAME, "yes")
249 ? ps->hostname : ps->localdomain;
250 #endif
254 * Tell c-client what domain to use when completing unqualified
255 * addresses it finds in local mailboxes. Remember, it won't
256 * affect what's to the right of '@' for unqualified addresses in
257 * remote folders...
259 mail_parameters(NULL, SET_LOCALHOST, (void *) ps->maildomain);
260 if(F_OFF(F_QUELL_MAILDOMAIN_WARNING, ps) && !strchr(ps->maildomain, '.')){
261 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Incomplete maildomain \"%s\"."),
262 ps->maildomain);
263 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
264 strncpy(tmp_20k_buf,
265 _("Return address in mail you send may be incorrect."), SIZEOF_20KBUF);
266 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
267 init_error(ps, SM_ORDER | SM_DING, 3, 5, tmp_20k_buf);
270 dprint((1,"User domain name being used \"%s\"\n",
271 ps->userdomain == NULL ? "" : ps->userdomain));
272 dprint((1,"Local Domain name being used \"%s\"\n",
273 ps->localdomain ? ps->localdomain : "?"));
274 dprint((1,"Host name being used \"%s\"\n",
275 ps->hostname ? ps->hostname : "?"));
276 dprint((1,
277 "Mail Domain name being used (by c-client too) \"%s\"\n",
278 ps->maildomain ? ps->maildomain : "?"));
280 if(!ps->maildomain || !ps->maildomain[0]){
281 #if defined(DOS) || defined(OS2)
282 if(ps->blank_user_domain)
283 return(0); /* prompt for this in send.c:dos_valid_from */
284 #endif
285 fprintf(stderr, _("No host name or domain name set\n"));
286 return(-1);
288 else
289 return(0);
293 /*----------------------------------------------------------------------
294 Make sure the default save folders exist in the default
295 save context.
296 ----*/
297 void
298 init_save_defaults(void)
300 CONTEXT_S *save_cntxt;
302 if(!ps_global->VAR_DEFAULT_FCC ||
303 !*ps_global->VAR_DEFAULT_FCC ||
304 !ps_global->VAR_DEFAULT_SAVE_FOLDER ||
305 !*ps_global->VAR_DEFAULT_SAVE_FOLDER)
306 return;
308 if(!(save_cntxt = default_save_context(ps_global->context_list)))
309 save_cntxt = ps_global->context_list;
311 if(!(folder_exists(save_cntxt, ps_global->VAR_DEFAULT_FCC) & FEX_ISFILE))
312 context_create(save_cntxt, NULL, ps_global->VAR_DEFAULT_FCC);
314 if(!(folder_exists(save_cntxt, ps_global->VAR_DEFAULT_SAVE_FOLDER) &
315 FEX_ISFILE))
316 context_create(save_cntxt, NULL, ps_global->VAR_DEFAULT_SAVE_FOLDER);
318 free_folder_list(save_cntxt);
322 /*----------------------------------------------------------------------
323 Put sent-mail files in date order
325 Args: a, b -- The names of two files. Expects names to be sent-mail-mmm-yy
326 Other names will sort in order and come before those
327 in above format.
328 ----*/
329 int
330 compare_sm_files(const qsort_t *aa, const qsort_t *bb)
332 struct sm_folder *a = (struct sm_folder *)aa,
333 *b = (struct sm_folder *)bb;
335 if(a->month_num == -1 && b->month_num == -1 && a->name && b->name)
336 return(strucmp(a->name, b->name));
337 if(a->month_num == -1) return(-1);
338 if(b->month_num == -1) return(1);
340 return(a->month_num - b->month_num);
345 /*----------------------------------------------------------------------
346 Create an ordered list of sent-mail folders and their month numbers
348 Args: dir -- The directory to find the list of files in
350 Result: Pointer to list of files is returned.
352 This list includes all files that start with "sent-mail", but not "sent-mail"
353 itself.
354 ----*/
355 struct sm_folder *
356 get_mail_list(CONTEXT_S *list_cntxt, char *folder_base)
358 register struct sm_folder *sm = NULL;
359 struct sm_folder *sml = NULL;
360 char *filename;
361 int i, folder_base_len;
362 int max_files;
363 char searchname[MAXPATH+1];
365 if((folder_base_len = strlen(folder_base)) == 0 || !list_cntxt){
366 sml = (struct sm_folder *) fs_get(sizeof(struct sm_folder));
367 memset((void *)sml, 0, sizeof(struct sm_folder));
368 return(sml);
371 #ifdef DOS
372 if(*list_cntxt->context != '{'){ /* NOT an IMAP collection! */
373 snprintf(searchname, sizeof(searchname), "%4.4s*", folder_base);
374 folder_base_len = strlen(searchname) - 1;
376 else
377 #endif /* MAXPATH + 1 = sizeof(searchmane) */
378 snprintf(searchname, sizeof(searchname), "%.*s*", MAXPATH+1-2, folder_base);
380 build_folder_list(NULL, list_cntxt, searchname, NULL, BFL_FLDRONLY);
382 max_files = MIN(MAX(0, folder_total(FOLDERS(list_cntxt))), 5000);
383 sml = sm = (struct sm_folder *) fs_get(sizeof(struct sm_folder)*(max_files+1));
384 memset((void *)sml, 0, sizeof(struct sm_folder) * (max_files+1));
386 for(i = 0; i < folder_total(FOLDERS(list_cntxt)); i++){
387 filename = folder_entry(i, FOLDERS(list_cntxt))->name;
388 #ifdef DOS
389 if(struncmp(filename, folder_base, folder_base_len) == 0
390 && strucmp(filename, folder_base)){
392 if(*list_cntxt->context != '{'){
393 int j;
394 for(j = 0; j < 4; j++)
395 if(!isdigit((unsigned char)filename[folder_base_len + j]))
396 break;
398 if(j < 4) /* not proper date format! */
399 continue; /* keep trying */
401 #else
402 #ifdef OS2
403 if(strnicmp(filename, folder_base, folder_base_len) == 0
404 && stricmp(filename, folder_base)){
405 #else
406 if(strncmp(filename, folder_base, folder_base_len) == 0
407 && strcmp(filename, folder_base)){
408 #endif
409 #endif
410 sm->name = cpystr(filename);
411 #ifdef DOS
412 if(*list_cntxt->context != '{'){ /* NOT an IMAP collection! */
413 sm->month_num = (sm->name[folder_base_len] - '0') * 10;
414 sm->month_num += sm->name[folder_base_len + 1] - '0';
416 else
417 #endif
418 sm->month_num = month_num(sm->name + (size_t)folder_base_len + 1);
419 sm++;
420 if(sm >= &sml[max_files])
421 break; /* Too many files, ignore the rest ; shouldn't occur */
425 /* anything to sort?? */
426 if(sml->name && *(sml->name) && (sml+1)->name && *((sml+1)->name)){
427 qsort(sml,
428 sm - sml,
429 sizeof(struct sm_folder),
430 compare_sm_files);
433 return(sml);
439 check_prune_time(time_t *now, struct tm **tm_now)
441 char tmp[50];
443 *now = time((time_t *) 0);
444 *tm_now = localtime(now);
447 * If the last time we did this is blank (as if pine's run for
448 * first time), don't go thru list asking, but just note it for
449 * the next time...
451 if(ps_global->VAR_LAST_TIME_PRUNE_QUESTION == NULL){
452 ps_global->last_expire_year = (*tm_now)->tm_year;
453 ps_global->last_expire_month = (*tm_now)->tm_mon;
454 snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
455 ps_global->last_expire_month + 1);
456 set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 1, 1, Main);
457 return(0);
460 if(ps_global->last_expire_year != -1 &&
461 ((*tm_now)->tm_year < ps_global->last_expire_year ||
462 ((*tm_now)->tm_year == ps_global->last_expire_year &&
463 (*tm_now)->tm_mon <= ps_global->last_expire_month)))
464 return(0);
466 return(1);
471 first_run_of_month(void)
473 time_t now;
474 struct tm *tm_now;
476 now = time((time_t *) 0);
477 tm_now = localtime(&now);
479 if(ps_global->last_expire_year == -1 ||
480 (tm_now->tm_year < ps_global->last_expire_year ||
481 (tm_now->tm_year == ps_global->last_expire_year &&
482 tm_now->tm_mon <= ps_global->last_expire_month)))
483 return(0);
485 return(1);
490 first_run_of_year(void)
492 time_t now;
493 struct tm *tm_now;
495 now = time((time_t *) 0);
496 tm_now = localtime(&now);
498 if(ps_global->last_expire_year == -1 ||
499 (tm_now->tm_year <= ps_global->last_expire_year))
500 return(0);
502 return(1);
507 * prune_move_folder - rename folder in context and delete old copy
508 * Returns -1 if unsuccessful.
511 prune_move_folder(char *oldpath, char *newpath, CONTEXT_S *prune_cntxt)
513 char spath[MAXPATH+1];
515 strncpy(spath, oldpath, sizeof(spath)-1);
516 spath[sizeof(spath)-1] = '\0';
518 /*--- User says OK to rename ---*/
519 dprint((5, "rename \"%s\" to \"%s\"\n",
520 spath ? spath : "?", newpath ? newpath : "?"));
521 q_status_message1(SM_ORDER, 1, 3,
522 /* TRANSLATORS: arg is a filename */
523 _("Renaming \"%s\" at start of month"),
524 pretty_fn(spath ? spath : "?"));
526 if(!context_rename(prune_cntxt, NULL, spath, newpath)){
527 q_status_message2(SM_ORDER | SM_DING, 3, 4,
528 /* TRANSLATORS: 1st arg is filename, 2nd is error message */
529 _("Error renaming \"%s\": %s"),
530 pretty_fn(spath ? spath : "?"),
531 error_description(errno));
532 dprint((1, "Error renaming %s to %s: %s\n",
533 spath ? spath : "?", newpath ? newpath : "?",
534 error_description(errno)));
535 display_message('x');
536 return -1;
539 context_create(prune_cntxt, NULL, spath);
541 return 0;
544 char *
545 html_directory_path(char *sname, char *sbuf, size_t len)
547 *sbuf = '\0';
548 if(sname && *sname){
549 size_t snl = strlen(sname);
550 if(is_absolute_path(sname)){
551 strncpy(sbuf, sname, len-1);
552 sbuf[len-1] = '\0';
553 fnexpand(sbuf, len);
555 else if(ps_global->VAR_OPER_DIR){
556 if(strlen(ps_global->VAR_OPER_DIR) + snl < len - 1)
557 build_path(sbuf, ps_global->VAR_OPER_DIR, sname, len);
559 else if(strlen(ps_global->home_dir) + snl < len - 1)
560 build_path(sbuf, ps_global->home_dir, sname, len);
562 return(*sbuf ? sbuf : NULL);
566 init_html_directory(char *path)
568 int rv;
569 switch(is_writable_dir(path)){
570 case 0: rv = 0; break;
571 case 3: rv = create_mail_dir(path) < 0 ? -1 : 0; break;
572 default: rv = -1; break;
574 return rv;
577 HTML_LOG_S *
578 create_html_log(void)
580 HTML_LOG_S *rv;
581 rv = fs_get(sizeof(HTML_LOG_S));
582 memset((void *) rv, 0, sizeof(HTML_LOG_S));
584 return rv;
587 void
588 add_html_log(HTML_LOG_S **htmlp, char *d)
590 HTML_LOG_S *h;
592 for(h = *htmlp; h && h->next; h = h->next);
594 if(!h){
595 h = create_html_log();
596 *htmlp = h;
598 else {
599 h->next = create_html_log();
600 h = h->next;
602 h->dir = cpystr(d);
603 h->to_delete = time(0) + 10*60; /* 10 minutes life */
606 void
607 free_html_log(HTML_LOG_S **htmlp)
609 if (htmlp == NULL || *htmlp == NULL)
610 return;
612 if((*htmlp)->dir) fs_give((void **) &(*htmlp)->dir);
613 if((*htmlp)->next) free_html_log(&(*htmlp)->next);
614 fs_give((void **)htmlp);
617 void
618 html_dir_clean(int force)
620 HTML_LOG_S *h, prev, *j;
621 time_t now = time(0);
622 #ifndef _WINDOWS
623 DIR *dirp;
624 struct dirent *d;
625 #else
626 struct _finddata_t dbuf;
627 char buf[_MAX_PATH + 4];
628 long findrv;
629 #endif /* _WINDOWS */
630 struct stat sbuf;
631 char fpath[MAXPATH], *fname;
632 int delete_happened = 0;
634 /* these are barriers to try to not to enter here */
635 if(ps_global->html_dir == NULL
636 || ps_global->html_dir_list == NULL
637 || our_stat(ps_global->html_dir, &sbuf) < 0) return;
639 for(h = ps_global->html_dir_list; h; h = h->next){
640 if(force || now >= h->to_delete){ /* the time to delete has come */
641 if(strlen(h->dir) + strlen(ps_global->html_dir) + 3 < MAXPATH){
642 if(our_stat(h->dir, &sbuf) < 0)
643 continue;
644 if((sbuf.st_mode & S_IFMT) == S_IFDIR){
645 #ifndef _WINDOWS
646 if((dirp = opendir(h->dir)) != NULL){
647 while ((d = readdir(dirp)) != NULL){
648 fname = d->d_name;
649 #else /* _WINDOWS */
650 snprintf(buf, sizeof(buf), "%s%s*.*", ps_global->html_dir, (ps_global->html_dir[strlen(ps_global->html_dir)-1] == '\\') ? "" : "\\");
651 buf[sizeof(buf)-1] = '\0';
652 if((findrv = _findfirst(buf, &dbuf)) < 0)
653 return;
654 do {
655 fname = fname_to_utf8(dbuf.name);
656 #endif /* _WINDOWS */
657 if(!strcmp(fname, ".") || !strcmp(fname, ".."))
658 continue;
659 if(strlen(h->dir) + strlen(fname) + 3 < MAXPATH){
660 snprintf(fpath, sizeof(fpath) - 1, "%s%s%s", h->dir, S_FILESEP, fname);
661 fpath[sizeof(fpath)-1] = '\0';
662 our_unlink(fpath);
664 #ifndef _WINDOWS
666 closedir(dirp);
668 #else /* _WINDOWS */
669 } while(_findnext(findrv, &dbuf) == 0);
670 #endif /* _WINDOWS */
671 if(!our_rmdir(h->dir)){
672 h->to_delete = 0; /* mark it deleted */
673 delete_happened++;
680 /* remove all deleted directories from the list */
681 if(delete_happened){
682 prev.to_delete = 1; /* do not free this entry */
683 (&prev)->next = ps_global->html_dir_list;
684 for (j = &prev; j && j->next ; ){
685 if(j->next->to_delete == 0){
686 HTML_LOG_S *k;
687 k = j->next;
688 j->next = k->next;
689 k->next = NULL;
690 free_html_log(&k);
692 else j = j->next;
694 ps_global->html_dir_list = (&prev)->next;
697 /* this is wrong, but we do not care if this fails. What this
698 * is trying to do is to the delete the .alpine-html directory
699 * if it is empty. We could test if it is empty by reading the
700 * directory. Instead we will use a weaker test checking if we
701 * have anything else to erase. If we don't, then we try to
702 * erase ps_global->html_dir, and we do not worry if we fail.
704 if(ps_global->html_dir_list == NULL)
705 our_rmdir(ps_global->html_dir);