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

« back to all changes in this revision

Viewing changes to imap/src/tmail/tmail.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:     Mail Delivery Module
 
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:        5 April 1993
 
26
 * Last Edited: 22 December 2006
 
27
 */
 
28
 
 
29
#include <stdio.h>
 
30
#include <pwd.h>
 
31
#include <errno.h>
 
32
extern int errno;               /* just in case */
 
33
#include <sysexits.h>
 
34
#include <sys/file.h>
 
35
#include <sys/stat.h>
 
36
#include "mail.h"
 
37
#include "osdep.h"
 
38
#include "misc.h"
 
39
#include "linkage.h"
 
40
 
 
41
 
 
42
/* Globals */
 
43
 
 
44
char *version = "2006e.16";     /* tmail release version */
 
45
int debug = NIL;                /* debugging (don't fork) */
 
46
int trycreate = NIL;            /* flag saying gotta create before appending */
 
47
int critical = NIL;             /* flag saying in critical code */
 
48
char *sender = NIL;             /* message origin */
 
49
char *inbox = NIL;              /* inbox file */
 
50
 
 
51
 
 
52
/* Function prototypes */
 
53
 
 
54
void file_string_init (STRING *s,void *data,unsigned long size);
 
55
char file_string_next (STRING *s);
 
56
void file_string_setpos (STRING *s,unsigned long i);
 
57
int main (int argc,char *argv[]);
 
58
int deliver (FILE *f,unsigned long msglen,char *user);
 
59
long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
 
60
int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
 
61
                    uid_t uid,char *tmp);
 
62
int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
 
63
int fail (char *string,int code);
 
64
char *getusername (char *s,char **t);
 
65
 
 
66
 
 
67
/* File string driver for file stringstructs */
 
68
 
 
69
STRINGDRIVER file_string = {
 
70
  file_string_init,             /* initialize string structure */
 
71
  file_string_next,             /* get next byte in string structure */
 
72
  file_string_setpos            /* set position in string structure */
 
73
};
 
74
 
 
75
 
 
76
/* Cache buffer for file stringstructs */
 
77
 
 
78
#define CHUNKLEN 16384
 
79
char chunk[CHUNKLEN];
 
80
 
 
81
/* Initialize file string structure for file stringstruct
 
82
 * Accepts: string structure
 
83
 *          pointer to string
 
84
 *          size of string
 
85
 */
 
86
 
 
87
void file_string_init (STRING *s,void *data,unsigned long size)
 
88
{
 
89
  s->data = data;               /* note fd */
 
90
  s->size = size;               /* note size */
 
91
  s->chunk = chunk;
 
92
  s->chunksize = (unsigned long) CHUNKLEN;
 
93
  SETPOS (s,0);                 /* set initial position */
 
94
}
 
95
 
 
96
 
 
97
/* Get next character from file stringstruct
 
98
 * Accepts: string structure
 
99
 * Returns: character, string structure chunk refreshed
 
100
 */
 
101
 
 
102
char file_string_next (STRING *s)
 
103
{
 
104
  char c = *s->curpos++;        /* get next byte */
 
105
  SETPOS (s,GETPOS (s));        /* move to next chunk */
 
106
  return c;                     /* return the byte */
 
107
}
 
108
 
 
109
 
 
110
/* Set string pointer position for file stringstruct
 
111
 * Accepts: string structure
 
112
 *          new position
 
113
 */
 
114
 
 
115
void file_string_setpos (STRING *s,unsigned long i)
 
116
{
 
117
  if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
 
118
  s->offset = i;                /* set new offset */
 
119
  s->curpos = s->chunk;         /* reset position */
 
120
                                /* set size of data */
 
121
  if (s->cursize = min (s->chunksize,SIZE (s))) {
 
122
                                /* move to that position in the file */
 
123
    fseek ((FILE *) s->data,s->offset,SEEK_SET);
 
124
    fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
 
125
  }
 
126
}
 
127
 
 
128
/* Main program */
 
129
 
 
130
int main (int argc,char *argv[])
 
