/* * ======================================================================== * Copyright 2013-2022 Eduardo Chappa * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * ======================================================================== */ #include "../pith/headers.h" #include "../pith/imap.h" #include "../pith/msgno.h" #include "../pith/state.h" #include "../pith/flag.h" #include "../pith/pineelt.h" #include "../pith/status.h" #include "../pith/conftype.h" #include "../pith/context.h" #include "../pith/thread.h" #include "../pith/mailview.h" #include "../pith/mailpart.h" #include "../pith/mailindx.h" #include "../pith/mailcmd.h" #include "../pith/save.h" #include "../pith/util.h" #include "../pith/stream.h" #include "../pith/newmail.h" #include "../pith/icache.h" #include "../pith/options.h" #ifdef _WINDOWS #include "../pico/osdep/mswin.h" #endif /* * Internal prototypes */ long imap_seq_exec(MAILSTREAM *, char *, long (*)(MAILSTREAM *, long, void *), void *); long imap_seq_exec_append(MAILSTREAM *, long, void *); char *ps_get(size_t); /* * Exported globals setup by searching functions to tell mm_searched * where to put message numbers that matched the search criteria, * and to allow mm_searched to return number of matches. */ MAILSTREAM *mm_search_stream; long mm_search_count = 0L; MAILSTATUS mm_status_result; MM_LIST_S *mm_list_info; MMLOGIN_S *mm_login_list = NULL; MMLOGIN_S *cert_failure_list = NULL; /* * Instead of storing cached passwords in free storage, store them in this * private space. This makes it easier and more reliable when we want * to zero this space out. We only store passwords here (char *) so we * don't need to worry about alignment. */ static volatile char private_store[1024]; static int critical_depth = 0; /* hook to hang callback on "current" message expunge */ void (*pith_opt_current_expunged)(long unsigned int); #ifdef SIGINT RETSIGTYPE (*hold_int)(int); #endif #ifdef SIGTERM RETSIGTYPE (*hold_term)(int); #endif #ifdef SIGHUP RETSIGTYPE (*hold_hup)(int); #endif #ifdef SIGUSR2 RETSIGTYPE (*hold_usr2)(int); #endif /*---------------------------------------------------------------------- receive notification that search found something Input: mail stream and message number of located item Result: nothing, not used by pine ----*/ void mm_searched(MAILSTREAM *stream, long unsigned int rawno) { MESSAGECACHE *mc; if(rawno > 0L && stream && rawno <= stream->nmsgs && (mc = mail_elt(stream, rawno))){ mc->searched = 1; if(stream == mm_search_stream) mm_search_count++; } } /*---------------------------------------------------------------------- receive notification of new mail from imap daemon Args: stream -- The stream the message count report is for. number -- The number of messages now in folder. Result: Sets value in pine state indicating new mailbox size Called when the number of messages in the mailbox goes up. This may also be called as a result of an expunge. It increments the new_mail_count based on a the difference between the current idea of the maximum number of messages and what mm_exists claims. The new mail notification is done in newmail.c Only worry about the cases when the number grows, as mm_expunged handles shrinkage... ----*/ void mm_exists(MAILSTREAM *stream, long unsigned int number) { long new_this_call, n; int exbits = 0, lflags = 0; MSGNO_S *msgmap; #ifdef DEBUG if(ps_global->debug_imap > 1 || ps_global->debugmem) dprint((3, "=== mm_exists(%lu,%s) called ===\n", number, !stream ? "(no stream)" : !stream->mailbox ? "(null)" : stream->mailbox)); #endif msgmap = sp_msgmap(stream); if(!msgmap) return; if(mn_get_nmsgs(msgmap) != (long) number){ sp_set_mail_box_changed(stream, 1); /* titlebar will be affected */ if(ps_global->mail_stream == stream) ps_global->mangled_header = 1; } if(mn_get_nmsgs(msgmap) < (long) number){ new_this_call = (long) number - mn_get_nmsgs(msgmap); sp_set_new_mail_count(stream, sp_new_mail_count(stream) + new_this_call); sp_set_recent_since_visited(stream, sp_recent_since_visited(stream) + new_this_call); mn_add_raw(msgmap, new_this_call); /* * Set local "recent" and "hidden" bits... */ for(n = 0; n < new_this_call; n++, number--){ if(msgno_exceptions(stream, number, "0", &exbits, FALSE)) exbits |= MSG_EX_RECENT; else exbits = MSG_EX_RECENT; msgno_exceptions(stream, number, "0", &exbits, TRUE); if(SORT_IS_THREADED(msgmap)) lflags |= MN_USOR; /* * If we're zoomed, then hide this message too since * it couldn't have possibly been selected yet... */ lflags |= (any_lflagged(msgmap, MN_HIDE) ? MN_HIDE : 0); if(lflags) set_lflag(stream, msgmap, mn_get_total(msgmap) - n, lflags, 1); } } } /*---------------------------------------------------------------------- Receive notification from IMAP that a single message has been expunged Args: stream -- The stream/folder the message is expunged from rawno -- The raw message number that was expunged mm_expunged is always called on an expunge. Simply remove all reference to the expunged message, shifting internal mappings as necessary. ----*/ void mm_expunged(MAILSTREAM *stream, long unsigned int rawno) { MESSAGECACHE *mc = NULL; long i; int is_current = 0; MSGNO_S *msgmap; #ifdef DEBUG if(ps_global->debug_imap > 1 || ps_global->debugmem) dprint((3, "mm_expunged(%s,%lu)\n", stream ? (stream->mailbox ? stream->mailbox : "(no stream)") : "(null)", rawno)); #endif msgmap = sp_msgmap(stream); if(!msgmap) return; if(ps_global->mail_stream == stream) is_current++; if((i = mn_raw2m(msgmap, (long) rawno)) != 0L){ dprint((7, "mm_expunged: rawno=%lu msgno=%ld nmsgs=%ld max_msgno=%ld flagged_exld=%ld\n", rawno, i, mn_get_nmsgs(msgmap), mn_get_total(msgmap), msgmap->flagged_exld)); sp_set_mail_box_changed(stream, 1); sp_set_expunge_count(stream, sp_expunge_count(stream) + 1); if(is_current){ reset_check_point(stream); ps_global->mangled_header = 1; /* flush invalid cache entries */ while(i <= mn_get_total(msgmap)) clear_index_cache_ent(stream, i++, 0); /* let app know what happened */ if(pith_opt_current_expunged) (*pith_opt_current_expunged)(rawno); } } else{ dprint((7, "mm_expunged: rawno=%lu was excluded, flagged_exld was %d\n", rawno, msgmap->flagged_exld)); dprint((7, " nmsgs=%ld max_msgno=%ld\n", mn_get_nmsgs(msgmap), mn_get_total(msgmap))); if(rawno > 0L && rawno <= stream->nmsgs) mc = mail_elt(stream, rawno); if(!mc){ dprint((7, " cannot get mail_elt(%lu)\n", rawno)); } else if(!mc->sparep){ dprint((7, " mail_elt(%lu)->sparep is NULL\n", rawno)); } else{ dprint((7, " mail_elt(%lu)->sparep->excluded=%d\n", rawno, (int) (((PINELT_S *) mc->sparep)->excluded))); } } if(SORT_IS_THREADED(msgmap) && (SEP_THRDINDX() || ps_global->thread_disp_style != THREAD_NONE)){ long cur; /* * When we're sorting with a threaded method an expunged * message may cause the rest of the sort to be wrong. This * isn't so bad if we're just looking at the index. However, * it also causes the thread tree (PINETHRD_S) to become * invalid, so if we're using a threading view we need to * sort in order to fix the tree and to protect fetch_thread(). */ sp_set_need_to_rethread(stream, 1); /* * If we expunged the current message which was a member of the * viewed thread, and the adjustment to current will take us * out of that thread, fix it if we can, by backing current up * into the thread. We'd like to just check after mn_flush_raw * below but the problem is that the elts won't change until * after we return from mm_expunged. So we have to manually * check the other messages for CHID2 flags instead of thinking * that we can expunge the current message and then check. It won't * work because the elt will still refer to the expunged message. */ if(sp_viewing_a_thread(stream) && get_lflag(stream, NULL, rawno, MN_CHID2) && mn_total_cur(msgmap) == 1 && mn_is_cur(msgmap, mn_raw2m(msgmap, (long) rawno)) && (cur = mn_get_cur(msgmap)) > 1L && cur < mn_get_total(msgmap) && !get_lflag(stream, msgmap, cur + 1L, MN_CHID2) && get_lflag(stream, msgmap, cur - 1L, MN_CHID2)) mn_set_cur(msgmap, cur - 1L); } /* * Keep on top of our special flag counts. * * NOTE: This is allowed since mail_expunged releases * data for this message after the callback. */ if(rawno > 0L && rawno <= stream->nmsgs && (mc = mail_elt(stream, rawno))){ PINELT_S *pelt = (PINELT_S *) mc->sparep; if(pelt){ if(pelt->hidden) msgmap->flagged_hid--; if(pelt->excluded) msgmap->flagged_exld--; if(pelt->selected) msgmap->flagged_tmp--; if(pelt->colhid) msgmap->flagged_chid--; if(pelt->colhid2) msgmap->flagged_chid2--; if(pelt->collapsed) msgmap->flagged_coll--; if(pelt->tmp) msgmap->flagged_stmp--; if(pelt->unsorted) msgmap->flagged_usor--; if(pelt->searched) msgmap->flagged_srch--; if(pelt->hidden || pelt->colhid) msgmap->flagged_invisible--; free_pine_elt(&mc->sparep); } } /* * if it's in the sort array, flush it, otherwise * decrement raw sequence numbers greater than "rawno" */ mn_flush_raw(msgmap, (long) rawno); } void mm_flags(MAILSTREAM *stream, long unsigned int rawno) { /* * The idea here is to clean up any data pine might have cached * that has anything to do with the indicated message number. */ if(stream == ps_global->mail_stream){ long msgno, t; PINETHRD_S *thrd; if(scores_are_used(SCOREUSE_GET) & SCOREUSE_STATEDEP) clear_msg_score(stream, rawno); msgno = mn_raw2m(sp_msgmap(stream), (long) rawno); /* if in thread index */ if(THRD_INDX()){ if((thrd = fetch_thread(stream, rawno)) && thrd->top && (thrd = fetch_thread(stream, thrd->top)) && thrd->rawno && (t = mn_raw2m(sp_msgmap(stream), thrd->rawno))) clear_index_cache_ent(stream, t, 0); } else if(THREADING()){ if(msgno > 0L) clear_index_cache_ent(stream, msgno, 0); /* * If a parent is collapsed, clear that parent's * index cache entry. */ if((thrd = fetch_thread(stream, rawno)) && thrd->parent){ thrd = fetch_thread(stream, thrd->parent); while(thrd){ if(get_lflag(stream, NULL, thrd->rawno, MN_COLL) && (t = mn_raw2m(sp_msgmap(stream), (long) thrd->rawno))) clear_index_cache_ent(stream, t, 0); if(thrd->parent) thrd = fetch_thread(stream, thrd->parent); else thrd = NULL; } } } else if(msgno > 0L) clear_index_cache_ent(stream, msgno, 0); if(msgno && mn_is_cur(sp_msgmap(stream), msgno)) ps_global->mangled_header = 1; } /* * We count up flag changes here. The * dont_count_flagchanges variable tries to prevent us from * counting when we're just fetching flags. */ if(!(ps_global->dont_count_flagchanges && stream == ps_global->mail_stream)){ int exbits; check_point_change(stream); /* we also note flag changes for filtering purposes */ if(msgno_exceptions(stream, rawno, "0", &exbits, FALSE)) exbits |= MSG_EX_STATECHG; else exbits = MSG_EX_STATECHG; msgno_exceptions(stream, rawno, "0", &exbits, TRUE); } } void mm_list(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes) { #ifdef DEBUG if(ps_global->debug_imap > 2 || ps_global->debugmem) dprint((5, "mm_list \"%s\": delim: '%c', %s%s%s%s%s%s\n", mailbox ? mailbox : "?", delimiter ? delimiter : 'X', (attributes & LATT_NOINFERIORS) ? ", no inferiors" : "", (attributes & LATT_NOSELECT) ? ", no select" : "", (attributes & LATT_MARKED) ? ", marked" : "", (attributes & LATT_UNMARKED) ? ", unmarked" : "", (attributes & LATT_HASCHILDREN) ? ", has children" : "", (attributes & LATT_HASNOCHILDREN) ? ", has no children" : "")); #endif if(!mm_list_info || !mm_list_info->filter) return; if(!mm_list_info->stream || stream == mm_list_info->stream) (*mm_list_info->filter)(stream, mailbox, delimiter, attributes, mm_list_info->data, mm_list_info->options); } void mm_lsub(MAILSTREAM *stream, int delimiter, char *mailbox, long int attributes) { #ifdef DEBUG if(ps_global->debug_imap > 2 || ps_global->debugmem) dprint((5, "LSUB \"%s\": delim: '%c', %s%s%s%s%s%s\n", mailbox ? mailbox : "?", delimiter ? delimiter : 'X', (attributes & LATT_NOINFERIORS) ? ", no inferiors" : "", (attributes & LATT_NOSELECT) ? ", no select" : "", (attributes & LATT_MARKED) ? ", marked" : "", (attributes & LATT_UNMARKED) ? ", unmarked" : "", (attributes & LATT_HASCHILDREN) ? ", has children" : "", (attributes & LATT_HASNOCHILDREN) ? ", has no children" : "")); #endif if(!mm_list_info || !mm_list_info->filter) return; if(!mm_list_info->stream || stream == mm_list_info->stream) (*mm_list_info->filter)(stream, mailbox, delimiter, attributes, mm_list_info->data, mm_list_info->options); } void mm_status(MAILSTREAM *stream, char *mailbox, MAILSTATUS *status) { /* * We implement mail_status for the #move namespace by adding a wrapper * routine, pine_mail_status. It may have to call the real mail_status * twice for #move folders and combine the results. It sets * pine_cached_status to point to a local status variable to store the * intermediate results. */ if(status){ if(pine_cached_status != NULL) *pine_cached_status = *status; else mm_status_result = *status; } #ifdef DEBUG if(status){ if(pine_cached_status) dprint((2, "mm_status: Preliminary pass for #move\n")); dprint((2, "mm_status: Mailbox \"%s\"", mailbox ? mailbox : "?")); if(status->flags & SA_MESSAGES) dprint((2, ", %lu messages", status->messages)); if(status->flags & SA_RECENT) dprint((2, ", %lu recent", status->recent)); if(status->flags & SA_UNSEEN) dprint((2, ", %lu unseen", status->unseen)); if(status->flags & SA_UIDVALIDITY) dprint((2, ", %lu UID validity", status->uidvalidity)); if(status->flags & SA_UIDNEXT) dprint((2, ", %lu next UID", status->uidnext)); dprint((2, "\n")); } #endif } /*---------------------------------------------------------------------- Write imap debugging information into log file Args: strings -- the string for the debug file Result: message written to the debug log file ----*/ void mm_dlog(char *string) { char *p, *q = NULL, save, *continued; int more = 1; #ifdef _WINDOWS mswin_imaptelemetry(string); #endif #ifdef DEBUG continued = ""; p = string; #ifdef DEBUGJOURNAL /* because string can be really long and we don't want to lose any of it */ if(p) more = 1; while(more){ if(q){ *q = save; p = q; continued = "(Continuation line) "; } if(strlen(p) > 63000){ q = p + 60000; save = *q; *q = '\0'; } else more = 0; #endif dprint(((ps_global->debug_imap >= 4 && debug < 4) ? debug : 4, "IMAP DEBUG %s%s: %s\n", continued ? continued : "", debug_time(1, ps_global->debug_timestamp, ps_global->signal_in_progress), p ? p : "?")); #ifdef DEBUGJOURNAL } #endif #endif } /*---------------------------------------------------------------------- Ignore signals when imap is running through critical code Args: stream -- The stream on which critical operation is proceeding ----*/ void mm_critical(MAILSTREAM *stream) { stream = stream; /* For compiler complaints that this isn't used */ if(++critical_depth == 1){ #ifdef SIGHUP hold_hup = signal(SIGHUP, SIG_IGN); #endif #ifdef SIGUSR2 hold_usr2 = signal(SIGUSR2, SIG_IGN); #endif #ifdef SIGINT hold_int = signal(SIGINT, SIG_IGN); #endif #ifdef SIGTERM hold_term = signal(SIGTERM, SIG_IGN); #endif } dprint((9, "IMAP critical (depth now %d) on %s\n", critical_depth, (stream && stream->mailbox) ? stream->mailbox : "" )); } /*---------------------------------------------------------------------- Reset signals after critical imap code ----*/ void mm_nocritical(MAILSTREAM *stream) { stream = stream; /* For compiler complaints that this isn't used */ if(--critical_depth == 0){ #ifdef SIGHUP (void)signal(SIGHUP, hold_hup); #endif #ifdef SIGUSR2 (void)signal(SIGUSR2, hold_usr2); #endif #ifdef SIGINT (void)signal(SIGINT, hold_int); #endif #ifdef SIGTERM (void)signal(SIGTERM, hold_term); #endif } critical_depth = MAX(critical_depth, 0); dprint((9, "Done with IMAP critical (depth now %d) on %s\n", critical_depth, (stream && stream->mailbox) ? stream->mailbox : "" )); } void mm_fatal(char *message) { alpine_panic(message); } char * imap_referral(MAILSTREAM *stream, char *ref, long int code) { char *buf = NULL; if(ref && !struncmp(ref, "imap://", 7)){ char *folder = NULL; imapuid_t uid_val, uid; int rv; rv = url_imap_folder(ref, &folder, &uid, &uid_val, NULL, 1); switch(code){ case REFAUTHFAILED : case REFAUTH : if((rv & URL_IMAP_IMAILBOXLIST) && (rv & URL_IMAP_ISERVERONLY)) buf = cpystr(folder); break; case REFSELECT : case REFCREATE : case REFDELETE : case REFRENAME : case REFSUBSCRIBE : case REFUNSUBSCRIBE : case REFSTATUS : case REFCOPY : case REFAPPEND : if(rv & URL_IMAP_IMESSAGELIST) buf = cpystr(folder); break; default : break; } if(folder) fs_give((void **) &folder); } return(buf); } long imap_proxycopy(MAILSTREAM *stream, char *sequence, char *mailbox, long int flags) { SE_APP_S args; long ret; args.folder = mailbox; args.flags = flags; if(pith_opt_save_size_changed_prompt) (*pith_opt_save_size_changed_prompt)(0L, SSCP_INIT); ret = imap_seq_exec(stream, sequence, imap_seq_exec_append, &args); if(pith_opt_save_size_changed_prompt) (*pith_opt_save_size_changed_prompt)(0L, SSCP_END); return(ret); } long imap_seq_exec(MAILSTREAM *stream, char *sequence, long int (*func)(MAILSTREAM *, long int, void *), void *args) { unsigned long i,j,x; while (*sequence) { /* while there is something to parse */ if (*sequence == '*') { /* maximum message */ if(!(i = stream->nmsgs)){ mm_log ("No messages, so no maximum message number",ERROR); return(0L); } sequence++; /* skip past * */ } else if (!(i = strtoul ((const char *) sequence,&sequence,10)) || (i > stream->nmsgs)){ mm_log ("Sequence invalid",ERROR); return(0L); } switch (*sequence) { /* see what the delimiter is */ case ':': /* sequence range */ if (*++sequence == '*') { /* maximum message */ if (stream->nmsgs) j = stream->nmsgs; else { mm_log ("No messages, so no maximum message number",ERROR); return NIL; } sequence++; /* skip past * */ } /* parse end of range */ else if (!(j = strtoul ((const char *) sequence,&sequence,10)) || (j > stream->nmsgs)) { mm_log ("Sequence range invalid",ERROR); return NIL; } if (*sequence && *sequence++ != ',') { mm_log ("Sequence range syntax error",ERROR); return NIL; } if (i > j) { /* swap the range if backwards */ x = i; i = j; j = x; } while (i <= j) if(!(*func)(stream, i++, args)) return(0L); break; case ',': /* single message */ ++sequence; /* skip the delimiter, fall into end case */ case '\0': /* end of sequence */ if(!(*func)(stream, i, args)) return(0L); break; default: /* anything else is a syntax error! */ mm_log ("Sequence syntax error",ERROR); return NIL; } } return T; /* successfully parsed sequence */ } long imap_seq_exec_append(MAILSTREAM *stream, long int msgno, void *args) { char *save_folder, *flags = NULL, date[64]; CONTEXT_S *cntxt = NULL; int our_stream = 0; long rv = 0L; MAILSTREAM *save_stream; SE_APP_S *sa = (SE_APP_S *) args; MESSAGECACHE *mc; STORE_S *so; save_folder = (strucmp(sa->folder, ps_global->inbox_name) == 0) ? ps_global->VAR_INBOX_PATH : sa->folder; save_stream = save_msg_stream(cntxt, save_folder, &our_stream); if((so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL){ /* store flags before the fetch so UNSEEN bit isn't flipped */ mc = (msgno > 0L && stream && msgno <= stream->nmsgs) ? mail_elt(stream, msgno) : NULL; flags = flag_string(stream, msgno, F_ANS|F_FWD|F_FLAG|F_SEEN|F_KEYWORD); if(mc && mc->day) mail_date(date, mc); else *date = '\0'; rv = save_fetch_append(stream, msgno, NULL, save_stream, save_folder, NULL, mc ? mc->rfc822_size : 0L, flags, date, so); if(flags) fs_give((void **) &flags); if(rv < 0 || sp_expunge_count(stream)){ cmd_cancelled("Attached message Save"); rv = 0L; } /* else whatever broke in save_fetch_append shoulda bitched */ so_give(&so); } else{ dprint((1, "Can't allocate store for save: %s\n", error_description(errno))); q_status_message(SM_ORDER | SM_DING, 3, 4, "Problem creating space for message text."); } if(our_stream) mail_close(save_stream); return(rv); } /*---------------------------------------------------------------------- Get login and password from user for IMAP login Args: mb -- The mail box property struct user -- Buffer to return the user name in passwd -- Buffer to return the passwd in trial -- The trial number or number of attempts to login user is at least size NETMAXUSER passwd is apparently at least MAILTMPLEN, but mrc has asked us to use a max size of about 100 instead Result: username and password passed back to imap ----*/ void mm_login(NETMBX *mb, char *user, char **pwd, long int trial) { mm_login_work(mb, user, pwd, trial, NULL, NULL); } void mm_login_method(NETMBX *mb, char *user, void *login, long int trial, char *method) { mm_login_method_work(mb, user, login, trial, method, NULL, NULL); } /*---------------------------------------------------------------------- Exported method to retrieve logged in user name associated with stream Args: host -- host to find associated login name with. Result: ----*/ char * cached_user_name(char *host) { MMLOGIN_S *l; STRLIST_S *h; if((l = mm_login_list) && host) do for(h = l->hosts; h; h = h->next) if(!strucmp(host, h->name)) return(l->user); while((l = l->next) != NULL); return(NULL); } int imap_same_host(STRLIST_S *hl1, STRLIST_S *hl2) { return imap_same_host_auth(hl1, hl2, NULL); } /* An explanation about this function. The way this is used in the * new code for XOAUTH2, makes it so that when we are checking if * the hosts and orighosts are the same, we are given the mb->host * and mb->orighost pointers, and we cannot transform them in any * way to be sure that increasing their size will not overflow the * fixed width buffer that contain them, so we cannot change that. * For purposes of this function, these values come in the hl2 variable. * However, for purposes of this function, the values in hl1 are the ones * that come straight from the cache, and here no transformation is made, * that is, we save them as we read them, so when we compare the values * read from the cache, with those that we want to save, we need to make * sure we "shift" the hl1 variable, but not the hl2 variable. */ int imap_same_host_auth(STRLIST_S *hl1, STRLIST_S *hl2, char *authtype) { STRLIST_S *lp; size_t len, offset; len = authtype ? strlen(authtype) : 0; offset = authtype ? 1 : 0; for( ; hl1; hl1 = hl1->next) for(lp = hl2; lp; lp = lp->next) if((len == 0 || (!struncmp(hl1->name, authtype, len) && hl1->name[len] == PWDAUTHSEP)) && !strucmp(hl1->name + len + offset, lp->name)) return(TRUE); return(FALSE); } /* * For convenience, we use the same m_list structure (but a different * instance) for storing a list of hosts we've asked the user about when * SSL validation fails. If this function returns TRUE, that means we * have previously asked the user about this host. Ok_novalidate == 1 means * the user said yes, it was ok. Ok_novalidate == 0 means the user * said no. Warned means we warned them already. */ int imap_get_ssl(MMLOGIN_S *m_list, STRLIST_S *hostlist, int *ok_novalidate, int *warned) { MMLOGIN_S *l; for(l = m_list; l; l = l->next) if(imap_same_host(l->hosts, hostlist)){ if(ok_novalidate) *ok_novalidate = l->ok_novalidate; if(warned) *warned = l->warned; return(TRUE); } return(FALSE); } /* * Just trying to guess the username the user might want to use on this * host, the user will confirm. */ char * imap_get_user(MMLOGIN_S *m_list, STRLIST_S *hostlist) { MMLOGIN_S *l; for(l = m_list; l; l = l->next) if(imap_same_host(l->hosts, hostlist)) return(l->user); return(NULL); } /* * If we have a matching hostname, username, and altflag in our cache, * attempt to login with the password from the cache. */ int imap_get_passwd(MMLOGIN_S *m_list, char **passwd, char *user, STRLIST_S *hostlist, int altflag) { return imap_get_passwd_auth(m_list, passwd, user, hostlist, altflag, NULL); } int imap_get_passwd_auth(MMLOGIN_S *m_list, char **passwd, char *user, STRLIST_S *hostlist, int altflag, char *authtype) { MMLOGIN_S *l; int len, offset; dprint((9, "imap_get_passwd: checking user=%s alt=%d host=%s%s%s\n", user ? user : "(null)", altflag, hostlist->name ? hostlist->name : "", (hostlist->next && hostlist->next->name) ? ", " : "", (hostlist->next && hostlist->next->name) ? hostlist->next->name : "")); len = authtype ? strlen(authtype) : 0; offset = authtype ? 1 : 0; for(l = m_list; l; l = l->next) if(imap_same_host_auth(l->hosts, hostlist, authtype) && *user && (len == 0 || (!struncmp(l->user, authtype, len) && l->user[len] == PWDAUTHSEP)) && !strcmp(user, l->user + len + offset) && l->altflag == altflag){ if(passwd) *passwd = cpystr(l->passwd + len + offset); dprint((9, "imap_get_passwd: match\n")); dprint((10, "imap_get_passwd: trying passwd=\"%s\"\n", passwd && *passwd ? *passwd : "?")); return(TRUE); } dprint((9, "imap_get_passwd: no match\n")); return(FALSE); } void imap_set_passwd(MMLOGIN_S **l, char *passwd, char *user, STRLIST_S *hostlist, int altflag, int ok_novalidate, int warned) { imap_set_passwd_auth(l, passwd, user, hostlist, altflag, ok_novalidate, warned, NULL); } void imap_set_passwd_auth(MMLOGIN_S **l, char *passwd, char *user, STRLIST_S *hostlist, int altflag, int ok_novalidate, int warned, char *authtype) { STRLIST_S **listp; size_t len; size_t authlen, offset; authlen = authtype ? strlen(authtype) : 0; offset = authtype ? 1 : 0; dprint((9, "imap_set_passwd\n")); for(; *l; l = &(*l)->next) if((authlen == 0 || (!struncmp((*l)->user, authtype, authlen) && (*l)->user[authlen] == PWDAUTHSEP)) && !strcmp(user, (*l)->user + authlen + offset) && imap_same_host_auth((*l)->hosts, hostlist, authtype) && altflag == (*l)->altflag){ if(strcmp(passwd, (*l)->passwd + authlen + offset) || (*l)->ok_novalidate != ok_novalidate || (*l)->warned != warned) break; else return; } if(!*l){ *l = (MMLOGIN_S *)fs_get(sizeof(MMLOGIN_S)); memset(*l, 0, sizeof(MMLOGIN_S)); } len = strlen(passwd); (*l)->passwd = ps_get(len + authlen + offset + 1); if(authtype) sprintf((*l)->passwd, "%s%c%s", authtype, PWDAUTHSEP, passwd); else strncpy((*l)->passwd, passwd, len+1); (*l)->altflag = altflag; (*l)->ok_novalidate = ok_novalidate; (*l)->warned = warned; if(!(*l)->user){ if(authlen > 0){ (*l)->user = fs_get(strlen(user) + authlen + offset + 1); sprintf((*l)->user, "%s%c%s", authtype, PWDAUTHSEP, user); } else (*l)->user = cpystr(user); } dprint((9, "imap_set_passwd: user=%s altflag=%d\n", (*l)->user ? (*l)->user : "?", (*l)->altflag)); for( ; hostlist; hostlist = hostlist->next){ for(listp = &(*l)->hosts; *listp && strucmp((*listp)->name, hostlist->name); listp = &(*listp)->next) ; if(!*listp){ *listp = new_strlist_auth(hostlist->name, authtype, PWDAUTHSEP); dprint((9, "imap_set_passwd: host=%s\n", (*listp)->name ? (*listp)->name : "?")); } } dprint((10, "imap_set_passwd: passwd=\"%s\"\n", passwd ? passwd : "?")); } void imap_flush_passwd_cache(int dumpcache) { size_t len; volatile char *p; /* equivalent of memset but can't be optimized away */ len = sizeof(private_store); p = private_store; while(len-- > 0) *p++ = '\0'; if(dumpcache){ MMLOGIN_S *l; while((l = mm_login_list) != NULL){ mm_login_list = mm_login_list->next; if(l->user) fs_give((void **) &l->user); if(!(l->passwd >= (char *) private_store && l->passwd <= p)) fs_give((void **) &l->passwd); free_strlist(&l->hosts); fs_give((void **) &l); } while((l = cert_failure_list) != NULL){ cert_failure_list = cert_failure_list->next; if(l->user) fs_give((void **) &l->user); if(!(l->passwd >= (char *) private_store && l->passwd <= p)) fs_give((void **) &l->passwd); free_strlist(&l->hosts); fs_give((void **) &l); } } } void imap_delete_passwd(MMLOGIN_S **m_list, char *user, STRLIST_S *hostlist, int altflag) { if(m_list == NULL || *m_list == NULL) return; imap_delete_passwd_auth(m_list, user, hostlist, altflag, strchr((*m_list)->user, PWDAUTHSEP) != NULL ? OA2NAME : NULL); } void imap_delete_passwd_auth(MMLOGIN_S **m_list, char *user, STRLIST_S *hostlist, int altflag, char *authtype) { MMLOGIN_S *l, *p, *q; int len, offset; if(m_list == NULL || *m_list == NULL) return; dprint((9, "imap_delete_password: user=%s, host=%s, authtype=%s.", user ? user : "no user!?", hostlist && hostlist->name ? hostlist->name : "unknown host", authtype ? authtype : "password authentication")); len = authtype ? strlen(authtype) : 0; offset = authtype ? 1 : 0; for(p = *m_list; p; p = p->next){ if(imap_same_host_auth(p->hosts, hostlist, authtype) && *user && (len == 0 || (!struncmp(p->user, authtype, len) && p->user[len] == PWDAUTHSEP)) && !strcmp(user, p->user + len + offset) && p->altflag == altflag) break; } dprint((9, "imap_delete_password: %s", p ? "found a match!" : "did not find a match!")); if(!p) return; /* relink *mlist */ if(p == *m_list) q = (*m_list)->next; else{ q = *m_list; for(l = *m_list; l && l->next != p; l = l->next); l->next = p->next; } /* so now p is out of the list. Free it */ p->next = NULL; if(p->user) fs_give((void **) &p->user); if(!(p->passwd >= (char *) private_store && p->passwd <= (char *) private_store + sizeof(private_store))) fs_give((void **) &p->passwd); free_strlist(&p->hosts); fs_give((void **) &p); *m_list = q; dprint((9, "imap_delete_password: done with deletion.")); } /* * Mimics fs_get except it only works for char * (no alignment hacks), it * stores in a static array so it is easy to zero it out (that's the whole * purpose), allocations always happen at the end (no free). * If we go past array limit, we don't break, we just use free storage. * Should be awfully rare, though. */ char * ps_get(size_t size) { static char *last = (char *) private_store; char *block = NULL; /* there is enough space */ if(size <= sizeof(private_store) - (last - (char *) private_store)){ block = last; last += size; } else{ dprint((2, "Out of password caching space in private_store\n")); dprint((2, "Using free storage instead\n")); block = fs_get(size); } return(block); } #ifdef PASSFILE char * passfile_name(char *pinerc, char *path, size_t len) { struct stat sbuf; char *p = NULL; int i, j; if(!path || !((pinerc && pinerc[0]) || ps_global->passfile)) return(NULL); if(ps_global->passfile) strncpy(path, ps_global->passfile, len-1); else{ if((p = last_cmpnt(pinerc)) && *(p-1) && *(p-1) != PASSFILE[0]) for(i = 0; pinerc < p && i < len; pinerc++, i++) path[i] = *pinerc; else i = 0; for(j = 0; (i < len) && (path[i] = PASSFILE[j]); i++, j++) ; } path[len-1] = '\0'; dprint((9, "Looking for passfile \"%s\"\n", path ? path : "?")); #if defined(DOS) || defined(OS2) return((our_stat(path, &sbuf) == 0 && ((sbuf.st_mode & S_IFMT) == S_IFREG)) ? path : NULL); #else /* First, make sure it's ours and not sym-linked */ if(our_lstat(path, &sbuf) == 0 && ((sbuf.st_mode & S_IFMT) == S_IFREG) && sbuf.st_uid == getuid()){ /* if too liberal permissions, fix them */ if((sbuf.st_mode & 077) != 0) if(our_chmod(path, sbuf.st_mode & ~077) != 0) return(NULL); return(path); } else return(NULL); #endif } #endif /* PASSFILE */