~ubuntu-branches/debian/stretch/alpine/stretch

« back to all changes in this revision

Viewing changes to imap/src/ipopd/ipop3d.c

  • Committer: Bazaar Package Importer
  • Author(s): Asheesh Laroia
  • Date: 2007-02-17 13:17:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070217131742-99x5c6cpg1pbkdhw
Tags: upstream-0.82+dfsg
ImportĀ upstreamĀ versionĀ 0.82+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ========================================================================
 
2
 * Copyright 1988-2006 University of Washington
 
3
 *
 
4
 * Licensed under the Apache License, Version 2.0 (the "License");
 
5
 * you may not use this file except in compliance with the License.
 
6
 * You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * 
 
11
 * ========================================================================
 
12
 */
 
13
 
 
14
/*
 
15
 * Program:     IPOP3D - IMAP to POP3 conversion server
 
16
 *
 
17
 * Author:      Mark Crispin
 
18
 *              Networks and Distributed Computing
 
19
 *              Computing & Communications
 
20
 *              University of Washington
 
21
 *              Administration Building, AG-44
 
22
 *              Seattle, WA  98195
 
23
 *              Internet: MRC@CAC.Washington.EDU
 
24
 *
 
25
 * Date:        1 November 1990
 
26
 * Last Edited: 18 December 2006
 
27
 */
 
28
 
 
29
/* Parameter files */
 
30
 
 
31
#include <stdio.h>
 
32
#include <ctype.h>
 
33
#include <errno.h>
 
34
extern int errno;               /* just in case */
 
35
#include <signal.h>
 
36
#include <time.h>
 
37
#include "c-client.h"
 
38
 
 
39
 
 
40
#define CRLF PSOUT ("\015\012") /* primary output terpri */
 
41
 
 
42
 
 
43
/* Autologout timer */
 
44
#define KODTIMEOUT 60*5
 
45
#define LOGINTIMEOUT 60*3
 
46
#define TIMEOUT 60*10
 
47
 
 
48
 
 
49
/* Size of temporary buffers */
 
50
#define TMPLEN 1024
 
51
 
 
52
 
 
53
/* Server states */
 
54
 
 
55
#define AUTHORIZATION 0
 
56
#define TRANSACTION 1
 
57
#define UPDATE 2
 
58
#define LOGOUT 3
 
59
 
 
60
/* Eudora food */
 
61
 
 
62
#define STATUS "Status: %s%s\015\012"
 
63
#define SLEN (sizeof (STATUS)-3)
 
64
 
 
65
 
 
66
/* Global storage */
 
67
 
 
68
char *version = "2006e.96";     /* server version */
 
69
short state = AUTHORIZATION;    /* server state */
 
70
short critical = NIL;           /* non-zero if in critical code */
 
71
MAILSTREAM *stream = NIL;       /* mailbox stream */
 
72
time_t idletime = 0;            /* time we went idle */
 
73
unsigned long nmsgs = 0;        /* current number of messages */
 
74
unsigned long ndele = 0;        /* number of deletes */
 
75
unsigned long last = 0;         /* highest message accessed */
 
76
unsigned long il = 0;           /* initial last message */
 
77
char challenge[128];            /* challenge */
 
78
char *host = NIL;               /* remote host name */
 
79
char *user = NIL;               /* user name */
 
80
char *pass = NIL;               /* password */
 
81
char *initial = NIL;            /* initial response */
 
82
long *msg = NIL;                /* message translation vector */
 
83
char *logout = "Logout";
 
84
char *goodbye = "+OK Sayonara\015\012";
 
85
 
 
86
 
 
87
/* Function prototypes */
 
88
 
 
89
int main (int argc,char *argv[]);
 
90
void sayonara (int status);
 
91
void clkint ();
 
92
void kodint ();
 
93
void hupint ();
 
94
void trmint ();
 
95
int pass_login (char *t,int argc,char *argv[]);
 
96
char *apop_login (char *chal,char *user,char *md5,int argc,char *argv[]);
 
97
char *responder (void *challenge,unsigned long clen,unsigned long *rlen);
 
98
int mbxopen (char *mailbox);
 
99
long blat (char *text,long lines,unsigned long size,STRING *st);
 
100
void rset ();
 
101
 
 
102
/* Main program */
 
103
 
 
104
int main (int argc,char *argv[])
 