131
{
 
132
  FILE *f = NIL;
 
133
  int pid,c,ret = 0;
 
134
  unsigned long msglen,status = 0;
 
135
  char *s,tmp[MAILTMPLEN];
 
136
  uid_t ruid = getuid ();
 
137
  struct passwd *pwd;
 
138
  openlog ("tmail",LOG_PID,LOG_MAIL);
 
139
#include "linkage.c"
 
140
                                /* make sure have some arguments */
 
141
  if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
 
142
                                /* process all flags */
 
143
  while (argc && (*(s = *++argv)) == '-') {
 
144
    argc--;                     /* gobble this argument */
 
145
    switch (s[1]) {             /* what is this flag? */
 
146
    case 'D':                   /* debug */
 
147
      debug = T;                /* don't fork */
 
148
      break;
 
149
    case 'd':                   /* obsolete flag meaning multiple users */
 
150
      break;
 
151
    case 'I':                   /* inbox specifier */
 
152
      if (argc--) inbox = cpystr (*++argv);
 
153
      else _exit (fail ("missing argument to -I",EX_USAGE));
 
154
      break;
 
155
    case 'f':                   /* new name for this flag */
 
156
    case 'r':                   /* flag giving return path */
 
157
      if (argc--) sender = cpystr (*++argv);
 
158
      else _exit (fail ("missing argument to -r",EX_USAGE));
 
159
      break;
 
160
    default:                    /* anything else */
 
161
      _exit (fail ("unknown switch",EX_USAGE));
 
162
    }
 
163
  }
 
164
 
 
165
  if (!argc) ret = fail ("no recipients",EX_USAGE);
 
166
  else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
 
167
  else {                        /* build delivery headers */
 
168
    if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
 
169
                                /* start Received line: */
 
170
    fprintf (f,"Received: via tmail-%s",version);
 
171
                                /* not root or daemon? */
 
172
    if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
 
173
      pwd = getpwuid (ruid);    /* get unprivileged user's information */
 
174
      if (inbox) {
 
175
        if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
 
176
        else sprintf (tmp,"UID %ld",(long) ruid);
 
177
        strcat (tmp," is not privileged to use -I");
 
178
        _exit (fail (tmp,EX_USAGE));
 
179
      }
 
180
      fputs (" (invoked by ",f);
 
181
      if (pwd) fprintf (f,"user %s",pwd->pw_name);
 
182
      else fprintf (f,"UID %ld",(long) ruid);
 
183
      fputs (")",f);
 
184
    }
 
185
                                /* write "for" if single recipient */
 
186
    if (argc == 1) fprintf (f," for %s",*argv);
 
187
    fputs ("; ",f);
 
188
    rfc822_date (tmp);
 
189
    fputs (tmp,f);
 
190
    fputs ("\015\012",f);
 
191
                                /* copy text from standard input */
 
192
    if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
 
193
        (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
 
194
    if (s[-1] == '\015') {      /* nuke leading "From " line */
 
195
      if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
 
196
          (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
 
197
      while ((c = getchar ()) != EOF) putc (c,f);
 
198
    }
 
199
    else {
 
200
      mm_log ("tmail called with LF-only newlines",WARN);
 
201
      if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
 
202
          (tmp[3] != 'm') || (tmp[4] != ' ')) {
 
203
        *s++ = '\015';          /* overwrite NL with CRLF */
 
204
        *s++ = '\012';
 
205
        *s = '\0';              /* tie off string */
 
206
        fputs (tmp,f);          /* write line */
 
207
      }
 
208
                                /* copy text from standard input */
 
209
      while ((c = getchar ()) != EOF) {
 
210
                                /* add CR if needed */
 
211
        if (c == '\012') putc ('\015',f);
 
212
        putc (c,f);
 
213
      }
 
214
    }
 
215
    msglen = ftell (f);         /* size of message */
 
216
    fflush (f);                 /* make sure all changes written out */
 
217
 
 
218
    if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
 
219
    else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
 
220
                                /* single delivery */
 
221
    else if (argc == 1) ret = deliver (f,msglen,*argv);
 
222
    else do {                   /* multiple delivery uses daughter forks */
 
223
      if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
 
224
      else if (pid) {           /* mother process */
 
225
        grim_pid_reap_status (pid,NIL,(void *) status);
 
226
                                /* normal termination? */
 
227
        if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
 
228
      }
 
229
                                /* daughter process */
 
230
      else _exit (deliver (f,msglen,*argv));
 
231
    } while (--argc && *argv++);
 
232
    mm_dlog (ret ? "error in delivery" : "all recipients delivered");
 
233
  }
 
234
  if (f) fclose (f);            /* all done with temporary file */
 
235
  _exit (ret);                  /* normal exit */
 
236
  return 0;                     /* stupid gcc */
 
237
}
 
