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 */
140 NIL
/* renew stream */
143 /* prototype stream */
144 MAILSTREAM phileproto
= {&philedriver
};
146 /* File validate mailbox
147 * Accepts: mailbox name
148 * Returns: our driver if name is valid, NIL otherwise
151 DRIVER
*phile_valid (char *name
)
153 char tmp
[MAILTMPLEN
];
154 return phile_isvalid (name
,tmp
) ? &philedriver
: NIL
;
158 /* File test for valid mailbox
159 * Accepts: mailbox name
160 * Returns: T if valid, NIL otherwise
163 int phile_isvalid (char *name
,char *tmp
)
167 /* INBOX never accepted, any other name is */
168 return ((s
= mailboxfile (tmp
,name
)) && *s
&& !stat (s
,&sbuf
) &&
169 !(sbuf
.st_mode
& S_IFDIR
) &&
170 /* only allow empty files if no empty proto
172 (sbuf
.st_size
|| !default_proto (T
) ||
173 ((*name
== '#') && ((name
[1] == 'f') || (name
[1] == 'F')) &&
174 ((name
[2] == 't') || (name
[2] == 'T')) &&
175 ((name
[3] == 'p') || (name
[3] == 'P')) && (name
[4] == '/'))));
178 /* File manipulate driver parameters
179 * Accepts: function code
180 * function-dependent value
181 * Returns: function-dependent return value
184 void *phile_parameters (long function
,void *value
)
189 /* File mail scan mailboxes
190 * Accepts: mail stream
196 void phile_scan (MAILSTREAM
*stream
,char *ref
,char *pat
,char *contents
)
198 if (stream
) dummy_scan (NIL
,ref
,pat
,contents
);
202 /* File list mailboxes
203 * Accepts: mail stream
208 void phile_list (MAILSTREAM
*stream
,char *ref
,char *pat
)
210 if (stream
) dummy_list (NIL
,ref
,pat
);
214 /* File list subscribed mailboxes
215 * Accepts: mail stream
220 void phile_lsub (MAILSTREAM
*stream
,char *ref
,char *pat
)
222 if (stream
) dummy_lsub (NIL
,ref
,pat
);
227 * Accepts: mail stream
230 * Returns: T on success, NIL on failure
233 long phile_status (MAILSTREAM
*stream
,char *mbx
,long flags
)
235 char *s
,tmp
[MAILTMPLEN
];
239 if ((s
= mailboxfile (tmp
,mbx
)) && *s
&& !stat (s
,&sbuf
)) {
240 status
.flags
= flags
; /* return status values */
241 status
.unseen
= (stream
&& mail_elt (stream
,1)->seen
) ? 0 : 1;
242 status
.messages
= status
.recent
= status
.uidnext
= 1;
243 status
.uidvalidity
= sbuf
.st_mtime
;
244 /* pass status to main program */
245 mm_status (stream
,mbx
,&status
);
246 ret
= LONGT
; /* success */
252 * Accepts: Stream to open
253 * Returns: Stream on success, NIL on failure
256 MAILSTREAM
*phile_open (MAILSTREAM
*stream
)
260 char *s
,tmp
[MAILTMPLEN
];
266 /* return prototype for OP_PROTOTYPE call */
267 if (!stream
) return &phileproto
;
268 if (stream
->local
) fatal ("phile recycle stream");
269 /* open associated file */
270 if (!mailboxfile (tmp
,stream
->mailbox
) || !tmp
[0] || stat (tmp
,&sbuf
) ||
271 (fd
= open (tmp
,O_RDONLY
,NIL
)) < 0) {
272 sprintf (tmp
,"Unable to open file %s",stream
->mailbox
);
276 fs_give ((void **) &stream
->mailbox
);
277 stream
->mailbox
= cpystr (tmp
);
278 stream
->local
= fs_get (sizeof (PHILELOCAL
));
279 mail_exists (stream
,1); /* make sure upper level knows */
280 mail_recent (stream
,1);
281 elt
= mail_elt (stream
,1); /* instantiate cache element */
282 elt
->valid
= elt
->recent
= T
; /* mark valid flags */
283 stream
->sequence
++; /* bump sequence number */
284 stream
->rdonly
= T
; /* make sure upper level knows readonly */
285 /* instantiate a new envelope and body */
286 LOCAL
->env
= mail_newenvelope ();
287 LOCAL
->body
= mail_newbody ();
289 t
= gmtime (&sbuf
.st_mtime
); /* get UTC time and Julian day */
290 i
= t
->tm_hour
* 60 + t
->tm_min
;
292 t
= localtime(&sbuf
.st_mtime
);/* get local time */
293 /* calculate time delta */
294 i
= t
->tm_hour
* 60 + t
->tm_min
- i
;
295 if ((k
= t
->tm_yday
- k
) != 0) i
+= ((k
< 0) == (abs (k
) == 1)) ? -24*60 : 24*60;
296 k
= abs (i
); /* time from UTC either way */
297 elt
->hours
= t
->tm_hour
; elt
->minutes
= t
->tm_min
; elt
->seconds
= t
->tm_sec
;
298 elt
->day
= t
->tm_mday
; elt
->month
= t
->tm_mon
+ 1;
299 elt
->year
= t
->tm_year
- (BASEYEAR
- 1900);
300 elt
->zoccident
= (k
== i
) ? 0 : 1;
302 elt
->zminutes
= k
% 60;
303 sprintf (tmp
,"%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
304 days
[t
->tm_wday
],t
->tm_mday
,months
[t
->tm_mon
],t
->tm_year
+1900,
305 t
->tm_hour
,t
->tm_min
,t
->tm_sec
,elt
->zoccident
? '-' : '+',
306 elt
->zhours
,elt
->zminutes
);
307 /* set up Date field */
308 LOCAL
->env
->date
= cpystr (tmp
);
310 /* fill in From field from file owner */
311 LOCAL
->env
->from
= mail_newaddr ();
312 if ((pw
= getpwuid (sbuf
.st_uid
)) != NULL
) strcpy (tmp
,pw
->pw_name
);
313 else sprintf (tmp
,"User-Number-%ld",(long) sbuf
.st_uid
);
314 LOCAL
->env
->from
->mailbox
= cpystr (tmp
);
315 LOCAL
->env
->from
->host
= cpystr (mylocalhost ());
316 /* set subject to be mailbox name */
317 LOCAL
->env
->subject
= cpystr (stream
->mailbox
);
319 (buf
= &elt
->private.special
.text
)->size
= sbuf
.st_size
;
320 read (fd
,buf
->data
= (unsigned char *) fs_get (buf
->size
+ 1),buf
->size
);
321 buf
->data
[buf
->size
] = '\0';
322 close (fd
); /* close the file */
323 /* analyze data type */
324 if ((i
= phile_type (buf
->data
,buf
->size
,&j
)) != 0){
325 LOCAL
->body
->type
= TYPETEXT
;
326 LOCAL
->body
->subtype
= cpystr ("PLAIN");
327 if (!(i
& PTYPECRTEXT
)) { /* change Internet newline format as needed */
328 s
= (char *) buf
->data
; /* make copy of UNIX-format string */
329 buf
->data
= NIL
; /* zap the buffer */
330 buf
->size
= strcrlfcpy (&buf
->data
,&m
,s
,buf
->size
);
331 fs_give ((void **) &s
); /* flush original UNIX-format string */
333 LOCAL
->body
->parameter
= mail_newbody_parameter ();
334 LOCAL
->body
->parameter
->attribute
= cpystr ("charset");
335 LOCAL
->body
->parameter
->value
=
336 cpystr ((i
& PTYPEISO2022JP
) ? "ISO-2022-JP" :
337 (i
& PTYPEISO2022KR
) ? "ISO-2022-KR" :
338 (i
& PTYPEISO2022CN
) ? "ISO-2022-CN" :
339 (i
& PTYPE8
) ? "X-UNKNOWN" : "US-ASCII");
340 LOCAL
->body
->encoding
= (i
& PTYPE8
) ? ENC8BIT
: ENC7BIT
;
341 LOCAL
->body
->size
.lines
= j
;
343 else { /* binary data */
344 LOCAL
->body
->type
= TYPEAPPLICATION
;
345 LOCAL
->body
->subtype
= cpystr ("OCTET-STREAM");
346 LOCAL
->body
->parameter
= mail_newbody_parameter ();
347 LOCAL
->body
->parameter
->attribute
= cpystr ("name");
348 LOCAL
->body
->parameter
->value
=
349 cpystr ((s
= (strrchr (stream
->mailbox
,'/'))) ? s
+1 : stream
->mailbox
);
350 LOCAL
->body
->encoding
= ENCBASE64
;
351 buf
->data
= rfc822_binary (s
= (char *) buf
->data
,buf
->size
,&buf
->size
);
352 fs_give ((void **) &s
); /* flush originary binary contents */
354 phile_header (stream
,1,&j
,NIL
);
355 LOCAL
->body
->size
.bytes
= LOCAL
->body
->contents
.text
.size
= buf
->size
;
356 elt
->rfc822_size
= j
+ buf
->size
;
357 /* only one message ever... */
358 stream
->uid_validity
= sbuf
.st_mtime
;
359 stream
->uid_last
= elt
->private.uid
= 1;
360 return stream
; /* return stream alive to caller */
363 /* File determine data type
364 * Accepts: data to examine
366 * pointer to line count return
367 * Returns: PTYPE mask of data type
370 int phile_type (unsigned char *s
,unsigned long i
,unsigned long *j
)
373 char *charvec
= "bbbbbbbaaalaacaabbbbbbbbbbbebbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
374 *j
= 0; /* no lines */
375 /* check type of every character */
376 while (i
--) switch (charvec
[*s
++]) {
378 ret
|= PTYPE8
; /* 8bit character */
381 break; /* ASCII character */
383 return PTYPEBINARY
; /* binary byte seen, stop immediately */
385 ret
|= PTYPECRTEXT
; /* CR indicates Internet text */
388 if (*s
== '$') { /* ISO-2022 sequence? */
390 case 'B': case '@': ret
|= PTYPEISO2022JP
; break;
393 case 'A': case 'E': case 'G': ret
|= PTYPEISO2022CN
; break;
394 case 'C': ret
|= PTYPEISO2022KR
; break;
398 case 'H': ret
|= PTYPEISO2022CN
; break;
402 case 'I': case 'J': case 'K': case 'L': case 'M':
403 ret
|= PTYPEISO2022CN
; break;
408 case 'l': /* newline */
412 return ret
; /* return type of data */
416 * Accepts: MAIL stream
420 void phile_close (MAILSTREAM
*stream
,long options
)
422 if (LOCAL
) { /* only if a file is open */
423 fs_give ((void **) &mail_elt (stream
,1)->private.special
.text
.data
);
424 /* nuke the local data */
425 fs_give ((void **) &stream
->local
);
426 stream
->dtb
= NIL
; /* log out the DTB */
430 /* File fetch structure
431 * Accepts: MAIL stream
433 * pointer to return body
435 * Returns: envelope of this message, body returned in body value
437 * Fetches the "fast" information as well
440 ENVELOPE
*phile_structure (MAILSTREAM
*stream
,unsigned long msgno
,BODY
**body
,
443 if (body
) *body
= LOCAL
->body
;
444 return LOCAL
->env
; /* return the envelope */
448 /* File fetch message header
449 * Accepts: MAIL stream
451 * pointer to returned header text length
453 * Returns: message header in RFC822 format
456 char *phile_header (MAILSTREAM
*stream
,unsigned long msgno
,
457 unsigned long *length
,long flags
)
459 rfc822_header (LOCAL
->tmp
,LOCAL
->env
,LOCAL
->body
);
460 *length
= strlen (LOCAL
->tmp
);
465 /* File fetch message text (body only)
466 * Accepts: MAIL stream
468 * pointer to returned stringstruct
473 long phile_text (MAILSTREAM
*stream
,unsigned long msgno
,STRING
*bs
,long flags
)
475 SIZEDTEXT
*buf
= &mail_elt (stream
,msgno
)->private.special
.text
;
476 if (!(flags
&FT_PEEK
)) { /* mark message as seen */
477 mail_elt (stream
,msgno
)->seen
= T
;
478 mm_flags (stream
,msgno
);
480 INIT (bs
,mail_string
,buf
->data
,buf
->size
);
485 * Accepts: MAIL stream
486 * Returns: T if stream alive, else NIL
487 * No-op for readonly files, since read/writer can expunge it from under us!
490 long phile_ping (MAILSTREAM
*stream
)
495 /* File check mailbox
496 * Accepts: MAIL stream
497 * No-op for readonly files, since read/writer can expunge it from under us!
500 void phile_check (MAILSTREAM
*stream
)
502 mm_log ("Check completed",NIL
);
505 /* File expunge mailbox
506 * Accepts: MAIL stream
507 * sequence to expunge if non-NIL
509 * Returns: T if success, NIL if failure
512 long phile_expunge (MAILSTREAM
*stream
,char *sequence
,long options
)
514 if (!stream
->silent
) mm_log ("Expunge ignored on readonly mailbox",NIL
);
518 /* File copy message(s)
519 * Accepts: MAIL stream
521 * destination mailbox
523 * Returns: T if copy successful, else NIL
526 long phile_copy (MAILSTREAM
*stream
,char *sequence
,char *mailbox
,long options
)
528 char tmp
[MAILTMPLEN
];
530 (mailproxycopy_t
) mail_parameters (stream
,GET_MAILPROXYCOPY
,NIL
);
531 if (pc
) return (*pc
) (stream
,sequence
,mailbox
,options
);
532 sprintf (tmp
,"Can't copy - file \"%s\" is not in valid mailbox format",
539 /* File append message from stringstruct
540 * Accepts: MAIL stream
541 * destination mailbox
542 * append callback function
544 * Returns: T if append successful, else NIL
547 long phile_append (MAILSTREAM
*stream
,char *mailbox
,append_t af
,void *data
)
549 char tmp
[MAILTMPLEN
],file
[MAILTMPLEN
];
550 char *s
= mailboxfile (file
,mailbox
);
552 sprintf (tmp
,"Can't append - not in valid mailbox format: %.80s",s
);
553 else sprintf (tmp
,"Can't append - invalid name: %.80s",mailbox
);