105
{
 
106
  unsigned long i,j,k;
 
107
  char *s,*t;
 
108
  char tmp[TMPLEN];
 
109
  time_t autologouttime;
 
110
  char *pgmname = (argc && argv[0]) ?
 
111
    (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
 
112
     s+1 : argv[0]) : "ipop3d";
 
113
                                /* set service name before linkage */
 
114
  mail_parameters (NIL,SET_SERVICENAME,(void *) "pop");
 
115
#include "linkage.c"
 
116
                                /* initialize server */
 
117
  server_init (pgmname,"pop3","pop3s",clkint,kodint,hupint,trmint);
 
118
  {                             /* set up MD5 challenge */
 
119
    AUTHENTICATOR *auth = mail_lookup_auth (1);
 
120
    while (auth && compare_cstring (auth->name,"CRAM-MD5")) auth = auth->next;
 
121
                                /* build challenge -- less than 128 chars */
 
122
    if (auth && auth->server && !(auth->flags & AU_DISABLE))
 
123
      sprintf (challenge,"<%lx.%lx@%.64s>",(unsigned long) getpid (),
 
124
               (unsigned long) time (0),tcp_serverhost ());
 
125
    else challenge[0] = '\0';   /* no MD5 authentication */
 
126
  }
 
127
  /* There are reports of POP3 clients which get upset if anything appears
 
128
   * between the "+OK" and the "POP3" in the greeting.
 
129
   */
 
130
  PSOUT ("+OK POP3 ");
 
131
  if (!challenge[0]) {          /* if no MD5 enable, output host name */
 
132
    PSOUT (tcp_serverhost ());
 
133
    PBOUT (' ');
 
134
  }
 
135
  PSOUT (version);
 
136
  PSOUT (" server ready");
 
137
  if (challenge[0]) {           /* if MD5 enable, output challenge here */
 
138
    PBOUT (' ');
 
139
    PSOUT (challenge);
 
140
  }
 
141
  CRLF;
 
142
  PFLUSH ();                    /* dump output buffer */
 
143
  autologouttime = time (0) + LOGINTIMEOUT;
 
144
                                /* command processing loop */
 
145
  while ((state != UPDATE) && (state != LOGOUT)) {
 
146
    idletime = time (0);        /* get a command under timeout */
 
147
    alarm ((state == TRANSACTION) ? TIMEOUT : LOGINTIMEOUT);
 
148
    clearerr (stdin);           /* clear stdin errors */
 
149
    while (!PSIN (tmp,TMPLEN)){ /* read command line */
 
150
                                /* ignore if some interrupt */
 
151
      if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
 
152
      else {
 
153
        char *e = ferror (stdin) ?
 
154
          strerror (errno) : "Command stream end of file";
 
155
        alarm (0);              /* disable all interrupts */
 
156
        server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
157
        sprintf (logout = tmp,"%.80s, while reading line",strerror (errno));
 
158
        goodbye = NIL;
 
159
        rset ();                /* try to gracefully close the stream */
 
160
        if (state == TRANSACTION) mail_close (stream);
 
161
        stream = NIL;
 
162
        state = LOGOUT;
 
163
        sayonara (1);
 
164
      }
 
165
    }
 
166
    alarm (0);                  /* make sure timeout disabled */
 
167
    idletime = 0;               /* no longer idle */
 
168
 
 
169
    if (!strchr (tmp,'\012'))   /* find end of line */
 
170
      PSOUT ("-ERR Command line too long\015\012");
 
171
    else if (!(s = strtok (tmp," \015\012")))
 
172
      PSOUT ("-ERR Null command\015\012");
 
173
    else {                      /* dispatch based on command */
 
174
      ucase (s);                /* canonicalize case */
 
175
                                /* snarf argument */
 
176
      t = strtok (NIL,"\015\012");
 
177
                                /* QUIT command always valid */
 
178
      if (!strcmp (s,"QUIT")) state = UPDATE;
 
179
      else if (!strcmp (s,"CAPA")) {
 
180
        AUTHENTICATOR *auth;
 
181
        PSOUT ("+OK Capability list follows:\015\012");
 
182
        PSOUT ("TOP\015\012LOGIN-DELAY 180\015\012UIDL\015\012");
 
183
        if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
 
184
        else PSOUT ("STLS\015\012");
 
185
        if (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL))
 
186
          PSOUT ("USER\015\012");
 
187
                                /* display secure server authenticators */
 
188
        for (auth = mail_lookup_auth (1), s = "SASL"; auth; auth = auth->next)
 
189
          if (auth->server && !(auth->flags & AU_DISABLE) &&
 
190
              !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
 
191
            if (s) {
 
192
              PSOUT (s);
 
193
              s = NIL;
 
194
            }
 
195
            PBOUT (' ');
 
196
            PSOUT (auth->name);
 
197
          }
 
198
        PSOUT (s ? ".\015\012" : "\015\012.\015\012");
 
199
      }
 
200
 
 
201
      else switch (state) {     /* else dispatch based on state */
 
202
      case AUTHORIZATION:       /* waiting to get logged in */
 
203
        if (!strcmp (s,"AUTH")) {
 
204
          if (t && *t) {        /* mechanism given? */
 
205
            if (host) fs_give ((void **) &host);
 
206
            if (user) fs_give ((void **) &user);
 
207
            if (pass) fs_give ((void **) &pass);
 
208
            s = strtok (t," "); /* get mechanism name */
 
209
                                /* get initial response */
 
210
            initial = strtok (NIL,"\015\012");
 
211
            if (!(user = cpystr (mail_auth (s,responder,argc,argv)))) {
 
212
              PSOUT ("-ERR Bad authentication\015\012");
 
213
              syslog (LOG_INFO,"AUTHENTICATE %s failure host=%.80s",s,
 
214
                      tcp_clienthost ());
 
215
            }
 
216
            else if ((state = mbxopen ("INBOX")) == TRANSACTION)
 
217
              syslog (LOG_INFO,"Auth user=%.80s host=%.80s nmsgs=%ld/%ld",
 
218
                      user,tcp_clienthost (),nmsgs,stream->nmsgs);
 
219
            else syslog (LOG_INFO,"Auth user=%.80s host=%.80s no mailbox",
 
220
                         user,tcp_clienthost ());
 
221
          }
 
222
          else {
 
223
            AUTHENTICATOR *auth;
 
224
            PSOUT ("+OK Supported authentication mechanisms:\015\012");
 
225
            i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
 
226
            for (auth = mail_lookup_auth (1); auth; auth = auth->next)
 
227
              if (auth->server && !(auth->flags & AU_DISABLE) &&
 
228
                  !(auth->flags & AU_HIDE) &&
 
229
                  (i || (auth->flags & AU_SECURE))) {
 
230
                PSOUT (auth->name);
 
231
                CRLF;
 
232
              }
 
233
            PBOUT ('.');
 
234
            CRLF;
 
235
          }
 
236
        }
 
237
 
 
238
        else if (!strcmp (s,"APOP")) {
 
239
          if (challenge[0]) {   /* can do it if have an MD5 challenge */
 
240
            if (host) fs_give ((void **) &host);
 
241
            if (user) fs_give ((void **) &user);
 
242
            if (pass) fs_give ((void **) &pass);
 
243
                                /* get user name */
 
244
            if (!(t && *t && (s = strtok (t," ")) && (t = strtok(NIL,"\012"))))
 
245
              PSOUT ("-ERR Missing APOP argument\015\012");
 
246
            else if (!(user = apop_login (challenge,s,t,argc,argv)))
 
247
              PSOUT ("-ERR Bad APOP\015\012");
 
248
            else if ((state = mbxopen ("INBOX")) == TRANSACTION)
 
249
              syslog (LOG_INFO,"APOP user=%.80s host=%.80s nmsgs=%ld/%ld",
 
250
                      user,tcp_clienthost (),nmsgs,stream->nmsgs);
 
251
            else syslog (LOG_INFO,"APOP user=%.80s host=%.80s no mailbox",
 
252
                         user,tcp_clienthost ());
 
253
          }
 
254
          else PSOUT ("-ERR Not supported\015\012");
 
255
        }
 
256
                                /* (chuckle) */
 
257
        else if (!strcmp (s,"RPOP"))
 
258
          PSOUT ("-ERR Nice try, bunkie\015\012");
 
259
        else if (!strcmp (s,"STLS")) {
 
260
          if (t = ssl_start_tls (pgmname)) {
 
261
            PSOUT ("-ERR STLS failed: ");
 
262
            PSOUT (t);
 
263
            CRLF;
 
264
          }
 
265
          else PSOUT ("+OK STLS completed\015\012");
 
266
        }
 
267
        else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
 
268
                 !strcmp (s,"USER")) {
 
269
          if (host) fs_give ((void **) &host);
 
270
          if (user) fs_give ((void **) &user);
 
271
          if (pass) fs_give ((void **) &pass);
 
272
          if (t && *t) {        /* if user name given */
 
273
                                /* skip leading whitespace (bogus clients!) */
 
274
            while (*t == ' ') ++t;
 
275
                                /* remote user name? */
 
276
            if (s = strchr (t,':')) {
 
277
              *s++ = '\0';      /* tie off host name */
 
278
              host = cpystr (t);/* copy host name */
 
279
              user = cpystr (s);/* copy user name */
 
280
            }
 
281
                                /* local user name */
 
282
            else user = cpystr (t);
 
283
            PSOUT ("+OK User name accepted, password please\015\012");
 
284
          }
 
285
          else PSOUT ("-ERR Missing username argument\015\012");
 
286
        }
 
287
        else if (!mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL) &&
 
288
                 user && *user && !strcmp (s,"PASS"))
 
289
          state = pass_login (t,argc,argv);
 
290
        else PSOUT ("-ERR Unknown AUTHORIZATION state command\015\012");
 
291
        break;
 
292
 
 
293
      case TRANSACTION:         /* logged in */
 
294
        if (!strcmp (s,"STAT")) {
 
295
          for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
 
296
            if (msg[i] > 0) {   /* message still exists? */
 
297
              j++;              /* count one more undeleted message */
 
298
              k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
 
299
            }
 
300
          sprintf (tmp,"+OK %lu %lu\015\012",j,k);
 
301
          PSOUT (tmp);
 
302
        }
 
303
        else if (!strcmp (s,"LIST")) {
 
304
          if (t && *t) {        /* argument do single message */
 
305
            if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
 
306
              sprintf (tmp,"+OK %lu %lu\015\012",i,
 
307
                       mail_elt(stream,msg[i])->rfc822_size + SLEN);
 
308
              PSOUT (tmp);
 
309
            }
 
310
            else PSOUT ("-ERR No such message\015\012");
 
311
          }
 
312
          else {                /* entire mailbox */
 
313
            PSOUT ("+OK Mailbox scan listing follows\015\012");
 
314
            for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
 
315
              sprintf (tmp,"%lu %lu\015\012",i,
 
316
                       mail_elt (stream,msg[i])->rfc822_size + SLEN);
 
317
              PSOUT (tmp);
 
318
            }
 
319
            PBOUT ('.');        /* end of list */
 
320
            CRLF;
 
321
          }
 