238
 
 
239
/* Deliver message to recipient list
 
240
 * Accepts: file description of message temporary file
 
241
 *          size of message temporary file in bytes
 
242
 *          recipient name
 
243
 * Returns: NIL if success, else error code
 
244
 */
 
245
 
 
246
int deliver (FILE *f,unsigned long msglen,char *user)
 
247
{
 
248
  MAILSTREAM *ds = NIL;
 
249
  DRIVER *dv = NIL;
 
250
  char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
 
251
  struct passwd *pwd;
 
252
  STRING st;
 
253
  struct stat sbuf;
 
254
  uid_t duid;
 
255
  uid_t euid = geteuid ();
 
256
                                /* get user record */
 
257
  if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
 
258
    sprintf (tmp,"no such user as %.80s",user);
 
259
    return fail (tmp,EX_NOUSER);
 
260
  }
 
261
                                /* absurd is absurd */
 
262
  if (mailbox && (strlen (mailbox) > 256))
 
263
    return fail ("absurd folder name",EX_NOUSER);
 
264
                                /* big security hole if this is allowed */
 
265
  if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
 
266
                                /* log in as user if different than euid */
 
267
  if ((duid != euid) && !loginpw (pwd,1,&user)) {
 
268
    sprintf (tmp,"unable to log in UID %ld from UID %ld",
 
269
             (long) duid,(long) euid);
 
270
    return fail (tmp,EX_NOUSER);
 
271
  }
 
272
                                /* can't use pwd after this point */
 
273
  env_init (pwd->pw_name,pwd->pw_dir);
 
274
  sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
 
275
  mm_dlog (tmp);
 
276
                                /* prepare stringstruct */
 
277
  INIT (&st,file_string,(void *) f,msglen);
 
278
  if (mailbox) {                /* non-INBOX name */
 
279
    switch (mailbox[0]) {       /* make sure a valid name */
 
280
    default:                    /* other names, try to deliver if not INBOX */
 
281
      if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
 
282
          !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
 
283
          !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
 
284
    case '%': case '*':         /* wildcards not valid */
 
285
    case '#':                   /* namespace name not valid */
 
286
    case '/':                   /* absolute path names not valid */
 
287
    case '~':                   /* user names not valid */
 
288
      sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
 
289
      mm_log (tmp,WARN);
 
290
      break;
 
291
    }
 
292
    mm_dlog ("retrying delivery to INBOX");
 
293
    SETPOS (&st,0);             /* rewind stringstruct just in case */
 
294
  }
 
295
 
 
296
                                /* -I specified and not "-I INBOX"? */
 
297
  if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
 
298
                 ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
 
299
                 ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
 
300
                 ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
 
301
                 ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
 
302
                                /* "-I #driver.xxx/name"? */
 
303
    if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
 
304
        ((inbox[2] == 'r') || (inbox[2] == 'R')) &&
 
305
        ((inbox[3] == 'i') || (inbox[3] == 'I')) &&
 
306
        ((inbox[4] == 'v') || (inbox[4] == 'V')) &&
 
307
        ((inbox[5] == 'e') || (inbox[5] == 'E')) &&
 
308
        ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
 
