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
4 
5 /* ========================================================================
6  * Copyright 2006-2007 University of Washington
7  *
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
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * ========================================================================
15  */
16 
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
23 
24  ====*/
25 
26 #include <system.h>
27 #include <general.h>
28 
29 #include "../../../c-client/c-client.h"
30 
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"
38 
39 #include "debug.h"
40 #include "alpined.h"
41 
42 
43 
44 /*
45  * Internal prototypes
46  */
47 long  imap_seq_exec(MAILSTREAM *, char *,long (*)(MAILSTREAM *, long, void *), void *);
48 long  imap_seq_exec_append(MAILSTREAM *, long, void *);
49 
50 
51 /*
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.
55  */
56 //MAILSTREAM *mm_search_stream;
57 
58 //MM_LIST_S  *mm_list_info;
59 
60 
61 
62 /*----------------------------------------------------------------------
63       Queue imap log message for display in the message line
64 
65    Args: string -- The message
66          errflg -- flag set to 1 if pertains to an error
67 
68  Result: Message queued for display
69 
70  The c-client/imap reports most of it's status and errors here
71   ---*/
72 void
mm_log(char * string,long errflg)73 mm_log(char *string, long errflg)
74 {
75     char        message[300];
76     char       *occurance;
77     int         was_capitalized;
78     time_t      now;
79     struct tm  *tm_now;
80 
81     if(errflg == ERROR){
82 	dprint((SYSDBG_ERR, "%.*s (%ld)", 128, string, errflg));
83     }
84 
85     now = time((time_t *)0);
86     tm_now = localtime(&now);
87 
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));
100 
101     if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
102 	ps_global->try_to_create = 1;
103 	return;
104     }
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"))))
108       /*
109        * Don't display if creating new folder OR
110        * warning about a dead stream ...
111        */
112       return;
113 
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');
124 	}
125 	else
126 	  occurance += 7;
127 
128         occurance = srchstr(occurance, "mailbox");
129     }
130 
131     if(errflg == ERROR)
132       ps_global->mm_log_error = 1;
133 
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';
137     }
138 
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 */
143 
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';
150     }
151 }
152 
153 
154 
155 /*----------------------------------------------------------------------
156          receive notification from IMAP
157 
158   Args: stream  --  Mail stream message is relevant to
159         string  --  The message text
160         errflag --  Set if it is a serious error
161 
162  Result: message displayed in status line
163 
164  The facility is for general notices, such as connection to server;
165  server shutting down etc... It is used infrequently.
166   ----------------------------------------------------------------------*/
167 void
mm_notify(MAILSTREAM * stream,char * string,long errflag)168 mm_notify(MAILSTREAM *stream, char *string, long errflag)
169 {
170     if(errflag == ERROR){
171 	dprint((SYSDBG_ERR, "mm_notify: %s (%ld)", string, errflag));
172     }
173 
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
188 
189     strncpy(ps_global->last_error, string, 500);
190     ps_global->last_error[499] = '\0';
191 
192     /*
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)
196      */
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);
203 	}
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);
209 	}
210     }
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;
221 
222 	for(p = string + 8; isdigit(*p); p++)
223 	  n = (n * 10) + (*p - '0');
224 
225 	sp_set_first_unseen(ps_global->mail_stream, n);
226     }
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);
236 }
237 
238 void
mm_login_method_work(NETMBX * mb,char * user,void * login,long trial,char * method,char * usethisprompt,char * altuserforcache)239 mm_login_method_work (NETMBX *mb,char *user,void *login,long trial, char *method, char *usethisprompt, char *altuserforcache)
240 {
241 
242 }
243 
244 /*----------------------------------------------------------------------
245       Do work of getting login and password from user for IMAP login
246 
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
251 
252  Result: username and password passed back to imap
253   ----*/
254 void
mm_login_work(NETMBX * mb,char * user,char ** passwd,long trial,char * usethisprompt,char * altuserforcache)255 mm_login_work(NETMBX *mb, char *user, char **passwd, long trial, char *usethisprompt, char *altuserforcache)
256 {
257     STRLIST_S hostlist[2];
258     NETMBX    cmb;
259     int	      l;
260     char *pwd = *passwd;
261 
262     pwd[0] = '\0';
263 
264     if((l = strlen(mb->orighost)) > 0 && l < CRED_REQ_SIZE)
265       strcpy(peCredentialRequestor, mb->orighost);
266 
267     if(trial){			/* one shot only! */
268 	user[0] = '\0';
269 	peCredentialError = 1;
270 	*passwd = pwd;
271 	return;
272     }
273 
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);
281 	}
282 	else
283 	  user[0] = pwd[0] = '\0';
284 
285 	*passwd = pwd;
286         return;
287     }
288 #endif
289 
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;
296     }
297 #endif
298 
299     /*
300      * heavily paranoid about offering password to server
301      * that the users hasn't also indicated the remote user
302      * name
303      */
304     if(*mb->user){
305 	strcpy(user, mb->user);
306     }
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);
312     }
313     else{
314 	/*
315 	 * don't blindly offer user/pass
316 	 */
317 	user[0] = pwd[0] = '\0';
318 	*passwd = pwd;
319         return;
320     }
321 
322     /*
323      * set up host list for sybil servers...
324      */
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;
330     }
331     else
332       hostlist[0].next = NULL;
333 
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;
339     }
340 }
341 
342 
343 
344 /*----------------------------------------------------------------------
345        Receive notification of an error writing to disk
346 
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)
350 
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
mm_diskerror(MAILSTREAM * stream,long errcode,long serious)356 mm_diskerror (MAILSTREAM *stream, long errcode, long serious)
357 {
358     if(!serious && stream == ps_global->mail_stream) {
359 	sp_set_io_error_on_stream(ps_global->mail_stream, 1);
360     }
361 
362     dprint((SYSDBG_ERR, "mm_diskerror: mailbox: %s, errcode: %ld, serious: %ld\n",
363 	    (stream && stream->mailbox) ? stream->mailbox : "", errcode, serious));
364 
365     return(1);
366 }
367 
368 
369 /*
370  * alpine_tcptimeout - C-client callback to handle tcp-related timeouts.
371  */
372 long
alpine_tcptimeout(long elapsed,long sincelast)373 alpine_tcptimeout(long elapsed, long sincelast)
374 {
375     long rv = 1L;			/* keep trying by default */
376 
377     dprint((SYSDBG_INFO, "tcptimeout: waited %s seconds\n", long2string(elapsed)));
378 
379     if(elapsed > WP_TCP_TIMEOUT){
380 	dprint((SYSDBG_ERR, "tcptimeout: BAIL after %s seconds\n", long2string(elapsed)));
381 	rv = 0L;
382     }
383 
384     return(rv);
385 }
386 
387 
388 /*
389  * C-client callback to handle SSL/TLS certificate validation failures
390  *
391  * Returning 0 means error becomes fatal
392  *           Non-zero means certificate problem is ignored and SSL session is
393  *             established
394  *
395  *  We remember the answer and won't re-ask for subsequent open attempts to
396  *  the same hostname.
397  */
398 long
alpine_sslcertquery(char * reason,char * host,char * cert)399 alpine_sslcertquery(char *reason, char *host, char *cert)
400 {
401     STRLIST_S *p;
402 
403     for(p = peCertHosts; p; p = p->next)
404       if(!strucmp(p->name, host))
405 	return(1);
406 
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);
414 }
415 
416 
417 /*
418  * C-client callback to handle SSL/TLS certificate validation failures
419  */
420 void
alpine_sslfailure(char * host,char * reason,unsigned long flags)421 alpine_sslfailure(char *host, char *reason, unsigned long flags)
422 {
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));
427 }
428 
429 
430 
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.
435 
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
set_read_predicted(int i)440 set_read_predicted(int i)
441 {
442 }
443 
444 /*----------------------------------------------------------------------
445    Exported method to display status of mail check
446 
447    Args: putstr -- should be NO LONGER THAN 2 bytes
448 
449  Result: putstr displayed at upper-left-hand corner of screen
450   ----*/
451 void
check_cue_display(char * putstr)452 check_cue_display(char *putstr)
453 {
454 }
455 
456 
457 
458 void
alpine_set_passwd(char * user,char * passwd,char * host,int altflag)459 alpine_set_passwd(char *user, char *passwd, char *host, int altflag)
460 {
461     STRLIST_S hostlist[1];
462 
463     hostlist[0].name = host;
464     hostlist[0].next = NULL;
465 
466     imap_set_passwd(&mm_login_list, passwd, user, hostlist, altflag, 1, 0);
467 }
468 
469 
470 void
alpine_clear_passwd(char * user,char * host)471 alpine_clear_passwd(char *user, char *host)
472 {
473     MMLOGIN_S **lp, *l;
474     STRLIST_S  hostlist[1];
475 
476     hostlist[0].name = host;
477     hostlist[0].next = NULL;
478 
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;
484 
485 	  if(l->user)
486 	    fs_give((void **) &l->user);
487 
488 	  free_strlist(&l->hosts);
489 
490 	  if(l->passwd){
491 	      char *p = l->passwd;
492 
493 	      while(*p)
494 		*p++ = '\0';
495 	  }
496 
497 	  fs_give((void **) &l);
498 
499 	  break;
500       }
501 }
502 
503 
504 int
alpine_have_passwd(char * user,char * host,int altflag)505 alpine_have_passwd(char *user, char *host, int altflag)
506 {
507     STRLIST_S hostlist[1];
508 
509     hostlist[0].name = host;
510     hostlist[0].next = NULL;
511 
512     return(imap_get_passwd(mm_login_list, NULL, user, hostlist, altflag));
513 }
514 
515 
516 char *
alpine_get_user(char * host)517 alpine_get_user(char *host)
518 {
519     STRLIST_S hostlist[1];
520 
521     hostlist[0].name = host;
522     hostlist[0].next = NULL;
523 
524     return(imap_get_user(mm_login_list, hostlist));
525 }
526