322
        }
 
323
        else if (!strcmp (s,"UIDL")) {
 
324
          if (t && *t) {        /* argument do single message */
 
325
            if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
 
326
              sprintf (tmp,"+OK %lu %08lx%08lx\015\012",i,stream->uid_validity,
 
327
                       mail_uid (stream,msg[i]));
 
328
              PSOUT (tmp);
 
329
            }
 
330
            else PSOUT ("-ERR No such message\015\012");
 
331
          }
 
332
          else {                /* entire mailbox */
 
333
            PSOUT ("+OK Unique-ID listing follows\015\012");
 
334
            for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0) {
 
335
              sprintf (tmp,"%lu %08lx%08lx\015\012",i,stream->uid_validity,
 
336
                       mail_uid (stream,msg[i]));
 
337
              PSOUT (tmp);
 
338
            }
 
339
            PBOUT ('.');        /* end of list */
 
340
            CRLF;
 
341
          }
 
342
        }
 
343
 
 
344
        else if (!strcmp (s,"RETR")) {
 
345
          if (t && *t) {        /* must have an argument */
 
346
            if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
 
347
              MESSAGECACHE *elt;
 
348
                                /* update highest message accessed */
 
349
              if (i > last) last = i;
 
350
              sprintf (tmp,"+OK %lu octets\015\012",
 
351
                       (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
 
352
              PSOUT (tmp);
 
353
                                /* get header */
 
354
              t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
 
355
              blat (t,-1,k,NIL);/* write up to trailing CRLF */
 
356
                                /* build status */
 
357
              sprintf (tmp,STATUS,elt->seen ? "R" : " ",
 
358
                       elt->recent ? " " : "O");
 
359
              if (k < 4) CRLF;  /* don't write Status: if no header */
 
360
                                /* normal header ending with CRLF CRLF? */
 
361
              else if (t[k-3] == '\012') {
 
362
                PSOUT (tmp);    /* write status */
 
363
                CRLF;           /* then write second CRLF */
 
364
              }
 
365
              else {            /* abnormal - no blank line at end of header */
 
366
                CRLF;           /* write CRLF first then */
 
367
                PSOUT (tmp);
 
368
              }
 
369
                                /* output text */
 
370
              t = mail_fetch_text (stream,msg[i],NIL,&k,
 
371
                                   FT_RETURNSTRINGSTRUCT);
 
372
              if (k) {          /* only if there is a text body */
 
373
                blat (t,-1,k,&stream->private.string);
 
374
                CRLF;           /* end of list */
 
375
              }
 
376
              PBOUT ('.');
 
377
              CRLF;
 
378
            }
 
379
            else PSOUT ("-ERR No such message\015\012");
 
380
          }
 
381
          else PSOUT ("-ERR Missing message number argument\015\012");
 
382
        }
 
383
        else if (!strcmp (s,"DELE")) {
 
384
          if (t && *t) {        /* must have an argument */
 
385
            if ((i = strtoul (t,NIL,10)) && (i <= nmsgs) && (msg[i] > 0)) {
 
386
                                /* update highest message accessed */
 
387
              if (i > last) last = i;
 
388
                                /* delete message */
 
389
              sprintf (tmp,"%ld",msg[i]);
 
390
              mail_setflag (stream,tmp,"\\Deleted");
 
391
              msg[i] = -msg[i]; /* note that we deleted this message */
 
392
              PSOUT ("+OK Message deleted\015\012");
 
393
              ndele++;          /* one more message deleted */
 
394
            }
 
395
            else PSOUT ("-ERR No such message\015\012");
 
396
          }
 
397
          else PSOUT ("-ERR Missing message number argument\015\012");
 
398
        }
 
399
 
 
400
        else if (!strcmp (s,"NOOP"))
 
401
          PSOUT ("+OK No-op to you too!\015\012");
 
402
        else if (!strcmp (s,"LAST")) {
 
403
          sprintf (tmp,"+OK %lu\015\012",last);
 
404
          PSOUT (tmp);
 
405
        }
 
406
        else if (!strcmp (s,"RSET")) {
 
407
          rset ();              /* reset the mailbox */
 
408
          PSOUT ("+OK Reset state\015\012");
 
409
        }
 
410
        else if (!strcmp (s,"TOP")) {
 
411
          if (t && *t && (i =strtoul (t,&s,10)) && (i <= nmsgs) &&
 
412
              (msg[i] > 0)) {
 
413
                                /* skip whitespace */
 
414
            while (*s == ' ') s++;
 
415
                                /* make sure line count argument good */
 
416
            if ((*s >= '0') && (*s <= '9')) {
 
417
              MESSAGECACHE *elt = mail_elt (stream,msg[i]);
 
418
              j = strtoul (s,NIL,10);
 
419
                                /* update highest message accessed */
 
420
              if (i > last) last = i;
 
421
              PSOUT ("+OK Top of message follows\015\012");
 
422
                                /* get header */
 
423
              t = mail_fetch_header (stream,msg[i],NIL,NIL,&k,FT_PEEK);
 
424
              blat (t,-1,k,NIL);/* write up to trailing CRLF */
 
425
                                /* build status */
 
426
              sprintf (tmp,STATUS,elt->seen ? "R" : " ",
 
427
                       elt->recent ? " " : "O");
 
428
              if (k < 4) CRLF;  /* don't write Status: if no header */
 
429
                                /* normal header ending with CRLF CRLF? */
 
430
              else if (t[k-3] == '\012') {
 
431
                PSOUT (tmp);    /* write status */
 
432
                CRLF;           /* then write second CRLF */
 
433
              }
 
434
              else {            /* abnormal - no blank line at end of header */
 
435
                CRLF;           /* write CRLF first then */
 
436
                PSOUT (tmp);
 
437
              }
 
438
              if (j) {          /* want any text lines? */
 
439
                                /* output text */
 
440
                t = mail_fetch_text (stream,msg[i],NIL,&k,
 
441
                                     FT_PEEK | FT_RETURNSTRINGSTRUCT);
 
442
                                /* tie off final line if full text output */
 
443
                if (k && (j -= blat (t,j,k,&stream->private.string))) CRLF;
 
444
              }
 
445
              PBOUT ('.');      /* end of list */
 
446
              CRLF;
 
447
            }
 
448
            else PSOUT ("-ERR Bad line count argument\015\012");
 
449
          }
 
450
          else PSOUT ("-ERR Bad message number argument\015\012");
 
451
        }
 
452
        else if (!strcmp (s,"XTND"))
 
453
          PSOUT ("-ERR Sorry I can't do that\015\012");
 
454
        else PSOUT ("-ERR Unknown TRANSACTION state command\015\012");
 
455
        break;
 
456
      default:
 
457
        PSOUT ("-ERR Server in unknown state\015\012");
 
458
        break;
 
459
      }
 