309
        (s = strchr (inbox+8,'/'))) {
 
310
      *s = '\0';                /* temporarily tie off driver name */
 
311
      if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
 
312
            (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
 
313
            (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
 
314
        path[0] = '\0';         /* bad -I argument, no path resolved */
 
315
        sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
 
316
        mm_log (tmp,WARN);
 
317
      }
 
318
      *s = '/';                 /* restore delimiter */
 
319
    }
 
320
                                /* resolve "-I other" specification */
 
321
    else if (mailboxfile (path,inbox) && path[0]) {
 
322
                                /* resolution succeeded, impute driver */
 
323
      if (!strcmp (inbox,"mail.txt"))
 
324
        dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
 
325
      else if (!strcmp (inbox,"INBOX.MTX"))
 
326
        dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
 
327
      else if (!strcmp (inbox,"mbox"))
 
328
        dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
 
329
    }
 
330
    else {                      /* bad -I argument */
 
331
      path[0] = '\0';           /* no path resolved */
 
332
      sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
 
333
      mm_log (tmp,WARN);
 
334
    }
 
335
    if (*path) {                /* -I successfully resolved a path? */
 
336
      MAILSTREAM dpr;
 
337
      dpr.dtb = dv;
 
338
      if (dv) ds = &dpr;
 
339
                                /* supplicate to the Evil One if necessary */
 
340
      if (lstat (path,&sbuf) && !path_create (ds,path)) {
 
341
                                /* the Evil One rejected the plea */
 
342
        sprintf (tmp,"Unable to create %.80s, -I ignored",path);
 
343
        mm_log (tmp,WARN);
 
344
      }
 
345
                                /* now attempt delivery */
 
346
      else return deliver_safely (ds,&st,inbox,path,duid,tmp);
 
347
    }
 
348
  }
 
349
 
 
350
                                /* no -I, resolve "INBOX" into path */
 
351
  if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
 
352
                                /* clear box, get generic INBOX prototype */
 
353
    if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
 
354
      fatal ("no INBOX prototype");
 
355
                                /* standard system driver? */
 
356
    if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
 
357
      strcpy (path,sysinbox ());/* use system INBOX */
 
358
      if (!lstat (path,&sbuf))  /* deliver to existing system INBOX */
 
359
        return deliver_safely (ds,&st,mailbox,path,duid,tmp);
 
360
    }
 
361
    else {                      /* other driver, try ~/INBOX */
 
362
      if ((mailboxfile (path,"&&&&&") == path) &&
 
363
          (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
 
364
          !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
 
365
        sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
 
366
        return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
 
367
      }
 
368
    }
 
369
                                /* not dummy, deliver to driver imputed path */
 
370
    if (strcmp (ds->dtb->name,"dummy"))
 
371
      return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
 
372
        deliver_safely (ds,&st,mailbox,path,duid,tmp) :
 
373
          fail ("unable to resolve INBOX path",EX_CANTCREAT);
 
374
                                /* dummy, empty imputed append path exist? */
 
375
    if (ibxpath (ds = default_proto (T),&mailbox,path) &&
 
376
        !lstat (path,&sbuf) && !sbuf.st_size)
 
377
      return deliver_safely (ds,&st,mailbox,path,duid,tmp);
 
378
                                /* impute path that we will create */
 
379
    if (!ibxpath (ds = default_proto (NIL),&mailbox,path))
 
380
      return fail ("unable to resolve INBOX",EX_CANTCREAT);
 
381
  }
 
382
                                /* black box, must create, get create proto */
 
383
  else if (lstat (path,&sbuf)) ds = default_proto (NIL);
 
384
  else {                        /* black box, existing file */
 
385
                                /* empty file, get append prototype */
 
386
    if (!sbuf.st_size) ds = default_proto (T);
 
387
                                /* non-empty, get prototype from its data */
 
388
    else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
 
389
      fatal ("no INBOX prototype");
 
390
                                /* error if unknown format */
 
391
    if (!strcmp (ds->dtb->name,"phile"))
 
392
      return fail ("unknown format INBOX",EX_UNAVAILABLE);
 
393
                                /* otherwise can deliver to it */
 
394
    return deliver_safely (ds,&st,mailbox,path,duid,tmp);
 
395
  }
 
396
  sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
 
397
  mm_dlog (tmp);
 
398
                                /* supplicate to the Evil One */
 
399
  if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
 
400
  sprintf (tmp,"created %.80s",path);
 
401
  mm_dlog (tmp);
 
402
                                /* deliver the message */
 
403
  return deliver_safely (ds,&st,mailbox,path,duid,tmp);
 
404
}
 
405
 
 
406
/* Resolve INBOX from driver prototype into mailbox name and filesystem path
 
407
 * Accepts: driver prototype
 
408
 *          pointer to mailbox name string pointer
 
409
 *          buffer to return mailbox path
 
410
 * Returns: T if success, NIL if error
 
411
 */
 
412
 
 
413
long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
 
