1 /* ========================================================================
2 * Copyright 1988-2006 University of Washington
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
8 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
15 * Program: File routines
17 * Author: Mark Crispin
18 * Networks and Distributed Computing
19 * Computing & Communications
20 * University of Washington
21 * Administration Building, AG-44
23 * Internet: MRC@CAC.Washington.EDU
25 * Date: 25 August 1993
26 * Last Edited: 9 May 2006
34 extern int errno
; /* just in case */
46 /* Types returned from phile_type() */
48 #define PTYPEBINARY 0 /* binary data */
49 #define PTYPETEXT 1 /* textual data */
50 #define PTYPECRTEXT 2 /* textual data with CR */
51 #define PTYPE8 4 /* textual 8bit data */
52 #define PTYPEISO2022JP 8 /* textual Japanese */
53 #define PTYPEISO2022KR 16 /* textual Korean */
54 #define PTYPEISO2022CN 32 /* textual Chinese */
57 /* PHILE I/O stream local data */
59 typedef struct phile_local
{
60 ENVELOPE
*env
; /* file envelope */
61 BODY
*body
; /* file body */
62 char tmp
[MAILTMPLEN
]; /* temporary buffer */
66 /* Convenient access to local data */
68 #define LOCAL ((PHILELOCAL *) stream->local)
71 /* Function prototypes */
73 DRIVER
*phile_valid (char *name
);
74 int phile_isvalid (char *name
,char *tmp
);
75 void *phile_parameters (long function
,void *value
);
76 void phile_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
);
77 void phile_list (MAILSTREAM
*stream
,char *ref
,char *pat
);
78 void phile_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
);
79 long phile_create (MAILSTREAM
*stream
,char *mailbox
);
80 long phile_delete (MAILSTREAM
*stream
,char *mailbox
);
81 long phile_rename (MAILSTREAM
*stream
,char *old
,char *newname
);
82 long phile_status (MAILSTREAM
*stream
,char *mbx
,long flags
);
83 MAILSTREAM
*phile_open (MAILSTREAM
*stream
);
84 int phile_type (unsigned char *s
,unsigned long i
,unsigned long *j
);
85 void phile_close (MAILSTREAM
*stream
,long options
);
86 ENVELOPE
*phile_structure (MAILSTREAM
*stream
,unsigned long msgno
,BODY
**body
,
88 char *phile_header (MAILSTREAM
*stream
,unsigned long msgno
,
89 unsigned long *length
,long flags
);
90 long phile_text (MAILSTREAM
*stream
,unsigned long msgno
,STRING
*bs
,long flags
);
91 long phile_ping (MAILSTREAM
*stream
);
92 void phile_check (MAILSTREAM
*stream
);
93 long phile_expunge (MAILSTREAM
*stream
,char *sequence
,long options
);
94 long phile_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
);
95 long phile_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
);
100 /* Driver dispatch used by MAIL */
102 DRIVER philedriver
= {
103 "phile", /* driver name */
105 DR_LOCAL
|DR_READONLY
|DR_NOSTICKY
,
106 (DRIVER
*) NIL
, /* next driver */
107 phile_valid
, /* mailbox is valid for us */
108 phile_parameters
, /* manipulate parameters */
109 phile_scan
, /* scan mailboxes */
110 phile_list
, /* list mailboxes */
111 phile_lsub
, /* list subscribed mailboxes */
112 NIL
, /* subscribe to mailbox */
113 NIL
, /* unsubscribe from mailbox */
114 dummy_create
, /* create mailbox */
115 dummy_delete
, /* delete mailbox */
116 dummy_rename
, /* rename mailbox */
117 phile_status
, /* status of mailbox */
118 phile_open
, /* open mailbox */
119 phile_close
, /* close mailbox */
120 NIL
, /* fetch message "fast" attributes */
121 NIL
, /* fetch message flags */
122 NIL
, /* fetch overview */
123 phile_structure
, /* fetch message envelopes */
124 phile_header
, /* fetch message header only */
125 phile_text
, /* fetch message body only */
126 NIL
, /* fetch partial message text */
127 NIL
, /* unique identifier */
128 NIL
, /* message number */
129 NIL
, /* modify flags */
130 NIL
, /* per-message modify flags */
131 NIL
, /* search for message based on criteria */
132 NIL
, /* sort messages */
133 NIL
, /* thread messages */
134 phile_ping
, /* ping mailbox to see if still alive */
135 phile_check
, /* check for new messages */
136 phile_expunge
, /* expunge deleted messages */
137 phile_copy
, /* copy messages to another mailbox */
138 phile_append
, /* append string message to mailbox */
139 NIL
/* garbage collect stream */
142 /* prototype stream */
143 MAILSTREAM phileproto
= {&philedriver
};
145 /* File validate mailbox
146 * Accepts: mailbox name
147 * Returns: our driver if name is valid, NIL otherwise
150 DRIVER
*phile_valid (char *name
)
152 char tmp
[MAILTMPLEN
];
153 return phile_isvalid (name
,tmp
) ? &philedriver
: NIL
;
157 /* File test for valid mailbox
158 * Accepts: mailbox name
159 * Returns: T if valid, NIL otherwise
162 int phile_isvalid (char *name
,char *tmp
)
166 /* INBOX never accepted, any other name is */
167 return ((s
= mailboxfile (tmp
,name
)) && *s
&& !stat (s
,&sbuf
) &&
168 !(sbuf
.st_mode
& S_IFDIR
) &&
169 /* only allow empty files if no empty proto
171 (sbuf
.st_size
|| !default_proto (T
) ||
172 ((*name
== '#') && ((name
[1] == 'f') || (name
[1] == 'F')) &&
173 ((name
[2] == 't') || (name
[2] == 'T')) &&
174 ((name
[3] == 'p') || (name
[3] == 'P')) && (name
[4] == '/'))));
177 /* File manipulate driver parameters
178 * Accepts: function code
179 * function-dependent value
180 * Returns: function-dependent return value
183 void *phile_parameters (long function
,void *value
)
188 /* File mail scan mailboxes
189 * Accepts: mail stream
195 void phile_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
)
197 if (stream
) dummy_scan (NIL
,ref
,pat
,contents
);
201 /* File list mailboxes
202 * Accepts: mail stream
207 void phile_list (MAILSTREAM
*stream
,char *ref
,char *pat
)
209 if (stream
) dummy_list (NIL
,ref
,pat
);
213 /* File list subscribed mailboxes
214 * Accepts: mail stream
219 void phile_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
)
221 if (stream
) dummy_lsub (NIL
,ref
,pat
);
226 * Accepts: mail stream
229 * Returns: T on success, NIL on failure
232 long phile_status (MAILSTREAM
*stream
,char *mbx
,long flags
)
234 char *s
,tmp
[MAILTMPLEN
];
238 if ((s
= mailboxfile (tmp
,mbx
)) && *s
&& !stat (s
,&sbuf
)) {
239 status
.flags
= flags
; /* return status values */
240 status
.unseen
= (stream
&& mail_elt (stream
,1)->seen
) ? 0 : 1;
241 status
.messages
= status
.recent
= status
.uidnext
= 1;
242 status
.uidvalidity
= sbuf
.st_mtime
;
243 /* pass status to main program */
244 mm_status (stream
,mbx
,&status
);
245 ret
= LONGT
; /* success */
251 * Accepts: Stream to open
252 * Returns: Stream on success, NIL on failure
255 MAILSTREAM
*phile_open (MAILSTREAM
*stream
)
259 char *s
,tmp
[MAILTMPLEN
];
265 /* return prototype for OP_PROTOTYPE call */
266 if (!stream
) return &phileproto
;
267 if (stream
->local
) fatal ("phile recycle stream");
268 /* open associated file */
269 if (!mailboxfile (tmp
,stream
->mailbox
) || !tmp
[0] || stat (tmp
,&sbuf
) ||
270 (fd
= open (tmp
,O_RDONLY
,NIL
)) < 0) {
271 sprintf (tmp
,"Unable to open file %s",stream
->mailbox
);
275 fs_give ((void **) &stream
->mailbox
);
276 stream
->mailbox
= cpystr (tmp
);
277 stream
->local
= fs_get (sizeof (PHILELOCAL
));
278 mail_exists (stream
,1); /* make sure upper level knows */
279 mail_recent (stream
,1);
280 elt
= mail_elt (stream
,1); /* instantiate cache element */
281 elt
->valid
= elt
->recent
= T
; /* mark valid flags */
282 stream
->sequence
++; /* bump sequence number */
283 stream
->rdonly
= T
; /* make sure upper level knows readonly */
284 /* instantiate a new envelope and body */
285 LOCAL
->env
= mail_newenvelope ();
286 LOCAL
->body
= mail_newbody ();
288 t
= gmtime (&sbuf
.st_mtime
); /* get UTC time and Julian day */
289 i
= t
->tm_hour
* 60 + t
->tm_min
;
291 t
= localtime(&sbuf
.st_mtime
);/* get local time */
292 /* calculate time delta */
293 i
= t
->tm_hour
* 60 + t
->tm_min
- i
;
294 if (k
= t
->tm_yday
- k
) i
+= ((k
< 0) == (abs (k
) == 1)) ? -24*60 : 24*60;
295 k
= abs (i
); /* time from UTC either way */
296 elt
->hours
= t
->tm_hour
; elt
->minutes
= t
->tm_min
; elt
->seconds
= t
->tm_sec
;
297 elt
->day
= t
->tm_mday
; elt
->month
= t
->tm_mon
+ 1;
298 elt
->year
= t
->tm_year
- (BASEYEAR
- 1900);
299 elt
->zoccident
= (k
== i
) ? 0 : 1;
301 elt
->zminutes
= k
% 60;
302 sprintf (tmp
,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
303 days
[t
->tm_wday
],t
->tm_mday
,months
[t
->tm_mon
],t
->tm_year
+1900,
304 t
->tm_hour
,t
->tm_min
,t
->tm_sec
,elt
->zoccident
? '-' : '+',
305 elt
->zhours
,elt
->zminutes
);
306 /* set up Date field */
307 LOCAL
->env
->date
= cpystr (tmp
);
309 /* fill in From field from file owner */
310 LOCAL
->env
->from
= mail_newaddr ();
311 if (pw
= getpwuid (sbuf
.st_uid
)) strcpy (tmp
,pw
->pw_name
);
312 else sprintf (tmp
,"User-Number-%ld",(long) sbuf
.st_uid
);
313 LOCAL
->env
->from
->mailbox
= cpystr (tmp
);
314 LOCAL
->env
->from
->host
= cpystr (mylocalhost ());
315 /* set subject to be mailbox name */
316 LOCAL
->env
->subject
= cpystr (stream
->mailbox
);
318 (buf
= &elt
->private.special
.text
)->size
= sbuf
.st_size
;
319 read (fd
,buf
->data
= (unsigned char *) fs_get (buf
->size
+ 1),buf
->size
);
320 buf
->data
[buf
->size
] = '\0';
321 close (fd
); /* close the file */
322 /* analyze data type */
323 if (i
= phile_type (buf
->data
,buf
->size
,&j
)) {
324 LOCAL
->body
->type
= TYPETEXT
;
325 LOCAL
->body
->subtype
= cpystr ("PLAIN");
326 if (!(i
& PTYPECRTEXT
)) { /* change Internet newline format as needed */
327 s
= (char *) buf
->data
; /* make copy of UNIX-format string */
328 buf
->data
= NIL
; /* zap the buffer */
329 buf
->size
= strcrlfcpy (&buf
->data
,&m
,s
,buf
->size
);
330 fs_give ((void **) &s
); /* flush original UNIX-format string */
332 LOCAL
->body
->parameter
= mail_newbody_parameter ();
333 LOCAL
->body
->parameter
->attribute
= cpystr ("charset");
334 LOCAL
->body
->parameter
->value
=
335 cpystr ((i
& PTYPEISO2022JP
) ? "ISO-2022-JP" :
336 (i
& PTYPEISO2022KR
) ? "ISO-2022-KR" :
337 (i
& PTYPEISO2022CN
) ? "ISO-2022-CN" :
338 (i
& PTYPE8
) ? "X-UNKNOWN" : "US-ASCII");
339 LOCAL
->body
->encoding
= (i
& PTYPE8
) ? ENC8BIT
: ENC7BIT
;
340 LOCAL
->body
->size
.lines
= j
;
342 else { /* binary data */
343 LOCAL
->body
->type
= TYPEAPPLICATION
;
344 LOCAL
->body
->subtype
= cpystr ("OCTET-STREAM");
345 LOCAL
->body
->parameter
= mail_newbody_parameter ();
346 LOCAL
->body
->parameter
->attribute
= cpystr ("name");
347 LOCAL
->body
->parameter
->value
=
348 cpystr ((s
= (strrchr (stream
->mailbox
,'/'))) ? s
+1 : stream
->mailbox
);
349 LOCAL
->body
->encoding
= ENCBASE64
;
350 buf
->data
= rfc822_binary (s
= (char *) buf
->data
,buf
->size
,&buf
->size
);
351 fs_give ((void **) &s
); /* flush originary binary contents */
353 phile_header (stream
,1,&j
,NIL
);
354 LOCAL
->body
->size
.bytes
= LOCAL
->body
->contents
.text
.size
= buf
->size
;
355 elt
->rfc822_size
= j
+ buf
->size
;
356 /* only one message ever... */
357 stream
->uid_validity
= sbuf
.st_mtime
;
358 stream
->uid_last
= elt
->private.uid
= 1;
359 return stream
; /* return stream alive to caller */
362 /* File determine data type
363 * Accepts: data to examine
365 * pointer to line count return
366 * Returns: PTYPE mask of data type
369 int phile_type (unsigned char *s
,unsigned long i
,unsigned long *j
)
372 char *charvec
= "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
373 *j
= 0; /* no lines */
374 /* check type of every character */
375 while (i
--) switch (charvec
[*s
++]) {
377 ret
|= PTYPE8
; /* 8bit character */
380 break; /* ASCII character */
382 return PTYPEBINARY
; /* binary byte seen, stop immediately */
384 ret
|= PTYPECRTEXT
; /* CR indicates Internet text */
387 if (*s
== '$') { /* ISO-2022 sequence? */
389 case 'B': case '@': ret
|= PTYPEISO2022JP
; break;
392 case 'A': case 'E': case 'G': ret
|= PTYPEISO2022CN
; break;
393 case 'C': ret
|= PTYPEISO2022KR
; break;
397 case 'H': ret
|= PTYPEISO2022CN
; break;
401 case 'I': case 'J': case 'K': case 'L': case 'M':
402 ret
|= PTYPEISO2022CN
; break;
407 case 'l': /* newline */
411 return ret
; /* return type of data */
415 * Accepts: MAIL stream
419 void phile_close (MAILSTREAM
*stream
,long options
)
421 if (LOCAL
) { /* only if a file is open */
422 fs_give ((void **) &mail_elt (stream
,1)->private.special
.text
.data
);
423 /* nuke the local data */
424 fs_give ((void **) &stream
->local
);
425 stream
->dtb
= NIL
; /* log out the DTB */
429 /* File fetch structure
430 * Accepts: MAIL stream
432 * pointer to return body
434 * Returns: envelope of this message, body returned in body value
436 * Fetches the "fast" information as well
439 ENVELOPE
*phile_structure (MAILSTREAM
*stream
,unsigned long msgno
,BODY
**body
,
442 if (body
) *body
= LOCAL
->body
;
443 return LOCAL
->env
; /* return the envelope */
447 /* File fetch message header
448 * Accepts: MAIL stream
450 * pointer to returned header text length
452 * Returns: message header in RFC822 format
455 char *phile_header (MAILSTREAM
*stream
,unsigned long msgno
,
456 unsigned long *length
,long flags
)
458 rfc822_header (LOCAL
->tmp
,LOCAL
->env
,LOCAL
->body
);
459 *length
= strlen (LOCAL
->tmp
);
464 /* File fetch message text (body only)
465 * Accepts: MAIL stream
467 * pointer to returned stringstruct
472 long phile_text (MAILSTREAM
*stream
,unsigned long msgno
,STRING
*bs
,long flags
)
474 SIZEDTEXT
*buf
= &mail_elt (stream
,msgno
)->private.special
.text
;
475 if (!(flags
&FT_PEEK
)) { /* mark message as seen */
476 mail_elt (stream
,msgno
)->seen
= T
;
477 mm_flags (stream
,msgno
);
479 INIT (bs
,mail_string
,buf
->data
,buf
->size
);
484 * Accepts: MAIL stream
485 * Returns: T if stream alive, else NIL
486 * No-op for readonly files, since read/writer can expunge it from under us!
489 long phile_ping (MAILSTREAM
*stream
)
494 /* File check mailbox
495 * Accepts: MAIL stream
496 * No-op for readonly files, since read/writer can expunge it from under us!
499 void phile_check (MAILSTREAM
*stream
)
501 mm_log ("Check completed",NIL
);
504 /* File expunge mailbox
505 * Accepts: MAIL stream
506 * sequence to expunge if non-NIL
508 * Returns: T if success, NIL if failure
511 long phile_expunge (MAILSTREAM
*stream
,char *sequence
,long options
)
513 if (!stream
->silent
) mm_log ("Expunge ignored on readonly mailbox",NIL
);
517 /* File copy message(s)
518 * Accepts: MAIL stream
520 * destination mailbox
522 * Returns: T if copy successful, else NIL
525 long phile_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
)
527 char tmp
[MAILTMPLEN
];
529 (mailproxycopy_t
) mail_parameters (stream
,GET_MAILPROXYCOPY
,NIL
);
530 if (pc
) return (*pc
) (stream
,sequence
,mailbox
,options
);
531 sprintf (tmp
,"Can't copy - file \"%s\" is not in valid mailbox format",
538 /* File append message from stringstruct
539 * Accepts: MAIL stream
540 * destination mailbox
541 * append callback function
543 * Returns: T if append successful, else NIL
546 long phile_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
)
548 char tmp
[MAILTMPLEN
],file
[MAILTMPLEN
];
549 char *s
= mailboxfile (file
,mailbox
);
551 sprintf (tmp
,"Can't append - not in valid mailbox format: %.80s",s
);
552 else sprintf (tmp
,"Can't append - invalid name: %.80s",mailbox
);