460
    }
 
461
    PFLUSH ();                  /* make sure output finished */
 
462
    if (autologouttime) {       /* have an autologout in effect? */
 
463
                                /* cancel if no longer waiting for login */
 
464
      if (state != AUTHORIZATION) autologouttime = 0;
 
465
                                /* took too long to login */
 
466
      else if (autologouttime < time (0)) {
 
467
        goodbye = "-ERR Autologout\015\012";
 
468
        logout = "Autologout";
 
469
        state = LOGOUT;         /* sayonara */
 
470
      }
 
471
    }
 
472
  }
 
473
  if (stream && (state == UPDATE)) {
 
474
    mail_expunge (stream);
 
475
    syslog (LOG_INFO,"Update user=%.80s host=%.80s nmsgs=%ld ndele=%ld",
 
476
            user,tcp_clienthost (),stream->nmsgs,ndele);
 
477
    mail_close (stream);
 
478
  }
 
479
  sayonara (0);
 
480
  return 0;                     /* stupid compilers */
 
481
}
 
482
 
 
483
 
 
484
/* Say goodbye
 
485
 * Accepts: exit status
 
486
 *
 
487
 * Does not return
 
488
 */
 
489
 
 
490
void sayonara (int status)
 
491
{
 
492
  logouthook_t lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL);
 