414
{
 
415
  char *s,tmp[MAILTMPLEN];
 
416
  long ret = T;
 
417
  if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
 
418
    strcpy (path,sysinbox ());  /* use system INBOX for unix and MMDF */
 
419
  else if (!strcmp (ds->dtb->name,"tenex"))
 
420
    ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
 
421
  else if (!strcmp (ds->dtb->name,"mtx"))
 
422
    ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
 
423
  else if (!strcmp (ds->dtb->name,"mbox"))
 
424
    ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
 
425
                                /* better not be a namespace driver */
 
426
  else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
 
427
                                /* INBOX in home directory */
 
428
  else ret = ((mailboxfile (path,"&&&&&") == path) &&
 
429
              (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
 
430
  if (ret) {                    /* don't bother if lossage */
 
431
    sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
 
432
    *mailbox = cpystr (tmp);    /* name of INBOX in this namespace */
 
433
  }
 
434
  return ret;
 
435
}
 
436
 
 
437
/* Deliver safely
 
438
 * Accepts: prototype stream to force mailbox format
 
439
 *          stringstruct of message temporary file or NIL for check only
 
440
 *          mailbox name
 
441
 *          filesystem path name
 
442
 *          user id
 
443
 *          scratch buffer for messages
 
444
 * Returns: NIL if success, else error code
 
445
 */
 
446
 
 
447
int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
 
448
                    uid_t uid,char *tmp)
 
449
{
 
450
  struct stat sbuf;
 
451
  int i = delivery_unsafe (path,uid,&sbuf,tmp);
 
452
  if (i) return i;              /* give up now if delivery unsafe */
 
453
                                /* directory, not file */
 
454
  if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
 
455
    if (sbuf.st_mode & 0001) {  /* listable directories may be worrisome */
 
456
      sprintf (tmp,"WARNING: directory %.80s is listable",path);
 
457
      mm_log (tmp,WARN);
 
458
    }
 
459
  }
 
460
  else {                        /* file, not directory */
 
461
    if (sbuf.st_nlink != 1) {   /* multiple links may be worrisome */
 
462
      sprintf (tmp,"WARNING: multiple links to file %.80s",path);
 
463
      mm_log (tmp,WARN);
 
464
    }
 
465
    if (sbuf.st_mode & 0111) {  /* executable files may be worrisome */
 
466
      sprintf (tmp,"WARNING: file %.80s is executable",path);
 
467
      mm_log (tmp,WARN);
 
468
    }
 
469
  }
 
470
  if (sbuf.st_mode & 0002) {    /* public-write files may be worrisome */
 
471
    sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
 
472
    mm_log (tmp,WARN);
 
473
  }
 
474
  if (sbuf.st_mode & 0004) {    /* public-write files may be worrisome */
 
475
    sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
 
476
    mm_log (tmp,WARN);
 
477
  }
 
478
                                /* so far, so good */
 
479
  sprintf (tmp,"%s appending to %.80s (%s %.80s)",
 
480
           prt ? prt->dtb->name : "default",mailbox,
 
481
           ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
 
482
  mm_dlog (tmp);
 
483
                                /* do the append now! */
 
484
  if (!mail_append (prt,mailbox,st)) {
 
485
    sprintf (tmp,"message delivery failed to %.80s",path);
 
486
    return fail (tmp,EX_CANTCREAT);
 
487
  }
 
488
                                /* note success */
 
489
  sprintf (tmp,"delivered to %.80s",path);
 
490
  mm_log (tmp,NIL);
 
491
                                /* make sure nothing evil this way comes */
 
492
  return delivery_unsafe (path,uid,&sbuf,tmp);
 
493
}
 
494
 
 
495
/* Verify that delivery is safe
 
496
 * Accepts: path name
 
497
 *          user id
 
498
 *          stat buffer
 
499
 *          scratch buffer for messages
 
500
 * Returns: NIL if delivery is safe, error code if unsafe
 
501
 */
 
502
 
 
503
int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
 
504
{
 
505
  u_short type;
 
506
  sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
 
507
  mm_dlog (tmp);
 
508
                                /* prepare message just in case */
 
509
  sprintf (tmp,"delivery to %.80s unsafe: ",path);
 
510
                                /* unsafe if can't get its status */
 
511
  if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
 
512
  else if (sbuf->st_uid != uid) /* unsafe if UID does not match */
 
513
    sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
 
514
             (long) sbuf->st_uid,(long) uid);
 
515
                                /* unsafe if not a regular file */
 
516
  else if (((type = sbuf->st_mode & (S_IFMT | S_ISUID | S_ISGID)) != S_IFREG)&&
 
517
           (type != S_IFDIR)) {
 
518
    strcat (tmp,"can't deliver to ");
 
519
                                /* unsafe if setuid */
 
520
    if (type & S_ISUID) strcat (tmp,"setuid file");
 
521
                                /* unsafe if setgid */
 
522
    else if (type & S_ISGID) strcat (tmp,"setgid file");
 
523
    else switch (type) {
 
524
    case S_IFCHR: strcat (tmp,"character special"); break;
 
525
    case S_IFBLK: strcat (tmp,"block special"); break;
 
526
    case S_IFLNK: strcat (tmp,"symbolic link"); break;
 
527
    case S_IFSOCK: strcat (tmp,"socket"); break;
 
528
    default:
 
529
      sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
 
530
    }
 
531
  }
 
532
  else return NIL;              /* OK to deliver */
 
533
  return fail (tmp,EX_CANTCREAT);
 
534
}
 
