* clear out some warnings by gcc 9.3.1.
[alpine.git] / web / src / alpined.d / imap.c
blob19d1672afe02a3eea39525e8d3144ce98e49c6c3
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: imap.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
3 #endif
5 /* ========================================================================
6 * Copyright 2006-2007 University of Washington
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * ========================================================================
17 /*======================================================================
18 imap.c
19 The call back routines for the c-client/imap
20 - handles error messages and other notification
21 - handles prelimirary notification of new mail and expunged mail
22 - prompting for imap server login and password
24 ====*/
26 #include <system.h>
27 #include <general.h>
29 #include "../../../c-client/c-client.h"
31 #include "../../../pith/state.h"
32 #include "../../../pith/debug.h"
33 #include "../../../pith/string.h"
34 #include "../../../pith/flag.h"
35 #include "../../../pith/imap.h"
36 #include "../../../pith/status.h"
37 #include "../../../pith/osdep/collate.h"
39 #include "debug.h"
40 #include "alpined.h"
45 * Internal prototypes
47 long imap_seq_exec(MAILSTREAM *, char *,long (*)(MAILSTREAM *, long, void *), void *);
48 long imap_seq_exec_append(MAILSTREAM *, long, void *);
52 * Exported globals setup by searching functions to tell mm_searched
53 * where to put message numbers that matched the search criteria,
54 * and to allow mm_searched to return number of matches.
56 //MAILSTREAM *mm_search_stream;
58 //MM_LIST_S *mm_list_info;
62 /*----------------------------------------------------------------------
63 Queue imap log message for display in the message line
65 Args: string -- The message
66 errflg -- flag set to 1 if pertains to an error
68 Result: Message queued for display
70 The c-client/imap reports most of it's status and errors here
71 ---*/
72 void
73 mm_log(char *string, long errflg)
75 char message[300];
76 char *occurance;
77 int was_capitalized;
78 time_t now;
79 struct tm *tm_now;
81 if(errflg == ERROR){
82 dprint((SYSDBG_ERR, "%.*s (%ld)", 128, string, errflg));
85 now = time((time_t *)0);
86 tm_now = localtime(&now);
88 dprint((ps_global->debug_imap ? 0 : (errflg == ERROR ? 1 : 2),
89 "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
90 tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec, tm_now->tm_mon+1,
91 tm_now->tm_mday,
92 (errflg == ERROR)
93 ? "ERROR"
94 : (errflg == WARN)
95 ? "warn"
96 : (errflg == PARSE)
97 ? "parse"
98 : "babble",
99 string));
101 if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
102 ps_global->try_to_create = 1;
103 return;
105 else if(ps_global->try_to_create
106 || (sp_dead_stream(ps_global->mail_stream)
107 && (!strncmp(string, "[CLOSED]", 8) || strstr(string, "No-op"))))
109 * Don't display if creating new folder OR
110 * warning about a dead stream ...
112 return;
114 /*---- replace all "mailbox" with "folder" ------*/
115 strncpy(message, string, sizeof(message));
116 message[sizeof(message) - 1] = '\0';
117 occurance = srchstr(message, "mailbox");
118 while(occurance) {
119 if(!*(occurance+7) || isspace((unsigned char)*(occurance+7))){
120 was_capitalized = isupper((unsigned char)*occurance);
121 rplstr(occurance, 7, 7, (errflg == PARSE ? "address" : "folder"));
122 if(was_capitalized)
123 *occurance = (errflg == PARSE ? 'A' : 'F');
125 else
126 occurance += 7;
128 occurance = srchstr(occurance, "mailbox");
131 if(errflg == ERROR)
132 ps_global->mm_log_error = 1;
134 if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error)){
135 strncpy(ps_global->c_client_error, message, sizeof(ps_global->c_client_error));
136 ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
139 if(ps_global->noshow_error
140 || (ps_global->noshow_warn && errflg == WARN)
141 || !(errflg == ERROR || errflg == WARN))
142 return; /* Only care about errors; don't print when asked not to */
144 /*---- Display the message ------*/
145 q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
146 3, 5, message);
147 if(errflg == ERROR){
148 strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
149 ps_global->last_error[sizeof(ps_global->last_error)-1] = '\0';
155 /*----------------------------------------------------------------------
156 receive notification from IMAP
158 Args: stream -- Mail stream message is relevant to
159 string -- The message text
160 errflag -- Set if it is a serious error
162 Result: message displayed in status line
164 The facility is for general notices, such as connection to server;
165 server shutting down etc... It is used infrequently.
166 ----------------------------------------------------------------------*/
167 void
168 mm_notify(MAILSTREAM *stream, char *string, long errflag)
170 if(errflag == ERROR){
171 dprint((SYSDBG_ERR, "mm_notify: %s (%ld)", string, errflag));
174 /* be sure to log the message... */
175 #ifdef DEBUG
176 if(ps_global->debug_imap)
177 dprint((0, "IMAP mm_notify %s : %s (%s) : %s\n",
178 (!errflag) ? "NIL" :
179 (errflag == ERROR) ? "error" :
180 (errflag == WARN) ? "warning" :
181 (errflag == BYE) ? "bye" : "unknown",
182 (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
183 (stream && stream == sp_inbox_stream()) ? "inboxstream" :
184 (stream && stream == ps_global->mail_stream) ? "mailstream" :
185 (stream) ? "abookstream?" : "nostream",
186 string));
187 #endif
189 strncpy(ps_global->last_error, string, 500);
190 ps_global->last_error[499] = '\0';
193 * Then either set special bits in the pine struct or
194 * display the message if it's tagged as an "ALERT" or
195 * its errflag > NIL (i.e., WARN, or ERROR)
197 if(errflag == BYE){
198 if(stream == ps_global->mail_stream){
199 if(sp_dead_stream(ps_global->mail_stream))
200 return;
201 else
202 sp_set_dead_stream(ps_global->mail_stream, 1);
204 else if(stream && stream == sp_inbox_stream()){
205 if(sp_dead_stream(stream))
206 return;
207 else
208 sp_set_dead_stream(stream, 1);
211 else if(!strncmp(string, "[TRYCREATE]", 11))
212 ps_global->try_to_create = 1;
213 else if(!strncmp(string, "[ALERT]", 7))
214 q_status_message2(SM_MODAL, 3, 3, "Alert received while accessing \"%s\": %s",
215 (stream && stream->mailbox)
216 ? stream->mailbox : "-no folder-",
217 rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, string));
218 else if(!strncmp(string, "[UNSEEN ", 8)){
219 char *p;
220 long n = 0;
222 for(p = string + 8; isdigit(*p); p++)
223 n = (n * 10) + (*p - '0');
225 sp_set_first_unseen(ps_global->mail_stream, n);
227 else if(!strncmp(string, "[READ-ONLY]", 11)
228 && !(stream && stream->mailbox && IS_NEWS(stream)))
229 q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
230 (stream && stream->mailbox)
231 ? stream->mailbox : "-no folder-",
232 string + 11);
233 else if(errflag && (errflag == WARN || errflag == ERROR))
234 q_status_message(SM_ORDER | ((errflag == ERROR) ? SM_DING : 0),
235 3, 6, ps_global->last_error);
238 void
239 mm_login_method_work (NETMBX *mb,char *user,void *login,long trial, char *method, char *usethisprompt, char *altuserforcache)
244 /*----------------------------------------------------------------------
245 Do work of getting login and password from user for IMAP login
247 Args: mb -- The mail box property struct
248 user -- Buffer to return the user name in
249 passwd -- Buffer to return the passwd in
250 trial -- The trial number or number of attempts to login
252 Result: username and password passed back to imap
253 ----*/
254 void
255 mm_login_work(NETMBX *mb, char *user, char **passwd, long trial, char *usethisprompt, char *altuserforcache)
257 STRLIST_S hostlist[2];
258 NETMBX cmb;
259 int l;
260 char *pwd = *passwd;
262 pwd[0] = '\0';
264 if((l = strlen(mb->orighost)) > 0 && l < CRED_REQ_SIZE)
265 strcpy(peCredentialRequestor, mb->orighost);
267 if(trial){ /* one shot only! */
268 user[0] = '\0';
269 peCredentialError = 1;
270 *passwd = pwd;
271 return;
274 #if 0
275 if(ps_global && ps_global->anonymous) {
276 /*------ Anonymous login mode --------*/
277 if(trial < 1) {
278 strcpy(user, "anonymous");
279 sprintf(pwd, "%s@%s", ps_global->VAR_USER_ID,
280 ps_global->hostname);
282 else
283 user[0] = pwd[0] = '\0';
285 *passwd = pwd;
286 return;
288 #endif
290 #if WEB_REQUIRE_SECURE_IMAP
291 /* we *require* secure authentication */
292 if(!(mb->sslflag || mb->tlsflag) && strcmp("localhost",mb->host)){
293 user[0] = pwd[0] = '\0';
294 *passwd = pwd;
295 return;
297 #endif
300 * heavily paranoid about offering password to server
301 * that the users hasn't also indicated the remote user
302 * name
304 if(*mb->user){
305 strcpy(user, mb->user);
307 else if(ps_global->prc
308 && ps_global->prc->name
309 && mail_valid_net_parse(ps_global->prc->name,&cmb)
310 && cmb.user){
311 strcpy(user, cmb.user);
313 else{
315 * don't blindly offer user/pass
317 user[0] = pwd[0] = '\0';
318 *passwd = pwd;
319 return;
323 * set up host list for sybil servers...
325 hostlist[0].name = mb->host;
326 if(mb->orighost[0] && strucmp(mb->host, mb->orighost)){
327 hostlist[0].next = &hostlist[1];
328 hostlist[1].name = mb->orighost;
329 hostlist[1].next = NULL;
331 else
332 hostlist[0].next = NULL;
334 /* try last working password associated with this host. */
335 if(!imap_get_passwd(mm_login_list, passwd, user, hostlist, (mb->sslflag || mb->tlsflag))){
336 peNoPassword = 1;
337 user[0] = pwd[0] = '\0';
338 *passwd = pwd;
344 /*----------------------------------------------------------------------
345 Receive notification of an error writing to disk
347 Args: stream -- The stream the error occurred on
348 errcode -- The system error code (errno)
349 serious -- Flag indicating error is serious (mail may be lost)
351 Result: If error is non serious, the stream is marked as having an error
352 and deletes are disallowed until error clears
353 If error is serious this returns with syslogging if possible
354 ----*/
355 long
356 mm_diskerror (MAILSTREAM *stream, long errcode, long serious)
358 if(!serious && stream == ps_global->mail_stream) {
359 sp_set_io_error_on_stream(ps_global->mail_stream, 1);
362 dprint((SYSDBG_ERR, "mm_diskerror: mailbox: %s, errcode: %ld, serious: %ld\n",
363 (stream && stream->mailbox) ? stream->mailbox : "", errcode, serious));
365 return(1);
370 * alpine_tcptimeout - C-client callback to handle tcp-related timeouts.
372 long
373 alpine_tcptimeout(long elapsed, long sincelast)
375 long rv = 1L; /* keep trying by default */
377 dprint((SYSDBG_INFO, "tcptimeout: waited %s seconds\n", long2string(elapsed)));
379 if(elapsed > WP_TCP_TIMEOUT){
380 dprint((SYSDBG_ERR, "tcptimeout: BAIL after %s seconds\n", long2string(elapsed)));
381 rv = 0L;
384 return(rv);
389 * C-client callback to handle SSL/TLS certificate validation failures
391 * Returning 0 means error becomes fatal
392 * Non-zero means certificate problem is ignored and SSL session is
393 * established
395 * We remember the answer and won't re-ask for subsequent open attempts to
396 * the same hostname.
398 long
399 alpine_sslcertquery(char *reason, char *host, char *cert)
401 STRLIST_S *p;
403 for(p = peCertHosts; p; p = p->next)
404 if(!strucmp(p->name, host))
405 return(1);
407 peCertQuery = 1;
408 snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
409 q_status_message(SM_ORDER, 0, 3, "SSL Certificate Problem");
410 dprint((SYSDBG_INFO, "sslcertificatequery: host=%s reason=%s cert=%s\n",
411 host ? host : "?", reason ? reason : "?",
412 cert ? cert : "?"));
413 return(0);
418 * C-client callback to handle SSL/TLS certificate validation failures
420 void
421 alpine_sslfailure(char *host, char *reason, unsigned long flags)
423 peCertFailure = 1;
424 snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
425 q_status_message1(SM_ORDER, 0, 3, "SSL Certificate Failure: %s", reason ? reason : "?");
426 dprint((SYSDBG_INFO, "SSL Invalid Cert (%s) : %s", host, reason));
431 /*----------------------------------------------------------------------
432 This can be used to prevent the flickering of the check_cue char
433 caused by numerous (5000+) fetches by c-client. Right now, the only
434 practical use found is newsgroup subsciption.
436 check_cue_display will check if this global is set, and won't clear
437 the check_cue_char if set.
438 ----*/
439 void
440 set_read_predicted(int i)
444 /*----------------------------------------------------------------------
445 Exported method to display status of mail check
447 Args: putstr -- should be NO LONGER THAN 2 bytes
449 Result: putstr displayed at upper-left-hand corner of screen
450 ----*/
451 void
452 check_cue_display(char *putstr)
458 void
459 alpine_set_passwd(char *user, char *passwd, char *host, int altflag)
461 STRLIST_S hostlist[1];
463 hostlist[0].name = host;
464 hostlist[0].next = NULL;
466 imap_set_passwd(&mm_login_list, passwd, user, hostlist, altflag, 1, 0);
470 void
471 alpine_clear_passwd(char *user, char *host)
473 MMLOGIN_S **lp, *l;
474 STRLIST_S hostlist[1];
476 hostlist[0].name = host;
477 hostlist[0].next = NULL;
479 for(lp = &mm_login_list; *lp; lp = &(*lp)->next)
480 if(imap_same_host((*lp)->hosts, hostlist)
481 && (!*user || !strcmp(user, (*lp)->user))){
482 l = *lp;
483 *lp = (*lp)->next;
485 if(l->user)
486 fs_give((void **) &l->user);
488 free_strlist(&l->hosts);
490 if(l->passwd){
491 char *p = l->passwd;
493 while(*p)
494 *p++ = '\0';
497 fs_give((void **) &l);
499 break;
505 alpine_have_passwd(char *user, char *host, int altflag)
507 STRLIST_S hostlist[1];
509 hostlist[0].name = host;
510 hostlist[0].next = NULL;
512 return(imap_get_passwd(mm_login_list, NULL, user, hostlist, altflag));
516 char *
517 alpine_get_user(char *host)
519 STRLIST_S hostlist[1];
521 hostlist[0].name = host;
522 hostlist[0].next = NULL;
524 return(imap_get_user(mm_login_list, hostlist));