493
  if (goodbye) {                /* have a goodbye message? */
 
494
    PSOUT (goodbye);
 
495
    PFLUSH ();                  /* make sure blatted */
 
496
  }
 
497
  syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
 
498
          user ? (char *) user : "???",tcp_clienthost ());
 
499
                                /* do logout hook if needed */
 
500
  if (lgoh) (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
 
501
  _exit (status);               /* all done */
 
502
}
 
503
 
 
504
/* Clock interrupt
 
505
 */
 
506
 
 
507
void clkint ()
 
508
{
 
509
  alarm (0);                    /* disable all interrupts */
 
510
  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
511
  goodbye = "-ERR Autologout; idle for too long\015\012";
 
512
  logout = "Autologout";
 
513
  if (critical) state = LOGOUT; /* badly hosed if in critical code */
 
514
  else {                        /* try to gracefully close the stream */
 
515
    if ((state == TRANSACTION) && !stream->lock) {
 
516
      rset ();
 
517
      mail_close (stream);
 
518
    }
 
519
    state = LOGOUT;
 
520
    stream = NIL;
 
521
    sayonara (1);
 
522
  }
 
523
}
 
524
 
 
525
 
 
526
/* Kiss Of Death interrupt
 
527
 */
 
528
 
 
529
void kodint ()
 
530
{
 
531
                                /* only if idle */
 
532
  if (idletime && ((time (0) - idletime) > KODTIMEOUT)) {
 
533
    alarm (0);                  /* disable all interrupts */
 
534
    server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
535
    goodbye = "-ERR Received Kiss of Death\015\012";
 
536
    logout = "Killed (lost mailbox lock)";
 
537
    if (critical) state =LOGOUT;/* must defer if in critical code */
 
538
    else {                      /* try to gracefully close the stream */
 
539
      if ((state == TRANSACTION) && !stream->lock) {
 
540
        rset ();
 
541
        mail_close (stream);
 
542
      }
 
543
      state = LOGOUT;
 
544
      stream = NIL;
 
545
      sayonara (1);             /* die die die */
 
546
    }
 
547
  }
 
548
}
 
549
 
 
550
 
 
551
/* Hangup interrupt
 
552
 */
 
553
 
 
554
void hupint ()
 
555
{
 
556
  alarm (0);                    /* disable all interrupts */
 
557
  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
558
  goodbye = NIL;                /* nobody left to talk to */
 
559
  logout = "Hangup";
 
560
  if (critical) state = LOGOUT; /* must defer if in critical code */
 
561
  else {                        /* try to gracefully close the stream */
 
562
    if ((state == TRANSACTION) && !stream->lock) {
 
563
      rset ();
 
564
      mail_close (stream);
 
565
    }
 
566
    state = LOGOUT;
 
567
    stream = NIL;
 
568
    sayonara (1);               /* die die die */
 
569
  }
 
570
}
 
571
 
 
572
 
 
573
/* Termination interrupt
 
574
 */
 
575
 
 
576
void trmint ()
 
577
{
 
578
  alarm (0);                    /* disable all interrupts */
 
579
  server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
580
  goodbye = "-ERR Killed\015\012";
 
581
  logout = "Killed";
 
582
  if (critical) state = LOGOUT; /* must defer if in critical code */
 
583
  /* Make no attempt at graceful closure since a shutdown may be in
 
584
   * progress, and we won't have any time to do mail_close() actions.
 
585
   */
 
586
  else sayonara (1);            /* die die die */
 
587
}
 
588
 
 
589
/* Parse PASS command
 
590
 * Accepts: pointer to command argument
 
591
 * Returns: new state
 
592
 */
 
593
 
 
594
int pass_login (char *t,int argc,char *argv[])
 
595
{
 
596
  char tmp[TMPLEN];
 
597
                                /* flush old passowrd */
 
598
  if (pass) fs_give ((void **) &pass);
 
599
  if (!(t && *t)) {             /* if no password given */
 
600
    PSOUT ("-ERR Missing password argument\015\012");
 
601
    return AUTHORIZATION;
 
602
  }
 
603
  pass = cpystr (t);            /* copy password argument */
 
604
  if (!host) {                  /* want remote mailbox? */
 
605
                                /* no, delimit user from possible admin */
 
606
    if (t = strchr (user,'*')) *t++ ='\0';
 
607
                                /* attempt the login */
 
608
    if (server_login (user,pass,t,argc,argv)) {
 
609
      int ret = mbxopen ("INBOX");
 
610
      if (ret == TRANSACTION)   /* mailbox opened OK? */
 
611
        syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s nmsgs=%ld/%ld",
 
612
                t ? "Admin " : "",user,tcp_clienthost (),nmsgs,stream->nmsgs);
 
613
      else syslog (LOG_INFO,"%sLogin user=%.80s host=%.80s no mailbox",
 
614
                   t ? "Admin " : "",user,tcp_clienthost ());
 
615
      return ret;
 
616
    }
 
617
  }
 