535
 
 
536
/* Report an error
 
537
 * Accepts: string to output
 
538
 */
 
539
 
 
540
int fail (char *string,int code)
 
541
{
 
542
  mm_log (string,ERROR);        /* pass up the string */
 
543
#if T
 
544
  switch (code) {
 
545
  case EX_USAGE:
 
546
  case EX_OSERR:
 
547
  case EX_SOFTWARE:
 
548
  case EX_NOUSER:
 
549
  case EX_CANTCREAT:
 
550
  case EX_UNAVAILABLE:
 
551
    code = EX_TEMPFAIL;         /* coerce these to TEMPFAIL */
 
552
  default:
 
553
    break;
 
554
  }
 
555
#endif
 
556
  return code;                  /* error code to return */
 
557
}
 
558
 
 
559
 
 
560
/* Get user name from username+mailbox specifier
 
561
 * Accepts: username/mailbox specifier
 
562
 *          pointer to return location for mailbox specifier
 
563
 * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
 
564
 */
 
565
 
 
566
char *getusername (char *s,char **t)
 
567
{
 
568
  char tmp[MAILTMPLEN];
 
569
  if (*t = strchr (s,'+')) {    /* have a mailbox specifier? */
 
570
    *(*t)++ = '\0';             /* yes, tie off user name */
 
571
                                /* user+ and user+INBOX same as user */
 
572
    if (!**t || !strcmp ("INBOX",ucase (strcpy (tmp,*t)))) *t = NIL;
 
573
  }
 
574
  return s;                     /* return user name */
 
575
}
 
576
 
 
577
/* Co-routines from MAIL library */
 
578
 
 
579
 
 
580
/* Message matches a search
 
581
 * Accepts: MAIL stream
 
582
 *          message number
 
583
 */
 
584
 
 
585
void mm_searched (MAILSTREAM *stream,unsigned long msgno)
 
586
{
 
587
  fatal ("mm_searched() call");
 
588
}
 
589
 
 
590
 
 
591
/* Message exists (i.e. there are that many messages in the mailbox)
 
592
 * Accepts: MAIL stream
 
593
 *          message number
 
594
 */
 
595
 
 
596
void mm_exists (MAILSTREAM *stream,unsigned long number)
 
597
{
 
598
  fatal ("mm_exists() call");
 
599
}
 
600
 
 
601
 
 
602
/* Message expunged
 
603
 * Accepts: MAIL stream
 
604
 *          message number
 
605
 */
 
606
 
 
607
void mm_expunged (MAILSTREAM *stream,unsigned long number)
 
608
{
 
609
  fatal ("mm_expunged() call");
 
610
}
 
611
 
 
612
 
 
613
/* Message flags update seen
 
614
 * Accepts: MAIL stream
 
615
 *          message number
 
616
 */
 
617
 
 
618
void mm_flags (MAILSTREAM *stream,unsigned long number)
 
619
{
 
620
}
 