618
#ifndef DISABLE_POP_PROXY
 
619
                                /* remote; build remote INBOX */
 
620
  else if (anonymous_login (argc,argv)) {
 
621
    syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
 
622
            user,tcp_clienthost ());
 
623
    sprintf (tmp,"{%.128s/user=%.128s}INBOX",host,user);
 
624
                                /* disable rimap just in case */
 
625
    mail_parameters (NIL,SET_RSHTIMEOUT,0);
 
626
    return mbxopen (tmp);
 
627
  }
 
628
#endif
 
629
                                /* vague error message to confuse crackers */
 
630
  PSOUT ("-ERR Bad login\015\012");
 
631
  return AUTHORIZATION;
 
632
}
 
633
 
 
634
/* Authentication responder
 
635
 * Accepts: challenge
 
636
 *          length of challenge
 
637
 *          pointer to response length return location if non-NIL
 
638
 * Returns: response
 
639
 */
 
640
 
 
641
#define RESPBUFLEN 8*MAILTMPLEN
 
642
 
 
643
char *responder (void *challenge,unsigned long clen,unsigned long *rlen)
 
644
{
 
645
  unsigned long i,j;
 
646
  unsigned char *t,resp[RESPBUFLEN];
 
647
  char tmp[MAILTMPLEN];
 
648
  if (initial) {                /* initial response given? */
 
649
    if (clen) return NIL;       /* not permitted */
 
650
                                /* set up response */
 
651
    t = (unsigned char *) initial;
 
652
    initial = NIL;              /* no more initial response */
 
653
    return (char *) rfc822_base64 (t,strlen ((char *) t),rlen ? rlen : &i);
 
654
  }
 
655
  PSOUT ("+ ");
 
656
  for (t = rfc822_binary (challenge,clen,&i),j = 0; j < i; j++)
 
657
    if (t[j] > ' ') PBOUT (t[j]);
 
658
  fs_give ((void **) &t);
 
659
  CRLF;
 
660
  PFLUSH ();                    /* dump output buffer */
 
661
  resp[RESPBUFLEN-1] = '\0';    /* last buffer character is guaranteed NUL */
 
662
  alarm (LOGINTIMEOUT);         /* get a response under timeout */
 
663
  clearerr (stdin);             /* clear stdin errors */
 
664
                                /* read buffer */
 
665
  while (!PSIN ((char *) resp,RESPBUFLEN)) {
 
666
                                /* ignore if some interrupt */
 
667
    if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
 
668
    else {
 
669
      char *e = ferror (stdin) ?
 
670
        strerror (errno) : "Command stream end of file";
 
671
      alarm (0);                /* disable all interrupts */
 
672
      server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
673
      sprintf (logout = tmp,"%.80s, while reading authentication",e);
 
674
      goodbye = NIL;
 
675
      state = LOGOUT;
 
676
      sayonara (1);
 
677
    }
 
678
  }
 
679
  if (!(t = (unsigned char *) strchr ((char *) resp,'\012'))) {
 
680
    int c;
 
681
    while ((c = PBIN ()) != '\012') if (c == EOF) {
 
682
                                /* ignore if some interrupt */
 
683
      if (ferror (stdin) && (errno == EINTR)) clearerr (stdin);
 
684
      else {
 
685
        char *e = ferror (stdin) ?
 
686
          strerror (errno) : "Command stream end of file";
 
687
        alarm (0);              /* disable all interrupts */
 
688
        server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
689
        sprintf (logout = tmp,"%.80s, while reading auth char",e);
 
690
        goodbye = NIL;
 
691
        state = LOGOUT;
 
692
        sayonara (1);
 
693
      }
 
694
    }
 
695
    return NIL;
 
696
  }
 
697
  alarm (0);                    /* make sure timeout disabled */
 
698
  if (t[-1] == '\015') --t;     /* remove CR */
 
699
  *t = '\0';                    /* tie off buffer */
 
700
  return (resp[0] != '*') ?
 
701
    (char *) rfc822_base64 (resp,t-resp,rlen ? rlen : &i) : NIL;
 
702
}
 
703
 
 
704
/* Select mailbox
 
705
 * Accepts: mailbox name
 
706
 * Returns: new state
 
707
 */
 
708
 
 
709
int mbxopen (char *mailbox)
 
710
{
 
711
  unsigned long i,j;
 
712
  char tmp[TMPLEN];
 
713
  MESSAGECACHE *elt;
 
714
  nmsgs = 0;                    /* no messages yet */
 
715
  if (msg) fs_give ((void **) &msg);
 
716
                                /* open mailbox */
 
717
  if (stream = mail_open (stream,mailbox,NIL)) {
 
718
    if (!stream->rdonly) {      /* make sure not readonly */
 
719
      if (j = stream->nmsgs) {  /* if mailbox non-empty */
 
720
        sprintf (tmp,"1:%lu",j);/* fetch fast information for all messages */
 
721
        mail_fetch_fast (stream,tmp,NIL);
 
722
        msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long));
 
723
        for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) {
 
724
          msg[++nmsgs] = i;     /* note the presence of this message */
 
725
          if (elt->seen) il = last = nmsgs;
 
726
        }
 
727
      }
 