621
 
 
622
/* Mailbox found
 
623
 * Accepts: MAIL stream
 
624
 *          delimiter
 
625
 *          mailbox name
 
626
 *          mailbox attributes
 
627
 */
 
628
 
 
629
void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
 
630
{
 
631
  fatal ("mm_list() call");
 
632
}
 
633
 
 
634
 
 
635
/* Subscribed mailbox found
 
636
 * Accepts: MAIL stream
 
637
 *          delimiter
 
638
 *          mailbox name
 
639
 *          mailbox attributes
 
640
 */
 
641
 
 
642
void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
 
643
{
 
644
  fatal ("mm_lsub() call");
 
645
}
 
646
 
 
647
 
 
648
/* Mailbox status
 
649
 * Accepts: MAIL stream
 
650
 *          mailbox name
 
651
 *          mailbox status
 
652
 */
 
653
 
 
654
void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
 
655
{
 
656
  fatal ("mm_status() call");
 
657
}
 
658
 
 
659
/* Notification event
 
660
 * Accepts: MAIL stream
 
661
 *          string to log
 
662
 *          error flag
 
663
 */
 
664
 
 
665
void mm_notify (MAILSTREAM *stream,char *string,long errflg)
 
666
{
 
667
  char tmp[MAILTMPLEN];
 
668
  tmp[11] = '\0';               /* see if TRYCREATE */
 
669
  if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
 
670
  mm_log (string,errflg);       /* just do mm_log action */
 
671
}
 
672
 
 
673
 
 
674
/* Log an event for the user to see
 
675
 * Accepts: string to log
 
676
 *          error flag
 
677
 */
 
678
 
 
679
void mm_log (char *string,long errflg)
 
680
{
 
681
  if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
 
682
  else {                        /* ordinary logging */
 
683
    fprintf (stderr,"%s\n",string);
 
684
    switch (errflg) {  
 
685
    case NIL:                   /* no error */
 
686
      syslog (LOG_INFO,"%s",string);
 
687
      break;
 
688
    case PARSE:                 /* parsing problem */
 
689
    case WARN:                  /* warning */
 
690
      syslog (LOG_WARNING,"%s",string);
 
691
      break;
 
692
    case ERROR:                 /* error */
 
693
    default:
 
694
      syslog (LOG_ERR,"%s",string);
 
695
      break;
 
696
    }
 
697
  }
 
698
}
 
699
 
 
700
 
 
701
/* Log an event to debugging telemetry
 
702
 * Accepts: string to log
 
703
 */
 
704
 
 
705
void mm_dlog (char *string)
 
706
{
 
707
  if (debug) fprintf (stderr,"%s\n",string);
 
708
  syslog (LOG_DEBUG,"%s",string);
 
709
}
 
710
 
 
711
/* Get user name and password for this host
 
712
 * Accepts: parse of network mailbox name
 
713
 *          where to return user name
 
714
 *          where to return password
 
715
 *          trial count
 
716
 */
 
717
 
 
718
void mm_login (NETMBX *mb,char *username,char *password,long trial)
 
719
{
 
720
  fatal ("mm_login() call");
 
721
}
 
722
 
 
723
 
 
724
/* About to enter critical code
 
725
 * Accepts: stream
 
726
 */
 
727
 
 
728
void mm_critical (MAILSTREAM *stream)
 
729
{
 
730
  critical = T;                 /* note in critical code */
 
731
}
 
732
 
 
733
 
 
734
/* About to exit critical code
 
735
 * Accepts: stream
 
736
 */
 
737
 
 
738
void mm_nocritical (MAILSTREAM *stream)
 
739
{
 
740
  critical = NIL;               /* note not in critical code */
 
741
}
 
742
 
 
743
 
 
744
/* Disk error found
 
745
 * Accepts: stream
 
746
 *          system error code
 
747
 *          flag indicating that mailbox may be clobbered
 
748
 * Returns: T if user wants to abort
 
749
 */
 
750
 
 
751
long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
 
752
{
 
753
  return T;
 
754
}
 
755
 
 
756
 
 
757
/* Log a fatal error event
 
758
 * Accepts: string to log
 
759
 */
 
760
 
 
761
void mm_fatal (char *string)
 
762
{
 
763
  printf ("?%s\n",string);      /* shouldn't happen normally */
 
764
}