728
      sprintf (tmp,"+OK Mailbox open, %lu messages\015\012",nmsgs);
 
729
      PSOUT (tmp);
 
730
      return TRANSACTION;
 
731
    }
 
732
    else goodbye = "-ERR Can't get lock.  Mailbox in use\015\012";
 
733
  }
 
734
  else goodbye = "-ERR Unable to open user's INBOX\015\012";
 
735
  syslog (LOG_INFO,"Error opening or locking INBOX user=%.80s host=%.80s",
 
736
          user,tcp_clienthost ());
 
737
  return UPDATE;
 
738
}
 
739
 
 
740
/* Blat a string with dot checking
 
741
 * Accepts: string
 
742
 *          maximum number of lines if greater than zero
 
743
 *          maximum number of bytes to output
 
744
 *          alternative stringstruct
 
745
 * Returns: number of lines output
 
746
 *
 
747
 * This routine is uglier and kludgier than it should be, just to be robust
 
748
 * in the case of a message which doesn't end in a newline.  Yes, this routine
 
749
 * does truncate the last two bytes from the text.  Since it is normally a
 
750
 * newline and the main routine adds it back, it usually does not make a
 
751
 * difference.  But if it isn't, since the newline is required and the octet
 
752
 * counts have to match, there's no choice but to truncate.
 
753
 */
 
754
 
 
755
long blat (char *text,long lines,unsigned long size,STRING *st)
 
756
{
 
757
  char c,d,e;
 
758
  long ret = 0;
 
759
                                /* no-op if zero lines or empty string */
 
760
  if (!(lines && (size-- > 2))) return 0;
 
761
  if (text) {
 
762
    c = *text++; d = *text++;   /* collect first two bytes */
 
763
    if (c == '.') PBOUT ('.');  /* double string-leading dot if necessary */
 
764
    while (lines && --size) {   /* copy loop */
 
765
      e = *text++;              /* get next byte */
 
766
      PBOUT (c);                /* output character */
 
767
      if (c == '\012') {        /* end of line? */
 
768
        ret++; --lines;         /* count another line */
 
769
                                /* double leading dot as necessary */
 
770
        if (lines && size && (d == '.')) PBOUT ('.');
 
771
      }
 
772
      c = d; d = e;             /* move to next character */
 
773
    }
 
774
  }
 
775
  else {
 
776
    c = SNX (st); d = SNX (st); /* collect first two bytes */
 
777
    if (c == '.') PBOUT ('.');  /* double string-leading dot if necessary */
 
778
    while (lines && --size) {   /* copy loop */
 
779
      e = SNX (st);             /* get next byte */
 
780
      PBOUT (c);                /* output character */
 
781
      if (c == '\012') {        /* end of line? */
 
782
        ret++; --lines;         /* count another line */
 
783
                                /* double leading dot as necessary */
 
784
        if (lines && size && (d == '.')) PBOUT ('.');
 
785
      }
 
786
      c = d; d = e;             /* move to next character */
 
787
    }
 
788
  }
 
789
  return ret;
 
790
}
 
791
 
 
792
/* Reset mailbox
 
793
 */
 
794
 
 
795
void rset ()
 
796
{
 
797
  unsigned long i;
 
798
  char tmp[20];
 
799
  if (nmsgs) {                  /* undelete and unmark all of our messages */
 
800
    for (i = 1; i <= nmsgs; i++) { /*  */
 
801
      if (msg[i] < 0) {         /* ugly and inefficient, but trustworthy */
 
802
        sprintf (tmp,"%ld",msg[i] = -msg[i]);
 
803
        mail_clearflag (stream,tmp,i <= il ? "\\Deleted" : "\\Deleted \\Seen");
 
804
      }
 
805
      else if (i > il) {
 
806
        sprintf (tmp,"%ld",msg[i]);
 
807
        mail_clearflag (stream,tmp,"\\Seen");
 
808
      }
 
809
    }
 
810
    last = il;
 
811
  }
 
812
  ndele = 0;                    /* no more deleted messages */
 
813
}
 
814
 
 
815
/* Co-routines from MAIL library */
 
816
 
 
817
 
 
818
/* Message matches a search
 
819
 * Accepts: MAIL stream
 
820
 *          message number
 
821
 */
 
822
 
 
823
void mm_searched (MAILSTREAM *stream,unsigned long msgno)
 
824
{
 
825
  /* Never called */
 
826
}
 
827
 
 
828
 
 
829
/* Message exists (i.e. there are that many messages in the mailbox)
 
830
 * Accepts: MAIL stream
 
831
 *          message number
 
832
 */
 
833
 
 
834
void mm_exists (MAILSTREAM *stream,unsigned long number)
 
835
{
 
836
  /* Can't use this mechanism.  POP has no means of notifying the client of
 
837
     new mail during the session. */
 
838
}
 
839
 
 
840
 
 
841
/* Message expunged
 
842
 * Accepts: MAIL stream
 
843
 *          message number
 
844
 */
 
845
 
 
846
void mm_expunged (MAILSTREAM *stream,unsigned long number)
 
847
{
 
848
  unsigned long i = number + 1;
 
849
  msg[number] = 0;              /* I bet that this will annoy someone */
 
850
  while (i <= nmsgs) --msg[i++];
 
851
}
 
852
 
 
853
 
 
854
/* Message flag status change
 
855
 * Accepts: MAIL stream
 
856
 *          message number
 
857
 */
 
858
 
 
859
void mm_flags (MAILSTREAM *stream,unsigned long number)
 
860
{
 
861
  /* This isn't used */
 
862
}
 
863
 
 
864
 
 
865
/* Mailbox found
 
866
 * Accepts: MAIL stream
 
867
 *          hierarchy delimiter
 
868
 *          mailbox name
 
869
 *          mailbox attributes
 
870
 */
 
871
 
 
872
void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
 
873
{
 
874
  /* This isn't used */
 
875
}
 
876
 
 
877
 
 
878
/* Subscribe mailbox found
 
879
 * Accepts: MAIL stream
 
880
 *          hierarchy delimiter
 
881
 *          mailbox name
 
882
 *          mailbox attributes
 
883
 */
 
884
 
 
885
void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
 
886
{
 
887
  /* This isn't used */
 
888
}
 
889
 
 
890
 
 
891
/* Mailbox status
 
892
 * Accepts: MAIL stream
 
893
 *          mailbox name
 
894
 *          mailbox status
 
895
 */
 
896
 
 
897
void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
 
898
{
 
899
  /* This isn't used */
 
900
}
 
901
 
 
902
/* Notification event
 
903
 * Accepts: MAIL stream
 
904
 *          string to log
 
905
 *          error flag
 
906
 */
 
907
 
 
908
void mm_notify (MAILSTREAM *stream,char *string,long errflg)
 
909
{
 
910
  mm_log (string,errflg);       /* just do mm_log action */
 
911
}
 
912
 
 
913
 
 
914
/* Log an event for the user to see
 
915
 * Accepts: string to log
 
916
 *          error flag
 
917
 */
 
918
 
 
919
void mm_log (char *string,long errflg)
 
920
{
 
921
  switch (errflg) {
 
922
  case NIL:                     /* information message */
 
923
  case PARSE:                   /* parse glitch */
 
924
    break;                      /* too many of these to log */
 
925
  case WARN:                    /* warning */
 
926
    syslog (LOG_DEBUG,"%s",string);
 
927
    break;
 
928
  case BYE:                     /* driver broke connection */
 
929
    if (state != UPDATE) {
 
930
      char tmp[MAILTMPLEN];
 
931
      alarm (0);                /* disable all interrupts */
 
932
      server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
 
933
      sprintf (logout = tmp,"Mailbox closed (%.80s)",string);
 
934
      goodbye = NIL;
 
935
      state = LOGOUT;
 
936
      sayonara (1);
 
937
    }
 
938
    break;
 
939
  case ERROR:                   /* error that broke command */
 
940
  default:                      /* default should never happen */
 
941
    syslog (LOG_NOTICE,"%s",string);
 
942
    break;
 
943
  }
 
944
}    
 
945
 
 
946
 
 
947
/* Log an event to debugging telemetry
 
948
 * Accepts: string to log
 
949
 */
 
950
 
 
951
void mm_dlog (char *string)
 
952
{
 
953
  /* Not doing anything here for now */
 
954
}
 
955
 
 
956
 
 
957
/* Get user name and password for this host
 
958
 * Accepts: parse of network mailbox name
 
959
 *          where to return user name
 
960
 *          where to return password
 
961
 *          trial count
 
962
 */
 
963
 
 
964
void mm_login (NETMBX *mb,char *username,char *password,long trial)
 
965
{
 
966
                                /* set user name */
 
967
  strncpy (username,*mb->user ? mb->user : user,NETMAXUSER-1);
 
968
  if (pass) {
 
969
    strncpy (password,pass,255);/* and password */
 
970
    fs_give ((void **) &pass);
 
971
  }
 
972
  else memset (password,0,256); /* no password to send, abort login */
 
973
  username[NETMAXUSER] = password[255] = '\0';
 
974
}
 
975
 
 
976
/* About to enter critical code
 
977
 * Accepts: stream
 
978
 */
 
979
 
 
980
void mm_critical (MAILSTREAM *stream)
 
981
{
 
982
  ++critical;
 
983
}
 
984
 
 
985
 
 
986
/* About to exit critical code
 
987
 * Accepts: stream
 
988
 */
 
989
 
 
990
void mm_nocritical (MAILSTREAM *stream)
 
991
{
 
992
  --critical;
 
993
}
 
994
 
 
995
 
 
996
/* Disk error found
 
997
 * Accepts: stream
 
998
 *          system error code
 
999
 *          flag indicating that mailbox may be clobbered
 
1000
 * Returns: abort flag
 
1001
 */
 
1002
 
 
1003
long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
 
1004
{
 
1005
  if (serious) {                /* try your damnest if clobberage likely */
 
1006
    syslog (LOG_ALERT,
 
1007
            "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
 
1008
            user,tcp_clienthost (),
 
1009
            (stream && stream->mailbox) ? stream->mailbox : "???",
 
1010
            strerror (errcode));
 
1011
    alarm (0);                  /* make damn sure timeout disabled */
 
1012
    sleep (60);                 /* give it some time to clear up */
 
1013
    return NIL;
 
1014
  }
 
1015
  syslog (LOG_ALERT,"Fatal disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
 
1016
          user,tcp_clienthost (),
 
1017
          (stream && stream->mailbox) ? stream->mailbox : "???",
 
1018
          strerror (errcode));
 
1019
  return T;
 
1020
}
 
1021
 
 
1022
 
 
1023
/* Log a fatal error event
 
1024
 * Accepts: string to log
 
1025
 */
 
1026
 
 
1027
void mm_fatal (char *string)
 
1028
{
 
1029
  mm_log (string,ERROR);        /* shouldn't happen normally */
 
1030
}