rev |
line source |
yuuji@0
|
1 /* ========================================================================
|
yuuji@0
|
2 * Copyright 1988-2008 University of Washington
|
yuuji@0
|
3 *
|
yuuji@0
|
4 * Licensed under the Apache License, Version 2.0 (the "License");
|
yuuji@0
|
5 * you may not use this file except in compliance with the License.
|
yuuji@0
|
6 * You may obtain a copy of the License at
|
yuuji@0
|
7 *
|
yuuji@0
|
8 * http://www.apache.org/licenses/LICENSE-2.0
|
yuuji@0
|
9 *
|
yuuji@0
|
10 *
|
yuuji@0
|
11 * ========================================================================
|
yuuji@0
|
12 */
|
yuuji@0
|
13
|
yuuji@0
|
14 /*
|
yuuji@0
|
15 * Program: IMAP4rev1 server
|
yuuji@0
|
16 *
|
yuuji@0
|
17 * Author: Mark Crispin
|
yuuji@0
|
18 * UW Technology
|
yuuji@0
|
19 * University of Washington
|
yuuji@0
|
20 * Seattle, WA 98195
|
yuuji@0
|
21 * Internet: MRC@Washington.EDU
|
yuuji@0
|
22 *
|
yuuji@0
|
23 * Date: 5 November 1990
|
yuuji@0
|
24 * Last Edited: 3 March 2008
|
yuuji@0
|
25 */
|
yuuji@0
|
26
|
yuuji@0
|
27 /* Parameter files */
|
yuuji@0
|
28
|
yuuji@0
|
29 #include <stdio.h>
|
yuuji@0
|
30 #include <ctype.h>
|
yuuji@0
|
31 #include <errno.h>
|
yuuji@0
|
32 extern int errno; /* just in case */
|
yuuji@0
|
33 #include <signal.h>
|
yuuji@0
|
34 #include <setjmp.h>
|
yuuji@0
|
35 #include <time.h>
|
yuuji@0
|
36 #include "c-client.h"
|
yuuji@0
|
37 #include "newsrc.h"
|
yuuji@0
|
38 #include <sys/stat.h>
|
yuuji@0
|
39
|
yuuji@0
|
40
|
yuuji@0
|
41 #define CRLF PSOUT ("\015\012") /* primary output terpri */
|
yuuji@0
|
42
|
yuuji@0
|
43
|
yuuji@0
|
44 /* Timeouts and timers */
|
yuuji@0
|
45
|
yuuji@0
|
46 #define MINUTES *60
|
yuuji@0
|
47
|
yuuji@0
|
48 #define LOGINTIMEOUT 3 MINUTES /* not logged in autologout timer */
|
yuuji@0
|
49 #define TIMEOUT 30 MINUTES /* RFC 3501 minimum autologout timer */
|
yuuji@0
|
50 #define INPUTTIMEOUT 5 MINUTES /* timer for additional command input */
|
yuuji@0
|
51 #define ALERTTIMER 1 MINUTES /* alert check timer */
|
yuuji@0
|
52 #define SHUTDOWNTIMER 1 MINUTES /* shutdown dally timer */
|
yuuji@0
|
53 #define IDLETIMER 1 MINUTES /* IDLE command poll timer */
|
yuuji@0
|
54 #define CHECKTIMER 15 MINUTES /* IDLE command last checkpoint timer */
|
yuuji@0
|
55
|
yuuji@0
|
56
|
yuuji@0
|
57 #define LITSTKLEN 20 /* length of literal stack */
|
yuuji@0
|
58 #define MAXCLIENTLIT 10000 /* maximum non-APPEND client literal size
|
yuuji@0
|
59 * must be smaller than 4294967295
|
yuuji@0
|
60 */
|
yuuji@0
|
61 #define MAXAPPENDTXT 0x40000000 /* maximum APPEND literal size
|
yuuji@0
|
62 * must be smaller than 4294967295
|
yuuji@0
|
63 */
|
yuuji@0
|
64 #define CMDLEN 65536 /* size of command buffer */
|
yuuji@0
|
65
|
yuuji@0
|
66
|
yuuji@0
|
67 /* Server states */
|
yuuji@0
|
68
|
yuuji@0
|
69 #define LOGIN 0
|
yuuji@0
|
70 #define SELECT 1
|
yuuji@0
|
71 #define OPEN 2
|
yuuji@0
|
72 #define LOGOUT 3
|
yuuji@0
|
73
|
yuuji@0
|
74 /* Body text fetching */
|
yuuji@0
|
75
|
yuuji@0
|
76 typedef struct text_args {
|
yuuji@0
|
77 char *section; /* body section */
|
yuuji@0
|
78 STRINGLIST *lines; /* header lines */
|
yuuji@0
|
79 unsigned long first; /* first octet to fetch */
|
yuuji@0
|
80 unsigned long last; /* number of octets to fetch */
|
yuuji@0
|
81 long flags; /* fetch flags */
|
yuuji@0
|
82 long binary; /* binary flags */
|
yuuji@0
|
83 } TEXTARGS;
|
yuuji@0
|
84
|
yuuji@0
|
85 #define FTB_BINARY 0x1 /* fetch as binary */
|
yuuji@0
|
86 #define FTB_SIZE 0x2 /* fetch size only */
|
yuuji@0
|
87
|
yuuji@0
|
88
|
yuuji@0
|
89 /* Append data */
|
yuuji@0
|
90
|
yuuji@0
|
91 typedef struct append_data {
|
yuuji@0
|
92 unsigned char *arg; /* append argument pointer */
|
yuuji@0
|
93 char *flags; /* message flags */
|
yuuji@0
|
94 char *date; /* message date */
|
yuuji@0
|
95 char *msg; /* message text */
|
yuuji@0
|
96 STRING *message; /* message stringstruct */
|
yuuji@0
|
97 } APPENDDATA;
|
yuuji@0
|
98
|
yuuji@0
|
99
|
yuuji@0
|
100 /* Message pointer */
|
yuuji@0
|
101
|
yuuji@0
|
102 typedef struct msg_data {
|
yuuji@0
|
103 MAILSTREAM *stream; /* stream */
|
yuuji@0
|
104 unsigned long msgno; /* message number */
|
yuuji@0
|
105 char *flags; /* current flags */
|
yuuji@0
|
106 char *date; /* current date */
|
yuuji@0
|
107 STRING *message; /* stringstruct of message */
|
yuuji@0
|
108 } MSGDATA;
|
yuuji@0
|
109
|
yuuji@0
|
110 /* Function prototypes */
|
yuuji@0
|
111
|
yuuji@0
|
112 int main (int argc,char *argv[]);
|
yuuji@0
|
113 void ping_mailbox (unsigned long uid);
|
yuuji@0
|
114 time_t palert (char *file,time_t oldtime);
|
yuuji@0
|
115 void msg_string_init (STRING *s,void *data,unsigned long size);
|
yuuji@0
|
116 char msg_string_next (STRING *s);
|
yuuji@0
|
117 void msg_string_setpos (STRING *s,unsigned long i);
|
yuuji@0
|
118 void new_flags (MAILSTREAM *stream);
|
yuuji@0
|
119 void settimeout (unsigned int i);
|
yuuji@0
|
120 void clkint (void);
|
yuuji@0
|
121 void kodint (void);
|
yuuji@0
|
122 void hupint (void);
|
yuuji@0
|
123 void trmint (void);
|
yuuji@0
|
124 void staint (void);
|
yuuji@0
|
125 char *sout (char *s,char *t);
|
yuuji@0
|
126 char *nout (char *s,unsigned long n,unsigned long base);
|
yuuji@0
|
127 void slurp (char *s,int n,unsigned long timeout);
|
yuuji@0
|
128 void inliteral (char *s,unsigned long n);
|
yuuji@0
|
129 unsigned char *flush (void);
|
yuuji@0
|
130 void ioerror (FILE *f,char *reason);
|
yuuji@0
|
131 unsigned char *parse_astring (unsigned char **arg,unsigned long *i,
|
yuuji@0
|
132 unsigned char *del);
|
yuuji@0
|
133 unsigned char *snarf (unsigned char **arg);
|
yuuji@0
|
134 unsigned char *snarf_base64 (unsigned char **arg);
|
yuuji@0
|
135 unsigned char *snarf_list (unsigned char **arg);
|
yuuji@0
|
136 STRINGLIST *parse_stringlist (unsigned char **s,int *list);
|
yuuji@0
|
137 unsigned long uidmax (MAILSTREAM *stream);
|
yuuji@0
|
138 long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
|
yuuji@0
|
139 unsigned long maxuid,unsigned long depth);
|
yuuji@0
|
140 long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long msgmsg,
|
yuuji@0
|
141 unsigned long maxuid,unsigned long depth);
|
yuuji@0
|
142 long crit_date (unsigned short *date,unsigned char **arg);
|
yuuji@0
|
143 long crit_date_work (unsigned short *date,unsigned char **arg);
|
yuuji@0
|
144 long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima);
|
yuuji@0
|
145 long crit_number (unsigned long *number,unsigned char **arg);
|
yuuji@0
|
146 long crit_string (STRINGLIST **string,unsigned char **arg);
|
yuuji@0
|
147
|
yuuji@0
|
148 void fetch (char *t,unsigned long uid);
|
yuuji@0
|
149 typedef void (*fetchfn_t) (unsigned long i,void *args);
|
yuuji@0
|
150 void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[]);
|
yuuji@0
|
151 void fetch_bodystructure (unsigned long i,void *args);
|
yuuji@0
|
152 void fetch_body (unsigned long i,void *args);
|
yuuji@0
|
153 void fetch_body_part_mime (unsigned long i,void *args);
|
yuuji@0
|
154 void fetch_body_part_contents (unsigned long i,void *args);
|
yuuji@0
|
155 void fetch_body_part_binary (unsigned long i,void *args);
|
yuuji@0
|
156 void fetch_body_part_header (unsigned long i,void *args);
|
yuuji@0
|
157 void fetch_body_part_text (unsigned long i,void *args);
|
yuuji@0
|
158 void remember (unsigned long uid,char *id,SIZEDTEXT *st);
|
yuuji@0
|
159 void fetch_envelope (unsigned long i,void *args);
|
yuuji@0
|
160 void fetch_encoding (unsigned long i,void *args);
|
yuuji@0
|
161 void changed_flags (unsigned long i,int f);
|
yuuji@0
|
162 void fetch_flags (unsigned long i,void *args);
|
yuuji@0
|
163 void put_flag (int *c,char *s);
|
yuuji@0
|
164 void fetch_internaldate (unsigned long i,void *args);
|
yuuji@0
|
165 void fetch_uid (unsigned long i,void *args);
|
yuuji@0
|
166 void fetch_rfc822 (unsigned long i,void *args);
|
yuuji@0
|
167 void fetch_rfc822_header (unsigned long i,void *args);
|
yuuji@0
|
168 void fetch_rfc822_size (unsigned long i,void *args);
|
yuuji@0
|
169 void fetch_rfc822_text (unsigned long i,void *args);
|
yuuji@0
|
170 void penv (ENVELOPE *env);
|
yuuji@0
|
171 void pbodystructure (BODY *body);
|
yuuji@0
|
172 void pbody (BODY *body);
|
yuuji@0
|
173 void pparam (PARAMETER *param);
|
yuuji@0
|
174 void paddr (ADDRESS *a);
|
yuuji@0
|
175 void pset (SEARCHSET **set);
|
yuuji@0
|
176 void pnum (unsigned long i);
|
yuuji@0
|
177 void pstring (char *s);
|
yuuji@0
|
178 void pnstring (char *s);
|
yuuji@0
|
179 void pastring (char *s);
|
yuuji@0
|
180 void psizedquoted (SIZEDTEXT *s);
|
yuuji@0
|
181 void psizedliteral (SIZEDTEXT *s,STRING *st);
|
yuuji@0
|
182 void psizedstring (SIZEDTEXT *s,STRING *st);
|
yuuji@0
|
183 void psizedastring (SIZEDTEXT *s);
|
yuuji@0
|
184 void pastringlist (STRINGLIST *s);
|
yuuji@0
|
185 void pnstringorlist (STRINGLIST *s);
|
yuuji@0
|
186 void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
|
yuuji@0
|
187 TEXTARGS *ta);
|
yuuji@0
|
188 void ptext (SIZEDTEXT *s,STRING *st);
|
yuuji@0
|
189 void pthread (THREADNODE *thr);
|
yuuji@0
|
190 void pcapability (long flag);
|
yuuji@0
|
191 long nameok (char *ref,char *name);
|
yuuji@0
|
192 char *bboardname (char *cmd,char *name);
|
yuuji@0
|
193 long isnewsproxy (char *name);
|
yuuji@0
|
194 long newsproxypattern (char *ref,char *pat,char *pattern,long flag);
|
yuuji@0
|
195 char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen);
|
yuuji@0
|
196 long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
|
yuuji@0
|
197 long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
|
yuuji@0
|
198 STRING **message);
|
yuuji@0
|
199 long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
|
yuuji@0
|
200 STRING **message);
|
yuuji@0
|
201 void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
|
yuuji@0
|
202 SEARCHSET *sourceset,SEARCHSET *destset);
|
yuuji@0
|
203 void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set);
|
yuuji@0
|
204 char *referral (MAILSTREAM *stream,char *url,long code);
|
yuuji@0
|
205 void mm_list_work (char *what,int delimiter,char *name,long attributes);
|
yuuji@0
|
206 char *lasterror (void);
|
yuuji@0
|
207
|
yuuji@0
|
208 /* Global storage */
|
yuuji@0
|
209
|
yuuji@0
|
210 char *version = "404"; /* edit number of this server */
|
yuuji@0
|
211 char *logout = "Logout"; /* syslogreason for logout */
|
yuuji@0
|
212 char *goodbye = NIL; /* bye reason */
|
yuuji@0
|
213 time_t alerttime = 0; /* time of last alert */
|
yuuji@0
|
214 time_t sysalerttime = 0; /* time of last system alert */
|
yuuji@0
|
215 time_t useralerttime = 0; /* time of last user alert */
|
yuuji@0
|
216 time_t lastcheck = 0; /* time of last checkpoint */
|
yuuji@0
|
217 time_t shutdowntime = 0; /* time of last shutdown */
|
yuuji@0
|
218 int state = LOGIN; /* server state */
|
yuuji@0
|
219 int cancelled = NIL; /* authenticate cancelled */
|
yuuji@0
|
220 int trycreate = 0; /* saw a trycreate */
|
yuuji@0
|
221 int finding = NIL; /* doing old FIND command */
|
yuuji@0
|
222 int anonymous = 0; /* non-zero if anonymous */
|
yuuji@0
|
223 int critical = NIL; /* non-zero if in critical code */
|
yuuji@0
|
224 int quell_events = NIL; /* non-zero if in FETCH response */
|
yuuji@0
|
225 int existsquelled = NIL; /* non-zero if an EXISTS was quelled */
|
yuuji@0
|
226 int proxylist = NIL; /* doing a proxy LIST */
|
yuuji@0
|
227 MAILSTREAM *stream = NIL; /* mailbox stream */
|
yuuji@0
|
228 DRIVER *curdriver = NIL; /* note current driver */
|
yuuji@0
|
229 MAILSTREAM *tstream = NIL; /* temporary mailbox stream */
|
yuuji@0
|
230 unsigned int nflags = 0; /* current number of keywords */
|
yuuji@0
|
231 unsigned long nmsgs =0xffffffff;/* last reported # of messages and recent */
|
yuuji@0
|
232 unsigned long recent = 0xffffffff;
|
yuuji@0
|
233 char *nntpproxy = NIL; /* NNTP proxy name */
|
yuuji@0
|
234 unsigned char *user = NIL; /* user name */
|
yuuji@0
|
235 unsigned char *pass = NIL; /* password */
|
yuuji@0
|
236 unsigned char *initial = NIL; /* initial response */
|
yuuji@0
|
237 unsigned char cmdbuf[CMDLEN]; /* command buffer */
|
yuuji@0
|
238 char *status = "starting up"; /* server status */
|
yuuji@0
|
239 char *tag; /* tag portion of command */
|
yuuji@0
|
240 unsigned char *cmd; /* command portion of command */
|
yuuji@0
|
241 unsigned char *arg; /* pointer to current argument of command */
|
yuuji@0
|
242 char *lstwrn = NIL; /* last warning message from c-client */
|
yuuji@0
|
243 char *lsterr = NIL; /* last error message from c-client */
|
yuuji@0
|
244 char *lstref = NIL; /* last referral from c-client */
|
yuuji@0
|
245 char *response = NIL; /* command response */
|
yuuji@0
|
246 struct {
|
yuuji@0
|
247 unsigned long size; /* size of current LITERAL+ */
|
yuuji@0
|
248 unsigned int ok : 1; /* LITERAL+ in effect */
|
yuuji@0
|
249 } litplus;
|
yuuji@0
|
250 int litsp = 0; /* literal stack pointer */
|
yuuji@0
|
251 char *litstk[LITSTKLEN]; /* stack to hold literals */
|
yuuji@0
|
252 unsigned long uidvalidity = 0; /* last reported UID validity */
|
yuuji@0
|
253 unsigned long lastuid = 0; /* last fetched uid */
|
yuuji@0
|
254 char *lastid = NIL; /* last fetched body id for this message */
|
yuuji@0
|
255 char *lastsel = NIL; /* last selected mailbox name */
|
yuuji@0
|
256 SIZEDTEXT lastst = {NIL,0}; /* last sizedtext */
|
yuuji@0
|
257 unsigned long cauidvalidity = 0;/* UIDVALIDITY for COPYUID/APPENDUID */
|
yuuji@0
|
258 SEARCHSET *csset = NIL; /* COPYUID source set */
|
yuuji@0
|
259 SEARCHSET *caset = NIL; /* COPYUID/APPENDUID destination set */
|
yuuji@0
|
260 jmp_buf jmpenv; /* stack context for setjmp */
|
yuuji@0
|
261
|
yuuji@0
|
262
|
yuuji@0
|
263 /* Response texts which appear in multiple places */
|
yuuji@0
|
264
|
yuuji@0
|
265 char *win = "%.80s OK ";
|
yuuji@0
|
266 char *rowin = "%.80s OK [READ-ONLY] %.80s completed\015\012";
|
yuuji@0
|
267 char *rwwin = "%.80s OK [READ-WRITE] %.80s completed\015\012";
|
yuuji@0
|
268 char *lose = "%.80s NO ";
|
yuuji@0
|
269 char *logwin = "%.80s OK [";
|
yuuji@0
|
270 char *losetry = "%.80s NO [TRYCREATE] %.80s failed: %.900s\015\012";
|
yuuji@0
|
271 char *loseunknowncte = "%.80s NO [UNKNOWN-CTE] %.80s failed: %.900s\015\012";
|
yuuji@0
|
272 char *badcmd = "%.80s BAD Command unrecognized: %.80s\015\012";
|
yuuji@0
|
273 char *misarg = "%.80s BAD Missing or invalid argument to %.80s\015\012";
|
yuuji@0
|
274 char *badarg = "%.80s BAD Argument given to %.80s when none expected\015\012";
|
yuuji@0
|
275 char *badseq = "%.80s BAD Bogus sequence in %.80s: %.80s\015\012";
|
yuuji@0
|
276 char *badatt = "%.80s BAD Bogus attribute list in %.80s\015\012";
|
yuuji@0
|
277 char *badbin = "%.80s BAD Syntax error in binary specifier\015\012";
|
yuuji@0
|
278
|
yuuji@0
|
279 /* Message string driver for message stringstructs */
|
yuuji@0
|
280
|
yuuji@0
|
281 STRINGDRIVER msg_string = {
|
yuuji@0
|
282 msg_string_init, /* initialize string structure */
|
yuuji@0
|
283 msg_string_next, /* get next byte in string structure */
|
yuuji@0
|
284 msg_string_setpos /* set position in string structure */
|
yuuji@0
|
285 };
|
yuuji@0
|
286
|
yuuji@0
|
287 /* Main program */
|
yuuji@0
|
288
|
yuuji@0
|
289 int main (int argc,char *argv[])
|
yuuji@0
|
290 {
|
yuuji@0
|
291 unsigned long i,uid;
|
yuuji@0
|
292 long f;
|
yuuji@0
|
293 unsigned char *s,*t,*u,*v,tmp[MAILTMPLEN];
|
yuuji@0
|
294 struct stat sbuf;
|
yuuji@0
|
295 logouthook_t lgoh;
|
yuuji@0
|
296 int ret = 0;
|
yuuji@0
|
297 time_t autologouttime = 0;
|
yuuji@0
|
298 char *pgmname;
|
yuuji@0
|
299 /* if case we get borked immediately */
|
yuuji@0
|
300 if (setjmp (jmpenv)) _exit (1);
|
yuuji@0
|
301 pgmname = (argc && argv[0]) ?
|
yuuji@0
|
302 (((s = strrchr (argv[0],'/')) || (s = strrchr (argv[0],'\\'))) ?
|
yuuji@0
|
303 (char *) s+1 : argv[0]) : "imapd";
|
yuuji@0
|
304 /* set service name before linkage */
|
yuuji@0
|
305 mail_parameters (NIL,SET_SERVICENAME,(void *) "imap");
|
yuuji@0
|
306 #include "linkage.c"
|
yuuji@0
|
307 rfc822_date (tmp); /* get date/time at startup */
|
yuuji@0
|
308 /* initialize server */
|
yuuji@0
|
309 server_init (pgmname,"imap","imaps",clkint,kodint,hupint,trmint,staint);
|
yuuji@0
|
310 /* forbid automatic untagged expunge */
|
yuuji@0
|
311 mail_parameters (NIL,SET_EXPUNGEATPING,NIL);
|
yuuji@0
|
312 /* arm proxy copy callback */
|
yuuji@0
|
313 mail_parameters (NIL,SET_MAILPROXYCOPY,(void *) proxycopy);
|
yuuji@0
|
314 /* arm referral callback */
|
yuuji@0
|
315 mail_parameters (NIL,SET_IMAPREFERRAL,(void *) referral);
|
yuuji@0
|
316 /* arm COPYUID callback */
|
yuuji@0
|
317 mail_parameters (NIL,SET_COPYUID,(void *) copyuid);
|
yuuji@0
|
318 /* arm APPENDUID callback */
|
yuuji@0
|
319 mail_parameters (NIL,SET_APPENDUID,(void *) appenduid);
|
yuuji@0
|
320
|
yuuji@0
|
321 if (stat (SHUTDOWNFILE,&sbuf)) {
|
yuuji@0
|
322 char proxy[MAILTMPLEN];
|
yuuji@0
|
323 FILE *nntp = fopen (NNTPFILE,"r");
|
yuuji@0
|
324 if (nntp) { /* desire NNTP proxy? */
|
yuuji@0
|
325 if (fgets (proxy,MAILTMPLEN,nntp)) {
|
yuuji@0
|
326 /* remove newline and set NNTP proxy */
|
yuuji@0
|
327 if (s = strchr (proxy,'\n')) *s = '\0';
|
yuuji@0
|
328 nntpproxy = cpystr (proxy);
|
yuuji@0
|
329 /* disable the news driver */
|
yuuji@0
|
330 mail_parameters (NIL,DISABLE_DRIVER,"news");
|
yuuji@0
|
331 }
|
yuuji@0
|
332 fclose (nntp); /* done reading proxy name */
|
yuuji@0
|
333 }
|
yuuji@0
|
334 s = myusername_full (&i); /* get user name and flags */
|
yuuji@0
|
335 switch (i) {
|
yuuji@0
|
336 case MU_NOTLOGGEDIN:
|
yuuji@0
|
337 PSOUT ("* OK ["); /* not logged in, ordinary startup */
|
yuuji@0
|
338 pcapability (-1);
|
yuuji@0
|
339 break;
|
yuuji@0
|
340 case MU_ANONYMOUS:
|
yuuji@0
|
341 anonymous = T; /* anonymous user, fall into default */
|
yuuji@0
|
342 s = "ANONYMOUS";
|
yuuji@0
|
343 case MU_LOGGEDIN:
|
yuuji@0
|
344 PSOUT ("* PREAUTH ["); /* already logged in, pre-authorized */
|
yuuji@0
|
345 pcapability (1);
|
yuuji@0
|
346 user = cpystr (s); /* copy user name */
|
yuuji@0
|
347 pass = cpystr ("*"); /* set fake password */
|
yuuji@0
|
348 state = SELECT; /* enter select state */
|
yuuji@0
|
349 break;
|
yuuji@0
|
350 default:
|
yuuji@0
|
351 fatal ("Unknown state from myusername_full()");
|
yuuji@0
|
352 }
|
yuuji@0
|
353 PSOUT ("] ");
|
yuuji@0
|
354 if (user) { /* preauthenticated as someone? */
|
yuuji@0
|
355 PSOUT ("Pre-authenticated user ");
|
yuuji@0
|
356 PSOUT (user);
|
yuuji@0
|
357 PBOUT (' ');
|
yuuji@0
|
358 }
|
yuuji@0
|
359 }
|
yuuji@0
|
360 else { /* login disabled */
|
yuuji@0
|
361 PSOUT ("* BYE Service not available ");
|
yuuji@0
|
362 state = LOGOUT;
|
yuuji@0
|
363 }
|
yuuji@0
|
364 PSOUT (tcp_serverhost ());
|
yuuji@0
|
365 PSOUT (" IMAP4rev1 ");
|
yuuji@0
|
366 PSOUT (CCLIENTVERSION);
|
yuuji@0
|
367 PBOUT ('.');
|
yuuji@0
|
368 PSOUT (version);
|
yuuji@0
|
369 PSOUT (" at ");
|
yuuji@0
|
370 PSOUT (tmp);
|
yuuji@0
|
371 CRLF;
|
yuuji@0
|
372 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
373 switch (state) { /* do this after the banner */
|
yuuji@0
|
374 case LOGIN:
|
yuuji@0
|
375 autologouttime = time (0) + LOGINTIMEOUT;
|
yuuji@0
|
376 break;
|
yuuji@0
|
377 case SELECT:
|
yuuji@0
|
378 syslog (LOG_INFO,"Preauthenticated user=%.80s host=%.80s",
|
yuuji@0
|
379 user,tcp_clienthost ());
|
yuuji@0
|
380 break;
|
yuuji@0
|
381 }
|
yuuji@0
|
382
|
yuuji@0
|
383 if (setjmp (jmpenv)) { /* die if a signal handler say so */
|
yuuji@0
|
384 /* in case we get borked now */
|
yuuji@0
|
385 if (setjmp (jmpenv)) _exit (1);
|
yuuji@0
|
386 /* need to close stream gracefully? */
|
yuuji@0
|
387 if (stream && !stream->lock && (stream->dtb->flags & DR_XPOINT))
|
yuuji@0
|
388 stream = mail_close (stream);
|
yuuji@0
|
389 ret = 1; /* set exit status */
|
yuuji@0
|
390 }
|
yuuji@0
|
391 else while (state != LOGOUT) {/* command processing loop */
|
yuuji@0
|
392 slurp (cmdbuf,CMDLEN,TIMEOUT);
|
yuuji@0
|
393 /* no more last error or literal */
|
yuuji@0
|
394 if (lstwrn) fs_give ((void **) &lstwrn);
|
yuuji@0
|
395 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
396 if (lstref) fs_give ((void **) &lstref);
|
yuuji@0
|
397 while (litsp) fs_give ((void **) &litstk[--litsp]);
|
yuuji@0
|
398 /* find end of line */
|
yuuji@0
|
399 if (!strchr (cmdbuf,'\012')) {
|
yuuji@0
|
400 if (t = strchr (cmdbuf,' ')) *t = '\0';
|
yuuji@0
|
401 if ((t - cmdbuf) > 100) t = NIL;
|
yuuji@0
|
402 flush (); /* flush excess */
|
yuuji@0
|
403 if (state == LOGIN) /* error if NLI */
|
yuuji@0
|
404 syslog (LOG_INFO,"Line too long before authentication host=%.80s",
|
yuuji@0
|
405 tcp_clienthost ());
|
yuuji@0
|
406 sprintf (tmp,response,t ? (char *) cmdbuf : "*");
|
yuuji@0
|
407 PSOUT (tmp);
|
yuuji@0
|
408 }
|
yuuji@0
|
409 else if (!(tag = strtok (cmdbuf," \015\012"))) {
|
yuuji@0
|
410 if (state == LOGIN) /* error if NLI */
|
yuuji@0
|
411 syslog (LOG_INFO,"Null command before authentication host=%.80s",
|
yuuji@0
|
412 tcp_clienthost ());
|
yuuji@0
|
413 PSOUT ("* BAD Null command\015\012");
|
yuuji@0
|
414 }
|
yuuji@0
|
415 else if (strlen (tag) > 50) PSOUT ("* BAD Excessively long tag\015\012");
|
yuuji@0
|
416 else if (!(s = strtok (NIL," \015\012"))) {
|
yuuji@0
|
417 if (state == LOGIN) /* error if NLI */
|
yuuji@0
|
418 syslog (LOG_INFO,"Missing command before authentication host=%.80s",
|
yuuji@0
|
419 tcp_clienthost ());
|
yuuji@0
|
420 PSOUT (tag);
|
yuuji@0
|
421 PSOUT (" BAD Missing command\015\012");
|
yuuji@0
|
422 }
|
yuuji@0
|
423 else { /* parse command */
|
yuuji@0
|
424 response = win; /* set default response */
|
yuuji@0
|
425 finding = NIL; /* no longer FINDing */
|
yuuji@0
|
426 ucase (s); /* canonicalize command case */
|
yuuji@0
|
427 /* UID command? */
|
yuuji@0
|
428 if (!strcmp (s,"UID") && strtok (NIL," \015\012")) {
|
yuuji@0
|
429 uid = T; /* a UID command */
|
yuuji@0
|
430 s[3] = ' '; /* restore the space delimiter */
|
yuuji@0
|
431 ucase (s); /* make sure command all uppercase */
|
yuuji@0
|
432 }
|
yuuji@0
|
433 else uid = NIL; /* not a UID command */
|
yuuji@0
|
434 /* flush previous saved command */
|
yuuji@0
|
435 if (cmd) fs_give ((void **) &cmd);
|
yuuji@0
|
436 cmd = cpystr (s); /* save current command */
|
yuuji@0
|
437 /* snarf argument, see if possible litplus */
|
yuuji@0
|
438 if ((arg = strtok (NIL,"\015\012")) && ((i = strlen (arg)) > 3) &&
|
yuuji@0
|
439 (arg[i - 1] == '}') && (arg[i - 2] == '+') && isdigit (arg[i - 3])) {
|
yuuji@0
|
440 /* back over possible count */
|
yuuji@0
|
441 for (i -= 4; i && isdigit (arg[i]); i--);
|
yuuji@0
|
442 if (arg[i] == '{') { /* found a literal? */
|
yuuji@0
|
443 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
|
yuuji@0
|
444 litplus.size = strtoul (arg + i + 1,NIL,10);
|
yuuji@0
|
445 }
|
yuuji@0
|
446 }
|
yuuji@0
|
447
|
yuuji@0
|
448 /* these commands always valid */
|
yuuji@0
|
449 if (!strcmp (cmd,"NOOP")) {
|
yuuji@0
|
450 if (arg) response = badarg;
|
yuuji@0
|
451 else if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
452 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
453 }
|
yuuji@0
|
454 else if (!strcmp (cmd,"LOGOUT")) {
|
yuuji@0
|
455 if (arg) response = badarg;
|
yuuji@0
|
456 else { /* time to say farewell */
|
yuuji@0
|
457 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
458 if (lastsel) fs_give ((void **) &lastsel);
|
yuuji@0
|
459 if (state == OPEN) stream = mail_close (stream);
|
yuuji@0
|
460 state = LOGOUT;
|
yuuji@0
|
461 PSOUT ("* BYE ");
|
yuuji@0
|
462 PSOUT (mylocalhost ());
|
yuuji@0
|
463 PSOUT (" IMAP4rev1 server terminating connection\015\012");
|
yuuji@0
|
464 }
|
yuuji@0
|
465 }
|
yuuji@0
|
466 else if (!strcmp (cmd,"CAPABILITY")) {
|
yuuji@0
|
467 if (arg) response = badarg;
|
yuuji@0
|
468 else {
|
yuuji@0
|
469 PSOUT ("* ");
|
yuuji@0
|
470 pcapability (0); /* print capabilities */
|
yuuji@0
|
471 CRLF;
|
yuuji@0
|
472 }
|
yuuji@0
|
473 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
474 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
475 }
|
yuuji@0
|
476 #ifdef NETSCAPE_BRAIN_DAMAGE
|
yuuji@0
|
477 else if (!strcmp (cmd,"NETSCAPE")) {
|
yuuji@0
|
478 PSOUT ("* OK [NETSCAPE]\015\012* VERSION 1.0 UNIX\015\012* ACCOUNT-URL \"");
|
yuuji@0
|
479 PSOUT (NETSCAPE_BRAIN_DAMAGE);
|
yuuji@0
|
480 PBOUT ('"');
|
yuuji@0
|
481 CRLF;
|
yuuji@0
|
482 }
|
yuuji@0
|
483 #endif
|
yuuji@0
|
484
|
yuuji@0
|
485 else switch (state) { /* dispatch depending upon state */
|
yuuji@0
|
486 case LOGIN: /* waiting to get logged in */
|
yuuji@0
|
487 /* new style authentication */
|
yuuji@0
|
488 if (!strcmp (cmd,"AUTHENTICATE")) {
|
yuuji@0
|
489 if (user) fs_give ((void **) &user);
|
yuuji@0
|
490 if (pass) fs_give ((void **) &pass);
|
yuuji@0
|
491 initial = NIL; /* no initial argument */
|
yuuji@0
|
492 cancelled = NIL; /* not cancelled */
|
yuuji@0
|
493 /* mandatory first argument */
|
yuuji@0
|
494 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
495 else if (arg && !(initial = snarf_base64 (&arg)))
|
yuuji@0
|
496 response = misarg; /* optional second argument */
|
yuuji@0
|
497 else if (arg) response = badarg;
|
yuuji@0
|
498 else if (!strcmp (ucase (s),"ANONYMOUS") && !stat (ANOFILE,&sbuf)) {
|
yuuji@0
|
499 if (!(s = imap_responder ("",0,NIL)))
|
yuuji@0
|
500 response ="%.80s BAD AUTHENTICATE ANONYMOUS cancelled\015\012";
|
yuuji@0
|
501 else if (anonymous_login (argc,argv)) {
|
yuuji@0
|
502 anonymous = T; /* note we are anonymous */
|
yuuji@0
|
503 user = cpystr ("ANONYMOUS");
|
yuuji@0
|
504 pass = cpystr ("*");
|
yuuji@0
|
505 state = SELECT; /* make select */
|
yuuji@0
|
506 alerttime = 0; /* force alert */
|
yuuji@0
|
507 response = logwin;/* return logged-in capabilities */
|
yuuji@0
|
508 syslog (LOG_INFO,"Authenticated anonymous=%.80s host=%.80s",s,
|
yuuji@0
|
509 tcp_clienthost ());
|
yuuji@0
|
510 fs_give ((void **) &s);
|
yuuji@0
|
511 }
|
yuuji@0
|
512 else response ="%.80s NO AUTHENTICATE ANONYMOUS failed\015\012";
|
yuuji@0
|
513 }
|
yuuji@0
|
514 else if (user = cpystr (mail_auth (s,imap_responder,argc,argv))) {
|
yuuji@0
|
515 pass = cpystr ("*");
|
yuuji@0
|
516 state = SELECT; /* make select */
|
yuuji@0
|
517 alerttime = 0; /* force alert */
|
yuuji@0
|
518 response = logwin; /* return logged-in capabilities */
|
yuuji@0
|
519 syslog (LOG_INFO,"Authenticated user=%.80s host=%.80s mech=%.80s",
|
yuuji@0
|
520 user,tcp_clienthost (),s);
|
yuuji@0
|
521 }
|
yuuji@0
|
522
|
yuuji@0
|
523 else {
|
yuuji@0
|
524 AUTHENTICATOR *auth = mail_lookup_auth (1);
|
yuuji@0
|
525 char *msg = (char *) fs_get (strlen (cmd) + strlen (s) + 2);
|
yuuji@0
|
526 sprintf (msg,"%s %s",cmd,s);
|
yuuji@0
|
527 fs_give ((void **) &cmd);
|
yuuji@0
|
528 cmd = msg;
|
yuuji@0
|
529 for (i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL);
|
yuuji@0
|
530 auth && compare_cstring (s,auth->name); auth = auth->next);
|
yuuji@0
|
531 /* Failed authentication when hidden looks like invalid command.
|
yuuji@0
|
532 * This is intentional but confused me when I was debugging.
|
yuuji@0
|
533 */
|
yuuji@0
|
534 if (auth && auth->server && !(auth->flags & AU_DISABLE) &&
|
yuuji@0
|
535 !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
|
yuuji@0
|
536 response = lose;
|
yuuji@0
|
537 if (cancelled) {
|
yuuji@0
|
538 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
539 lsterr = cpystr ("cancelled by user");
|
yuuji@0
|
540 }
|
yuuji@0
|
541 if (!lsterr) /* catch-all */
|
yuuji@0
|
542 lsterr = cpystr ("Invalid authentication credentials");
|
yuuji@0
|
543 syslog (LOG_INFO,"AUTHENTICATE %.80s failure host=%.80s",s,
|
yuuji@0
|
544 tcp_clienthost ());
|
yuuji@0
|
545 }
|
yuuji@0
|
546 else {
|
yuuji@0
|
547 response = badcmd;
|
yuuji@0
|
548 syslog (LOG_INFO,"AUTHENTICATE %.80s invalid host=%.80s",s,
|
yuuji@0
|
549 tcp_clienthost ());
|
yuuji@0
|
550 }
|
yuuji@0
|
551 }
|
yuuji@0
|
552 }
|
yuuji@0
|
553
|
yuuji@0
|
554 /* plaintext login with password */
|
yuuji@0
|
555 else if (!strcmp (cmd,"LOGIN")) {
|
yuuji@0
|
556 if (user) fs_give ((void **) &user);
|
yuuji@0
|
557 if (pass) fs_give ((void **) &pass);
|
yuuji@0
|
558 /* two arguments */
|
yuuji@0
|
559 if (!((user = cpystr (snarf (&arg))) &&
|
yuuji@0
|
560 (pass = cpystr (snarf (&arg))))) response = misarg;
|
yuuji@0
|
561 else if (arg) response = badarg;
|
yuuji@0
|
562 /* see if we allow anonymous */
|
yuuji@0
|
563 else if (!compare_cstring (user,"ANONYMOUS") &&
|
yuuji@0
|
564 !stat (ANOFILE,&sbuf) && anonymous_login (argc,argv)) {
|
yuuji@0
|
565 anonymous = T; /* note we are anonymous */
|
yuuji@0
|
566 ucase (user); /* make all uppercase for consistency */
|
yuuji@0
|
567 state = SELECT; /* make select */
|
yuuji@0
|
568 alerttime = 0; /* force alert */
|
yuuji@0
|
569 response = logwin; /* return logged-in capabilities */
|
yuuji@0
|
570 syslog (LOG_INFO,"Login anonymous=%.80s host=%.80s",pass,
|
yuuji@0
|
571 tcp_clienthost ());
|
yuuji@0
|
572 }
|
yuuji@0
|
573 else { /* delimit user from possible admin */
|
yuuji@0
|
574 if (s = strchr (user,'*')) *s++ ='\0';
|
yuuji@0
|
575 /* see if username and password are OK */
|
yuuji@0
|
576 if (server_login (user,pass,s,argc,argv)) {
|
yuuji@0
|
577 state = SELECT; /* make select */
|
yuuji@0
|
578 alerttime = 0; /* force alert */
|
yuuji@0
|
579 response = logwin;/* return logged-in capabilities */
|
yuuji@0
|
580 syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,
|
yuuji@0
|
581 tcp_clienthost ());
|
yuuji@0
|
582 }
|
yuuji@0
|
583 else {
|
yuuji@0
|
584 response = lose;
|
yuuji@0
|
585 if (!lsterr) lsterr = cpystr ("Invalid login credentials");
|
yuuji@0
|
586 }
|
yuuji@0
|
587 }
|
yuuji@0
|
588 }
|
yuuji@0
|
589 /* start TLS security */
|
yuuji@0
|
590 else if (!strcmp (cmd,"STARTTLS")) {
|
yuuji@0
|
591 if (arg) response = badarg;
|
yuuji@0
|
592 else if (lsterr = ssl_start_tls (pgmname)) response = lose;
|
yuuji@0
|
593 }
|
yuuji@0
|
594 else response = badcmd;
|
yuuji@0
|
595 break;
|
yuuji@0
|
596
|
yuuji@0
|
597 case OPEN: /* valid only when mailbox open */
|
yuuji@0
|
598 /* fetch mailbox attributes */
|
yuuji@0
|
599 if (!strcmp (cmd,"FETCH") || !strcmp (cmd,"UID FETCH")) {
|
yuuji@0
|
600 if (!(arg && (s = strtok (arg," ")) && (t = strtok(NIL,"\015\012"))))
|
yuuji@0
|
601 response = misarg;
|
yuuji@0
|
602 else if (uid ? mail_uid_sequence (stream,s) :
|
yuuji@0
|
603 mail_sequence (stream,s)) fetch (t,uid);
|
yuuji@0
|
604 else response = badseq;
|
yuuji@0
|
605 }
|
yuuji@0
|
606 /* store mailbox attributes */
|
yuuji@0
|
607 else if (!strcmp (cmd,"STORE") || !strcmp (cmd,"UID STORE")) {
|
yuuji@0
|
608 /* must have three arguments */
|
yuuji@0
|
609 if (!(arg && (s = strtok (arg," ")) && (v = strtok (NIL," ")) &&
|
yuuji@0
|
610 (t = strtok (NIL,"\015\012")))) response = misarg;
|
yuuji@0
|
611 else if (!(uid ? mail_uid_sequence (stream,s) :
|
yuuji@0
|
612 mail_sequence (stream,s))) response = badseq;
|
yuuji@0
|
613 else {
|
yuuji@0
|
614 f = ST_SET | (uid ? ST_UID : NIL)|((v[5]&&v[6]) ? ST_SILENT : NIL);
|
yuuji@0
|
615 if (!strcmp (ucase (v),"FLAGS") || !strcmp (v,"FLAGS.SILENT")) {
|
yuuji@0
|
616 strcpy (tmp,"\\Answered \\Flagged \\Deleted \\Draft \\Seen");
|
yuuji@0
|
617 for (i = 0, u = tmp;
|
yuuji@0
|
618 (i < NUSERFLAGS) && (v = stream->user_flags[i]); i++)
|
yuuji@0
|
619 if (strlen (v) <
|
yuuji@0
|
620 ((size_t) (MAILTMPLEN - ((u += strlen (u)) + 2 - tmp)))) {
|
yuuji@0
|
621 *u++ = ' '; /* write next flag */
|
yuuji@0
|
622 strcpy (u,v);
|
yuuji@0
|
623 }
|
yuuji@0
|
624 mail_flag (stream,s,tmp,f & ~ST_SET);
|
yuuji@0
|
625 }
|
yuuji@0
|
626 else if (!strcmp (v,"-FLAGS") || !strcmp (v,"-FLAGS.SILENT"))
|
yuuji@0
|
627 f &= ~ST_SET; /* clear flags */
|
yuuji@0
|
628 else if (strcmp (v,"+FLAGS") && strcmp (v,"+FLAGS.SILENT")) {
|
yuuji@0
|
629 response = badatt;
|
yuuji@0
|
630 break;
|
yuuji@0
|
631 }
|
yuuji@0
|
632 /* find last keyword */
|
yuuji@0
|
633 for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; i++);
|
yuuji@0
|
634 mail_flag (stream,s,t,f);
|
yuuji@0
|
635 /* any new keywords appeared? */
|
yuuji@0
|
636 if (i < NUSERFLAGS && stream->user_flags[i]) new_flags (stream);
|
yuuji@0
|
637 /* return flags if silence not wanted */
|
yuuji@0
|
638 if (uid ? mail_uid_sequence (stream,s) : mail_sequence (stream,s))
|
yuuji@0
|
639 for (i = 1; i <= nmsgs; i++) if (mail_elt(stream,i)->sequence)
|
yuuji@0
|
640 mail_elt (stream,i)->spare2 = (f & ST_SILENT) ? NIL : T;
|
yuuji@0
|
641 }
|
yuuji@0
|
642 }
|
yuuji@0
|
643
|
yuuji@0
|
644 /* check for new mail */
|
yuuji@0
|
645 else if (!strcmp (cmd,"CHECK")) {
|
yuuji@0
|
646 /* no arguments */
|
yuuji@0
|
647 if (arg) response = badarg;
|
yuuji@0
|
648 else if (!anonymous) {
|
yuuji@0
|
649 mail_check (stream);
|
yuuji@0
|
650 /* remember last check time */
|
yuuji@0
|
651 lastcheck = time (0);
|
yuuji@0
|
652 }
|
yuuji@0
|
653 }
|
yuuji@0
|
654 /* expunge deleted messages */
|
yuuji@0
|
655 else if (!(anonymous || (strcmp (cmd,"EXPUNGE") &&
|
yuuji@0
|
656 strcmp (cmd,"UID EXPUNGE")))) {
|
yuuji@0
|
657 if (uid && !arg) response = misarg;
|
yuuji@0
|
658 else if (!uid && arg) response = badarg;
|
yuuji@0
|
659 else { /* expunge deleted or specified UIDs */
|
yuuji@0
|
660 mail_expunge_full (stream,arg,arg ? EX_UID : NIL);
|
yuuji@0
|
661 /* remember last checkpoint */
|
yuuji@0
|
662 lastcheck = time (0);
|
yuuji@0
|
663 }
|
yuuji@0
|
664 }
|
yuuji@0
|
665 /* close mailbox */
|
yuuji@0
|
666 else if (!strcmp (cmd,"CLOSE") || !strcmp (cmd,"UNSELECT")) {
|
yuuji@0
|
667 /* no arguments */
|
yuuji@0
|
668 if (arg) response = badarg;
|
yuuji@0
|
669 else {
|
yuuji@0
|
670 /* no last uid */
|
yuuji@0
|
671 uidvalidity = lastuid = 0;
|
yuuji@0
|
672 if (lastsel) fs_give ((void **) &lastsel);
|
yuuji@0
|
673 if (lastid) fs_give ((void **) &lastid);
|
yuuji@0
|
674 if (lastst.data) fs_give ((void **) &lastst.data);
|
yuuji@0
|
675 stream = mail_close_full (stream,((*cmd == 'C') && !anonymous) ?
|
yuuji@0
|
676 CL_EXPUNGE : NIL);
|
yuuji@0
|
677 state = SELECT; /* no longer opened */
|
yuuji@0
|
678 lastcheck = 0; /* no last checkpoint */
|
yuuji@0
|
679 }
|
yuuji@0
|
680 }
|
yuuji@0
|
681 else if (!anonymous && /* copy message(s) */
|
yuuji@0
|
682 (!strcmp (cmd,"COPY") || !strcmp (cmd,"UID COPY"))) {
|
yuuji@0
|
683 trycreate = NIL; /* no trycreate status */
|
yuuji@0
|
684 if (!(arg && (s = strtok (arg," ")) && (arg = strtok(NIL,"\015\012"))
|
yuuji@0
|
685 && (t = snarf (&arg)))) response = misarg;
|
yuuji@0
|
686 else if (arg) response = badarg;
|
yuuji@0
|
687 else if (!nmsgs) {
|
yuuji@0
|
688 response = lose;
|
yuuji@0
|
689 if (!lsterr) lsterr = cpystr ("Mailbox is empty");
|
yuuji@0
|
690 }
|
yuuji@0
|
691 else if (!(uid ? mail_uid_sequence (stream,s) :
|
yuuji@0
|
692 mail_sequence (stream,s))) response = badseq;
|
yuuji@0
|
693 /* try copy */
|
yuuji@0
|
694 else if (!mail_copy_full (stream,s,t,uid ? CP_UID : NIL)) {
|
yuuji@0
|
695 response = trycreate ? losetry : lose;
|
yuuji@0
|
696 if (!lsterr) lsterr = cpystr ("No such destination mailbox");
|
yuuji@0
|
697 }
|
yuuji@0
|
698 }
|
yuuji@0
|
699
|
yuuji@0
|
700 /* sort mailbox */
|
yuuji@0
|
701 else if (!strcmp (cmd,"SORT") || !strcmp (cmd,"UID SORT")) {
|
yuuji@0
|
702 /* must have four arguments */
|
yuuji@0
|
703 if (!(arg && (*arg == '(') && (t = strchr (s = arg + 1,')')) &&
|
yuuji@0
|
704 (t[1] == ' ') && (*(arg = t + 2)))) response = misarg;
|
yuuji@0
|
705 else { /* read criteria */
|
yuuji@0
|
706 SEARCHPGM *spg = NIL;
|
yuuji@0
|
707 char *cs = NIL;
|
yuuji@0
|
708 SORTPGM *pgm = NIL,*pg = NIL;
|
yuuji@0
|
709 unsigned long *slst,*sl;
|
yuuji@0
|
710 *t = NIL; /* tie off criteria list */
|
yuuji@0
|
711 if (!(s = strtok (ucase (s)," "))) response = badatt;
|
yuuji@0
|
712 else {
|
yuuji@0
|
713 do { /* parse sort attributes */
|
yuuji@0
|
714 if (pg) pg = pg->next = mail_newsortpgm ();
|
yuuji@0
|
715 else pgm = pg = mail_newsortpgm ();
|
yuuji@0
|
716 if (!strcmp (s,"REVERSE")) {
|
yuuji@0
|
717 pg->reverse = T;
|
yuuji@0
|
718 if (!(s = strtok (NIL," "))) {
|
yuuji@0
|
719 s = ""; /* end of attributes */
|
yuuji@0
|
720 break;
|
yuuji@0
|
721 }
|
yuuji@0
|
722 }
|
yuuji@0
|
723 if (!strcmp (s,"DATE")) pg->function = SORTDATE;
|
yuuji@0
|
724 else if (!strcmp (s,"ARRIVAL")) pg->function = SORTARRIVAL;
|
yuuji@0
|
725 else if (!strcmp (s,"FROM")) pg->function = SORTFROM;
|
yuuji@0
|
726 else if (!strcmp (s,"SUBJECT")) pg->function = SORTSUBJECT;
|
yuuji@0
|
727 else if (!strcmp (s,"TO")) pg->function = SORTTO;
|
yuuji@0
|
728 else if (!strcmp (s,"CC")) pg->function = SORTCC;
|
yuuji@0
|
729 else if (!strcmp (s,"SIZE")) pg->function = SORTSIZE;
|
yuuji@0
|
730 else break;
|
yuuji@0
|
731 } while (s = strtok (NIL," "));
|
yuuji@0
|
732 /* bad SORT attribute */
|
yuuji@0
|
733 if (s) response = badatt;
|
yuuji@0
|
734 /* get charset and search criteria */
|
yuuji@0
|
735 else if (!((t = snarf (&arg)) && (cs = cpystr (t)) && arg &&
|
yuuji@0
|
736 *arg)) response = misarg;
|
yuuji@0
|
737 /* parse search criteria */
|
yuuji@0
|
738 else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
|
yuuji@0
|
739 uidmax (stream),0)) response = badatt;
|
yuuji@0
|
740 else if (arg && *arg) response = badarg;
|
yuuji@0
|
741 else if (slst = mail_sort (stream,cs,spg,pgm,uid ? SE_UID:NIL)) {
|
yuuji@0
|
742 PSOUT ("* SORT");
|
yuuji@0
|
743 for (sl = slst; *sl; sl++) {
|
yuuji@0
|
744 PBOUT (' ');
|
yuuji@0
|
745 pnum (*sl);
|
yuuji@0
|
746 }
|
yuuji@0
|
747 CRLF;
|
yuuji@0
|
748 fs_give ((void **) &slst);
|
yuuji@0
|
749 }
|
yuuji@0
|
750 }
|
yuuji@0
|
751 if (pgm) mail_free_sortpgm (&pgm);
|
yuuji@0
|
752 if (spg) mail_free_searchpgm (&spg);
|
yuuji@0
|
753 if (cs) fs_give ((void **) &cs);
|
yuuji@0
|
754 }
|
yuuji@0
|
755 }
|
yuuji@0
|
756
|
yuuji@0
|
757 /* thread mailbox */
|
yuuji@0
|
758 else if (!strcmp (cmd,"THREAD") || !strcmp (cmd,"UID THREAD")) {
|
yuuji@0
|
759 THREADNODE *thr;
|
yuuji@0
|
760 SEARCHPGM *spg = NIL;
|
yuuji@0
|
761 char *cs = NIL;
|
yuuji@0
|
762 /* must have four arguments */
|
yuuji@0
|
763 if (!(arg && (s = strtok (arg," ")) && (cs = strtok (NIL," ")) &&
|
yuuji@0
|
764 (cs = cpystr (cs)) && (arg = strtok (NIL,"\015\012"))))
|
yuuji@0
|
765 response = misarg;
|
yuuji@0
|
766 else if (!parse_criteria (spg = mail_newsearchpgm (),&arg,nmsgs,
|
yuuji@0
|
767 uidmax (stream),0)) response = badatt;
|
yuuji@0
|
768 else if (arg && *arg) response = badarg;
|
yuuji@0
|
769 else {
|
yuuji@0
|
770 if (thr = mail_thread (stream,s,cs,spg,uid ? SE_UID : NIL)) {
|
yuuji@0
|
771 PSOUT ("* THREAD ");
|
yuuji@0
|
772 pthread (thr);
|
yuuji@0
|
773 mail_free_threadnode (&thr);
|
yuuji@0
|
774 }
|
yuuji@0
|
775 else PSOUT ("* THREAD");
|
yuuji@0
|
776 CRLF;
|
yuuji@0
|
777 }
|
yuuji@0
|
778 if (spg) mail_free_searchpgm (&spg);
|
yuuji@0
|
779 if (cs) fs_give ((void **) &cs);
|
yuuji@0
|
780 }
|
yuuji@0
|
781
|
yuuji@0
|
782 /* search mailbox */
|
yuuji@0
|
783 else if (!strcmp (cmd,"SEARCH") || !strcmp (cmd,"UID SEARCH")) {
|
yuuji@0
|
784 int retval = NIL;
|
yuuji@0
|
785 char *charset = NIL;
|
yuuji@0
|
786 SEARCHPGM *pgm;
|
yuuji@0
|
787 response = misarg; /* assume failure */
|
yuuji@0
|
788 if (!arg) break; /* one or more arguments required */
|
yuuji@0
|
789 if (((arg[0] == 'R') || (arg[0] == 'r')) &&
|
yuuji@0
|
790 ((arg[1] == 'E') || (arg[1] == 'e')) &&
|
yuuji@0
|
791 ((arg[2] == 'T') || (arg[2] == 't')) &&
|
yuuji@0
|
792 ((arg[3] == 'U') || (arg[3] == 'u')) &&
|
yuuji@0
|
793 ((arg[4] == 'R') || (arg[4] == 'r')) &&
|
yuuji@0
|
794 ((arg[5] == 'N') || (arg[5] == 'n')) &&
|
yuuji@0
|
795 (arg[6] == ' ') && (arg[7] == '(')) {
|
yuuji@0
|
796 retval = 0x4000; /* return is specified */
|
yuuji@0
|
797 for (arg += 8; *arg && (*arg != ')'); ) {
|
yuuji@0
|
798 if (((arg[0] == 'M') || (arg[0] == 'm')) &&
|
yuuji@0
|
799 ((arg[1] == 'I') || (arg[1] == 'i')) &&
|
yuuji@0
|
800 ((arg[2] == 'N') || (arg[2] == 'n')) &&
|
yuuji@0
|
801 ((arg[3] == ' ') || (arg[3] == ')'))) {
|
yuuji@0
|
802 retval |= 0x1;
|
yuuji@0
|
803 arg += 3;
|
yuuji@0
|
804 }
|
yuuji@0
|
805 else if (((arg[0] == 'M') || (arg[0] == 'm')) &&
|
yuuji@0
|
806 ((arg[1] == 'A') || (arg[1] == 'a')) &&
|
yuuji@0
|
807 ((arg[2] == 'X') || (arg[2] == 'x')) &&
|
yuuji@0
|
808 ((arg[3] == ' ') || (arg[3] == ')'))) {
|
yuuji@0
|
809 retval |= 0x2;
|
yuuji@0
|
810 arg += 3;
|
yuuji@0
|
811 }
|
yuuji@0
|
812 else if (((arg[0] == 'A') || (arg[0] == 'a')) &&
|
yuuji@0
|
813 ((arg[1] == 'L') || (arg[1] == 'l')) &&
|
yuuji@0
|
814 ((arg[2] == 'L') || (arg[2] == 'l')) &&
|
yuuji@0
|
815 ((arg[3] == ' ') || (arg[3] == ')'))) {
|
yuuji@0
|
816 retval |= 0x4;
|
yuuji@0
|
817 arg += 3;
|
yuuji@0
|
818 }
|
yuuji@0
|
819 else if (((arg[0] == 'C') || (arg[0] == 'c')) &&
|
yuuji@0
|
820 ((arg[1] == 'O') || (arg[1] == 'o')) &&
|
yuuji@0
|
821 ((arg[2] == 'U') || (arg[2] == 'u')) &&
|
yuuji@0
|
822 ((arg[3] == 'N') || (arg[3] == 'n')) &&
|
yuuji@0
|
823 ((arg[4] == 'T') || (arg[4] == 't')) &&
|
yuuji@0
|
824 ((arg[5] == ' ') || (arg[5] == ')'))) {
|
yuuji@0
|
825 retval |= 0x10;
|
yuuji@0
|
826 arg += 5;
|
yuuji@0
|
827 }
|
yuuji@0
|
828 else break; /* unknown return value */
|
yuuji@0
|
829 /* more return values to come */
|
yuuji@0
|
830 if ((*arg == ' ') && (arg[1] != ')')) ++arg;
|
yuuji@0
|
831 }
|
yuuji@0
|
832 /* RETURN list must be properly terminated */
|
yuuji@0
|
833 if ((*arg++ != ')') || (*arg++ != ' ')) break;
|
yuuji@0
|
834 /* default return value is ALL */
|
yuuji@0
|
835 if (!(retval &= 0x3fff)) retval = 0x4;
|
yuuji@0
|
836 }
|
yuuji@0
|
837
|
yuuji@0
|
838 /* character set specified? */
|
yuuji@0
|
839 if (((arg[0] == 'C') || (arg[0] == 'c')) &&
|
yuuji@0
|
840 ((arg[1] == 'H') || (arg[1] == 'h')) &&
|
yuuji@0
|
841 ((arg[2] == 'A') || (arg[2] == 'a')) &&
|
yuuji@0
|
842 ((arg[3] == 'R') || (arg[3] == 'r')) &&
|
yuuji@0
|
843 ((arg[4] == 'S') || (arg[4] == 's')) &&
|
yuuji@0
|
844 ((arg[5] == 'E') || (arg[5] == 'e')) &&
|
yuuji@0
|
845 ((arg[6] == 'T') || (arg[6] == 't')) &&
|
yuuji@0
|
846 (arg[7] == ' ')) {
|
yuuji@0
|
847 arg += 8; /* yes, skip over CHARSET token */
|
yuuji@0
|
848 if (s = snarf (&arg)) charset = cpystr (s);
|
yuuji@0
|
849 else break; /* missing character set */
|
yuuji@0
|
850 }
|
yuuji@0
|
851 /* must have arguments here */
|
yuuji@0
|
852 if (!(arg && *arg)) break;
|
yuuji@0
|
853 if (parse_criteria (pgm = mail_newsearchpgm (),&arg,nmsgs,
|
yuuji@0
|
854 uidmax (stream),0) && !*arg) {
|
yuuji@0
|
855 response = win; /* looks good, try the search */
|
yuuji@0
|
856 mail_search_full (stream,charset,pgm,SE_FREE);
|
yuuji@0
|
857 /* output search results if success */
|
yuuji@0
|
858 if (response == win) {
|
yuuji@0
|
859 if (retval) { /* ESEARCH desired */
|
yuuji@0
|
860 PSOUT ("* ESEARCH (TAG ");
|
yuuji@0
|
861 pstring (tag);
|
yuuji@0
|
862 PBOUT (')');
|
yuuji@0
|
863 if (uid) PSOUT (" UID");
|
yuuji@0
|
864 /* wants MIN */
|
yuuji@0
|
865 if (retval & 0x1) {
|
yuuji@0
|
866 for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
|
yuuji@0
|
867 ++i);
|
yuuji@0
|
868 if (i <= nmsgs) {
|
yuuji@0
|
869 PSOUT (" MIN ");
|
yuuji@0
|
870 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
871 }
|
yuuji@0
|
872 }
|
yuuji@0
|
873 /* wants MAX */
|
yuuji@0
|
874 if (retval & 0x2) {
|
yuuji@0
|
875 for (i = nmsgs; i && !mail_elt (stream,i)->searched; --i);
|
yuuji@0
|
876 if (i) {
|
yuuji@0
|
877 PSOUT (" MAX ");
|
yuuji@0
|
878 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
879 }
|
yuuji@0
|
880 }
|
yuuji@0
|
881
|
yuuji@0
|
882 /* wants ALL */
|
yuuji@0
|
883 if (retval & 0x4) {
|
yuuji@0
|
884 unsigned long j;
|
yuuji@0
|
885 /* find first match */
|
yuuji@0
|
886 for (i = 1; (i <= nmsgs) && !mail_elt (stream,i)->searched;
|
yuuji@0
|
887 ++i);
|
yuuji@0
|
888 if (i <= nmsgs) {
|
yuuji@0
|
889 PSOUT (" ALL ");
|
yuuji@0
|
890 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
891 j = i; /* last message output */
|
yuuji@0
|
892 }
|
yuuji@0
|
893 while (++i <= nmsgs) {
|
yuuji@0
|
894 if (mail_elt (stream,i)->searched) {
|
yuuji@0
|
895 while ((++i <= nmsgs) && mail_elt (stream,i)->searched);
|
yuuji@0
|
896 /* previous message is end of range */
|
yuuji@0
|
897 if (j != --i) {
|
yuuji@0
|
898 PBOUT (':');
|
yuuji@0
|
899 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
900 }
|
yuuji@0
|
901 }
|
yuuji@0
|
902 /* search for next match */
|
yuuji@0
|
903 while ((++i <= nmsgs) && !mail_elt (stream,i)->searched);
|
yuuji@0
|
904 if (i <= nmsgs) {
|
yuuji@0
|
905 PBOUT (',');
|
yuuji@0
|
906 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
907 j = i; /* last message output */
|
yuuji@0
|
908 }
|
yuuji@0
|
909 }
|
yuuji@0
|
910 }
|
yuuji@0
|
911 /* wants COUNT */
|
yuuji@0
|
912 if (retval & 0x10) {
|
yuuji@0
|
913 unsigned long j;
|
yuuji@0
|
914 for (i = 1, j = 0; i <= nmsgs; ++i)
|
yuuji@0
|
915 if (mail_elt (stream,i)->searched) ++j;
|
yuuji@0
|
916 PSOUT (" COUNT ");
|
yuuji@0
|
917 pnum (j);
|
yuuji@0
|
918 }
|
yuuji@0
|
919 }
|
yuuji@0
|
920 else { /* standard search */
|
yuuji@0
|
921 PSOUT ("* SEARCH");
|
yuuji@0
|
922 for (i = 1; i <= nmsgs; ++i)
|
yuuji@0
|
923 if (mail_elt (stream,i)->searched) {
|
yuuji@0
|
924 PBOUT (' ');
|
yuuji@0
|
925 pnum (uid ? mail_uid (stream,i) : i);
|
yuuji@0
|
926 }
|
yuuji@0
|
927 }
|
yuuji@0
|
928 CRLF;
|
yuuji@0
|
929 }
|
yuuji@0
|
930 }
|
yuuji@0
|
931 else mail_free_searchpgm (&pgm);
|
yuuji@0
|
932 if (charset) fs_give ((void **) &charset);
|
yuuji@0
|
933 }
|
yuuji@0
|
934
|
yuuji@0
|
935 else /* fall into select case */
|
yuuji@0
|
936 case SELECT: /* valid whenever logged in */
|
yuuji@0
|
937 /* select new mailbox */
|
yuuji@0
|
938 if (!(strcmp (cmd,"SELECT") && strcmp (cmd,"EXAMINE") &&
|
yuuji@0
|
939 strcmp (cmd,"BBOARD"))) {
|
yuuji@0
|
940 /* single argument */
|
yuuji@0
|
941 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
942 else if (arg) response = badarg;
|
yuuji@0
|
943 else if (nameok (NIL,s = bboardname (cmd,s))) {
|
yuuji@0
|
944 DRIVER *factory = mail_valid (NIL,s,NIL);
|
yuuji@0
|
945 f = (anonymous ? OP_ANONYMOUS + OP_READONLY : NIL) |
|
yuuji@0
|
946 ((*cmd == 'S') ? NIL : OP_READONLY);
|
yuuji@0
|
947 curdriver = NIL; /* no drivers known */
|
yuuji@0
|
948 /* no last uid */
|
yuuji@0
|
949 uidvalidity = lastuid = 0;
|
yuuji@0
|
950 if (lastid) fs_give ((void **) &lastid);
|
yuuji@0
|
951 if (lastst.data) fs_give ((void **) &lastst.data);
|
yuuji@0
|
952 nflags = 0; /* force update */
|
yuuji@0
|
953 nmsgs = recent = 0xffffffff;
|
yuuji@0
|
954 if (factory && !strcmp (factory->name,"phile") &&
|
yuuji@0
|
955 (stream = mail_open (stream,s,f | OP_SILENT)) &&
|
yuuji@0
|
956 (response == win)) {
|
yuuji@0
|
957 BODY *b;
|
yuuji@0
|
958 /* see if proxy open */
|
yuuji@0
|
959 if ((mail_elt (stream,1)->rfc822_size < 400) &&
|
yuuji@0
|
960 mail_fetchstructure (stream,1,&b) && (b->type == TYPETEXT) &&
|
yuuji@0
|
961 (t = mail_fetch_text (stream,1,NIL,&i,NIL)) &&
|
yuuji@0
|
962 (i < MAILTMPLEN) && (t[0] == '{')) {
|
yuuji@0
|
963 /* copy and tie off */
|
yuuji@0
|
964 strncpy (tmp,t,i)[i] = '\0';
|
yuuji@0
|
965 /* nuke any trailing newline */
|
yuuji@0
|
966 if (t = strpbrk (tmp,"\r\n")) *t = '\0';
|
yuuji@0
|
967 /* try to open proxy */
|
yuuji@0
|
968 if ((tstream = mail_open (NIL,tmp,f | OP_SILENT)) &&
|
yuuji@0
|
969 (response == win) && tstream->nmsgs) {
|
yuuji@0
|
970 s = tmp; /* got it, close the link */
|
yuuji@0
|
971 mail_close (stream);
|
yuuji@0
|
972 stream = tstream;
|
yuuji@0
|
973 tstream = NIL;
|
yuuji@0
|
974 }
|
yuuji@0
|
975 }
|
yuuji@0
|
976 /* now give the exists event */
|
yuuji@0
|
977 stream->silent = NIL;
|
yuuji@0
|
978 mm_exists (stream,stream->nmsgs);
|
yuuji@0
|
979 }
|
yuuji@0
|
980 else if (!factory && isnewsproxy (s)) {
|
yuuji@0
|
981 sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
|
yuuji@0
|
982 stream = mail_open (stream,tmp,f);
|
yuuji@0
|
983 }
|
yuuji@0
|
984 /* open stream normally then */
|
yuuji@0
|
985 else stream = mail_open (stream,s,f);
|
yuuji@0
|
986
|
yuuji@0
|
987 if (stream && (response == win)) {
|
yuuji@0
|
988 state = OPEN; /* note state open */
|
yuuji@0
|
989 if (lastsel) fs_give ((void **) &lastsel);
|
yuuji@0
|
990 /* canonicalize INBOX */
|
yuuji@0
|
991 if (!compare_cstring (s,"#MHINBOX"))
|
yuuji@0
|
992 lastsel = cpystr ("#MHINBOX");
|
yuuji@0
|
993 else lastsel = cpystr (compare_cstring (s,"INBOX") ?
|
yuuji@0
|
994 (char *) s : "INBOX");
|
yuuji@0
|
995 /* note readonly/readwrite */
|
yuuji@0
|
996 response = stream->rdonly ? rowin : rwwin;
|
yuuji@0
|
997 if (anonymous)
|
yuuji@0
|
998 syslog (LOG_INFO,"Anonymous select of %.80s host=%.80s",
|
yuuji@0
|
999 stream->mailbox,tcp_clienthost ());
|
yuuji@0
|
1000 lastcheck = 0; /* no last check */
|
yuuji@0
|
1001 }
|
yuuji@0
|
1002 else { /* failed, nuke old selection */
|
yuuji@0
|
1003 if (stream) stream = mail_close (stream);
|
yuuji@0
|
1004 state = SELECT; /* no mailbox open now */
|
yuuji@0
|
1005 if (lastsel) fs_give ((void **) &lastsel);
|
yuuji@0
|
1006 response = lose; /* open failed */
|
yuuji@0
|
1007 }
|
yuuji@0
|
1008 }
|
yuuji@0
|
1009 }
|
yuuji@0
|
1010
|
yuuji@0
|
1011 /* APPEND message to mailbox */
|
yuuji@0
|
1012 else if (!(anonymous || strcmp (cmd,"APPEND"))) {
|
yuuji@0
|
1013 /* parse mailbox name */
|
yuuji@0
|
1014 if ((s = snarf (&arg)) && arg) {
|
yuuji@0
|
1015 STRING st; /* message stringstruct */
|
yuuji@0
|
1016 APPENDDATA ad;
|
yuuji@0
|
1017 ad.arg = arg; /* command arguments */
|
yuuji@0
|
1018 /* no message yet */
|
yuuji@0
|
1019 ad.flags = ad.date = ad.msg = NIL;
|
yuuji@0
|
1020 ad.message = &st; /* pointer to stringstruct to use */
|
yuuji@0
|
1021 trycreate = NIL; /* no trycreate status */
|
yuuji@0
|
1022 if (!mail_append_multiple (NIL,s,append_msg,(void *) &ad)) {
|
yuuji@0
|
1023 if (response == win) response = trycreate ? losetry : lose;
|
yuuji@0
|
1024 /* this can happen with #driver. hack */
|
yuuji@0
|
1025 if (!lsterr) lsterr = cpystr ("No such destination mailbox");
|
yuuji@0
|
1026 }
|
yuuji@0
|
1027 /* clean up any message text left behind */
|
yuuji@0
|
1028 if (ad.flags) fs_give ((void **) &ad.flags);
|
yuuji@0
|
1029 if (ad.date) fs_give ((void **) &ad.date);
|
yuuji@0
|
1030 if (ad.msg) fs_give ((void **) &ad.msg);
|
yuuji@0
|
1031 }
|
yuuji@0
|
1032 else response = misarg;
|
yuuji@0
|
1033 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1034 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1035 }
|
yuuji@0
|
1036 /* list mailboxes */
|
yuuji@0
|
1037 else if (!strcmp (cmd,"LIST") || !strcmp (cmd,"RLIST")) {
|
yuuji@0
|
1038 /* get reference and mailbox argument */
|
yuuji@0
|
1039 if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
|
yuuji@0
|
1040 response = misarg;
|
yuuji@0
|
1041 else if (arg) response = badarg;
|
yuuji@0
|
1042 /* make sure anonymous can't do bad things */
|
yuuji@0
|
1043 else if (nameok (s,t)) {
|
yuuji@0
|
1044 if (newsproxypattern (s,t,tmp,LONGT)) {
|
yuuji@0
|
1045 proxylist = T;
|
yuuji@0
|
1046 mail_list (NIL,"",tmp);
|
yuuji@0
|
1047 proxylist = NIL;
|
yuuji@0
|
1048 }
|
yuuji@0
|
1049 else mail_list (NIL,s,t);
|
yuuji@0
|
1050 }
|
yuuji@0
|
1051 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1052 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1053 }
|
yuuji@0
|
1054 /* scan mailboxes */
|
yuuji@0
|
1055 else if (!strcmp (cmd,"SCAN")) {
|
yuuji@0
|
1056 /* get arguments */
|
yuuji@0
|
1057 if (!((s = snarf (&arg)) && (t = snarf_list (&arg)) &&
|
yuuji@0
|
1058 (u = snarf (&arg)))) response = misarg;
|
yuuji@0
|
1059 else if (arg) response = badarg;
|
yuuji@0
|
1060 /* make sure anonymous can't do bad things */
|
yuuji@0
|
1061 else if (nameok (s,t)) {
|
yuuji@0
|
1062 if (newsproxypattern (s,t,tmp,NIL))
|
yuuji@0
|
1063 mm_log ("SCAN not permitted for news",ERROR);
|
yuuji@0
|
1064 else mail_scan (NIL,s,t,u);
|
yuuji@0
|
1065 }
|
yuuji@0
|
1066 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1067 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1068 }
|
yuuji@0
|
1069 /* list subscribed mailboxes */
|
yuuji@0
|
1070 else if (!strcmp (cmd,"LSUB") || !strcmp (cmd,"RLSUB")) {
|
yuuji@0
|
1071 /* get reference and mailbox argument */
|
yuuji@0
|
1072 if (!((s = snarf (&arg)) && (t = snarf_list (&arg))))
|
yuuji@0
|
1073 response = misarg;
|
yuuji@0
|
1074 else if (arg) response = badarg;
|
yuuji@0
|
1075 /* make sure anonymous can't do bad things */
|
yuuji@0
|
1076 else if (nameok (s,t)) {
|
yuuji@0
|
1077 if (newsproxypattern (s,t,tmp,NIL)) newsrc_lsub (NIL,tmp);
|
yuuji@0
|
1078 else mail_lsub (NIL,s,t);
|
yuuji@0
|
1079 }
|
yuuji@0
|
1080 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1081 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1082 }
|
yuuji@0
|
1083
|
yuuji@0
|
1084 /* find mailboxes */
|
yuuji@0
|
1085 else if (!strcmp (cmd,"FIND")) {
|
yuuji@0
|
1086 /* get subcommand and true argument */
|
yuuji@0
|
1087 if (!(arg && (s = strtok (arg," \015\012")) && (s == cmd + 5) &&
|
yuuji@0
|
1088 (cmd[4] = ' ') && ucase (s) &&
|
yuuji@0
|
1089 (arg = strtok (NIL,"\015\012")) && (s = snarf_list (&arg))))
|
yuuji@0
|
1090 response = misarg; /* missing required argument */
|
yuuji@0
|
1091 else if (arg) response = badarg;
|
yuuji@0
|
1092 /* punt on single-char wildcards */
|
yuuji@0
|
1093 else if (strpbrk (s,"%?")) response =
|
yuuji@0
|
1094 "%.80s NO IMAP2 ? and %% wildcards not supported: %.80s\015\012";
|
yuuji@0
|
1095 else if (nameok (NIL,s)) {
|
yuuji@0
|
1096 finding = T; /* note that we are FINDing */
|
yuuji@0
|
1097 /* dispatch based on type */
|
yuuji@0
|
1098 if (!strcmp (cmd,"FIND MAILBOXES") && !anonymous)
|
yuuji@0
|
1099 mail_lsub (NIL,NIL,s);
|
yuuji@0
|
1100 else if (!strcmp (cmd,"FIND ALL.MAILBOXES")) {
|
yuuji@0
|
1101 /* convert * to % for compatible behavior */
|
yuuji@0
|
1102 for (t = s; *t; t++) if (*t == '*') *t = '%';
|
yuuji@0
|
1103 mail_list (NIL,NIL,s);
|
yuuji@0
|
1104 }
|
yuuji@0
|
1105 else response = badcmd;
|
yuuji@0
|
1106 }
|
yuuji@0
|
1107 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1108 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1109 }
|
yuuji@0
|
1110
|
yuuji@0
|
1111 /* status of mailbox */
|
yuuji@0
|
1112 else if (!strcmp (cmd,"STATUS")) {
|
yuuji@0
|
1113 if (!((s = snarf (&arg)) && arg && (*arg++ == '(') &&
|
yuuji@0
|
1114 (t = strchr (arg,')')) && (t - arg) && !t[1]))
|
yuuji@0
|
1115 response = misarg;
|
yuuji@0
|
1116 else {
|
yuuji@0
|
1117 f = NIL; /* initially no flags */
|
yuuji@0
|
1118 *t = '\0'; /* tie off flag string */
|
yuuji@0
|
1119 /* read flags */
|
yuuji@0
|
1120 t = strtok (ucase (arg)," ");
|
yuuji@0
|
1121 do { /* parse each one; unknown generate warning */
|
yuuji@0
|
1122 if (!strcmp (t,"MESSAGES")) f |= SA_MESSAGES;
|
yuuji@0
|
1123 else if (!strcmp (t,"RECENT")) f |= SA_RECENT;
|
yuuji@0
|
1124 else if (!strcmp (t,"UNSEEN")) f |= SA_UNSEEN;
|
yuuji@0
|
1125 else if (!strcmp (t,"UIDNEXT")) f |= SA_UIDNEXT;
|
yuuji@0
|
1126 else if (!strcmp (t,"UIDVALIDITY")) f |= SA_UIDVALIDITY;
|
yuuji@0
|
1127 else {
|
yuuji@0
|
1128 PSOUT ("* NO Unknown status flag ");
|
yuuji@0
|
1129 PSOUT (t);
|
yuuji@0
|
1130 CRLF;
|
yuuji@0
|
1131 }
|
yuuji@0
|
1132 } while (t = strtok (NIL," "));
|
yuuji@0
|
1133 ping_mailbox (uid); /* in case the fool did STATUS on open mbx */
|
yuuji@0
|
1134 PFLUSH (); /* make sure stdout is dumped in case slave */
|
yuuji@0
|
1135 if (!compare_cstring (s,"INBOX")) s = "INBOX";
|
yuuji@0
|
1136 else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
|
yuuji@0
|
1137 if (state == LOGOUT) response = lose;
|
yuuji@0
|
1138 /* get mailbox status */
|
yuuji@0
|
1139 else if (lastsel && (!strcmp (s,lastsel) ||
|
yuuji@0
|
1140 (stream && !strcmp (s,stream->mailbox)))) {
|
yuuji@0
|
1141 unsigned long unseen;
|
yuuji@0
|
1142 /* snarl at cretins which do this */
|
yuuji@0
|
1143 PSOUT ("* NO CLIENT BUG DETECTED: STATUS on selected mailbox: ");
|
yuuji@0
|
1144 PSOUT (s);
|
yuuji@0
|
1145 CRLF;
|
yuuji@0
|
1146 tmp[0] = ' '; tmp[1] = '\0';
|
yuuji@0
|
1147 if (f & SA_MESSAGES)
|
yuuji@0
|
1148 sprintf (tmp + strlen (tmp)," MESSAGES %lu",stream->nmsgs);
|
yuuji@0
|
1149 if (f & SA_RECENT)
|
yuuji@0
|
1150 sprintf (tmp + strlen (tmp)," RECENT %lu",stream->recent);
|
yuuji@0
|
1151 if (f & SA_UNSEEN) {
|
yuuji@0
|
1152 for (i = 1,unseen = 0; i <= stream->nmsgs; i++)
|
yuuji@0
|
1153 if (!mail_elt (stream,i)->seen) unseen++;
|
yuuji@0
|
1154 sprintf (tmp + strlen (tmp)," UNSEEN %lu",unseen);
|
yuuji@0
|
1155 }
|
yuuji@0
|
1156 if (f & SA_UIDNEXT)
|
yuuji@0
|
1157 sprintf (tmp + strlen (tmp)," UIDNEXT %lu",stream->uid_last+1);
|
yuuji@0
|
1158 if (f & SA_UIDVALIDITY)
|
yuuji@0
|
1159 sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",
|
yuuji@0
|
1160 stream->uid_validity);
|
yuuji@0
|
1161 tmp[1] = '(';
|
yuuji@0
|
1162 strcat (tmp,")\015\012");
|
yuuji@0
|
1163 PSOUT ("* STATUS ");
|
yuuji@0
|
1164 pastring (s);
|
yuuji@0
|
1165 PSOUT (tmp);
|
yuuji@0
|
1166 }
|
yuuji@0
|
1167 else if (isnewsproxy (s)) {
|
yuuji@0
|
1168 sprintf (tmp,"{%.300s/nntp}%.300s",nntpproxy,(char *) s+6);
|
yuuji@0
|
1169 if (!mail_status (NIL,tmp,f)) response = lose;
|
yuuji@0
|
1170 }
|
yuuji@0
|
1171 else if (!mail_status (NIL,s,f)) response = lose;
|
yuuji@0
|
1172 }
|
yuuji@0
|
1173 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1174 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1175 }
|
yuuji@0
|
1176
|
yuuji@0
|
1177 /* subscribe to mailbox */
|
yuuji@0
|
1178 else if (!(anonymous || strcmp (cmd,"SUBSCRIBE"))) {
|
yuuji@0
|
1179 /* get <mailbox> or MAILBOX <mailbox> */
|
yuuji@0
|
1180 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1181 else if (arg) { /* IMAP2bis form */
|
yuuji@0
|
1182 if (compare_cstring (s,"MAILBOX")) response = badarg;
|
yuuji@0
|
1183 else if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1184 else if (arg) response = badarg;
|
yuuji@0
|
1185 else mail_subscribe (NIL,s);
|
yuuji@0
|
1186 }
|
yuuji@0
|
1187 else if (isnewsproxy (s)) newsrc_update (NIL,s+6,':');
|
yuuji@0
|
1188 else mail_subscribe (NIL,s);
|
yuuji@0
|
1189 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1190 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1191 }
|
yuuji@0
|
1192 /* unsubscribe to mailbox */
|
yuuji@0
|
1193 else if (!(anonymous || strcmp (cmd,"UNSUBSCRIBE"))) {
|
yuuji@0
|
1194 /* get <mailbox> or MAILBOX <mailbox> */
|
yuuji@0
|
1195 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1196 else if (arg) { /* IMAP2bis form */
|
yuuji@0
|
1197 if (compare_cstring (s,"MAILBOX")) response = badarg;
|
yuuji@0
|
1198 else if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1199 else if (arg) response = badarg;
|
yuuji@0
|
1200 else if (isnewsproxy (s)) newsrc_update (NIL,s+6,'!');
|
yuuji@0
|
1201 else mail_unsubscribe (NIL,s);
|
yuuji@0
|
1202 }
|
yuuji@0
|
1203 else mail_unsubscribe (NIL,s);
|
yuuji@0
|
1204 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1205 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1206 }
|
yuuji@0
|
1207
|
yuuji@0
|
1208 else if (!strcmp (cmd,"NAMESPACE")) {
|
yuuji@0
|
1209 if (arg) response = badarg;
|
yuuji@0
|
1210 else {
|
yuuji@0
|
1211 NAMESPACE **ns = (NAMESPACE **) mail_parameters(NIL,GET_NAMESPACE,
|
yuuji@0
|
1212 NIL);
|
yuuji@0
|
1213 NAMESPACE *n;
|
yuuji@0
|
1214 PARAMETER *p;
|
yuuji@0
|
1215 PSOUT ("* NAMESPACE");
|
yuuji@0
|
1216 if (ns) for (i = 0; i < 3; i++) {
|
yuuji@0
|
1217 if (n = ns[i]) {
|
yuuji@0
|
1218 PSOUT (" (");
|
yuuji@0
|
1219 do {
|
yuuji@0
|
1220 PBOUT ('(');
|
yuuji@0
|
1221 pstring (n->name);
|
yuuji@0
|
1222 switch (n->delimiter) {
|
yuuji@0
|
1223 case '\\': /* quoted delimiter */
|
yuuji@0
|
1224 case '"':
|
yuuji@0
|
1225 PSOUT (" \"\\\\\"");
|
yuuji@0
|
1226 break;
|
yuuji@0
|
1227 case '\0': /* no delimiter */
|
yuuji@0
|
1228 PSOUT (" NIL");
|
yuuji@0
|
1229 break;
|
yuuji@0
|
1230 default: /* unquoted delimiter */
|
yuuji@0
|
1231 PSOUT (" \"");
|
yuuji@0
|
1232 PBOUT (n->delimiter);
|
yuuji@0
|
1233 PBOUT ('"');
|
yuuji@0
|
1234 break;
|
yuuji@0
|
1235 }
|
yuuji@0
|
1236 /* NAMESPACE extensions are hairy */
|
yuuji@0
|
1237 if (p = n->param) do {
|
yuuji@0
|
1238 PBOUT (' ');
|
yuuji@0
|
1239 pstring (p->attribute);
|
yuuji@0
|
1240 PSOUT (" (");
|
yuuji@0
|
1241 do pstring (p->value);
|
yuuji@0
|
1242 while (p->next && !p->next->attribute && (p = p->next));
|
yuuji@0
|
1243 PBOUT (')');
|
yuuji@0
|
1244 } while (p = p->next);
|
yuuji@0
|
1245 PBOUT (')');
|
yuuji@0
|
1246 } while (n = n->next);
|
yuuji@0
|
1247 PBOUT (')');
|
yuuji@0
|
1248 }
|
yuuji@0
|
1249 else PSOUT (" NIL");
|
yuuji@0
|
1250 }
|
yuuji@0
|
1251 else PSOUT (" NIL NIL NIL");
|
yuuji@0
|
1252 CRLF;
|
yuuji@0
|
1253 }
|
yuuji@0
|
1254 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1255 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1256 }
|
yuuji@0
|
1257
|
yuuji@0
|
1258 /* create mailbox */
|
yuuji@0
|
1259 else if (!(anonymous || strcmp (cmd,"CREATE"))) {
|
yuuji@0
|
1260 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1261 else if (arg) response = badarg;
|
yuuji@0
|
1262 else mail_create (NIL,s);
|
yuuji@0
|
1263 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1264 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1265 }
|
yuuji@0
|
1266 /* delete mailbox */
|
yuuji@0
|
1267 else if (!(anonymous || strcmp (cmd,"DELETE"))) {
|
yuuji@0
|
1268 if (!(s = snarf (&arg))) response = misarg;
|
yuuji@0
|
1269 else if (arg) response = badarg;
|
yuuji@0
|
1270 else { /* make sure not selected */
|
yuuji@0
|
1271 if (lastsel && (!strcmp (s,lastsel) ||
|
yuuji@0
|
1272 (stream && !strcmp (s,stream->mailbox))))
|
yuuji@0
|
1273 mm_log ("Can not DELETE the selected mailbox",ERROR);
|
yuuji@0
|
1274 else mail_delete (NIL,s);
|
yuuji@0
|
1275 }
|
yuuji@0
|
1276 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1277 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1278 }
|
yuuji@0
|
1279 /* rename mailbox */
|
yuuji@0
|
1280 else if (!(anonymous || strcmp (cmd,"RENAME"))) {
|
yuuji@0
|
1281 if (!((s = snarf (&arg)) && (t = snarf (&arg)))) response = misarg;
|
yuuji@0
|
1282 else if (arg) response = badarg;
|
yuuji@0
|
1283 else { /* make sure not selected */
|
yuuji@0
|
1284 if (!compare_cstring (s,"INBOX")) s = "INBOX";
|
yuuji@0
|
1285 else if (!compare_cstring (s,"#MHINBOX")) s = "#MHINBOX";
|
yuuji@0
|
1286 if (lastsel && (!strcmp (s,lastsel) ||
|
yuuji@0
|
1287 (stream && !strcmp (s,stream->mailbox))))
|
yuuji@0
|
1288 mm_log ("Can not RENAME the selected mailbox",ERROR);
|
yuuji@0
|
1289 else mail_rename (NIL,s,t);
|
yuuji@0
|
1290 }
|
yuuji@0
|
1291 if (stream) /* allow untagged EXPUNGE */
|
yuuji@0
|
1292 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,(void *) stream);
|
yuuji@0
|
1293 }
|
yuuji@0
|
1294
|
yuuji@0
|
1295 /* idle mode */
|
yuuji@0
|
1296 else if (!strcmp (cmd,"IDLE")) {
|
yuuji@0
|
1297 /* no arguments */
|
yuuji@0
|
1298 if (arg) response = badarg;
|
yuuji@0
|
1299 else { /* tell client ready for argument */
|
yuuji@0
|
1300 unsigned long donefake = 0;
|
yuuji@0
|
1301 PSOUT ("+ Waiting for DONE\015\012");
|
yuuji@0
|
1302 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
1303 /* inactivity countdown */
|
yuuji@0
|
1304 i = ((TIMEOUT) / (IDLETIMER)) + 1;
|
yuuji@0
|
1305 do { /* main idle loop */
|
yuuji@0
|
1306 if (!donefake) { /* don't ping mailbox if faking */
|
yuuji@0
|
1307 mail_parameters (stream,SET_ONETIMEEXPUNGEATPING,
|
yuuji@0
|
1308 (void *) stream);
|
yuuji@0
|
1309 ping_mailbox (uid);
|
yuuji@0
|
1310 /* maybe do a checkpoint if not anonymous */
|
yuuji@0
|
1311 if (!anonymous && stream && (time (0) > lastcheck + CHECKTIMER)) {
|
yuuji@0
|
1312 mail_check (stream);
|
yuuji@0
|
1313 /* cancel likely altwin from mail_check() */
|
yuuji@0
|
1314 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
1315 if (lstwrn) fs_give ((void **) &lstwrn);
|
yuuji@0
|
1316 /* remember last checkpoint */
|
yuuji@0
|
1317 lastcheck = time (0);
|
yuuji@0
|
1318 }
|
yuuji@0
|
1319 }
|
yuuji@0
|
1320 if (lstwrn) { /* have a warning? */
|
yuuji@0
|
1321 PSOUT ("* NO ");
|
yuuji@0
|
1322 PSOUT (lstwrn);
|
yuuji@0
|
1323 CRLF;
|
yuuji@0
|
1324 fs_give ((void **) &lstwrn);
|
yuuji@0
|
1325 }
|
yuuji@0
|
1326 if (!(i % 2)) { /* prevent NAT timeouts */
|
yuuji@0
|
1327 sprintf (tmp,"* OK Timeout in %lu minutes\015\012",
|
yuuji@0
|
1328 (i * IDLETIMER) / 60);
|
yuuji@0
|
1329 PSOUT (tmp);
|
yuuji@0
|
1330 }
|
yuuji@0
|
1331 /* two minutes before the end... */
|
yuuji@0
|
1332 if ((state == OPEN) && (i <= 2)) {
|
yuuji@0
|
1333 sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
|
yuuji@0
|
1334 donefake = nmsgs + 1,recent + 1);
|
yuuji@0
|
1335 PSOUT (tmp); /* prod client to wake up */
|
yuuji@0
|
1336 }
|
yuuji@0
|
1337 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
1338 } while ((state != LOGOUT) && !INWAIT (IDLETIMER) && --i);
|
yuuji@0
|
1339
|
yuuji@0
|
1340 /* time to exit idle loop */
|
yuuji@0
|
1341 if (state != LOGOUT) {
|
yuuji@0
|
1342 if (i) { /* still have time left? */
|
yuuji@0
|
1343 /* yes, read expected DONE */
|
yuuji@0
|
1344 slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
|
yuuji@0
|
1345 if (((tmp[0] != 'D') && (tmp[0] != 'd')) ||
|
yuuji@0
|
1346 ((tmp[1] != 'O') && (tmp[1] != 'o')) ||
|
yuuji@0
|
1347 ((tmp[2] != 'N') && (tmp[2] != 'n')) ||
|
yuuji@0
|
1348 ((tmp[3] != 'E') && (tmp[3] != 'e')) ||
|
yuuji@0
|
1349 (((tmp[4] != '\015') || (tmp[5] != '\012')) &&
|
yuuji@0
|
1350 (tmp[4] != '\012')))
|
yuuji@0
|
1351 response = "%.80s BAD Bogus IDLE continuation\015\012";
|
yuuji@0
|
1352 if (donefake) { /* if faking at the end */
|
yuuji@0
|
1353 /* send EXPUNGE (should be just 1) */
|
yuuji@0
|
1354 while (donefake > nmsgs) {
|
yuuji@0
|
1355 sprintf (tmp,"* %lu EXPUNGE\015\012",donefake--);
|
yuuji@0
|
1356 PSOUT (tmp);
|
yuuji@0
|
1357 }
|
yuuji@0
|
1358 sprintf (tmp,"* %lu EXISTS\015\012* %lu RECENT\015\012",
|
yuuji@0
|
1359 nmsgs,recent);
|
yuuji@0
|
1360 PSOUT (tmp);
|
yuuji@0
|
1361 }
|
yuuji@0
|
1362 }
|
yuuji@0
|
1363 else clkint (); /* otherwise do autologout action */
|
yuuji@0
|
1364 }
|
yuuji@0
|
1365 }
|
yuuji@0
|
1366 }
|
yuuji@0
|
1367 else response = badcmd;
|
yuuji@0
|
1368 break;
|
yuuji@0
|
1369 default:
|
yuuji@0
|
1370 response = "%.80s BAD Unknown state for %.80s command\015\012";
|
yuuji@0
|
1371 break;
|
yuuji@0
|
1372 }
|
yuuji@0
|
1373
|
yuuji@0
|
1374 while (litplus.ok) { /* any unread LITERAL+? */
|
yuuji@0
|
1375 litplus.ok = NIL; /* yes, cancel it now */
|
yuuji@0
|
1376 clearerr (stdin); /* clear stdin errors */
|
yuuji@0
|
1377 status = "discarding unread literal";
|
yuuji@0
|
1378 /* read literal and discard it */
|
yuuji@0
|
1379 while (i = (litplus.size > MAILTMPLEN) ? MAILTMPLEN : litplus.size) {
|
yuuji@0
|
1380 if (state == LOGOUT) litplus.size = 0;
|
yuuji@0
|
1381 else {
|
yuuji@0
|
1382 settimeout (INPUTTIMEOUT);
|
yuuji@0
|
1383 if (PSINR (tmp,i)) litplus.size -= i;
|
yuuji@0
|
1384 else {
|
yuuji@0
|
1385 ioerror (stdin,status);
|
yuuji@0
|
1386 litplus.size = 0; /* in case it continues */
|
yuuji@0
|
1387 }
|
yuuji@0
|
1388 }
|
yuuji@0
|
1389 }
|
yuuji@0
|
1390 settimeout (0); /* stop timeout */
|
yuuji@0
|
1391 /* get new command tail */
|
yuuji@0
|
1392 slurp (tmp,MAILTMPLEN,INPUTTIMEOUT);
|
yuuji@0
|
1393 /* locate end of line */
|
yuuji@0
|
1394 if (t = strchr (tmp,'\012')) {
|
yuuji@0
|
1395 /* back over CR */
|
yuuji@0
|
1396 if ((t > tmp) && (t[-1] == '\015')) --t;
|
yuuji@0
|
1397 *t = NIL; /* tie off CRLF */
|
yuuji@0
|
1398 /* possible LITERAL+? */
|
yuuji@0
|
1399 if (((i = strlen (tmp)) > 3) && (tmp[i - 1] == '}') &&
|
yuuji@0
|
1400 (tmp[i - 2] == '+') && isdigit (tmp[i - 3])) {
|
yuuji@0
|
1401 /* back over possible count */
|
yuuji@0
|
1402 for (i -= 4; i && isdigit (tmp[i]); i--);
|
yuuji@0
|
1403 if (tmp[i] == '{') { /* found a literal? */
|
yuuji@0
|
1404 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
|
yuuji@0
|
1405 litplus.size = strtoul (tmp + i + 1,NIL,10);
|
yuuji@0
|
1406 }
|
yuuji@0
|
1407 }
|
yuuji@0
|
1408 }
|
yuuji@0
|
1409 else flush (); /* overlong line after LITERAL+, punt */
|
yuuji@0
|
1410 }
|
yuuji@0
|
1411 ping_mailbox (uid); /* update mailbox status before response */
|
yuuji@0
|
1412 if (lstwrn && lsterr) { /* output most recent warning */
|
yuuji@0
|
1413 PSOUT ("* NO ");
|
yuuji@0
|
1414 PSOUT (lstwrn);
|
yuuji@0
|
1415 CRLF;
|
yuuji@0
|
1416 fs_give ((void **) &lstwrn);
|
yuuji@0
|
1417 }
|
yuuji@0
|
1418
|
yuuji@0
|
1419 if (response == logwin) { /* authentication win message */
|
yuuji@0
|
1420 sprintf (tmp,response,lstref ? "*" : tag);
|
yuuji@0
|
1421 PSOUT (tmp); /* start response */
|
yuuji@0
|
1422 pcapability (1); /* print logged-in capabilities */
|
yuuji@0
|
1423 PSOUT ("] User ");
|
yuuji@0
|
1424 PSOUT (user);
|
yuuji@0
|
1425 PSOUT (" authenticated\015\012");
|
yuuji@0
|
1426 if (lstref) {
|
yuuji@0
|
1427 sprintf (tmp,response,tag);
|
yuuji@0
|
1428 PSOUT (tmp); /* start response */
|
yuuji@0
|
1429 PSOUT ("[REFERRAL ");
|
yuuji@0
|
1430 PSOUT (lstref);
|
yuuji@0
|
1431 PSOUT ("] ");
|
yuuji@0
|
1432 PSOUT (lasterror ());
|
yuuji@0
|
1433 CRLF;
|
yuuji@0
|
1434 }
|
yuuji@0
|
1435 }
|
yuuji@0
|
1436 else if ((response == win) || (response == lose)) {
|
yuuji@0
|
1437 sprintf (tmp,response,tag);
|
yuuji@0
|
1438 PSOUT (tmp);
|
yuuji@0
|
1439 if (cauidvalidity) { /* COPYUID/APPENDUID response? */
|
yuuji@0
|
1440 sprintf (tmp,"[%.80sUID %lu ",(char *)
|
yuuji@0
|
1441 ((s = strchr (cmd,' ')) ? s+1 : cmd),cauidvalidity);
|
yuuji@0
|
1442 PSOUT (tmp);
|
yuuji@0
|
1443 cauidvalidity = 0; /* cancel response for future */
|
yuuji@0
|
1444 if (csset) {
|
yuuji@0
|
1445 pset (&csset);
|
yuuji@0
|
1446 PBOUT (' ');
|
yuuji@0
|
1447 }
|
yuuji@0
|
1448 pset (&caset);
|
yuuji@0
|
1449 PSOUT ("] ");
|
yuuji@0
|
1450 }
|
yuuji@0
|
1451 else if (lstref) { /* have a referral? */
|
yuuji@0
|
1452 PSOUT ("[REFERRAL ");
|
yuuji@0
|
1453 PSOUT (lstref);
|
yuuji@0
|
1454 PSOUT ("] ");
|
yuuji@0
|
1455 }
|
yuuji@0
|
1456 if (lsterr || lstwrn) PSOUT (lasterror ());
|
yuuji@0
|
1457 else {
|
yuuji@0
|
1458 PSOUT (cmd);
|
yuuji@0
|
1459 PSOUT ((response == win) ? " completed" : "failed");
|
yuuji@0
|
1460 }
|
yuuji@0
|
1461 CRLF;
|
yuuji@0
|
1462 }
|
yuuji@0
|
1463 else { /* normal response */
|
yuuji@0
|
1464 if ((response == rowin) || (response == rwwin)) {
|
yuuji@0
|
1465 if (lstwrn) { /* output most recent warning */
|
yuuji@0
|
1466 PSOUT ("* NO ");
|
yuuji@0
|
1467 PSOUT (lstwrn);
|
yuuji@0
|
1468 CRLF;
|
yuuji@0
|
1469 fs_give ((void **) &lstwrn);
|
yuuji@0
|
1470 }
|
yuuji@0
|
1471 }
|
yuuji@0
|
1472 sprintf (tmp,response,tag,cmd,lasterror ());
|
yuuji@0
|
1473 PSOUT (tmp); /* output response */
|
yuuji@0
|
1474 }
|
yuuji@0
|
1475 }
|
yuuji@0
|
1476 PFLUSH (); /* make sure output blatted */
|
yuuji@0
|
1477
|
yuuji@0
|
1478 if (autologouttime) { /* have an autologout in effect? */
|
yuuji@0
|
1479 /* cancel if no longer waiting for login */
|
yuuji@0
|
1480 if (state != LOGIN) autologouttime = 0;
|
yuuji@0
|
1481 /* took too long to login */
|
yuuji@0
|
1482 else if (autologouttime < time (0)) {
|
yuuji@0
|
1483 logout = goodbye = "Autologout";
|
yuuji@0
|
1484 stream = NIL;
|
yuuji@0
|
1485 state = LOGOUT; /* sayonara */
|
yuuji@0
|
1486 }
|
yuuji@0
|
1487 }
|
yuuji@0
|
1488 }
|
yuuji@0
|
1489 if (goodbye && !quell_events){/* have a goodbye message? */
|
yuuji@0
|
1490 PSOUT ("* BYE "); /* utter it */
|
yuuji@0
|
1491 PSOUT (goodbye);
|
yuuji@0
|
1492 CRLF;
|
yuuji@0
|
1493 PFLUSH (); /* make sure blatted */
|
yuuji@0
|
1494 }
|
yuuji@0
|
1495 syslog (LOG_INFO,"%s user=%.80s host=%.80s",logout,
|
yuuji@0
|
1496 user ? (char *) user : "???",tcp_clienthost ());
|
yuuji@0
|
1497 /* do logout hook if needed */
|
yuuji@0
|
1498 if (lgoh = (logouthook_t) mail_parameters (NIL,GET_LOGOUTHOOK,NIL))
|
yuuji@0
|
1499 (*lgoh) (mail_parameters (NIL,GET_LOGOUTDATA,NIL));
|
yuuji@0
|
1500 _exit (ret); /* all done */
|
yuuji@0
|
1501 return ret; /* stupid compilers */
|
yuuji@0
|
1502 }
|
yuuji@0
|
1503
|
yuuji@0
|
1504 /* Ping mailbox during each cycle. Also check alerts
|
yuuji@0
|
1505 * Accepts: last command was UID flag
|
yuuji@0
|
1506 */
|
yuuji@0
|
1507
|
yuuji@0
|
1508 void ping_mailbox (unsigned long uid)
|
yuuji@0
|
1509 {
|
yuuji@0
|
1510 unsigned long i;
|
yuuji@0
|
1511 char tmp[MAILTMPLEN];
|
yuuji@0
|
1512 if (state == OPEN) {
|
yuuji@0
|
1513 if (!mail_ping (stream)) { /* make sure stream still alive */
|
yuuji@0
|
1514 PSOUT ("* BYE ");
|
yuuji@0
|
1515 PSOUT (mylocalhost ());
|
yuuji@0
|
1516 PSOUT (" Fatal mailbox error: ");
|
yuuji@0
|
1517 PSOUT (lasterror ());
|
yuuji@0
|
1518 CRLF;
|
yuuji@0
|
1519 stream = NIL; /* don't try to clean up stream */
|
yuuji@0
|
1520 state = LOGOUT; /* go away */
|
yuuji@0
|
1521 syslog (LOG_INFO,
|
yuuji@0
|
1522 "Fatal mailbox error user=%.80s host=%.80s mbx=%.80s: %.80s",
|
yuuji@0
|
1523 user ? (char *) user : "???",tcp_clienthost (),
|
yuuji@0
|
1524 (stream && stream->mailbox) ? stream->mailbox : "???",
|
yuuji@0
|
1525 lasterror ());
|
yuuji@0
|
1526 return;
|
yuuji@0
|
1527 }
|
yuuji@0
|
1528 /* change in number of messages? */
|
yuuji@0
|
1529 if (existsquelled || (nmsgs != stream->nmsgs)) {
|
yuuji@0
|
1530 PSOUT ("* ");
|
yuuji@0
|
1531 pnum (nmsgs = stream->nmsgs);
|
yuuji@0
|
1532 PSOUT (" EXISTS\015\012");
|
yuuji@0
|
1533 }
|
yuuji@0
|
1534 /* change in recent messages? */
|
yuuji@0
|
1535 if (existsquelled || (recent != stream->recent)) {
|
yuuji@0
|
1536 PSOUT ("* ");
|
yuuji@0
|
1537 pnum (recent = stream->recent);
|
yuuji@0
|
1538 PSOUT (" RECENT\015\012");
|
yuuji@0
|
1539 }
|
yuuji@0
|
1540 existsquelled = NIL; /* don't do this until asked again */
|
yuuji@0
|
1541 if (stream->uid_validity && (stream->uid_validity != uidvalidity)) {
|
yuuji@0
|
1542 PSOUT ("* OK [UIDVALIDITY ");
|
yuuji@0
|
1543 pnum (stream->uid_validity);
|
yuuji@0
|
1544 PSOUT ("] UID validity status\015\012* OK [UIDNEXT ");
|
yuuji@0
|
1545 pnum (stream->uid_last + 1);
|
yuuji@0
|
1546 PSOUT ("] Predicted next UID\015\012");
|
yuuji@0
|
1547 if (stream->uid_nosticky) {
|
yuuji@0
|
1548 PSOUT ("* NO [UIDNOTSTICKY] Non-permanent unique identifiers: ");
|
yuuji@0
|
1549 PSOUT (stream->mailbox);
|
yuuji@0
|
1550 CRLF;
|
yuuji@0
|
1551 }
|
yuuji@0
|
1552 uidvalidity = stream->uid_validity;
|
yuuji@0
|
1553 }
|
yuuji@0
|
1554
|
yuuji@0
|
1555 /* don't bother if driver changed */
|
yuuji@0
|
1556 if (curdriver == stream->dtb) {
|
yuuji@0
|
1557 /* first report any new flags */
|
yuuji@0
|
1558 if ((nflags < NUSERFLAGS) && stream->user_flags[nflags])
|
yuuji@0
|
1559 new_flags (stream);
|
yuuji@0
|
1560 for (i = 1; i <= nmsgs; i++) if (mail_elt (stream,i)->spare2) {
|
yuuji@0
|
1561 PSOUT ("* ");
|
yuuji@0
|
1562 pnum (i);
|
yuuji@0
|
1563 PSOUT (" FETCH (");
|
yuuji@0
|
1564 fetch_flags (i,NIL); /* output changed flags */
|
yuuji@0
|
1565 if (uid) { /* need to include UIDs in response? */
|
yuuji@0
|
1566 PBOUT (' ');
|
yuuji@0
|
1567 fetch_uid (i,NIL);
|
yuuji@0
|
1568 }
|
yuuji@0
|
1569 PSOUT (")\015\012");
|
yuuji@0
|
1570 }
|
yuuji@0
|
1571 }
|
yuuji@0
|
1572 else { /* driver changed */
|
yuuji@0
|
1573 new_flags (stream); /* send mailbox flags */
|
yuuji@0
|
1574 if (curdriver) { /* note readonly/write if possible change */
|
yuuji@0
|
1575 PSOUT ("* OK [READ-");
|
yuuji@0
|
1576 PSOUT (stream->rdonly ? "ONLY" : "WRITE");
|
yuuji@0
|
1577 PSOUT ("] Mailbox status\015\012");
|
yuuji@0
|
1578 }
|
yuuji@0
|
1579 curdriver = stream->dtb;
|
yuuji@0
|
1580 if (nmsgs) { /* get flags for all messages */
|
yuuji@0
|
1581 sprintf (tmp,"1:%lu",nmsgs);
|
yuuji@0
|
1582 mail_fetch_flags (stream,tmp,NIL);
|
yuuji@0
|
1583 /* don't do this if newsrc already did */
|
yuuji@0
|
1584 if (!(curdriver->flags & DR_NEWS)) {
|
yuuji@0
|
1585 /* find first unseen message */
|
yuuji@0
|
1586 for (i = 1; i <= nmsgs && mail_elt (stream,i)->seen; i++);
|
yuuji@0
|
1587 if (i <= nmsgs) {
|
yuuji@0
|
1588 PSOUT ("* OK [UNSEEN ");
|
yuuji@0
|
1589 pnum (i);
|
yuuji@0
|
1590 PSOUT ("] first unseen message in ");
|
yuuji@0
|
1591 PSOUT (stream->mailbox);
|
yuuji@0
|
1592 CRLF;
|
yuuji@0
|
1593 }
|
yuuji@0
|
1594 }
|
yuuji@0
|
1595 }
|
yuuji@0
|
1596 }
|
yuuji@0
|
1597 }
|
yuuji@0
|
1598 if (shutdowntime && (time (0) > shutdowntime + SHUTDOWNTIMER)) {
|
yuuji@0
|
1599 PSOUT ("* BYE Server shutting down\015\012");
|
yuuji@0
|
1600 state = LOGOUT;
|
yuuji@0
|
1601 }
|
yuuji@0
|
1602 /* don't do these stat()s every cycle */
|
yuuji@0
|
1603 else if (time (0) > alerttime + ALERTTIMER) {
|
yuuji@0
|
1604 struct stat sbuf;
|
yuuji@0
|
1605 /* have a shutdown file? */
|
yuuji@0
|
1606 if (!stat (SHUTDOWNFILE,&sbuf)) {
|
yuuji@0
|
1607 PSOUT ("* OK [ALERT] Server shutting down shortly\015\012");
|
yuuji@0
|
1608 shutdowntime = time (0);
|
yuuji@0
|
1609 }
|
yuuji@0
|
1610 alerttime = time (0); /* output any new alerts */
|
yuuji@0
|
1611 sysalerttime = palert (ALERTFILE,sysalerttime);
|
yuuji@0
|
1612 if (state != LOGIN) /* do user alert if logged in */
|
yuuji@0
|
1613 useralerttime = palert (mailboxfile (tmp,USERALERTFILE),useralerttime);
|
yuuji@0
|
1614 }
|
yuuji@0
|
1615 }
|
yuuji@0
|
1616
|
yuuji@0
|
1617 /* Print an alert file
|
yuuji@0
|
1618 * Accepts: path of alert file
|
yuuji@0
|
1619 * time of last printed alert file
|
yuuji@0
|
1620 * Returns: updated time of last printed alert file
|
yuuji@0
|
1621 */
|
yuuji@0
|
1622
|
yuuji@0
|
1623 time_t palert (char *file,time_t oldtime)
|
yuuji@0
|
1624 {
|
yuuji@0
|
1625 FILE *alf;
|
yuuji@0
|
1626 struct stat sbuf;
|
yuuji@0
|
1627 int c,lc = '\012';
|
yuuji@0
|
1628 /* have a new alert file? */
|
yuuji@0
|
1629 if (stat (file,&sbuf) || (sbuf.st_mtime <= oldtime) ||
|
yuuji@0
|
1630 !(alf = fopen (file,"r"))) return oldtime;
|
yuuji@0
|
1631 /* yes, display it */
|
yuuji@0
|
1632 while ((c = getc (alf)) != EOF) {
|
yuuji@0
|
1633 if (lc == '\012') PSOUT ("* OK [ALERT] ");
|
yuuji@0
|
1634 switch (c) { /* output character */
|
yuuji@0
|
1635 case '\012': /* newline means do CRLF */
|
yuuji@0
|
1636 CRLF;
|
yuuji@0
|
1637 case '\015': /* flush CRs */
|
yuuji@0
|
1638 case '\0': /* flush nulls */
|
yuuji@0
|
1639 break;
|
yuuji@0
|
1640 default:
|
yuuji@0
|
1641 PBOUT (c); /* output all other characters */
|
yuuji@0
|
1642 break;
|
yuuji@0
|
1643 }
|
yuuji@0
|
1644 lc = c; /* note previous character */
|
yuuji@0
|
1645 }
|
yuuji@0
|
1646 fclose (alf);
|
yuuji@0
|
1647 if (lc != '\012') CRLF; /* final terminating CRLF */
|
yuuji@0
|
1648 return sbuf.st_mtime; /* return updated last alert time */
|
yuuji@0
|
1649 }
|
yuuji@0
|
1650
|
yuuji@0
|
1651 /* Initialize file string structure for file stringstruct
|
yuuji@0
|
1652 * Accepts: string structure
|
yuuji@0
|
1653 * pointer to message data structure
|
yuuji@0
|
1654 * size of string
|
yuuji@0
|
1655 */
|
yuuji@0
|
1656
|
yuuji@0
|
1657 void msg_string_init (STRING *s,void *data,unsigned long size)
|
yuuji@0
|
1658 {
|
yuuji@0
|
1659 MSGDATA *md = (MSGDATA *) data;
|
yuuji@0
|
1660 s->data = data; /* note stream/msgno and header length */
|
yuuji@0
|
1661 #if 0
|
yuuji@0
|
1662 s->size = size; /* message size */
|
yuuji@0
|
1663 s->curpos = s->chunk = /* load header */
|
yuuji@0
|
1664 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,
|
yuuji@0
|
1665 FT_PREFETCHTEXT | FT_PEEK);
|
yuuji@0
|
1666 #else /* This kludge is necessary because of broken mail stores */
|
yuuji@0
|
1667 mail_fetchtext_full (md->stream,md->msgno,&s->size,FT_PEEK);
|
yuuji@0
|
1668 s->curpos = s->chunk = /* load header */
|
yuuji@0
|
1669 mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PEEK);
|
yuuji@0
|
1670 s->size += s->data1; /* header + body size */
|
yuuji@0
|
1671 #endif
|
yuuji@0
|
1672 s->cursize = s->chunksize = s->data1;
|
yuuji@0
|
1673 s->offset = 0; /* offset is start of message */
|
yuuji@0
|
1674 }
|
yuuji@0
|
1675
|
yuuji@0
|
1676
|
yuuji@0
|
1677 /* Get next character from file stringstruct
|
yuuji@0
|
1678 * Accepts: string structure
|
yuuji@0
|
1679 * Returns: character, string structure chunk refreshed
|
yuuji@0
|
1680 */
|
yuuji@0
|
1681
|
yuuji@0
|
1682 char msg_string_next (STRING *s)
|
yuuji@0
|
1683 {
|
yuuji@0
|
1684 char c = *s->curpos++; /* get next byte */
|
yuuji@0
|
1685 SETPOS (s,GETPOS (s)); /* move to next chunk */
|
yuuji@0
|
1686 return c; /* return the byte */
|
yuuji@0
|
1687 }
|
yuuji@0
|
1688
|
yuuji@0
|
1689
|
yuuji@0
|
1690 /* Set string pointer position for file stringstruct
|
yuuji@0
|
1691 * Accepts: string structure
|
yuuji@0
|
1692 * new position
|
yuuji@0
|
1693 */
|
yuuji@0
|
1694
|
yuuji@0
|
1695 void msg_string_setpos (STRING *s,unsigned long i)
|
yuuji@0
|
1696 {
|
yuuji@0
|
1697 MSGDATA *md = (MSGDATA *) s->data;
|
yuuji@0
|
1698 if (i < s->data1) { /* want header? */
|
yuuji@0
|
1699 s->chunk = mail_fetchheader_full (md->stream,md->msgno,NIL,NIL,FT_PEEK);
|
yuuji@0
|
1700 s->chunksize = s->data1; /* header length */
|
yuuji@0
|
1701 s->offset = 0; /* offset is start of message */
|
yuuji@0
|
1702 }
|
yuuji@0
|
1703 else if (i < s->size) { /* want body */
|
yuuji@0
|
1704 s->chunk = mail_fetchtext_full (md->stream,md->msgno,NIL,FT_PEEK);
|
yuuji@0
|
1705 s->chunksize = s->size - s->data1;
|
yuuji@0
|
1706 s->offset = s->data1; /* offset is end of header */
|
yuuji@0
|
1707 }
|
yuuji@0
|
1708 else { /* off end of message */
|
yuuji@0
|
1709 s->chunk = NIL; /* make sure that we crack on this then */
|
yuuji@0
|
1710 s->chunksize = 1; /* make sure SNX cracks the right way... */
|
yuuji@0
|
1711 s->offset = i;
|
yuuji@0
|
1712 }
|
yuuji@0
|
1713 /* initial position and size */
|
yuuji@0
|
1714 s->curpos = s->chunk + (i -= s->offset);
|
yuuji@0
|
1715 s->cursize = s->chunksize - i;
|
yuuji@0
|
1716 }
|
yuuji@0
|
1717
|
yuuji@0
|
1718 /* Send flags for stream
|
yuuji@0
|
1719 * Accepts: MAIL stream
|
yuuji@0
|
1720 * scratch buffer
|
yuuji@0
|
1721 */
|
yuuji@0
|
1722
|
yuuji@0
|
1723 void new_flags (MAILSTREAM *stream)
|
yuuji@0
|
1724 {
|
yuuji@0
|
1725 int i,c;
|
yuuji@0
|
1726 PSOUT ("* FLAGS (");
|
yuuji@0
|
1727 for (i = 0; i < NUSERFLAGS; i++) if (stream->user_flags[i]) {
|
yuuji@0
|
1728 PSOUT (stream->user_flags[i]);
|
yuuji@0
|
1729 PBOUT (' ');
|
yuuji@0
|
1730 nflags = i + 1;
|
yuuji@0
|
1731 }
|
yuuji@0
|
1732 PSOUT ("\\Answered \\Flagged \\Deleted \\Draft \\Seen)\015\012* OK [PERMANENTFLAGS (");
|
yuuji@0
|
1733 for (i = c = 0; i < NUSERFLAGS; i++)
|
yuuji@0
|
1734 if ((stream->perm_user_flags & (1 << i)) && stream->user_flags[i])
|
yuuji@0
|
1735 put_flag (&c,stream->user_flags[i]);
|
yuuji@0
|
1736 if (stream->kwd_create) put_flag (&c,"\\*");
|
yuuji@0
|
1737 if (stream->perm_answered) put_flag (&c,"\\Answered");
|
yuuji@0
|
1738 if (stream->perm_flagged) put_flag (&c,"\\Flagged");
|
yuuji@0
|
1739 if (stream->perm_deleted) put_flag (&c,"\\Deleted");
|
yuuji@0
|
1740 if (stream->perm_draft) put_flag (&c,"\\Draft");
|
yuuji@0
|
1741 if (stream->perm_seen) put_flag (&c,"\\Seen");
|
yuuji@0
|
1742 PSOUT (")] Permanent flags\015\012");
|
yuuji@0
|
1743 }
|
yuuji@0
|
1744
|
yuuji@0
|
1745 /* Set timeout
|
yuuji@0
|
1746 * Accepts: desired interval
|
yuuji@0
|
1747 */
|
yuuji@0
|
1748
|
yuuji@0
|
1749 void settimeout (unsigned int i)
|
yuuji@0
|
1750 {
|
yuuji@0
|
1751 /* limit if not logged in */
|
yuuji@0
|
1752 if (i) alarm ((state == LOGIN) ? LOGINTIMEOUT : i);
|
yuuji@0
|
1753 else alarm (0);
|
yuuji@0
|
1754 }
|
yuuji@0
|
1755
|
yuuji@0
|
1756
|
yuuji@0
|
1757 /* Clock interrupt
|
yuuji@0
|
1758 * Returns only if critical code in progress
|
yuuji@0
|
1759 */
|
yuuji@0
|
1760
|
yuuji@0
|
1761 void clkint (void)
|
yuuji@0
|
1762 {
|
yuuji@0
|
1763 settimeout (0); /* disable all interrupts */
|
yuuji@0
|
1764 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
1765 logout = "Autologout";
|
yuuji@0
|
1766 goodbye = "Autologout (idle for too long)";
|
yuuji@0
|
1767 if (critical) { /* must defer if in critical code(?) */
|
yuuji@0
|
1768 close (0); /* kill stdin */
|
yuuji@0
|
1769 state = LOGOUT; /* die as soon as we can */
|
yuuji@0
|
1770 }
|
yuuji@0
|
1771 else longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
1772 }
|
yuuji@0
|
1773
|
yuuji@0
|
1774
|
yuuji@0
|
1775 /* Kiss Of Death interrupt
|
yuuji@0
|
1776 * Returns only if critical code in progress
|
yuuji@0
|
1777 */
|
yuuji@0
|
1778
|
yuuji@0
|
1779 void kodint (void)
|
yuuji@0
|
1780 {
|
yuuji@0
|
1781 settimeout (0); /* disable all interrupts */
|
yuuji@0
|
1782 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
1783 logout = goodbye = "Killed (lost mailbox lock)";
|
yuuji@0
|
1784 if (critical) { /* must defer if in critical code */
|
yuuji@0
|
1785 close (0); /* kill stdin */
|
yuuji@0
|
1786 state = LOGOUT; /* die as soon as we can */
|
yuuji@0
|
1787 }
|
yuuji@0
|
1788 else longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
1789 }
|
yuuji@0
|
1790
|
yuuji@0
|
1791 /* Hangup interrupt
|
yuuji@0
|
1792 * Returns only if critical code in progress
|
yuuji@0
|
1793 */
|
yuuji@0
|
1794
|
yuuji@0
|
1795 void hupint (void)
|
yuuji@0
|
1796 {
|
yuuji@0
|
1797 settimeout (0); /* disable all interrupts */
|
yuuji@0
|
1798 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
1799 logout = "Hangup";
|
yuuji@0
|
1800 goodbye = NIL; /* other end is already gone */
|
yuuji@0
|
1801 if (critical) { /* must defer if in critical code */
|
yuuji@0
|
1802 close (0); /* kill stdin */
|
yuuji@0
|
1803 close (1); /* and stdout */
|
yuuji@0
|
1804 state = LOGOUT; /* die as soon as we can */
|
yuuji@0
|
1805 }
|
yuuji@0
|
1806 else longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
1807 }
|
yuuji@0
|
1808
|
yuuji@0
|
1809
|
yuuji@0
|
1810 /* Termination interrupt
|
yuuji@0
|
1811 * Returns only if critical code in progress
|
yuuji@0
|
1812 */
|
yuuji@0
|
1813
|
yuuji@0
|
1814 void trmint (void)
|
yuuji@0
|
1815 {
|
yuuji@0
|
1816 settimeout (0); /* disable all interrupts */
|
yuuji@0
|
1817 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
1818 logout = goodbye = "Killed (terminated)";
|
yuuji@0
|
1819 /* Make no attempt at graceful closure since a shutdown may be in
|
yuuji@0
|
1820 * progress, and we won't have any time to do mail_close() actions
|
yuuji@0
|
1821 */
|
yuuji@0
|
1822 stream = NIL;
|
yuuji@0
|
1823 if (critical) { /* must defer if in critical code */
|
yuuji@0
|
1824 close (0); /* kill stdin */
|
yuuji@0
|
1825 close (1); /* and stdout */
|
yuuji@0
|
1826 state = LOGOUT; /* die as soon as we can */
|
yuuji@0
|
1827 }
|
yuuji@0
|
1828 else longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
1829 }
|
yuuji@0
|
1830
|
yuuji@0
|
1831 /* The routines on this and the next page eschew the use of non-syscall libc
|
yuuji@0
|
1832 * routines (especially stdio) for a reason. Also, these hideous #if
|
yuuji@0
|
1833 * condtionals need to be replaced.
|
yuuji@0
|
1834 */
|
yuuji@0
|
1835
|
yuuji@0
|
1836 #ifndef unix
|
yuuji@0
|
1837 #define unix 0
|
yuuji@0
|
1838 #endif
|
yuuji@0
|
1839
|
yuuji@0
|
1840
|
yuuji@0
|
1841 /* Status request interrupt
|
yuuji@0
|
1842 * Always returns
|
yuuji@0
|
1843 */
|
yuuji@0
|
1844
|
yuuji@0
|
1845 void staint (void)
|
yuuji@0
|
1846 {
|
yuuji@0
|
1847 #if unix
|
yuuji@0
|
1848 int fd;
|
yuuji@0
|
1849 char *s,buf[8*MAILTMPLEN];
|
yuuji@0
|
1850 unsigned long pid = getpid ();
|
yuuji@0
|
1851 /* build file name */
|
yuuji@0
|
1852 s = nout (sout (buf,"/tmp/imapd-status."),pid,10);
|
yuuji@0
|
1853 if (user) s = sout (sout (s,"."),user);
|
yuuji@0
|
1854 *s = '\0'; /* tie off file name */
|
yuuji@0
|
1855 if ((fd = open (buf,O_WRONLY | O_CREAT | O_TRUNC,0666)) >= 0) {
|
yuuji@0
|
1856 fchmod (fd,0666);
|
yuuji@0
|
1857 s = nout (sout (buf,"PID="),pid,10);
|
yuuji@0
|
1858 if (user) s = sout (sout (s,", user="),user);
|
yuuji@0
|
1859 switch (state) {
|
yuuji@0
|
1860 case LOGIN:
|
yuuji@0
|
1861 s = sout (s,", not logged in");
|
yuuji@0
|
1862 break;
|
yuuji@0
|
1863 case SELECT:
|
yuuji@0
|
1864 s = sout (s,", logged in");
|
yuuji@0
|
1865 break;
|
yuuji@0
|
1866 case OPEN:
|
yuuji@0
|
1867 s = sout (s,", mailbox open");
|
yuuji@0
|
1868 break;
|
yuuji@0
|
1869 case LOGOUT:
|
yuuji@0
|
1870 s = sout (s,", logging out");
|
yuuji@0
|
1871 break;
|
yuuji@0
|
1872 }
|
yuuji@0
|
1873 if (stream && stream->mailbox)
|
yuuji@0
|
1874 s = sout (sout (s,"\nmailbox="),stream->mailbox);
|
yuuji@0
|
1875 *s++ = '\n';
|
yuuji@0
|
1876 if (status) {
|
yuuji@0
|
1877 s = sout (s,status);
|
yuuji@0
|
1878 if (cmd) s = sout (sout (s,", last command="),cmd);
|
yuuji@0
|
1879 }
|
yuuji@0
|
1880 else s = sout (sout (s,cmd)," in progress");
|
yuuji@0
|
1881 *s++ = '\n';
|
yuuji@0
|
1882 write (fd,buf,s-buf);
|
yuuji@0
|
1883 close (fd);
|
yuuji@0
|
1884 }
|
yuuji@0
|
1885 #endif
|
yuuji@0
|
1886 }
|
yuuji@0
|
1887
|
yuuji@0
|
1888 /* Write string
|
yuuji@0
|
1889 * Accepts: destination string pointer
|
yuuji@0
|
1890 * string
|
yuuji@0
|
1891 * Returns: updated string pointer
|
yuuji@0
|
1892 */
|
yuuji@0
|
1893
|
yuuji@0
|
1894 char *sout (char *s,char *t)
|
yuuji@0
|
1895 {
|
yuuji@0
|
1896 while (*t) *s++ = *t++;
|
yuuji@0
|
1897 return s;
|
yuuji@0
|
1898 }
|
yuuji@0
|
1899
|
yuuji@0
|
1900
|
yuuji@0
|
1901 /* Write number
|
yuuji@0
|
1902 * Accepts: destination string pointer
|
yuuji@0
|
1903 * number
|
yuuji@0
|
1904 * base
|
yuuji@0
|
1905 * Returns: updated string pointer
|
yuuji@0
|
1906 */
|
yuuji@0
|
1907
|
yuuji@0
|
1908 char *nout (char *s,unsigned long n,unsigned long base)
|
yuuji@0
|
1909 {
|
yuuji@0
|
1910 char stack[256];
|
yuuji@0
|
1911 char *t = stack;
|
yuuji@0
|
1912 /* push PID digits on stack */
|
yuuji@0
|
1913 do *t++ = (char) (n % base) + '0';
|
yuuji@0
|
1914 while (n /= base);
|
yuuji@0
|
1915 /* pop digits from stack */
|
yuuji@0
|
1916 while (t > stack) *s++ = *--t;
|
yuuji@0
|
1917 return s;
|
yuuji@0
|
1918 }
|
yuuji@0
|
1919
|
yuuji@0
|
1920 /* Slurp a command line
|
yuuji@0
|
1921 * Accepts: buffer pointer
|
yuuji@0
|
1922 * buffer size
|
yuuji@0
|
1923 * input timeout
|
yuuji@0
|
1924 */
|
yuuji@0
|
1925
|
yuuji@0
|
1926 void slurp (char *s,int n,unsigned long timeout)
|
yuuji@0
|
1927 {
|
yuuji@0
|
1928 memset (s,'\0',n); /* zap buffer */
|
yuuji@0
|
1929 if (state != LOGOUT) { /* get a command under timeout */
|
yuuji@0
|
1930 settimeout (timeout);
|
yuuji@0
|
1931 clearerr (stdin); /* clear stdin errors */
|
yuuji@0
|
1932 status = "reading line";
|
yuuji@0
|
1933 if (!PSIN (s,n-1)) ioerror (stdin,status);
|
yuuji@0
|
1934 settimeout (0); /* make sure timeout disabled */
|
yuuji@0
|
1935 status = NIL;
|
yuuji@0
|
1936 }
|
yuuji@0
|
1937 }
|
yuuji@0
|
1938
|
yuuji@0
|
1939
|
yuuji@0
|
1940 /* Read a literal
|
yuuji@0
|
1941 * Accepts: destination buffer (must be size+1 for trailing NUL)
|
yuuji@0
|
1942 * size of buffer (must be less than 4294967295)
|
yuuji@0
|
1943 */
|
yuuji@0
|
1944
|
yuuji@0
|
1945 void inliteral (char *s,unsigned long n)
|
yuuji@0
|
1946 {
|
yuuji@0
|
1947 unsigned long i;
|
yuuji@0
|
1948 if (litplus.ok) { /* no more LITERAL+ to worry about */
|
yuuji@0
|
1949 litplus.ok = NIL;
|
yuuji@0
|
1950 litplus.size = 0;
|
yuuji@0
|
1951 }
|
yuuji@0
|
1952 else { /* otherwise tell client ready for argument */
|
yuuji@0
|
1953 PSOUT ("+ Ready for argument\015\012");
|
yuuji@0
|
1954 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
1955 }
|
yuuji@0
|
1956 clearerr (stdin); /* clear stdin errors */
|
yuuji@0
|
1957 memset (s,'\0',n+1); /* zap buffer */
|
yuuji@0
|
1958 status = "reading literal";
|
yuuji@0
|
1959 while (n) { /* get data under timeout */
|
yuuji@0
|
1960 if (state == LOGOUT) n = 0;
|
yuuji@0
|
1961 else {
|
yuuji@0
|
1962 settimeout (INPUTTIMEOUT);
|
yuuji@0
|
1963 i = min (n,8192); /* must read at least 8K within timeout */
|
yuuji@0
|
1964 if (PSINR (s,i)) {
|
yuuji@0
|
1965 s += i;
|
yuuji@0
|
1966 n -= i;
|
yuuji@0
|
1967 }
|
yuuji@0
|
1968 else {
|
yuuji@0
|
1969 ioerror (stdin,status);
|
yuuji@0
|
1970 n = 0; /* in case it continues */
|
yuuji@0
|
1971 }
|
yuuji@0
|
1972 settimeout (0); /* stop timeout */
|
yuuji@0
|
1973 }
|
yuuji@0
|
1974 }
|
yuuji@0
|
1975 }
|
yuuji@0
|
1976
|
yuuji@0
|
1977 /* Flush until newline seen
|
yuuji@0
|
1978 * Returns: NIL, always
|
yuuji@0
|
1979 */
|
yuuji@0
|
1980
|
yuuji@0
|
1981 unsigned char *flush (void)
|
yuuji@0
|
1982 {
|
yuuji@0
|
1983 int c;
|
yuuji@0
|
1984 if (state != LOGOUT) {
|
yuuji@0
|
1985 settimeout (INPUTTIMEOUT);
|
yuuji@0
|
1986 clearerr (stdin); /* clear stdin errors */
|
yuuji@0
|
1987 status = "flushing line";
|
yuuji@0
|
1988 while ((c = PBIN ()) != '\012') if (c == EOF) ioerror (stdin,status);
|
yuuji@0
|
1989 settimeout (0); /* make sure timeout disabled */
|
yuuji@0
|
1990 }
|
yuuji@0
|
1991 response = "%.80s BAD Command line too long\015\012";
|
yuuji@0
|
1992 status = NIL;
|
yuuji@0
|
1993 return NIL;
|
yuuji@0
|
1994 }
|
yuuji@0
|
1995
|
yuuji@0
|
1996
|
yuuji@0
|
1997 /* Report command stream error and die
|
yuuji@0
|
1998 * Accepts: stdin or stdout (whichever got the error)
|
yuuji@0
|
1999 * reason (what caller was doing)
|
yuuji@0
|
2000 */
|
yuuji@0
|
2001
|
yuuji@0
|
2002 void ioerror (FILE *f,char *reason)
|
yuuji@0
|
2003 {
|
yuuji@0
|
2004 static char msg[MAILTMPLEN];
|
yuuji@0
|
2005 char *s,*t;
|
yuuji@0
|
2006 if (logout) { /* say nothing if already dying */
|
yuuji@0
|
2007 settimeout (0); /* disable all interrupts */
|
yuuji@0
|
2008 server_init (NIL,NIL,NIL,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN,SIG_IGN);
|
yuuji@0
|
2009 /* write error string */
|
yuuji@0
|
2010 for (s = ferror (f) ? strerror (errno) : "Unexpected client disconnect",
|
yuuji@0
|
2011 t = logout = msg; *s; *t++ = *s++);
|
yuuji@0
|
2012 for (s = ", while "; *s; *t++ = *s++);
|
yuuji@0
|
2013 for (s = reason; *s; *t++ = *s++);
|
yuuji@0
|
2014 if (critical) { /* must defer if in critical code */
|
yuuji@0
|
2015 close (0); /* kill stdin */
|
yuuji@0
|
2016 close (1); /* and stdout */
|
yuuji@0
|
2017 state = LOGOUT; /* die as soon as we can */
|
yuuji@0
|
2018 }
|
yuuji@0
|
2019 else longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
2020 }
|
yuuji@0
|
2021 }
|
yuuji@0
|
2022
|
yuuji@0
|
2023 /* Parse an IMAP astring
|
yuuji@0
|
2024 * Accepts: pointer to argument text pointer
|
yuuji@0
|
2025 * pointer to returned size
|
yuuji@0
|
2026 * pointer to returned delimiter
|
yuuji@0
|
2027 * Returns: argument
|
yuuji@0
|
2028 */
|
yuuji@0
|
2029
|
yuuji@0
|
2030 unsigned char *parse_astring (unsigned char **arg,unsigned long *size,
|
yuuji@0
|
2031 unsigned char *del)
|
yuuji@0
|
2032 {
|
yuuji@0
|
2033 unsigned long i;
|
yuuji@0
|
2034 unsigned char c,*s,*t,*v;
|
yuuji@0
|
2035 if (!*arg) return NIL; /* better be an argument */
|
yuuji@0
|
2036 switch (**arg) { /* see what the argument is */
|
yuuji@0
|
2037 default: /* atom */
|
yuuji@0
|
2038 for (s = t = *arg, i = 0;
|
yuuji@0
|
2039 (*t > ' ') && (*t < 0x7f) && (*t != '(') && (*t != ')') &&
|
yuuji@0
|
2040 (*t != '{') && (*t != '%') && (*t != '*') && (*t != '"') &&
|
yuuji@0
|
2041 (*t != '\\'); ++t,++i);
|
yuuji@0
|
2042 if (*size = i) break; /* got atom if non-empty */
|
yuuji@0
|
2043 case ')': case '%': case '*': case '\\': case '\0': case ' ':
|
yuuji@0
|
2044 return NIL; /* empty atom is a bogon */
|
yuuji@0
|
2045 case '"': /* hunt for trailing quote */
|
yuuji@0
|
2046 for (s = t = v = *arg + 1; (c = *t++) != '"'; *v++ = c) {
|
yuuji@0
|
2047 /* quote next character */
|
yuuji@0
|
2048 if (c == '\\') switch (c = *t++) {
|
yuuji@0
|
2049 case '"': case '\\': break;
|
yuuji@0
|
2050 default: return NIL; /* invalid quote-next */
|
yuuji@0
|
2051 }
|
yuuji@0
|
2052 /* else must be a CHAR */
|
yuuji@0
|
2053 if (!c || (c & 0x80)) return NIL;
|
yuuji@0
|
2054 }
|
yuuji@0
|
2055 *v = '\0'; /* tie off string */
|
yuuji@0
|
2056 *size = v - s; /* return size */
|
yuuji@0
|
2057 break;
|
yuuji@0
|
2058
|
yuuji@0
|
2059 case '{': /* literal string */
|
yuuji@0
|
2060 s = *arg + 1; /* get size */
|
yuuji@0
|
2061 if (!isdigit (*s)) return NIL;
|
yuuji@0
|
2062 if ((*size = i = strtoul (s,(char **) &t,10)) > MAXCLIENTLIT) {
|
yuuji@0
|
2063 mm_notify (NIL,"Absurdly long client literal",ERROR);
|
yuuji@0
|
2064 syslog (LOG_INFO,"Overlong (%lu) client literal user=%.80s host=%.80s",
|
yuuji@0
|
2065 i,user ? (char *) user : "???",tcp_clienthost ());
|
yuuji@0
|
2066 return NIL;
|
yuuji@0
|
2067 }
|
yuuji@0
|
2068 switch (*t) { /* validate end of literal */
|
yuuji@0
|
2069 case '+': /* non-blocking literal */
|
yuuji@0
|
2070 if (*++t != '}') return NIL;
|
yuuji@0
|
2071 case '}':
|
yuuji@0
|
2072 if (!t[1]) break; /* OK if end of line */
|
yuuji@0
|
2073 default:
|
yuuji@0
|
2074 return NIL; /* bad literal */
|
yuuji@0
|
2075 }
|
yuuji@0
|
2076 if (litsp >= LITSTKLEN) { /* make sure don't overflow stack */
|
yuuji@0
|
2077 mm_notify (NIL,"Too many literals in command",ERROR);
|
yuuji@0
|
2078 return NIL;
|
yuuji@0
|
2079 }
|
yuuji@0
|
2080 /* get a literal buffer */
|
yuuji@0
|
2081 inliteral (s = litstk[litsp++] = (char *) fs_get (i+1),i);
|
yuuji@0
|
2082 /* get new command tail */
|
yuuji@0
|
2083 slurp (*arg = t,CMDLEN - (t - cmdbuf),INPUTTIMEOUT);
|
yuuji@0
|
2084 if (!strchr (t,'\012')) return flush ();
|
yuuji@0
|
2085 /* reset strtok mechanism, tie off if done */
|
yuuji@0
|
2086 if (!strtok (t,"\015\012")) *t = '\0';
|
yuuji@0
|
2087 /* possible LITERAL+? */
|
yuuji@0
|
2088 if (((i = strlen (t)) > 3) && (t[i - 1] == '}') &&
|
yuuji@0
|
2089 (t[i - 2] == '+') && isdigit (t[i - 3])) {
|
yuuji@0
|
2090 /* back over possible count */
|
yuuji@0
|
2091 for (i -= 4; i && isdigit (t[i]); i--);
|
yuuji@0
|
2092 if (t[i] == '{') { /* found a literal? */
|
yuuji@0
|
2093 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
|
yuuji@0
|
2094 litplus.size = strtoul (t + i + 1,NIL,10);
|
yuuji@0
|
2095 }
|
yuuji@0
|
2096 }
|
yuuji@0
|
2097 break;
|
yuuji@0
|
2098 }
|
yuuji@0
|
2099 if (*del = *t) { /* have a delimiter? */
|
yuuji@0
|
2100 *t++ = '\0'; /* yes, stomp on it */
|
yuuji@0
|
2101 *arg = t; /* update argument pointer */
|
yuuji@0
|
2102 }
|
yuuji@0
|
2103 else *arg = NIL; /* no more arguments */
|
yuuji@0
|
2104 return s;
|
yuuji@0
|
2105 }
|
yuuji@0
|
2106
|
yuuji@0
|
2107 /* Snarf a command argument (simple jacket into parse_astring())
|
yuuji@0
|
2108 * Accepts: pointer to argument text pointer
|
yuuji@0
|
2109 * Returns: argument
|
yuuji@0
|
2110 */
|
yuuji@0
|
2111
|
yuuji@0
|
2112 unsigned char *snarf (unsigned char **arg)
|
yuuji@0
|
2113 {
|
yuuji@0
|
2114 unsigned long i;
|
yuuji@0
|
2115 unsigned char c;
|
yuuji@0
|
2116 unsigned char *s = parse_astring (arg,&i,&c);
|
yuuji@0
|
2117 return ((c == ' ') || !c) ? s : NIL;
|
yuuji@0
|
2118 }
|
yuuji@0
|
2119
|
yuuji@0
|
2120
|
yuuji@0
|
2121 /* Snarf a BASE64 argument for SASL-IR
|
yuuji@0
|
2122 * Accepts: pointer to argument text pointer
|
yuuji@0
|
2123 * Returns: argument
|
yuuji@0
|
2124 */
|
yuuji@0
|
2125
|
yuuji@0
|
2126 unsigned char *snarf_base64 (unsigned char **arg)
|
yuuji@0
|
2127 {
|
yuuji@0
|
2128 unsigned char *ret = *arg;
|
yuuji@0
|
2129 unsigned char *s = ret + 1;
|
yuuji@0
|
2130 static char base64mask[256] = {
|
yuuji@0
|
2131 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
yuuji@0
|
2132 0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
|
yuuji@0
|
2133 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
|
yuuji@0
|
2134 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
|
yuuji@0
|
2135 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
yuuji@0
|
2136 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
yuuji@0
|
2137 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
yuuji@0
|
2138 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
yuuji@0
|
2139 };
|
yuuji@0
|
2140 if (*(ret = *arg) == '='); /* easy case if zero-length argument */
|
yuuji@0
|
2141 /* must be at least one BASE64 char */
|
yuuji@0
|
2142 else if (!base64mask[*ret]) return NIL;
|
yuuji@0
|
2143 else { /* quick and dirty */
|
yuuji@0
|
2144 while (base64mask[*s++]); /* scan until end of BASE64 */
|
yuuji@0
|
2145 if (*s == '=') ++s; /* allow up to two padding chars */
|
yuuji@0
|
2146 if (*s == '=') ++s;
|
yuuji@0
|
2147 }
|
yuuji@0
|
2148 switch (*s) { /* anything following the argument? */
|
yuuji@0
|
2149 case ' ': /* another argument */
|
yuuji@0
|
2150 *s++ = '\0'; /* tie off previous argument */
|
yuuji@0
|
2151 *arg = s; /* and update argument pointer */
|
yuuji@0
|
2152 break;
|
yuuji@0
|
2153 case '\0': /* end of command */
|
yuuji@0
|
2154 *arg = NIL;
|
yuuji@0
|
2155 break;
|
yuuji@0
|
2156 default: /* syntax error */
|
yuuji@0
|
2157 return NIL;
|
yuuji@0
|
2158 }
|
yuuji@0
|
2159 return ret; /* return BASE64 string */
|
yuuji@0
|
2160 }
|
yuuji@0
|
2161
|
yuuji@0
|
2162 /* Snarf a list command argument (simple jacket into parse_astring())
|
yuuji@0
|
2163 * Accepts: pointer to argument text pointer
|
yuuji@0
|
2164 * Returns: argument
|
yuuji@0
|
2165 */
|
yuuji@0
|
2166
|
yuuji@0
|
2167 unsigned char *snarf_list (unsigned char **arg)
|
yuuji@0
|
2168 {
|
yuuji@0
|
2169 unsigned long i;
|
yuuji@0
|
2170 unsigned char c,*s,*t;
|
yuuji@0
|
2171 if (!*arg) return NIL; /* better be an argument */
|
yuuji@0
|
2172 switch (**arg) {
|
yuuji@0
|
2173 default: /* atom and/or wildcard chars */
|
yuuji@0
|
2174 for (s = t = *arg, i = 0;
|
yuuji@0
|
2175 (*t > ' ') && (*t != '(') && (*t != ')') && (*t != '{') &&
|
yuuji@0
|
2176 (*t != '"') && (*t != '\\'); ++t,++i);
|
yuuji@0
|
2177 if (c = *t) { /* have a delimiter? */
|
yuuji@0
|
2178 *t++ = '\0'; /* stomp on it */
|
yuuji@0
|
2179 *arg = t; /* update argument pointer */
|
yuuji@0
|
2180 }
|
yuuji@0
|
2181 else *arg = NIL;
|
yuuji@0
|
2182 break;
|
yuuji@0
|
2183 case ')': case '\\': case '\0': case ' ':
|
yuuji@0
|
2184 return NIL; /* empty name is bogus */
|
yuuji@0
|
2185 case '"': /* quoted string? */
|
yuuji@0
|
2186 case '{': /* or literal? */
|
yuuji@0
|
2187 s = parse_astring (arg,&i,&c);
|
yuuji@0
|
2188 break;
|
yuuji@0
|
2189 }
|
yuuji@0
|
2190 return ((c == ' ') || !c) ? s : NIL;
|
yuuji@0
|
2191 }
|
yuuji@0
|
2192
|
yuuji@0
|
2193 /* Get a list of header lines
|
yuuji@0
|
2194 * Accepts: pointer to string pointer
|
yuuji@0
|
2195 * pointer to list flag
|
yuuji@0
|
2196 * Returns: string list
|
yuuji@0
|
2197 */
|
yuuji@0
|
2198
|
yuuji@0
|
2199 STRINGLIST *parse_stringlist (unsigned char **s,int *list)
|
yuuji@0
|
2200 {
|
yuuji@0
|
2201 char c = ' ',*t;
|
yuuji@0
|
2202 unsigned long i;
|
yuuji@0
|
2203 STRINGLIST *ret = NIL,*cur = NIL;
|
yuuji@0
|
2204 if (*s && **s == '(') { /* proper list? */
|
yuuji@0
|
2205 ++*s; /* for each item in list */
|
yuuji@0
|
2206 while ((c == ' ') && (t = parse_astring (s,&i,&c))) {
|
yuuji@0
|
2207 /* get new block */
|
yuuji@0
|
2208 if (cur) cur = cur->next = mail_newstringlist ();
|
yuuji@0
|
2209 else cur = ret = mail_newstringlist ();
|
yuuji@0
|
2210 /* note text */
|
yuuji@0
|
2211 cur->text.data = (unsigned char *) fs_get (i + 1);
|
yuuji@0
|
2212 memcpy (cur->text.data,t,i);
|
yuuji@0
|
2213 cur->text.size = i; /* and size */
|
yuuji@0
|
2214 }
|
yuuji@0
|
2215 /* must be end of list */
|
yuuji@0
|
2216 if (c != ')') mail_free_stringlist (&ret);
|
yuuji@0
|
2217 }
|
yuuji@0
|
2218 if (t = *s) { /* need to reload strtok() state? */
|
yuuji@0
|
2219 /* end of a list? */
|
yuuji@0
|
2220 if (*list && (*t == ')') && !t[1]) *list = NIL;
|
yuuji@0
|
2221 else {
|
yuuji@0
|
2222 *--t = ' '; /* patch a space back in */
|
yuuji@0
|
2223 *--t = 'x'; /* and a hokey character before that */
|
yuuji@0
|
2224 t = strtok (t," "); /* reset to *s */
|
yuuji@0
|
2225 }
|
yuuji@0
|
2226 }
|
yuuji@0
|
2227 return ret;
|
yuuji@0
|
2228 }
|
yuuji@0
|
2229
|
yuuji@0
|
2230 /* Get value of UID * for criteria parsing
|
yuuji@0
|
2231 * Accepts: stream
|
yuuji@0
|
2232 * Returns: maximum UID
|
yuuji@0
|
2233 */
|
yuuji@0
|
2234
|
yuuji@0
|
2235 unsigned long uidmax (MAILSTREAM *stream)
|
yuuji@0
|
2236 {
|
yuuji@0
|
2237 return stream->nmsgs ? mail_uid (stream,stream->nmsgs) : 0xffffffff;
|
yuuji@0
|
2238 }
|
yuuji@0
|
2239
|
yuuji@0
|
2240
|
yuuji@0
|
2241 /* Parse search criteria
|
yuuji@0
|
2242 * Accepts: search program to write criteria into
|
yuuji@0
|
2243 * pointer to argument text pointer
|
yuuji@0
|
2244 * maximum message number
|
yuuji@0
|
2245 * maximum UID
|
yuuji@0
|
2246 * logical nesting depth
|
yuuji@0
|
2247 * Returns: T if success, NIL if error
|
yuuji@0
|
2248 */
|
yuuji@0
|
2249
|
yuuji@0
|
2250 long parse_criteria (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
|
yuuji@0
|
2251 unsigned long maxuid,unsigned long depth)
|
yuuji@0
|
2252 {
|
yuuji@0
|
2253 if (arg && *arg) { /* must be an argument */
|
yuuji@0
|
2254 /* parse criteria */
|
yuuji@0
|
2255 do if (!parse_criterion (pgm,arg,maxmsg,maxuid,depth)) return NIL;
|
yuuji@0
|
2256 /* as long as a space delimiter */
|
yuuji@0
|
2257 while (**arg == ' ' && (*arg)++);
|
yuuji@0
|
2258 /* failed if not end of criteria */
|
yuuji@0
|
2259 if (**arg && **arg != ')') return NIL;
|
yuuji@0
|
2260 }
|
yuuji@0
|
2261 return T; /* success */
|
yuuji@0
|
2262 }
|
yuuji@0
|
2263
|
yuuji@0
|
2264 /* Parse a search criterion
|
yuuji@0
|
2265 * Accepts: search program to write criterion into
|
yuuji@0
|
2266 * pointer to argument text pointer
|
yuuji@0
|
2267 * maximum message number
|
yuuji@0
|
2268 * maximum UID
|
yuuji@0
|
2269 * logical nesting depth
|
yuuji@0
|
2270 * Returns: T if success, NIL if error
|
yuuji@0
|
2271 */
|
yuuji@0
|
2272
|
yuuji@0
|
2273 long parse_criterion (SEARCHPGM *pgm,unsigned char **arg,unsigned long maxmsg,
|
yuuji@0
|
2274 unsigned long maxuid,unsigned long depth)
|
yuuji@0
|
2275 {
|
yuuji@0
|
2276 unsigned long i;
|
yuuji@0
|
2277 unsigned char c = NIL,*s,*t,*v,*tail,*del;
|
yuuji@0
|
2278 SEARCHSET **set;
|
yuuji@0
|
2279 SEARCHPGMLIST **not;
|
yuuji@0
|
2280 SEARCHOR **or;
|
yuuji@0
|
2281 SEARCHHEADER **hdr;
|
yuuji@0
|
2282 long ret = NIL;
|
yuuji@0
|
2283 /* better be an argument */
|
yuuji@0
|
2284 if ((depth > 500) || !(arg && *arg));
|
yuuji@0
|
2285 else if (**arg == '(') { /* list of criteria? */
|
yuuji@0
|
2286 (*arg)++; /* yes, parse the criteria */
|
yuuji@0
|
2287 if (parse_criteria (pgm,arg,maxmsg,maxuid,depth+1) && **arg == ')') {
|
yuuji@0
|
2288 (*arg)++; /* skip closing paren */
|
yuuji@0
|
2289 ret = T; /* successful parse of list */
|
yuuji@0
|
2290 }
|
yuuji@0
|
2291 }
|
yuuji@0
|
2292 else { /* find end of criterion */
|
yuuji@0
|
2293 if (!(tail = strpbrk ((s = *arg)," )"))) tail = *arg + strlen (*arg);
|
yuuji@0
|
2294 c = *(del = tail); /* remember the delimiter */
|
yuuji@0
|
2295 *del = '\0'; /* tie off criterion */
|
yuuji@0
|
2296 switch (*ucase (s)) { /* dispatch based on character */
|
yuuji@0
|
2297 case '*': /* sequence */
|
yuuji@0
|
2298 case '0': case '1': case '2': case '3': case '4':
|
yuuji@0
|
2299 case '5': case '6': case '7': case '8': case '9':
|
yuuji@0
|
2300 if (*(set = &pgm->msgno)){/* already a sequence? */
|
yuuji@0
|
2301 /* silly, but not as silly as the client! */
|
yuuji@0
|
2302 for (not = &pgm->not; *not; not = &(*not)->next);
|
yuuji@0
|
2303 *not = mail_newsearchpgmlist ();
|
yuuji@0
|
2304 set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->msgno;
|
yuuji@0
|
2305 }
|
yuuji@0
|
2306 ret = crit_set (set,&s,maxmsg) && (tail == s);
|
yuuji@0
|
2307 break;
|
yuuji@0
|
2308 case 'A': /* possible ALL, ANSWERED */
|
yuuji@0
|
2309 if (!strcmp (s+1,"LL")) ret = T;
|
yuuji@0
|
2310 else if (!strcmp (s+1,"NSWERED")) ret = pgm->answered = T;
|
yuuji@0
|
2311 break;
|
yuuji@0
|
2312
|
yuuji@0
|
2313 case 'B': /* possible BCC, BEFORE, BODY */
|
yuuji@0
|
2314 if (!strcmp (s+1,"CC") && c == ' ' && *++tail)
|
yuuji@0
|
2315 ret = crit_string (&pgm->bcc,&tail);
|
yuuji@0
|
2316 else if (!strcmp (s+1,"EFORE") && c == ' ' && *++tail)
|
yuuji@0
|
2317 ret = crit_date (&pgm->before,&tail);
|
yuuji@0
|
2318 else if (!strcmp (s+1,"ODY") && c == ' ' && *++tail)
|
yuuji@0
|
2319 ret = crit_string (&pgm->body,&tail);
|
yuuji@0
|
2320 break;
|
yuuji@0
|
2321 case 'C': /* possible CC */
|
yuuji@0
|
2322 if (!strcmp (s+1,"C") && c == ' ' && *++tail)
|
yuuji@0
|
2323 ret = crit_string (&pgm->cc,&tail);
|
yuuji@0
|
2324 break;
|
yuuji@0
|
2325 case 'D': /* possible DELETED */
|
yuuji@0
|
2326 if (!strcmp (s+1,"ELETED")) ret = pgm->deleted = T;
|
yuuji@0
|
2327 if (!strcmp (s+1,"RAFT")) ret = pgm->draft = T;
|
yuuji@0
|
2328 break;
|
yuuji@0
|
2329 case 'F': /* possible FLAGGED, FROM */
|
yuuji@0
|
2330 if (!strcmp (s+1,"LAGGED")) ret = pgm->flagged = T;
|
yuuji@0
|
2331 else if (!strcmp (s+1,"ROM") && c == ' ' && *++tail)
|
yuuji@0
|
2332 ret = crit_string (&pgm->from,&tail);
|
yuuji@0
|
2333 break;
|
yuuji@0
|
2334 case 'H': /* possible HEADER */
|
yuuji@0
|
2335 if (!strcmp (s+1,"EADER") && c == ' ' && *(v = tail + 1) &&
|
yuuji@0
|
2336 (s = parse_astring (&v,&i,&c)) && i && c == ' ' &&
|
yuuji@0
|
2337 (t = parse_astring (&v,&i,&c))) {
|
yuuji@0
|
2338 for (hdr = &pgm->header; *hdr; hdr = &(*hdr)->next);
|
yuuji@0
|
2339 *hdr = mail_newsearchheader (s,t);
|
yuuji@0
|
2340 /* update tail, restore delimiter */
|
yuuji@0
|
2341 *(tail = v ? v - 1 : t + i) = c;
|
yuuji@0
|
2342 ret = T; /* success */
|
yuuji@0
|
2343 }
|
yuuji@0
|
2344 break;
|
yuuji@0
|
2345 case 'K': /* possible KEYWORD */
|
yuuji@0
|
2346 if (!strcmp (s+1,"EYWORD") && c == ' ' && *++tail)
|
yuuji@0
|
2347 ret = crit_string (&pgm->keyword,&tail);
|
yuuji@0
|
2348 break;
|
yuuji@0
|
2349 case 'L':
|
yuuji@0
|
2350 if (!strcmp (s+1,"ARGER") && c == ' ' && *++tail)
|
yuuji@0
|
2351 ret = crit_number (&pgm->larger,&tail);
|
yuuji@0
|
2352 break;
|
yuuji@0
|
2353 case 'N': /* possible NEW, NOT */
|
yuuji@0
|
2354 if (!strcmp (s+1,"EW")) ret = pgm->recent = pgm->unseen = T;
|
yuuji@0
|
2355 else if (!strcmp (s+1,"OT") && c == ' ' && *++tail) {
|
yuuji@0
|
2356 for (not = &pgm->not; *not; not = &(*not)->next);
|
yuuji@0
|
2357 *not = mail_newsearchpgmlist ();
|
yuuji@0
|
2358 ret = parse_criterion ((*not)->pgm,&tail,maxmsg,maxuid,depth+1);
|
yuuji@0
|
2359 }
|
yuuji@0
|
2360 break;
|
yuuji@0
|
2361
|
yuuji@0
|
2362 case 'O': /* possible OLD, ON */
|
yuuji@0
|
2363 if (!strcmp (s+1,"LD")) ret = pgm->old = T;
|
yuuji@0
|
2364 else if (!strcmp (s+1,"N") && c == ' ' && *++tail)
|
yuuji@0
|
2365 ret = crit_date (&pgm->on,&tail);
|
yuuji@0
|
2366 else if (!strcmp (s+1,"R") && c == ' ') {
|
yuuji@0
|
2367 for (or = &pgm->or; *or; or = &(*or)->next);
|
yuuji@0
|
2368 *or = mail_newsearchor ();
|
yuuji@0
|
2369 ret = *++tail && parse_criterion((*or)->first,&tail,maxmsg,maxuid,
|
yuuji@0
|
2370 depth+1) &&
|
yuuji@0
|
2371 (*tail == ' ') && *++tail &&
|
yuuji@0
|
2372 parse_criterion ((*or)->second,&tail,maxmsg,maxuid,depth+1);
|
yuuji@0
|
2373 }
|
yuuji@0
|
2374 else if (!strcmp (s+1,"LDER") && c == ' ' && *++tail)
|
yuuji@0
|
2375 ret = crit_number (&pgm->older,&tail);
|
yuuji@0
|
2376 break;
|
yuuji@0
|
2377 case 'R': /* possible RECENT */
|
yuuji@0
|
2378 if (!strcmp (s+1,"ECENT")) ret = pgm->recent = T;
|
yuuji@0
|
2379 break;
|
yuuji@0
|
2380 case 'S': /* possible SEEN, SINCE, SUBJECT */
|
yuuji@0
|
2381 if (!strcmp (s+1,"EEN")) ret = pgm->seen = T;
|
yuuji@0
|
2382 else if (!strcmp (s+1,"ENTBEFORE") && c == ' ' && *++tail)
|
yuuji@0
|
2383 ret = crit_date (&pgm->sentbefore,&tail);
|
yuuji@0
|
2384 else if (!strcmp (s+1,"ENTON") && c == ' ' && *++tail)
|
yuuji@0
|
2385 ret = crit_date (&pgm->senton,&tail);
|
yuuji@0
|
2386 else if (!strcmp (s+1,"ENTSINCE") && c == ' ' && *++tail)
|
yuuji@0
|
2387 ret = crit_date (&pgm->sentsince,&tail);
|
yuuji@0
|
2388 else if (!strcmp (s+1,"INCE") && c == ' ' && *++tail)
|
yuuji@0
|
2389 ret = crit_date (&pgm->since,&tail);
|
yuuji@0
|
2390 else if (!strcmp (s+1,"MALLER") && c == ' ' && *++tail)
|
yuuji@0
|
2391 ret = crit_number (&pgm->smaller,&tail);
|
yuuji@0
|
2392 else if (!strcmp (s+1,"UBJECT") && c == ' ' && *++tail)
|
yuuji@0
|
2393 ret = crit_string (&pgm->subject,&tail);
|
yuuji@0
|
2394 break;
|
yuuji@0
|
2395 case 'T': /* possible TEXT, TO */
|
yuuji@0
|
2396 if (!strcmp (s+1,"EXT") && c == ' ' && *++tail)
|
yuuji@0
|
2397 ret = crit_string (&pgm->text,&tail);
|
yuuji@0
|
2398 else if (!strcmp (s+1,"O") && c == ' ' && *++tail)
|
yuuji@0
|
2399 ret = crit_string (&pgm->to,&tail);
|
yuuji@0
|
2400 break;
|
yuuji@0
|
2401
|
yuuji@0
|
2402 case 'U': /* possible UID, UN* */
|
yuuji@0
|
2403 if (!strcmp (s+1,"ID") && c== ' ' && *++tail) {
|
yuuji@0
|
2404 if (*(set = &pgm->uid)){/* already a sequence? */
|
yuuji@0
|
2405 /* silly, but not as silly as the client! */
|
yuuji@0
|
2406 for (not = &pgm->not; *not; not = &(*not)->next);
|
yuuji@0
|
2407 *not = mail_newsearchpgmlist ();
|
yuuji@0
|
2408 set = &((*not)->pgm->not = mail_newsearchpgmlist ())->pgm->uid;
|
yuuji@0
|
2409 }
|
yuuji@0
|
2410 ret = crit_set (set,&tail,maxuid);
|
yuuji@0
|
2411 }
|
yuuji@0
|
2412 else if (!strcmp (s+1,"NANSWERED")) ret = pgm->unanswered = T;
|
yuuji@0
|
2413 else if (!strcmp (s+1,"NDELETED")) ret = pgm->undeleted = T;
|
yuuji@0
|
2414 else if (!strcmp (s+1,"NDRAFT")) ret = pgm->undraft = T;
|
yuuji@0
|
2415 else if (!strcmp (s+1,"NFLAGGED")) ret = pgm->unflagged = T;
|
yuuji@0
|
2416 else if (!strcmp (s+1,"NKEYWORD") && c == ' ' && *++tail)
|
yuuji@0
|
2417 ret = crit_string (&pgm->unkeyword,&tail);
|
yuuji@0
|
2418 else if (!strcmp (s+1,"NSEEN")) ret = pgm->unseen = T;
|
yuuji@0
|
2419 break;
|
yuuji@0
|
2420 case 'Y': /* possible YOUNGER */
|
yuuji@0
|
2421 if (!strcmp (s+1,"OUNGER") && c == ' ' && *++tail)
|
yuuji@0
|
2422 ret = crit_number (&pgm->younger,&tail);
|
yuuji@0
|
2423 break;
|
yuuji@0
|
2424 default: /* oh dear */
|
yuuji@0
|
2425 break;
|
yuuji@0
|
2426 }
|
yuuji@0
|
2427 if (ret) { /* only bother if success */
|
yuuji@0
|
2428 *del = c; /* restore delimiter */
|
yuuji@0
|
2429 *arg = tail; /* update argument pointer */
|
yuuji@0
|
2430 }
|
yuuji@0
|
2431 }
|
yuuji@0
|
2432 return ret; /* return more to come */
|
yuuji@0
|
2433 }
|
yuuji@0
|
2434
|
yuuji@0
|
2435 /* Parse a search date criterion
|
yuuji@0
|
2436 * Accepts: date to write into
|
yuuji@0
|
2437 * pointer to argument text pointer
|
yuuji@0
|
2438 * Returns: T if success, NIL if error
|
yuuji@0
|
2439 */
|
yuuji@0
|
2440
|
yuuji@0
|
2441 long crit_date (unsigned short *date,unsigned char **arg)
|
yuuji@0
|
2442 {
|
yuuji@0
|
2443 if (*date) return NIL; /* can't double this value */
|
yuuji@0
|
2444 /* handle quoted form */
|
yuuji@0
|
2445 if (**arg != '"') return crit_date_work (date,arg);
|
yuuji@0
|
2446 (*arg)++; /* skip past opening quote */
|
yuuji@0
|
2447 if (!(crit_date_work (date,arg) && (**arg == '"'))) return NIL;
|
yuuji@0
|
2448 (*arg)++; /* skip closing quote */
|
yuuji@0
|
2449 return T;
|
yuuji@0
|
2450 }
|
yuuji@0
|
2451
|
yuuji@0
|
2452 /* Worker routine to parse a search date criterion
|
yuuji@0
|
2453 * Accepts: date to write into
|
yuuji@0
|
2454 * pointer to argument text pointer
|
yuuji@0
|
2455 * Returns: T if success, NIL if error
|
yuuji@0
|
2456 */
|
yuuji@0
|
2457
|
yuuji@0
|
2458 long crit_date_work (unsigned short *date,unsigned char **arg)
|
yuuji@0
|
2459 {
|
yuuji@0
|
2460 int d,m,y;
|
yuuji@0
|
2461 /* day */
|
yuuji@0
|
2462 if (isdigit (d = *(*arg)++) || ((d == ' ') && isdigit (**arg))) {
|
yuuji@0
|
2463 if (d == ' ') d = 0; /* leading space */
|
yuuji@0
|
2464 else d -= '0'; /* first digit */
|
yuuji@0
|
2465 if (isdigit (**arg)) { /* if a second digit */
|
yuuji@0
|
2466 d *= 10; /* slide over first digit */
|
yuuji@0
|
2467 d += *(*arg)++ - '0'; /* second digit */
|
yuuji@0
|
2468 }
|
yuuji@0
|
2469 if ((**arg == '-') && (y = *++(*arg))) {
|
yuuji@0
|
2470 m = (y >= 'a' ? y - 'a' : y - 'A') * 1024;
|
yuuji@0
|
2471 if ((y = *++(*arg))) {
|
yuuji@0
|
2472 m += (y >= 'a' ? y - 'a' : y - 'A') * 32;
|
yuuji@0
|
2473 if ((y = *++(*arg))) {
|
yuuji@0
|
2474 m += (y >= 'a' ? y - 'a' : y - 'A');
|
yuuji@0
|
2475 switch (m) { /* determine the month */
|
yuuji@0
|
2476 case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
|
yuuji@0
|
2477 case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
|
yuuji@0
|
2478 case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
|
yuuji@0
|
2479 case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
|
yuuji@0
|
2480 case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
|
yuuji@0
|
2481 case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
|
yuuji@0
|
2482 case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
|
yuuji@0
|
2483 case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
|
yuuji@0
|
2484 case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
|
yuuji@0
|
2485 case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10;break;
|
yuuji@0
|
2486 case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11;break;
|
yuuji@0
|
2487 case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12;break;
|
yuuji@0
|
2488 default: return NIL;
|
yuuji@0
|
2489 }
|
yuuji@0
|
2490 if ((*++(*arg) == '-') && isdigit (*++(*arg))) {
|
yuuji@0
|
2491 y = 0; /* init year */
|
yuuji@0
|
2492 do {
|
yuuji@0
|
2493 y *= 10; /* add this number */
|
yuuji@0
|
2494 y += *(*arg)++ - '0';
|
yuuji@0
|
2495 }
|
yuuji@0
|
2496 while (isdigit (**arg));
|
yuuji@0
|
2497 /* minimal validity check of date */
|
yuuji@0
|
2498 if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
|
yuuji@0
|
2499 /* time began on UNIX in 1970 */
|
yuuji@0
|
2500 if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
|
yuuji@0
|
2501 /* return value */
|
yuuji@0
|
2502 *date = mail_shortdate (y - BASEYEAR,m,d);
|
yuuji@0
|
2503 return T; /* success */
|
yuuji@0
|
2504 }
|
yuuji@0
|
2505 }
|
yuuji@0
|
2506 }
|
yuuji@0
|
2507 }
|
yuuji@0
|
2508 }
|
yuuji@0
|
2509 return NIL; /* else error */
|
yuuji@0
|
2510 }
|
yuuji@0
|
2511
|
yuuji@0
|
2512 /* Parse a search set criterion
|
yuuji@0
|
2513 * Accepts: set to write into
|
yuuji@0
|
2514 * pointer to argument text pointer
|
yuuji@0
|
2515 * maximum value permitted
|
yuuji@0
|
2516 * Returns: T if success, NIL if error
|
yuuji@0
|
2517 */
|
yuuji@0
|
2518
|
yuuji@0
|
2519 long crit_set (SEARCHSET **set,unsigned char **arg,unsigned long maxima)
|
yuuji@0
|
2520 {
|
yuuji@0
|
2521 unsigned long i = 0;
|
yuuji@0
|
2522 if (*set) return NIL; /* can't double this value */
|
yuuji@0
|
2523 *set = mail_newsearchset (); /* instantiate a new search set */
|
yuuji@0
|
2524 if (**arg == '*') { /* maxnum? */
|
yuuji@0
|
2525 (*arg)++; /* skip past that number */
|
yuuji@0
|
2526 (*set)->first = maxima;
|
yuuji@0
|
2527 }
|
yuuji@0
|
2528 else if (crit_number (&i,arg) && i) (*set)->first = i;
|
yuuji@0
|
2529 else return NIL; /* bogon */
|
yuuji@0
|
2530 switch (**arg) { /* decide based on delimiter */
|
yuuji@0
|
2531 case ':': /* sequence range */
|
yuuji@0
|
2532 i = 0; /* reset for crit_number() */
|
yuuji@0
|
2533 if (*++(*arg) == '*') { /* maxnum? */
|
yuuji@0
|
2534 (*arg)++; /* skip past that number */
|
yuuji@0
|
2535 (*set)->last = maxima;
|
yuuji@0
|
2536 }
|
yuuji@0
|
2537 else if (crit_number (&i,arg) && i) {
|
yuuji@0
|
2538 if (i < (*set)->first) { /* backwards range */
|
yuuji@0
|
2539 (*set)->last = (*set)->first;
|
yuuji@0
|
2540 (*set)->first = i;
|
yuuji@0
|
2541 }
|
yuuji@0
|
2542 else (*set)->last = i; /* set last number */
|
yuuji@0
|
2543 }
|
yuuji@0
|
2544 else return NIL; /* bogon */
|
yuuji@0
|
2545 if (**arg != ',') break; /* drop into comma case if comma seen */
|
yuuji@0
|
2546 case ',':
|
yuuji@0
|
2547 (*arg)++; /* skip past delimiter */
|
yuuji@0
|
2548 return crit_set (&(*set)->next,arg,maxima);
|
yuuji@0
|
2549 default:
|
yuuji@0
|
2550 break;
|
yuuji@0
|
2551 }
|
yuuji@0
|
2552 return T; /* return success */
|
yuuji@0
|
2553 }
|
yuuji@0
|
2554
|
yuuji@0
|
2555 /* Parse a search number criterion
|
yuuji@0
|
2556 * Accepts: number to write into
|
yuuji@0
|
2557 * pointer to argument text pointer
|
yuuji@0
|
2558 * Returns: T if success, NIL if error
|
yuuji@0
|
2559 */
|
yuuji@0
|
2560
|
yuuji@0
|
2561 long crit_number (unsigned long *number,unsigned char **arg)
|
yuuji@0
|
2562 {
|
yuuji@0
|
2563 /* can't double this value */
|
yuuji@0
|
2564 if (*number || !isdigit (**arg)) return NIL;
|
yuuji@0
|
2565 *number = 0;
|
yuuji@0
|
2566 while (isdigit (**arg)) { /* found a digit? */
|
yuuji@0
|
2567 *number *= 10; /* add a decade */
|
yuuji@0
|
2568 *number += *(*arg)++ - '0'; /* add number */
|
yuuji@0
|
2569 }
|
yuuji@0
|
2570 return T;
|
yuuji@0
|
2571 }
|
yuuji@0
|
2572
|
yuuji@0
|
2573
|
yuuji@0
|
2574 /* Parse a search string criterion
|
yuuji@0
|
2575 * Accepts: date to write into
|
yuuji@0
|
2576 * pointer to argument text pointer
|
yuuji@0
|
2577 * Returns: T if success, NIL if error
|
yuuji@0
|
2578 */
|
yuuji@0
|
2579
|
yuuji@0
|
2580 long crit_string (STRINGLIST **string,unsigned char **arg)
|
yuuji@0
|
2581 {
|
yuuji@0
|
2582 unsigned long i;
|
yuuji@0
|
2583 char c;
|
yuuji@0
|
2584 char *s = parse_astring (arg,&i,&c);
|
yuuji@0
|
2585 if (!s) return NIL;
|
yuuji@0
|
2586 /* find tail of list */
|
yuuji@0
|
2587 while (*string) string = &(*string)->next;
|
yuuji@0
|
2588 *string = mail_newstringlist ();
|
yuuji@0
|
2589 (*string)->text.data = (unsigned char *) fs_get (i + 1);
|
yuuji@0
|
2590 memcpy ((*string)->text.data,s,i);
|
yuuji@0
|
2591 (*string)->text.data[i] = '\0';
|
yuuji@0
|
2592 (*string)->text.size = i;
|
yuuji@0
|
2593 /* if end of arguments, wrap it up here */
|
yuuji@0
|
2594 if (!*arg) *arg = (char *) (*string)->text.data + i;
|
yuuji@0
|
2595 else (*--(*arg) = c); /* back up pointer, restore delimiter */
|
yuuji@0
|
2596 return T;
|
yuuji@0
|
2597 }
|
yuuji@0
|
2598
|
yuuji@0
|
2599 /* Fetch message data
|
yuuji@0
|
2600 * Accepts: string of data items to be fetched (must be writeable)
|
yuuji@0
|
2601 * UID fetch flag
|
yuuji@0
|
2602 */
|
yuuji@0
|
2603
|
yuuji@0
|
2604 #define MAXFETCH 100
|
yuuji@0
|
2605
|
yuuji@0
|
2606 void fetch (char *t,unsigned long uid)
|
yuuji@0
|
2607 {
|
yuuji@0
|
2608 fetchfn_t f[MAXFETCH +2];
|
yuuji@0
|
2609 void *fa[MAXFETCH + 2];
|
yuuji@0
|
2610 int k;
|
yuuji@0
|
2611 memset ((void *) f,NIL,sizeof (f));
|
yuuji@0
|
2612 memset ((void *) fa,NIL,sizeof (fa));
|
yuuji@0
|
2613 fetch_work (t,uid,f,fa); /* do the work */
|
yuuji@0
|
2614 /* clean up arguments */
|
yuuji@0
|
2615 for (k = 1; f[k]; k++) if (fa[k]) (*f[k]) (0,fa[k]);
|
yuuji@0
|
2616 }
|
yuuji@0
|
2617
|
yuuji@0
|
2618
|
yuuji@0
|
2619 /* Fetch message data worker routine
|
yuuji@0
|
2620 * Accepts: string of data items to be fetched (must be writeable)
|
yuuji@0
|
2621 * UID fetch flag
|
yuuji@0
|
2622 * function dispatch vector
|
yuuji@0
|
2623 * function argument vector
|
yuuji@0
|
2624 */
|
yuuji@0
|
2625
|
yuuji@0
|
2626 void fetch_work (char *t,unsigned long uid,fetchfn_t f[],void *fa[])
|
yuuji@0
|
2627 {
|
yuuji@0
|
2628 unsigned char *s,*v;
|
yuuji@0
|
2629 unsigned long i;
|
yuuji@0
|
2630 unsigned long k = 0;
|
yuuji@0
|
2631 BODY *b;
|
yuuji@0
|
2632 int list = NIL;
|
yuuji@0
|
2633 int parse_envs = NIL;
|
yuuji@0
|
2634 int parse_bodies = NIL;
|
yuuji@0
|
2635 if (uid) { /* need to fetch UIDs? */
|
yuuji@0
|
2636 fa[k] = NIL; /* no argument */
|
yuuji@0
|
2637 f[k++] = fetch_uid; /* push a UID fetch on the stack */
|
yuuji@0
|
2638 }
|
yuuji@0
|
2639
|
yuuji@0
|
2640 /* process macros */
|
yuuji@0
|
2641 if (!strcmp (ucase (t),"ALL"))
|
yuuji@0
|
2642 strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE)");
|
yuuji@0
|
2643 else if (!strcmp (t,"FULL"))
|
yuuji@0
|
2644 strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY)");
|
yuuji@0
|
2645 else if (!strcmp (t,"FAST")) strcpy (t,"(FLAGS INTERNALDATE RFC822.SIZE)");
|
yuuji@0
|
2646 if (list = (*t == '(')) t++; /* skip open paren */
|
yuuji@0
|
2647 if (s = strtok (t," ")) do { /* parse attribute list */
|
yuuji@0
|
2648 if (list && (i = strlen (s)) && (s[i-1] == ')')) {
|
yuuji@0
|
2649 list = NIL; /* done with list */
|
yuuji@0
|
2650 s[i-1] = '\0'; /* tie off last item */
|
yuuji@0
|
2651 }
|
yuuji@0
|
2652 fa[k] = NIL; /* default to no argument */
|
yuuji@0
|
2653 if (!strcmp (s,"UID")) { /* no-op if implicit */
|
yuuji@0
|
2654 if (!uid) f[k++] = fetch_uid;
|
yuuji@0
|
2655 }
|
yuuji@0
|
2656 else if (!strcmp (s,"FLAGS")) f[k++] = fetch_flags;
|
yuuji@0
|
2657 else if (!strcmp (s,"INTERNALDATE")) f[k++] = fetch_internaldate;
|
yuuji@0
|
2658 else if (!strcmp (s,"RFC822.SIZE")) f[k++] = fetch_rfc822_size;
|
yuuji@0
|
2659 else if (!strcmp (s,"ENVELOPE")) {
|
yuuji@0
|
2660 parse_envs = T; /* we will need to parse envelopes */
|
yuuji@0
|
2661 f[k++] = fetch_envelope;
|
yuuji@0
|
2662 }
|
yuuji@0
|
2663 else if (!strcmp (s,"BODY")) {
|
yuuji@0
|
2664 parse_envs = parse_bodies = T;
|
yuuji@0
|
2665 f[k++] = fetch_body;
|
yuuji@0
|
2666 }
|
yuuji@0
|
2667 else if (!strcmp (s,"BODYSTRUCTURE")) {
|
yuuji@0
|
2668 parse_envs = parse_bodies = T;
|
yuuji@0
|
2669 f[k++] = fetch_bodystructure;
|
yuuji@0
|
2670 }
|
yuuji@0
|
2671 else if (!strcmp (s,"RFC822")) {
|
yuuji@0
|
2672 fa[k] = s[6] ? (void *) FT_PEEK : NIL;
|
yuuji@0
|
2673 f[k++] = fetch_rfc822;
|
yuuji@0
|
2674 }
|
yuuji@0
|
2675 else if (!strcmp (s,"RFC822.HEADER")) f[k++] = fetch_rfc822_header;
|
yuuji@0
|
2676 else if (!strcmp (s,"RFC822.TEXT")) {
|
yuuji@0
|
2677 fa[k] = s[11] ? (void *) FT_PEEK : NIL;
|
yuuji@0
|
2678 f[k++] = fetch_rfc822_text;
|
yuuji@0
|
2679 }
|
yuuji@0
|
2680
|
yuuji@0
|
2681 else if (!strncmp (s,"BODY[",5) || !strncmp (s,"BODY.PEEK[",10) ||
|
yuuji@0
|
2682 !strncmp (s,"BINARY[",7) || !strncmp (s,"BINARY.PEEK[",12) ||
|
yuuji@0
|
2683 !strncmp (s,"BINARY.SIZE[",12)) {
|
yuuji@0
|
2684 TEXTARGS *ta = (TEXTARGS *)
|
yuuji@0
|
2685 memset (fs_get (sizeof (TEXTARGS)),0,sizeof (TEXTARGS));
|
yuuji@0
|
2686 if (s[1] == 'I') { /* body or binary? */
|
yuuji@0
|
2687 ta->binary = FTB_BINARY;/* binary */
|
yuuji@0
|
2688 f[k] = fetch_body_part_binary;
|
yuuji@0
|
2689 if (s[6] == '.') { /* wanted peek or size? */
|
yuuji@0
|
2690 if (s[7] == 'P') ta->flags = FT_PEEK;
|
yuuji@0
|
2691 else ta->binary |= FTB_SIZE;
|
yuuji@0
|
2692 s += 12; /* skip to section specifier */
|
yuuji@0
|
2693 }
|
yuuji@0
|
2694 else s += 7; /* skip to section specifier */
|
yuuji@0
|
2695 if (!isdigit (*s)) { /* make sure top-level digit */
|
yuuji@0
|
2696 fs_give ((void **) &ta);
|
yuuji@0
|
2697 response = badbin;
|
yuuji@0
|
2698 return;
|
yuuji@0
|
2699 }
|
yuuji@0
|
2700 }
|
yuuji@0
|
2701 else { /* body */
|
yuuji@0
|
2702 f[k] = fetch_body_part_contents;
|
yuuji@0
|
2703 if (s[4] == '.') { /* wanted peek? */
|
yuuji@0
|
2704 ta->flags = FT_PEEK;
|
yuuji@0
|
2705 s += 10; /* skip to section specifier */
|
yuuji@0
|
2706 }
|
yuuji@0
|
2707 else s += 5; /* skip to section specifier */
|
yuuji@0
|
2708 }
|
yuuji@0
|
2709 if (*(v = s) != ']') { /* non-empty section specifier? */
|
yuuji@0
|
2710 if (isdigit (*v)) { /* have section specifier? */
|
yuuji@0
|
2711 /* need envelopes and bodies */
|
yuuji@0
|
2712 parse_envs = parse_bodies = T;
|
yuuji@0
|
2713 while (isdigit (*v)) /* scan to end of section specifier */
|
yuuji@0
|
2714 if ((*++v == '.') && isdigit (v[1])) v++;
|
yuuji@0
|
2715 /* any IMAP4rev1 stuff following? */
|
yuuji@0
|
2716 if ((*v == '.') && isalpha (v[1])) {
|
yuuji@0
|
2717 if (ta->binary) { /* not if binary you don't */
|
yuuji@0
|
2718 fs_give ((void **) &ta);
|
yuuji@0
|
2719 response = badbin;
|
yuuji@0
|
2720 return;
|
yuuji@0
|
2721 }
|
yuuji@0
|
2722 *v++ = '\0'; /* yes, tie off section specifier */
|
yuuji@0
|
2723 if (!strncmp (v,"MIME",4)) {
|
yuuji@0
|
2724 v += 4; /* found <section>.MIME */
|
yuuji@0
|
2725 f[k] = fetch_body_part_mime;
|
yuuji@0
|
2726 }
|
yuuji@0
|
2727 }
|
yuuji@0
|
2728 else if (*v != ']') { /* better be the end if no IMAP4rev1 stuff */
|
yuuji@0
|
2729 fs_give ((void **) &ta);/* clean up */
|
yuuji@0
|
2730 response = "%.80s BAD Syntax error in section specifier\015\012";
|
yuuji@0
|
2731 return;
|
yuuji@0
|
2732 }
|
yuuji@0
|
2733 }
|
yuuji@0
|
2734
|
yuuji@0
|
2735 if (*v != ']') { /* IMAP4rev1 stuff here? */
|
yuuji@0
|
2736 if (!strncmp (v,"HEADER",6)) {
|
yuuji@0
|
2737 *v = '\0'; /* tie off in case top level */
|
yuuji@0
|
2738 v += 6; /* found [<section>.]HEADER */
|
yuuji@0
|
2739 f[k] = fetch_body_part_header;
|
yuuji@0
|
2740 /* partial headers wanted? */
|
yuuji@0
|
2741 if (!strncmp (v,".FIELDS",7)) {
|
yuuji@0
|
2742 v += 7; /* yes */
|
yuuji@0
|
2743 if (!strncmp (v,".NOT",4)) {
|
yuuji@0
|
2744 v += 4; /* want to exclude named headers */
|
yuuji@0
|
2745 ta->flags |= FT_NOT;
|
yuuji@0
|
2746 }
|
yuuji@0
|
2747 if (*v || !(v = strtok (NIL,"\015\012")) ||
|
yuuji@0
|
2748 !(ta->lines = parse_stringlist (&v,&list))) {
|
yuuji@0
|
2749 fs_give ((void **) &ta);/* clean up */
|
yuuji@0
|
2750 response = "%.80s BAD Syntax error in header fields\015\012";
|
yuuji@0
|
2751 return;
|
yuuji@0
|
2752 }
|
yuuji@0
|
2753 }
|
yuuji@0
|
2754 }
|
yuuji@0
|
2755 else if (!strncmp (v,"TEXT",4)) {
|
yuuji@0
|
2756 *v = '\0'; /* tie off in case top level */
|
yuuji@0
|
2757 v += 4; /* found [<section>.]TEXT */
|
yuuji@0
|
2758 f[k] = fetch_body_part_text;
|
yuuji@0
|
2759 }
|
yuuji@0
|
2760 else {
|
yuuji@0
|
2761 fs_give ((void **) &ta);/* clean up */
|
yuuji@0
|
2762 response = "%.80s BAD Unknown section text specifier\015\012";
|
yuuji@0
|
2763 return;
|
yuuji@0
|
2764 }
|
yuuji@0
|
2765 }
|
yuuji@0
|
2766 }
|
yuuji@0
|
2767 /* tie off section */
|
yuuji@0
|
2768 if (*v == ']') *v++ = '\0';
|
yuuji@0
|
2769 else { /* bogon */
|
yuuji@0
|
2770 if (ta->lines) mail_free_stringlist (&ta->lines);
|
yuuji@0
|
2771 fs_give ((void **) &ta);/* clean up */
|
yuuji@0
|
2772 response = "%.80s BAD Section specifier not terminated\015\012";
|
yuuji@0
|
2773 return;
|
yuuji@0
|
2774 }
|
yuuji@0
|
2775
|
yuuji@0
|
2776 if ((*v == '<') && /* partial specifier? */
|
yuuji@0
|
2777 ((ta->binary & FTB_SIZE) ||
|
yuuji@0
|
2778 !(isdigit (v[1]) && ((ta->first = strtoul (v+1,(char **) &v,10)) ||
|
yuuji@0
|
2779 v) &&
|
yuuji@0
|
2780 (*v++ == '.') && (ta->last = strtoul (v,(char **) &v,10)) &&
|
yuuji@0
|
2781 (*v++ == '>')))) {
|
yuuji@0
|
2782 if (ta->lines) mail_free_stringlist (&ta->lines);
|
yuuji@0
|
2783 fs_give ((void **) &ta);
|
yuuji@0
|
2784 response ="%.80s BAD Syntax error in partial text specifier\015\012";
|
yuuji@0
|
2785 return;
|
yuuji@0
|
2786 }
|
yuuji@0
|
2787 switch (*v) { /* what's there now? */
|
yuuji@0
|
2788 case ' ': /* more follows */
|
yuuji@0
|
2789 *--v = ' '; /* patch a space back in */
|
yuuji@0
|
2790 *--v = 'x'; /* and a hokey character before that */
|
yuuji@0
|
2791 strtok (v," "); /* reset strtok mechanism */
|
yuuji@0
|
2792 break;
|
yuuji@0
|
2793 case '\0': /* none */
|
yuuji@0
|
2794 break;
|
yuuji@0
|
2795 case ')': /* end of list */
|
yuuji@0
|
2796 if (list && !v[1]) { /* make sure of that */
|
yuuji@0
|
2797 list = NIL;
|
yuuji@0
|
2798 strtok (v," "); /* reset strtok mechanism */
|
yuuji@0
|
2799 break; /* all done */
|
yuuji@0
|
2800 }
|
yuuji@0
|
2801 /* otherwise it's a bogon, drop in */
|
yuuji@0
|
2802 default: /* bogon */
|
yuuji@0
|
2803 if (ta->lines) mail_free_stringlist (&ta->lines);
|
yuuji@0
|
2804 fs_give ((void **) &ta);
|
yuuji@0
|
2805 response = "%.80s BAD Syntax error after section specifier\015\012";
|
yuuji@0
|
2806 return;
|
yuuji@0
|
2807 }
|
yuuji@0
|
2808 /* make copy of section specifier */
|
yuuji@0
|
2809 if (s && *s) ta->section = cpystr (s);
|
yuuji@0
|
2810 fa[k++] = (void *) ta; /* set argument */
|
yuuji@0
|
2811 }
|
yuuji@0
|
2812 else { /* unknown attribute */
|
yuuji@0
|
2813 response = badatt;
|
yuuji@0
|
2814 return;
|
yuuji@0
|
2815 }
|
yuuji@0
|
2816 } while ((s = strtok (NIL," ")) && (k < MAXFETCH) && list);
|
yuuji@0
|
2817 else {
|
yuuji@0
|
2818 response = misarg; /* missing attribute list */
|
yuuji@0
|
2819 return;
|
yuuji@0
|
2820 }
|
yuuji@0
|
2821
|
yuuji@0
|
2822 if (s) { /* too many attributes? */
|
yuuji@0
|
2823 response = "%.80s BAD Excessively complex FETCH attribute list\015\012";
|
yuuji@0
|
2824 return;
|
yuuji@0
|
2825 }
|
yuuji@0
|
2826 if (list) { /* too many attributes? */
|
yuuji@0
|
2827 response = "%.80s BAD Unterminated FETCH attribute list\015\012";
|
yuuji@0
|
2828 return;
|
yuuji@0
|
2829 }
|
yuuji@0
|
2830 f[k] = NIL; /* tie off attribute list */
|
yuuji@0
|
2831 /* c-client clobbers sequence, use spare */
|
yuuji@0
|
2832 for (i = 1; i <= nmsgs; i++)
|
yuuji@0
|
2833 mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence;
|
yuuji@0
|
2834 /* for each requested message */
|
yuuji@0
|
2835 for (i = 1; (i <= nmsgs) && (response != loseunknowncte); i++) {
|
yuuji@0
|
2836 /* kill if dying */
|
yuuji@0
|
2837 if (state == LOGOUT) longjmp (jmpenv,1);
|
yuuji@0
|
2838 if (mail_elt (stream,i)->spare) {
|
yuuji@0
|
2839 /* parse envelope, set body, do warnings */
|
yuuji@0
|
2840 if (parse_envs) mail_fetchstructure (stream,i,parse_bodies ? &b : NIL);
|
yuuji@0
|
2841 quell_events = T; /* can't do any events now */
|
yuuji@0
|
2842 PSOUT ("* "); /* leader */
|
yuuji@0
|
2843 pnum (i);
|
yuuji@0
|
2844 PSOUT (" FETCH (");
|
yuuji@0
|
2845 (*f[0]) (i,fa[0]); /* do first attribute */
|
yuuji@0
|
2846 /* for each subsequent attribute */
|
yuuji@0
|
2847 for (k = 1; f[k] && (response != loseunknowncte); k++) {
|
yuuji@0
|
2848 PBOUT (' '); /* delimit with space */
|
yuuji@0
|
2849 (*f[k]) (i,fa[k]); /* do that attribute */
|
yuuji@0
|
2850 }
|
yuuji@0
|
2851 PSOUT (")\015\012"); /* trailer */
|
yuuji@0
|
2852 quell_events = NIL; /* events alright now */
|
yuuji@0
|
2853 }
|
yuuji@0
|
2854 }
|
yuuji@0
|
2855 }
|
yuuji@0
|
2856
|
yuuji@0
|
2857 /* Fetch message body structure (extensible)
|
yuuji@0
|
2858 * Accepts: message number
|
yuuji@0
|
2859 * extra argument
|
yuuji@0
|
2860 */
|
yuuji@0
|
2861
|
yuuji@0
|
2862 void fetch_bodystructure (unsigned long i,void *args)
|
yuuji@0
|
2863 {
|
yuuji@0
|
2864 BODY *body;
|
yuuji@0
|
2865 mail_fetchstructure (stream,i,&body);
|
yuuji@0
|
2866 PSOUT ("BODYSTRUCTURE ");
|
yuuji@0
|
2867 pbodystructure (body); /* output body */
|
yuuji@0
|
2868 }
|
yuuji@0
|
2869
|
yuuji@0
|
2870
|
yuuji@0
|
2871 /* Fetch message body structure (non-extensible)
|
yuuji@0
|
2872 * Accepts: message number
|
yuuji@0
|
2873 * extra argument
|
yuuji@0
|
2874 */
|
yuuji@0
|
2875
|
yuuji@0
|
2876
|
yuuji@0
|
2877 void fetch_body (unsigned long i,void *args)
|
yuuji@0
|
2878 {
|
yuuji@0
|
2879 BODY *body;
|
yuuji@0
|
2880 mail_fetchstructure (stream,i,&body);
|
yuuji@0
|
2881 PSOUT ("BODY "); /* output attribute */
|
yuuji@0
|
2882 pbody (body); /* output body */
|
yuuji@0
|
2883 }
|
yuuji@0
|
2884
|
yuuji@0
|
2885 /* Fetch body part MIME header
|
yuuji@0
|
2886 * Accepts: message number
|
yuuji@0
|
2887 * extra argument
|
yuuji@0
|
2888 */
|
yuuji@0
|
2889
|
yuuji@0
|
2890 void fetch_body_part_mime (unsigned long i,void *args)
|
yuuji@0
|
2891 {
|
yuuji@0
|
2892 TEXTARGS *ta = (TEXTARGS *) args;
|
yuuji@0
|
2893 if (i) { /* do work? */
|
yuuji@0
|
2894 SIZEDTEXT st;
|
yuuji@0
|
2895 unsigned long uid = mail_uid (stream,i);
|
yuuji@0
|
2896 char *tmp = (char *) fs_get (100 + strlen (ta->section));
|
yuuji@0
|
2897 sprintf (tmp,"BODY[%s.MIME]",ta->section);
|
yuuji@0
|
2898 /* try to use remembered text */
|
yuuji@0
|
2899 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
|
yuuji@0
|
2900 else { /* get data */
|
yuuji@0
|
2901 st.data = (unsigned char *)
|
yuuji@0
|
2902 mail_fetch_mime (stream,i,ta->section,&st.size,ta->flags);
|
yuuji@0
|
2903 if (ta->first || ta->last) remember (uid,tmp,&st);
|
yuuji@0
|
2904 }
|
yuuji@0
|
2905 pbodypartstring (i,tmp,&st,NIL,ta);
|
yuuji@0
|
2906 fs_give ((void **) &tmp);
|
yuuji@0
|
2907 }
|
yuuji@0
|
2908 else { /* clean up the arguments */
|
yuuji@0
|
2909 fs_give ((void **) &ta->section);
|
yuuji@0
|
2910 fs_give ((void **) &args);
|
yuuji@0
|
2911 }
|
yuuji@0
|
2912 }
|
yuuji@0
|
2913
|
yuuji@0
|
2914
|
yuuji@0
|
2915 /* Fetch body part contents
|
yuuji@0
|
2916 * Accepts: message number
|
yuuji@0
|
2917 * extra argument
|
yuuji@0
|
2918 */
|
yuuji@0
|
2919
|
yuuji@0
|
2920 void fetch_body_part_contents (unsigned long i,void *args)
|
yuuji@0
|
2921 {
|
yuuji@0
|
2922 TEXTARGS *ta = (TEXTARGS *) args;
|
yuuji@0
|
2923 if (i) { /* do work? */
|
yuuji@0
|
2924 SIZEDTEXT st;
|
yuuji@0
|
2925 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
|
yuuji@0
|
2926 unsigned long uid = mail_uid (stream,i);
|
yuuji@0
|
2927 sprintf (tmp,"BODY[%s]",ta->section ? ta->section : "");
|
yuuji@0
|
2928 /* try to use remembered text */
|
yuuji@0
|
2929 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
|
yuuji@0
|
2930 /* get data */
|
yuuji@0
|
2931 else if ((st.data = (unsigned char *)
|
yuuji@0
|
2932 mail_fetch_body (stream,i,ta->section,&st.size,
|
yuuji@0
|
2933 ta->flags | FT_RETURNSTRINGSTRUCT)) &&
|
yuuji@0
|
2934 (ta->first || ta->last)) remember (uid,tmp,&st);
|
yuuji@0
|
2935 pbodypartstring (i,tmp,&st,&stream->private.string,ta);
|
yuuji@0
|
2936 fs_give ((void **) &tmp);
|
yuuji@0
|
2937 }
|
yuuji@0
|
2938 else { /* clean up the arguments */
|
yuuji@0
|
2939 if (ta->section) fs_give ((void **) &ta->section);
|
yuuji@0
|
2940 fs_give ((void **) &args);
|
yuuji@0
|
2941 }
|
yuuji@0
|
2942 }
|
yuuji@0
|
2943
|
yuuji@0
|
2944 /* Fetch body part binary
|
yuuji@0
|
2945 * Accepts: message number
|
yuuji@0
|
2946 * extra argument
|
yuuji@0
|
2947 * Someday fix this to use stringstruct instead of memory
|
yuuji@0
|
2948 */
|
yuuji@0
|
2949
|
yuuji@0
|
2950 void fetch_body_part_binary (unsigned long i,void *args)
|
yuuji@0
|
2951 {
|
yuuji@0
|
2952 TEXTARGS *ta = (TEXTARGS *) args;
|
yuuji@0
|
2953 if (i) { /* do work? */
|
yuuji@0
|
2954 SIZEDTEXT st,cst;
|
yuuji@0
|
2955 BODY *body = mail_body (stream,i,ta->section);
|
yuuji@0
|
2956 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
|
yuuji@0
|
2957 unsigned long uid = mail_uid (stream,i);
|
yuuji@0
|
2958 /* try to use remembered text */
|
yuuji@0
|
2959 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
|
yuuji@0
|
2960 else { /* get data */
|
yuuji@0
|
2961 st.data = (unsigned char *)
|
yuuji@0
|
2962 mail_fetch_body (stream,i,ta->section,&st.size,ta->flags);
|
yuuji@0
|
2963 if (ta->first || ta->last) remember (uid,tmp,&st);
|
yuuji@0
|
2964 }
|
yuuji@0
|
2965 /* what encoding was used? */
|
yuuji@0
|
2966 if (body) switch (body->encoding) {
|
yuuji@0
|
2967 case ENCBASE64:
|
yuuji@0
|
2968 if (cst.data = rfc822_base64 (st.data,st.size,&cst.size)) break;
|
yuuji@0
|
2969 fetch_uid (i,NIL); /* wrote a space, so must do something */
|
yuuji@0
|
2970 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
2971 lsterr = cpystr ("Undecodable BASE64 contents");
|
yuuji@0
|
2972 response = loseunknowncte;
|
yuuji@0
|
2973 fs_give ((void **) &tmp);
|
yuuji@0
|
2974 return;
|
yuuji@0
|
2975 case ENCQUOTEDPRINTABLE:
|
yuuji@0
|
2976 if (cst.data = rfc822_qprint (st.data,st.size,&cst.size)) break;
|
yuuji@0
|
2977 fetch_uid (i,NIL); /* wrote a space, so must do something */
|
yuuji@0
|
2978 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
2979 lsterr = cpystr ("Undecodable QUOTED-PRINTABLE contents");
|
yuuji@0
|
2980 response = loseunknowncte;
|
yuuji@0
|
2981 fs_give ((void **) &tmp);
|
yuuji@0
|
2982 return;
|
yuuji@0
|
2983 case ENC7BIT: /* no need to convert any of these */
|
yuuji@0
|
2984 case ENC8BIT:
|
yuuji@0
|
2985 case ENCBINARY:
|
yuuji@0
|
2986 cst.data = NIL; /* no converted data to free */
|
yuuji@0
|
2987 break;
|
yuuji@0
|
2988 default: /* unknown encoding, oops */
|
yuuji@0
|
2989 fetch_uid (i,NIL); /* wrote a space, so must do something */
|
yuuji@0
|
2990 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
2991 lsterr = cpystr ("Unknown Content-Transfer-Encoding");
|
yuuji@0
|
2992 response = loseunknowncte;
|
yuuji@0
|
2993 fs_give ((void **) &tmp);
|
yuuji@0
|
2994 return;
|
yuuji@0
|
2995 }
|
yuuji@0
|
2996 else {
|
yuuji@0
|
2997 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
2998 lsterr = cpystr ("Invalid body part");
|
yuuji@0
|
2999 response = loseunknowncte;
|
yuuji@0
|
3000 fs_give ((void **) &tmp);
|
yuuji@0
|
3001 return;
|
yuuji@0
|
3002 }
|
yuuji@0
|
3003
|
yuuji@0
|
3004 /* use decoded version if exists */
|
yuuji@0
|
3005 if (cst.data) memcpy ((void *) &st,(void *) &cst,sizeof (SIZEDTEXT));
|
yuuji@0
|
3006 if (ta->binary & FTB_SIZE) {/* just want size? */
|
yuuji@0
|
3007 sprintf (tmp,"BINARY.SIZE[%s] %lu",ta->section ? ta->section : "",
|
yuuji@0
|
3008 st.size);
|
yuuji@0
|
3009 PSOUT (tmp);
|
yuuji@0
|
3010 }
|
yuuji@0
|
3011 else { /* no, blat binary data */
|
yuuji@0
|
3012 int f = mail_elt (stream,i)->seen;
|
yuuji@0
|
3013 if (st.data) { /* only if have useful data */
|
yuuji@0
|
3014 /* partial specifier */
|
yuuji@0
|
3015 if (ta->first || ta->last)
|
yuuji@0
|
3016 sprintf (tmp,"BINARY[%s]<%lu> ",
|
yuuji@0
|
3017 ta->section ? ta->section : "",ta->first);
|
yuuji@0
|
3018 else sprintf (tmp,"BINARY[%s] ",ta->section ? ta->section : "");
|
yuuji@0
|
3019 /* in case first byte beyond end of text */
|
yuuji@0
|
3020 if (st.size <= ta->first) st.size = ta->first = 0;
|
yuuji@0
|
3021 else { /* offset and truncate */
|
yuuji@0
|
3022 st.data += ta->first; /* move to desired position */
|
yuuji@0
|
3023 st.size -= ta->first; /* reduced size */
|
yuuji@0
|
3024 if (ta->last && (st.size > ta->last)) st.size = ta->last;
|
yuuji@0
|
3025 }
|
yuuji@0
|
3026 if (st.size) sprintf (tmp + strlen (tmp),"{%lu}\015\012",st.size);
|
yuuji@0
|
3027 else strcat (tmp,"\"\"");
|
yuuji@0
|
3028 PSOUT (tmp); /* write binary output */
|
yuuji@0
|
3029 if (st.size && (PSOUTR (&st) == EOF)) ioerror(stdout,"writing binary");
|
yuuji@0
|
3030 }
|
yuuji@0
|
3031 else {
|
yuuji@0
|
3032 sprintf (tmp,"BINARY[%s] NIL",ta->section ? ta->section : "");
|
yuuji@0
|
3033 PSOUT (tmp);
|
yuuji@0
|
3034 }
|
yuuji@0
|
3035 changed_flags (i,f); /* write changed flags */
|
yuuji@0
|
3036 }
|
yuuji@0
|
3037 /* free converted data */
|
yuuji@0
|
3038 if (cst.data) fs_give ((void **) &cst.data);
|
yuuji@0
|
3039 fs_give ((void **) &tmp); /* and temporary string */
|
yuuji@0
|
3040 }
|
yuuji@0
|
3041 else { /* clean up the arguments */
|
yuuji@0
|
3042 if (ta->section) fs_give ((void **) &ta->section);
|
yuuji@0
|
3043 fs_give ((void **) &args);
|
yuuji@0
|
3044 }
|
yuuji@0
|
3045 }
|
yuuji@0
|
3046
|
yuuji@0
|
3047 /* Fetch MESSAGE/RFC822 body part header
|
yuuji@0
|
3048 * Accepts: message number
|
yuuji@0
|
3049 * extra argument
|
yuuji@0
|
3050 */
|
yuuji@0
|
3051
|
yuuji@0
|
3052 void fetch_body_part_header (unsigned long i,void *args)
|
yuuji@0
|
3053 {
|
yuuji@0
|
3054 TEXTARGS *ta = (TEXTARGS *) args;
|
yuuji@0
|
3055 unsigned long len = 100 + (ta->section ? strlen (ta->section) : 0);
|
yuuji@0
|
3056 STRINGLIST *s;
|
yuuji@0
|
3057 for (s = ta->lines; s; s = s->next) len += s->text.size + 1;
|
yuuji@0
|
3058 if (i) { /* do work? */
|
yuuji@0
|
3059 SIZEDTEXT st;
|
yuuji@0
|
3060 char *tmp = (char *) fs_get (len);
|
yuuji@0
|
3061 PSOUT ("BODY[");
|
yuuji@0
|
3062 /* output attribute */
|
yuuji@0
|
3063 if (ta->section && *ta->section) {
|
yuuji@0
|
3064 PSOUT (ta->section);
|
yuuji@0
|
3065 PBOUT ('.');
|
yuuji@0
|
3066 }
|
yuuji@0
|
3067 PSOUT ("HEADER");
|
yuuji@0
|
3068 if (ta->lines) {
|
yuuji@0
|
3069 PSOUT ((ta->flags & FT_NOT) ? ".FIELDS.NOT " : ".FIELDS ");
|
yuuji@0
|
3070 pastringlist (ta->lines);
|
yuuji@0
|
3071 }
|
yuuji@0
|
3072 strcpy (tmp,"]"); /* close section specifier */
|
yuuji@0
|
3073 st.data = (unsigned char *) /* get data (no hope in using remember here) */
|
yuuji@0
|
3074 mail_fetch_header (stream,i,ta->section,ta->lines,&st.size,ta->flags);
|
yuuji@0
|
3075 pbodypartstring (i,tmp,&st,NIL,ta);
|
yuuji@0
|
3076 fs_give ((void **) &tmp);
|
yuuji@0
|
3077 }
|
yuuji@0
|
3078 else { /* clean up the arguments */
|
yuuji@0
|
3079 if (ta->lines) mail_free_stringlist (&ta->lines);
|
yuuji@0
|
3080 if (ta->section) fs_give ((void **) &ta->section);
|
yuuji@0
|
3081 fs_give ((void **) &args);
|
yuuji@0
|
3082 }
|
yuuji@0
|
3083 }
|
yuuji@0
|
3084
|
yuuji@0
|
3085 /* Fetch MESSAGE/RFC822 body part text
|
yuuji@0
|
3086 * Accepts: message number
|
yuuji@0
|
3087 * extra argument
|
yuuji@0
|
3088 */
|
yuuji@0
|
3089
|
yuuji@0
|
3090 void fetch_body_part_text (unsigned long i,void *args)
|
yuuji@0
|
3091 {
|
yuuji@0
|
3092 TEXTARGS *ta = (TEXTARGS *) args;
|
yuuji@0
|
3093 if (i) { /* do work? */
|
yuuji@0
|
3094 SIZEDTEXT st;
|
yuuji@0
|
3095 char *tmp = (char *) fs_get (100+(ta->section ? strlen (ta->section) : 0));
|
yuuji@0
|
3096 unsigned long uid = mail_uid (stream,i);
|
yuuji@0
|
3097 /* output attribute */
|
yuuji@0
|
3098 if (ta->section && *ta->section) sprintf (tmp,"BODY[%s.TEXT]",ta->section);
|
yuuji@0
|
3099 else strcpy (tmp,"BODY[TEXT]");
|
yuuji@0
|
3100 /* try to use remembered text */
|
yuuji@0
|
3101 if (lastuid && (uid == lastuid) && !strcmp (tmp,lastid)) st = lastst;
|
yuuji@0
|
3102 /* get data */
|
yuuji@0
|
3103 else if ((st.data = (unsigned char *)
|
yuuji@0
|
3104 mail_fetch_text (stream,i,ta->section,&st.size,
|
yuuji@0
|
3105 ta->flags | FT_RETURNSTRINGSTRUCT)) &&
|
yuuji@0
|
3106 (ta->first || ta->last)) remember (uid,tmp,&st);
|
yuuji@0
|
3107 pbodypartstring (i,tmp,&st,&stream->private.string,ta);
|
yuuji@0
|
3108 fs_give ((void **) &tmp);
|
yuuji@0
|
3109 }
|
yuuji@0
|
3110 else { /* clean up the arguments */
|
yuuji@0
|
3111 if (ta->section) fs_give ((void **) &ta->section);
|
yuuji@0
|
3112 fs_give ((void **) &args);
|
yuuji@0
|
3113 }
|
yuuji@0
|
3114 }
|
yuuji@0
|
3115
|
yuuji@0
|
3116
|
yuuji@0
|
3117 /* Remember body part text for subsequent partial fetching
|
yuuji@0
|
3118 * Accepts: message UID
|
yuuji@0
|
3119 * body part id
|
yuuji@0
|
3120 * text
|
yuuji@0
|
3121 * string
|
yuuji@0
|
3122 */
|
yuuji@0
|
3123
|
yuuji@0
|
3124 void remember (unsigned long uid,char *id,SIZEDTEXT *st)
|
yuuji@0
|
3125 {
|
yuuji@0
|
3126 lastuid = uid; /* remember UID */
|
yuuji@0
|
3127 if (lastid) fs_give ((void **) &lastid);
|
yuuji@0
|
3128 lastid = cpystr (id); /* remember body part id */
|
yuuji@0
|
3129 if (lastst.data) fs_give ((void **) &lastst.data);
|
yuuji@0
|
3130 /* remember text */
|
yuuji@0
|
3131 lastst.data = (unsigned char *)
|
yuuji@0
|
3132 memcpy (fs_get (st->size + 1),st->data,st->size);
|
yuuji@0
|
3133 lastst.size = st->size;
|
yuuji@0
|
3134 }
|
yuuji@0
|
3135
|
yuuji@0
|
3136
|
yuuji@0
|
3137 /* Fetch envelope
|
yuuji@0
|
3138 * Accepts: message number
|
yuuji@0
|
3139 * extra argument
|
yuuji@0
|
3140 */
|
yuuji@0
|
3141
|
yuuji@0
|
3142 void fetch_envelope (unsigned long i,void *args)
|
yuuji@0
|
3143 {
|
yuuji@0
|
3144 ENVELOPE *env = mail_fetchenvelope (stream,i);
|
yuuji@0
|
3145 PSOUT ("ENVELOPE "); /* output attribute */
|
yuuji@0
|
3146 penv (env); /* output envelope */
|
yuuji@0
|
3147 }
|
yuuji@0
|
3148
|
yuuji@0
|
3149 /* Fetch flags
|
yuuji@0
|
3150 * Accepts: message number
|
yuuji@0
|
3151 * extra argument
|
yuuji@0
|
3152 */
|
yuuji@0
|
3153
|
yuuji@0
|
3154 void fetch_flags (unsigned long i,void *args)
|
yuuji@0
|
3155 {
|
yuuji@0
|
3156 unsigned long u;
|
yuuji@0
|
3157 char *t,tmp[MAILTMPLEN];
|
yuuji@0
|
3158 int c = NIL;
|
yuuji@0
|
3159 MESSAGECACHE *elt = mail_elt (stream,i);
|
yuuji@0
|
3160 if (!elt->valid) { /* have valid flags yet? */
|
yuuji@0
|
3161 sprintf (tmp,"%lu",i);
|
yuuji@0
|
3162 mail_fetch_flags (stream,tmp,NIL);
|
yuuji@0
|
3163 }
|
yuuji@0
|
3164 PSOUT ("FLAGS ("); /* output attribute */
|
yuuji@0
|
3165 /* output system flags */
|
yuuji@0
|
3166 if (elt->recent) put_flag (&c,"\\Recent");
|
yuuji@0
|
3167 if (elt->seen) put_flag (&c,"\\Seen");
|
yuuji@0
|
3168 if (elt->deleted) put_flag (&c,"\\Deleted");
|
yuuji@0
|
3169 if (elt->flagged) put_flag (&c,"\\Flagged");
|
yuuji@0
|
3170 if (elt->answered) put_flag (&c,"\\Answered");
|
yuuji@0
|
3171 if (elt->draft) put_flag (&c,"\\Draft");
|
yuuji@0
|
3172 if (u = elt->user_flags) do /* any user flags? */
|
yuuji@0
|
3173 if (t = stream->user_flags[find_rightmost_bit (&u)]) put_flag (&c,t);
|
yuuji@0
|
3174 while (u); /* until no more user flags */
|
yuuji@0
|
3175 PBOUT (')'); /* end of flags */
|
yuuji@0
|
3176 elt->spare2 = NIL; /* we've sent the update */
|
yuuji@0
|
3177 }
|
yuuji@0
|
3178
|
yuuji@0
|
3179
|
yuuji@0
|
3180 /* Output a flag
|
yuuji@0
|
3181 * Accepts: pointer to current delimiter character
|
yuuji@0
|
3182 * flag to output
|
yuuji@0
|
3183 * Changes delimiter character to space
|
yuuji@0
|
3184 */
|
yuuji@0
|
3185
|
yuuji@0
|
3186 void put_flag (int *c,char *s)
|
yuuji@0
|
3187 {
|
yuuji@0
|
3188 if (*c) PBOUT (*c); /* put delimiter */
|
yuuji@0
|
3189 PSOUT (s); /* dump flag */
|
yuuji@0
|
3190 *c = ' '; /* change delimiter if necessary */
|
yuuji@0
|
3191 }
|
yuuji@0
|
3192
|
yuuji@0
|
3193
|
yuuji@0
|
3194 /* Output flags if was unseen
|
yuuji@0
|
3195 * Accepts: message number
|
yuuji@0
|
3196 * prior value of Seen flag
|
yuuji@0
|
3197 */
|
yuuji@0
|
3198
|
yuuji@0
|
3199 void changed_flags (unsigned long i,int f)
|
yuuji@0
|
3200 {
|
yuuji@0
|
3201 /* was unseen, now seen? */
|
yuuji@0
|
3202 if (!f && mail_elt (stream,i)->seen) {
|
yuuji@0
|
3203 PBOUT (' '); /* yes, delimit with space */
|
yuuji@0
|
3204 fetch_flags (i,NIL); /* output flags */
|
yuuji@0
|
3205 }
|
yuuji@0
|
3206 }
|
yuuji@0
|
3207
|
yuuji@0
|
3208 /* Fetch message internal date
|
yuuji@0
|
3209 * Accepts: message number
|
yuuji@0
|
3210 * extra argument
|
yuuji@0
|
3211 */
|
yuuji@0
|
3212
|
yuuji@0
|
3213 void fetch_internaldate (unsigned long i,void *args)
|
yuuji@0
|
3214 {
|
yuuji@0
|
3215 char tmp[MAILTMPLEN];
|
yuuji@0
|
3216 MESSAGECACHE *elt = mail_elt (stream,i);
|
yuuji@0
|
3217 if (!elt->day) { /* have internal date yet? */
|
yuuji@0
|
3218 sprintf (tmp,"%lu",i);
|
yuuji@0
|
3219 mail_fetch_fast (stream,tmp,NIL);
|
yuuji@0
|
3220 }
|
yuuji@0
|
3221 PSOUT ("INTERNALDATE \"");
|
yuuji@0
|
3222 PSOUT (mail_date (tmp,elt));
|
yuuji@0
|
3223 PBOUT ('"');
|
yuuji@0
|
3224 }
|
yuuji@0
|
3225
|
yuuji@0
|
3226
|
yuuji@0
|
3227 /* Fetch unique identifier
|
yuuji@0
|
3228 * Accepts: message number
|
yuuji@0
|
3229 * extra argument
|
yuuji@0
|
3230 */
|
yuuji@0
|
3231
|
yuuji@0
|
3232 void fetch_uid (unsigned long i,void *args)
|
yuuji@0
|
3233 {
|
yuuji@0
|
3234 PSOUT ("UID ");
|
yuuji@0
|
3235 pnum (mail_uid (stream,i));
|
yuuji@0
|
3236 }
|
yuuji@0
|
3237
|
yuuji@0
|
3238 /* Fetch complete RFC-822 format message
|
yuuji@0
|
3239 * Accepts: message number
|
yuuji@0
|
3240 * extra argument
|
yuuji@0
|
3241 */
|
yuuji@0
|
3242
|
yuuji@0
|
3243 void fetch_rfc822 (unsigned long i,void *args)
|
yuuji@0
|
3244 {
|
yuuji@0
|
3245 if (i) { /* do work? */
|
yuuji@0
|
3246 int f = mail_elt (stream,i)->seen;
|
yuuji@0
|
3247 #if 0
|
yuuji@0
|
3248 SIZEDTEXT st;
|
yuuji@0
|
3249 st.data = (unsigned char *)
|
yuuji@0
|
3250 mail_fetch_message (stream,i,&st.size,(long) args);
|
yuuji@0
|
3251 pbodypartstring (i,"RFC822",&st,NIL,NIL);
|
yuuji@0
|
3252 #else
|
yuuji@0
|
3253 /* Yes, this version is bletcherous, but mail_fetch_message() requires
|
yuuji@0
|
3254 too much memory */
|
yuuji@0
|
3255 SIZEDTEXT txt,hdr;
|
yuuji@0
|
3256 char *s = mail_fetch_header (stream,i,NIL,NIL,&hdr.size,FT_PEEK);
|
yuuji@0
|
3257 hdr.data = (unsigned char *) memcpy (fs_get (hdr.size),s,hdr.size);
|
yuuji@0
|
3258 txt.data = (unsigned char *)
|
yuuji@0
|
3259 mail_fetch_text (stream,i,NIL,&txt.size,
|
yuuji@0
|
3260 ((long) args) | FT_RETURNSTRINGSTRUCT);
|
yuuji@0
|
3261 PSOUT ("RFC822 {");
|
yuuji@0
|
3262 pnum (hdr.size + txt.size);
|
yuuji@0
|
3263 PSOUT ("}\015\012");
|
yuuji@0
|
3264 ptext (&hdr,NIL);
|
yuuji@0
|
3265 ptext (&txt,&stream->private.string);
|
yuuji@0
|
3266 fs_give ((void **) &hdr.data);
|
yuuji@0
|
3267 #endif
|
yuuji@0
|
3268 changed_flags (i,f); /* output changed flags */
|
yuuji@0
|
3269 }
|
yuuji@0
|
3270 }
|
yuuji@0
|
3271
|
yuuji@0
|
3272
|
yuuji@0
|
3273 /* Fetch RFC-822 header
|
yuuji@0
|
3274 * Accepts: message number
|
yuuji@0
|
3275 * extra argument
|
yuuji@0
|
3276 */
|
yuuji@0
|
3277
|
yuuji@0
|
3278 void fetch_rfc822_header (unsigned long i,void *args)
|
yuuji@0
|
3279 {
|
yuuji@0
|
3280 SIZEDTEXT st;
|
yuuji@0
|
3281 st.data = (unsigned char *)
|
yuuji@0
|
3282 mail_fetch_header (stream,i,NIL,NIL,&st.size,FT_PEEK);
|
yuuji@0
|
3283 pbodypartstring (i,"RFC822.HEADER",&st,NIL,NIL);
|
yuuji@0
|
3284 }
|
yuuji@0
|
3285
|
yuuji@0
|
3286
|
yuuji@0
|
3287 /* Fetch RFC-822 message length
|
yuuji@0
|
3288 * Accepts: message number
|
yuuji@0
|
3289 * extra argument
|
yuuji@0
|
3290 */
|
yuuji@0
|
3291
|
yuuji@0
|
3292 void fetch_rfc822_size (unsigned long i,void *args)
|
yuuji@0
|
3293 {
|
yuuji@0
|
3294 char tmp[MAILTMPLEN];
|
yuuji@0
|
3295 MESSAGECACHE *elt = mail_elt (stream,i);
|
yuuji@0
|
3296 if (!elt->rfc822_size) { /* have message size yet? */
|
yuuji@0
|
3297 sprintf (tmp,"%lu",i);
|
yuuji@0
|
3298 mail_fetch_fast (stream,tmp,NIL);
|
yuuji@0
|
3299 }
|
yuuji@0
|
3300 PSOUT ("RFC822.SIZE ");
|
yuuji@0
|
3301 pnum (elt->rfc822_size);
|
yuuji@0
|
3302 }
|
yuuji@0
|
3303
|
yuuji@0
|
3304 /* Fetch RFC-822 text only
|
yuuji@0
|
3305 * Accepts: message number
|
yuuji@0
|
3306 * extra argument
|
yuuji@0
|
3307 */
|
yuuji@0
|
3308
|
yuuji@0
|
3309 void fetch_rfc822_text (unsigned long i,void *args)
|
yuuji@0
|
3310 {
|
yuuji@0
|
3311 if (i) { /* do work? */
|
yuuji@0
|
3312 int f = mail_elt (stream,i)->seen;
|
yuuji@0
|
3313 SIZEDTEXT st;
|
yuuji@0
|
3314 st.data = (unsigned char *)
|
yuuji@0
|
3315 mail_fetch_text (stream,i,NIL,&st.size,
|
yuuji@0
|
3316 ((long) args) | FT_RETURNSTRINGSTRUCT);
|
yuuji@0
|
3317 pbodypartstring (i,"RFC822.TEXT",&st,&stream->private.string,NIL);
|
yuuji@0
|
3318 }
|
yuuji@0
|
3319 }
|
yuuji@0
|
3320
|
yuuji@0
|
3321 /* Print envelope
|
yuuji@0
|
3322 * Accepts: body
|
yuuji@0
|
3323 */
|
yuuji@0
|
3324
|
yuuji@0
|
3325 void penv (ENVELOPE *env)
|
yuuji@0
|
3326 {
|
yuuji@0
|
3327 PBOUT ('('); /* delimiter */
|
yuuji@0
|
3328 if (env) { /* only if there is an envelope */
|
yuuji@0
|
3329 pnstring (env->date); /* output envelope fields */
|
yuuji@0
|
3330 PBOUT (' ');
|
yuuji@0
|
3331 pnstring (env->subject);
|
yuuji@0
|
3332 PBOUT (' ');
|
yuuji@0
|
3333 paddr (env->from);
|
yuuji@0
|
3334 PBOUT (' ');
|
yuuji@0
|
3335 paddr (env->sender);
|
yuuji@0
|
3336 PBOUT (' ');
|
yuuji@0
|
3337 paddr (env->reply_to);
|
yuuji@0
|
3338 PBOUT (' ');
|
yuuji@0
|
3339 paddr (env->to);
|
yuuji@0
|
3340 PBOUT (' ');
|
yuuji@0
|
3341 paddr (env->cc);
|
yuuji@0
|
3342 PBOUT (' ');
|
yuuji@0
|
3343 paddr (env->bcc);
|
yuuji@0
|
3344 PBOUT (' ');
|
yuuji@0
|
3345 pnstring (env->in_reply_to);
|
yuuji@0
|
3346 PBOUT (' ');
|
yuuji@0
|
3347 pnstring (env->message_id);
|
yuuji@0
|
3348 }
|
yuuji@0
|
3349 /* no envelope */
|
yuuji@0
|
3350 else PSOUT ("NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL");
|
yuuji@0
|
3351 PBOUT (')'); /* end of envelope */
|
yuuji@0
|
3352 }
|
yuuji@0
|
3353
|
yuuji@0
|
3354 /* Print body structure (extensible)
|
yuuji@0
|
3355 * Accepts: body
|
yuuji@0
|
3356 */
|
yuuji@0
|
3357
|
yuuji@0
|
3358 void pbodystructure (BODY *body)
|
yuuji@0
|
3359 {
|
yuuji@0
|
3360 PBOUT ('('); /* delimiter */
|
yuuji@0
|
3361 if (body) { /* only if there is a body */
|
yuuji@0
|
3362 PART *part;
|
yuuji@0
|
3363 /* multipart type? */
|
yuuji@0
|
3364 if (body->type == TYPEMULTIPART) {
|
yuuji@0
|
3365 /* print each part */
|
yuuji@0
|
3366 if (part = body->nested.part)
|
yuuji@0
|
3367 for (; part; part = part->next) pbodystructure (&(part->body));
|
yuuji@0
|
3368 else pbodystructure (NIL);
|
yuuji@0
|
3369 PBOUT (' '); /* space delimiter */
|
yuuji@0
|
3370 pstring (body->subtype); /* subtype */
|
yuuji@0
|
3371 PBOUT (' ');
|
yuuji@0
|
3372 pparam (body->parameter); /* multipart body extension data */
|
yuuji@0
|
3373 PBOUT (' ');
|
yuuji@0
|
3374 if (body->disposition.type) {
|
yuuji@0
|
3375 PBOUT ('(');
|
yuuji@0
|
3376 pstring (body->disposition.type);
|
yuuji@0
|
3377 PBOUT (' ');
|
yuuji@0
|
3378 pparam (body->disposition.parameter);
|
yuuji@0
|
3379 PBOUT (')');
|
yuuji@0
|
3380 }
|
yuuji@0
|
3381 else PSOUT ("NIL");
|
yuuji@0
|
3382 PBOUT (' ');
|
yuuji@0
|
3383 pnstringorlist (body->language);
|
yuuji@0
|
3384 PBOUT (' ');
|
yuuji@0
|
3385 pnstring (body->location);
|
yuuji@0
|
3386 }
|
yuuji@0
|
3387
|
yuuji@0
|
3388 else { /* non-multipart body type */
|
yuuji@0
|
3389 pstring ((char *) body_types[body->type]);
|
yuuji@0
|
3390 PBOUT (' ');
|
yuuji@0
|
3391 pstring (body->subtype);
|
yuuji@0
|
3392 PBOUT (' ');
|
yuuji@0
|
3393 pparam (body->parameter);
|
yuuji@0
|
3394 PBOUT (' ');
|
yuuji@0
|
3395 pnstring (body->id);
|
yuuji@0
|
3396 PBOUT (' ');
|
yuuji@0
|
3397 pnstring (body->description);
|
yuuji@0
|
3398 PBOUT (' ');
|
yuuji@0
|
3399 pstring ((char *) body_encodings[body->encoding]);
|
yuuji@0
|
3400 PBOUT (' ');
|
yuuji@0
|
3401 pnum (body->size.bytes);
|
yuuji@0
|
3402 switch (body->type) { /* extra stuff depends upon body type */
|
yuuji@0
|
3403 case TYPEMESSAGE:
|
yuuji@0
|
3404 /* can't do this if not RFC822 */
|
yuuji@0
|
3405 if (strcmp (body->subtype,"RFC822")) break;
|
yuuji@0
|
3406 PBOUT (' ');
|
yuuji@0
|
3407 penv (body->nested.msg->env);
|
yuuji@0
|
3408 PBOUT (' ');
|
yuuji@0
|
3409 pbodystructure (body->nested.msg->body);
|
yuuji@0
|
3410 case TYPETEXT:
|
yuuji@0
|
3411 PBOUT (' ');
|
yuuji@0
|
3412 pnum (body->size.lines);
|
yuuji@0
|
3413 break;
|
yuuji@0
|
3414 default:
|
yuuji@0
|
3415 break;
|
yuuji@0
|
3416 }
|
yuuji@0
|
3417 PBOUT (' ');
|
yuuji@0
|
3418 pnstring (body->md5);
|
yuuji@0
|
3419 PBOUT (' ');
|
yuuji@0
|
3420 if (body->disposition.type) {
|
yuuji@0
|
3421 PBOUT ('(');
|
yuuji@0
|
3422 pstring (body->disposition.type);
|
yuuji@0
|
3423 PBOUT (' ');
|
yuuji@0
|
3424 pparam (body->disposition.parameter);
|
yuuji@0
|
3425 PBOUT (')');
|
yuuji@0
|
3426 }
|
yuuji@0
|
3427 else PSOUT ("NIL");
|
yuuji@0
|
3428 PBOUT (' ');
|
yuuji@0
|
3429 pnstringorlist (body->language);
|
yuuji@0
|
3430 PBOUT (' ');
|
yuuji@0
|
3431 pnstring (body->location);
|
yuuji@0
|
3432 }
|
yuuji@0
|
3433 }
|
yuuji@0
|
3434 /* no body */
|
yuuji@0
|
3435 else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0 NIL NIL NIL NIL");
|
yuuji@0
|
3436 PBOUT (')'); /* end of body */
|
yuuji@0
|
3437 }
|
yuuji@0
|
3438
|
yuuji@0
|
3439 /* Print body (non-extensible)
|
yuuji@0
|
3440 * Accepts: body
|
yuuji@0
|
3441 */
|
yuuji@0
|
3442
|
yuuji@0
|
3443 void pbody (BODY *body)
|
yuuji@0
|
3444 {
|
yuuji@0
|
3445 PBOUT ('('); /* delimiter */
|
yuuji@0
|
3446 if (body) { /* only if there is a body */
|
yuuji@0
|
3447 PART *part;
|
yuuji@0
|
3448 /* multipart type? */
|
yuuji@0
|
3449 if (body->type == TYPEMULTIPART) {
|
yuuji@0
|
3450 /* print each part */
|
yuuji@0
|
3451 if (part = body->nested.part)
|
yuuji@0
|
3452 for (; part; part = part->next) pbody (&(part->body));
|
yuuji@0
|
3453 else pbody (NIL);
|
yuuji@0
|
3454 PBOUT (' '); /* space delimiter */
|
yuuji@0
|
3455 pstring (body->subtype); /* and finally the subtype */
|
yuuji@0
|
3456 }
|
yuuji@0
|
3457 else { /* non-multipart body type */
|
yuuji@0
|
3458 pstring ((char *) body_types[body->type]);
|
yuuji@0
|
3459 PBOUT (' ');
|
yuuji@0
|
3460 pstring (body->subtype);
|
yuuji@0
|
3461 PBOUT (' ');
|
yuuji@0
|
3462 pparam (body->parameter);
|
yuuji@0
|
3463 PBOUT (' ');
|
yuuji@0
|
3464 pnstring (body->id);
|
yuuji@0
|
3465 PBOUT (' ');
|
yuuji@0
|
3466 pnstring (body->description);
|
yuuji@0
|
3467 PBOUT (' ');
|
yuuji@0
|
3468 pstring ((char *) body_encodings[body->encoding]);
|
yuuji@0
|
3469 PBOUT (' ');
|
yuuji@0
|
3470 pnum (body->size.bytes);
|
yuuji@0
|
3471 switch (body->type) { /* extra stuff depends upon body type */
|
yuuji@0
|
3472 case TYPEMESSAGE:
|
yuuji@0
|
3473 /* can't do this if not RFC822 */
|
yuuji@0
|
3474 if (strcmp (body->subtype,"RFC822")) break;
|
yuuji@0
|
3475 PBOUT (' ');
|
yuuji@0
|
3476 penv (body->nested.msg ? body->nested.msg->env : NIL);
|
yuuji@0
|
3477 PBOUT (' ');
|
yuuji@0
|
3478 pbody (body->nested.msg ? body->nested.msg->body : NIL);
|
yuuji@0
|
3479 case TYPETEXT:
|
yuuji@0
|
3480 PBOUT (' ');
|
yuuji@0
|
3481 pnum (body->size.lines);
|
yuuji@0
|
3482 break;
|
yuuji@0
|
3483 default:
|
yuuji@0
|
3484 break;
|
yuuji@0
|
3485 }
|
yuuji@0
|
3486 }
|
yuuji@0
|
3487 }
|
yuuji@0
|
3488 /* no body */
|
yuuji@0
|
3489 else PSOUT ("\"TEXT\" \"PLAIN\" (\"CHARSET\" \"US-ASCII\") NIL NIL \"7BIT\" 0 0");
|
yuuji@0
|
3490 PBOUT (')'); /* end of body */
|
yuuji@0
|
3491 }
|
yuuji@0
|
3492
|
yuuji@0
|
3493 /* Print parameter list
|
yuuji@0
|
3494 * Accepts: paramter
|
yuuji@0
|
3495 */
|
yuuji@0
|
3496
|
yuuji@0
|
3497 void pparam (PARAMETER *param)
|
yuuji@0
|
3498 {
|
yuuji@0
|
3499 if (param) { /* one specified? */
|
yuuji@0
|
3500 PBOUT ('(');
|
yuuji@0
|
3501 do {
|
yuuji@0
|
3502 pstring (param->attribute);
|
yuuji@0
|
3503 PBOUT (' ');
|
yuuji@0
|
3504 pstring (param->value);
|
yuuji@0
|
3505 if (param = param->next) PBOUT (' ');
|
yuuji@0
|
3506 } while (param);
|
yuuji@0
|
3507 PBOUT (')'); /* end of parameters */
|
yuuji@0
|
3508 }
|
yuuji@0
|
3509 else PSOUT ("NIL");
|
yuuji@0
|
3510 }
|
yuuji@0
|
3511
|
yuuji@0
|
3512
|
yuuji@0
|
3513 /* Print address list
|
yuuji@0
|
3514 * Accepts: address list
|
yuuji@0
|
3515 */
|
yuuji@0
|
3516
|
yuuji@0
|
3517 void paddr (ADDRESS *a)
|
yuuji@0
|
3518 {
|
yuuji@0
|
3519 if (a) { /* have anything in address? */
|
yuuji@0
|
3520 PBOUT ('('); /* open the address list */
|
yuuji@0
|
3521 do { /* for each address */
|
yuuji@0
|
3522 PBOUT ('('); /* open the address */
|
yuuji@0
|
3523 pnstring (a->personal); /* personal name */
|
yuuji@0
|
3524 PBOUT (' ');
|
yuuji@0
|
3525 pnstring (a->adl); /* at-domain-list */
|
yuuji@0
|
3526 PBOUT (' ');
|
yuuji@0
|
3527 pnstring (a->mailbox); /* mailbox */
|
yuuji@0
|
3528 PBOUT (' ');
|
yuuji@0
|
3529 pnstring (a->host); /* domain name of mailbox's host */
|
yuuji@0
|
3530 PBOUT (')'); /* terminate address */
|
yuuji@0
|
3531 } while (a = a->next); /* until end of address */
|
yuuji@0
|
3532 PBOUT (')'); /* close address list */
|
yuuji@0
|
3533 }
|
yuuji@0
|
3534 else PSOUT ("NIL"); /* empty address */
|
yuuji@0
|
3535 }
|
yuuji@0
|
3536
|
yuuji@0
|
3537 /* Print set
|
yuuji@0
|
3538 * Accepts: set
|
yuuji@0
|
3539 */
|
yuuji@0
|
3540
|
yuuji@0
|
3541 void pset (SEARCHSET **set)
|
yuuji@0
|
3542 {
|
yuuji@0
|
3543 SEARCHSET *cur = *set;
|
yuuji@0
|
3544 while (cur) { /* while there's a set to do */
|
yuuji@0
|
3545 pnum (cur->first); /* output first value */
|
yuuji@0
|
3546 if (cur->last) { /* if range, output second value of range */
|
yuuji@0
|
3547 PBOUT (':');
|
yuuji@0
|
3548 pnum (cur->last);
|
yuuji@0
|
3549 }
|
yuuji@0
|
3550 if (cur = cur->next) PBOUT (',');
|
yuuji@0
|
3551 }
|
yuuji@0
|
3552 mail_free_searchset (set); /* flush set */
|
yuuji@0
|
3553 }
|
yuuji@0
|
3554
|
yuuji@0
|
3555
|
yuuji@0
|
3556 /* Print number
|
yuuji@0
|
3557 * Accepts: number
|
yuuji@0
|
3558 */
|
yuuji@0
|
3559
|
yuuji@0
|
3560 void pnum (unsigned long i)
|
yuuji@0
|
3561 {
|
yuuji@0
|
3562 char tmp[MAILTMPLEN];
|
yuuji@0
|
3563 sprintf (tmp,"%lu",i);
|
yuuji@0
|
3564 PSOUT (tmp);
|
yuuji@0
|
3565 }
|
yuuji@0
|
3566
|
yuuji@0
|
3567
|
yuuji@0
|
3568 /* Print string
|
yuuji@0
|
3569 * Accepts: string
|
yuuji@0
|
3570 */
|
yuuji@0
|
3571
|
yuuji@0
|
3572 void pstring (char *s)
|
yuuji@0
|
3573 {
|
yuuji@0
|
3574 SIZEDTEXT st;
|
yuuji@0
|
3575 st.data = (unsigned char *) s;/* set up sized text */
|
yuuji@0
|
3576 st.size = strlen (s);
|
yuuji@0
|
3577 psizedstring (&st,NIL); /* print string */
|
yuuji@0
|
3578 }
|
yuuji@0
|
3579
|
yuuji@0
|
3580
|
yuuji@0
|
3581 /* Print nstring
|
yuuji@0
|
3582 * Accepts: string or NIL
|
yuuji@0
|
3583 */
|
yuuji@0
|
3584
|
yuuji@0
|
3585 void pnstring (char *s)
|
yuuji@0
|
3586 {
|
yuuji@0
|
3587 if (s) pstring (s); /* print string */
|
yuuji@0
|
3588 else PSOUT ("NIL");
|
yuuji@0
|
3589 }
|
yuuji@0
|
3590
|
yuuji@0
|
3591
|
yuuji@0
|
3592 /* Print atom or string
|
yuuji@0
|
3593 * Accepts: astring
|
yuuji@0
|
3594 */
|
yuuji@0
|
3595
|
yuuji@0
|
3596 void pastring (char *s)
|
yuuji@0
|
3597 {
|
yuuji@0
|
3598 char *t;
|
yuuji@0
|
3599 if (!*s) PSOUT ("\"\""); /* empty string */
|
yuuji@0
|
3600 else { /* see if atom */
|
yuuji@0
|
3601 for (t = s; (*t > ' ') && !(*t & 0x80) &&
|
yuuji@0
|
3602 (*t != '"') && (*t != '\\') && (*t != '(') && (*t != ')') &&
|
yuuji@0
|
3603 (*t != '{') && (*t != '%') && (*t != '*'); t++);
|
yuuji@0
|
3604 if (*t) pstring (s); /* not an atom */
|
yuuji@0
|
3605 else PSOUT (s); /* else plop down as atomic */
|
yuuji@0
|
3606 }
|
yuuji@0
|
3607 }
|
yuuji@0
|
3608
|
yuuji@0
|
3609 /* Print sized text as quoted
|
yuuji@0
|
3610 * Accepts: sized text
|
yuuji@0
|
3611 */
|
yuuji@0
|
3612
|
yuuji@0
|
3613 void psizedquoted (SIZEDTEXT *s)
|
yuuji@0
|
3614 {
|
yuuji@0
|
3615 PBOUT ('"'); /* use quoted string */
|
yuuji@0
|
3616 ptext (s,NIL);
|
yuuji@0
|
3617 PBOUT ('"');
|
yuuji@0
|
3618 }
|
yuuji@0
|
3619
|
yuuji@0
|
3620
|
yuuji@0
|
3621 /* Print sized text as literal
|
yuuji@0
|
3622 * Accepts: sized text
|
yuuji@0
|
3623 */
|
yuuji@0
|
3624
|
yuuji@0
|
3625 void psizedliteral (SIZEDTEXT *s,STRING *st)
|
yuuji@0
|
3626 {
|
yuuji@0
|
3627 PBOUT ('{'); /* print literal size */
|
yuuji@0
|
3628 pnum (s->size);
|
yuuji@0
|
3629 PSOUT ("}\015\012");
|
yuuji@0
|
3630 ptext (s,st);
|
yuuji@0
|
3631 }
|
yuuji@0
|
3632
|
yuuji@0
|
3633 /* Print sized text as literal or quoted string
|
yuuji@0
|
3634 * Accepts: sized text
|
yuuji@0
|
3635 * alternative stringstruct of text
|
yuuji@0
|
3636 */
|
yuuji@0
|
3637
|
yuuji@0
|
3638 void psizedstring (SIZEDTEXT *s,STRING *st)
|
yuuji@0
|
3639 {
|
yuuji@0
|
3640 unsigned char c;
|
yuuji@0
|
3641 unsigned long i;
|
yuuji@0
|
3642
|
yuuji@0
|
3643 if (s->data) { /* if text, check if must use literal */
|
yuuji@0
|
3644 for (i = 0; ((i < s->size) && ((c = s->data[i]) & 0xe0) &&
|
yuuji@0
|
3645 !(c & 0x80) && (c != '"') && (c != '\\')); ++i);
|
yuuji@0
|
3646 /* must use literal if not all QUOTED-CHAR */
|
yuuji@0
|
3647 if (i < s->size) psizedliteral (s,st);
|
yuuji@0
|
3648 else psizedquoted (s);
|
yuuji@0
|
3649 }
|
yuuji@0
|
3650 else psizedliteral (s,st);
|
yuuji@0
|
3651 }
|
yuuji@0
|
3652
|
yuuji@0
|
3653
|
yuuji@0
|
3654 /* Print sized text as literal or quoted string
|
yuuji@0
|
3655 * Accepts: sized text
|
yuuji@0
|
3656 */
|
yuuji@0
|
3657
|
yuuji@0
|
3658 void psizedastring (SIZEDTEXT *s)
|
yuuji@0
|
3659 {
|
yuuji@0
|
3660 unsigned long i;
|
yuuji@0
|
3661 unsigned int atomp = s->size ? T : NIL;
|
yuuji@0
|
3662 for (i = 0; i < s->size; i++){/* check if must use literal */
|
yuuji@0
|
3663 if (!(s->data[i] & 0xe0) || (s->data[i] & 0x80) ||
|
yuuji@0
|
3664 (s->data[i] == '"') || (s->data[i] == '\\')) {
|
yuuji@0
|
3665 psizedliteral (s,NIL);
|
yuuji@0
|
3666 return;
|
yuuji@0
|
3667 }
|
yuuji@0
|
3668 else switch (s->data[i]) { /* else see if any atom-specials */
|
yuuji@0
|
3669 case '(': case ')': case '{': case ' ':
|
yuuji@0
|
3670 case '%': case '*': /* list-wildcards */
|
yuuji@0
|
3671 case ']': /* resp-specials */
|
yuuji@0
|
3672 /* CTL and quoted-specials in literal check */
|
yuuji@0
|
3673 atomp = NIL; /* not an atom */
|
yuuji@0
|
3674 }
|
yuuji@0
|
3675 }
|
yuuji@0
|
3676 if (atomp) ptext (s,NIL); /* print as atom */
|
yuuji@0
|
3677 else psizedquoted (s); /* print as quoted string */
|
yuuji@0
|
3678 }
|
yuuji@0
|
3679
|
yuuji@0
|
3680 /* Print string list
|
yuuji@0
|
3681 * Accepts: string list
|
yuuji@0
|
3682 */
|
yuuji@0
|
3683
|
yuuji@0
|
3684 void pastringlist (STRINGLIST *s)
|
yuuji@0
|
3685 {
|
yuuji@0
|
3686 PBOUT ('('); /* start list */
|
yuuji@0
|
3687 do {
|
yuuji@0
|
3688 psizedastring (&s->text); /* output list member */
|
yuuji@0
|
3689 if (s->next) PBOUT (' ');
|
yuuji@0
|
3690 } while (s = s->next);
|
yuuji@0
|
3691 PBOUT (')'); /* terminate list */
|
yuuji@0
|
3692 }
|
yuuji@0
|
3693
|
yuuji@0
|
3694
|
yuuji@0
|
3695 /* Print nstring or list of strings
|
yuuji@0
|
3696 * Accepts: string / string list
|
yuuji@0
|
3697 */
|
yuuji@0
|
3698
|
yuuji@0
|
3699 void pnstringorlist (STRINGLIST *s)
|
yuuji@0
|
3700 {
|
yuuji@0
|
3701 if (!s) PSOUT ("NIL"); /* no argument given */
|
yuuji@0
|
3702 else if (s->next) { /* output list as list of strings*/
|
yuuji@0
|
3703 PBOUT ('('); /* start list */
|
yuuji@0
|
3704 do { /* output list member */
|
yuuji@0
|
3705 psizedstring (&s->text,NIL);
|
yuuji@0
|
3706 if (s->next) PBOUT (' ');
|
yuuji@0
|
3707 } while (s = s->next);
|
yuuji@0
|
3708 PBOUT (')'); /* terminate list */
|
yuuji@0
|
3709 }
|
yuuji@0
|
3710 /* and single-element list as string */
|
yuuji@0
|
3711 else psizedstring (&s->text,NIL);
|
yuuji@0
|
3712 }
|
yuuji@0
|
3713
|
yuuji@0
|
3714 /* Print body part string
|
yuuji@0
|
3715 * Accepts: message number
|
yuuji@0
|
3716 * body part id (note: must have space at end to append stuff)
|
yuuji@0
|
3717 * sized text of string
|
yuuji@0
|
3718 * alternative stringstruct of string
|
yuuji@0
|
3719 * text printing arguments
|
yuuji@0
|
3720 */
|
yuuji@0
|
3721
|
yuuji@0
|
3722 void pbodypartstring (unsigned long msgno,char *id,SIZEDTEXT *st,STRING *bs,
|
yuuji@0
|
3723 TEXTARGS *ta)
|
yuuji@0
|
3724 {
|
yuuji@0
|
3725 int f = mail_elt (stream,msgno)->seen;
|
yuuji@0
|
3726 /* ignore stringstruct if non-initialized */
|
yuuji@0
|
3727 if (bs && !bs->curpos) bs = NIL;
|
yuuji@0
|
3728 if (ta && st->size) { /* only if have useful data */
|
yuuji@0
|
3729 /* partial specifier */
|
yuuji@0
|
3730 if (ta->first || ta->last) sprintf (id + strlen (id),"<%lu>",ta->first);
|
yuuji@0
|
3731 /* in case first byte beyond end of text */
|
yuuji@0
|
3732 if (st->size <= ta->first) st->size = ta->first = 0;
|
yuuji@0
|
3733 else {
|
yuuji@0
|
3734 if (st->data) { /* offset and truncate */
|
yuuji@0
|
3735 st->data += ta->first; /* move to desired position */
|
yuuji@0
|
3736 st->size -= ta->first; /* reduced size */
|
yuuji@0
|
3737 }
|
yuuji@0
|
3738 else if (bs && (SIZE (bs) >= ta->first))
|
yuuji@0
|
3739 SETPOS (bs,ta->first + GETPOS (bs));
|
yuuji@0
|
3740 else st->size = 0; /* shouldn't happen */
|
yuuji@0
|
3741 if (ta->last && (st->size > ta->last)) st->size = ta->last;
|
yuuji@0
|
3742 }
|
yuuji@0
|
3743 }
|
yuuji@0
|
3744 PSOUT (id);
|
yuuji@0
|
3745 PBOUT (' ');
|
yuuji@0
|
3746 psizedstring (st,bs); /* output string */
|
yuuji@0
|
3747 changed_flags (msgno,f); /* and changed flags */
|
yuuji@0
|
3748 }
|
yuuji@0
|
3749
|
yuuji@0
|
3750 /* RFC 3501 technically forbids NULs in literals. Normally, the delivering
|
yuuji@0
|
3751 * MTA would take care of MIME converting the message text so that it is
|
yuuji@0
|
3752 * NUL-free. If it doesn't, then we have the choice of either violating
|
yuuji@0
|
3753 * IMAP by sending NULs, corrupting the data, or going to lots of work to do
|
yuuji@0
|
3754 * MIME conversion in the IMAP server.
|
yuuji@0
|
3755 */
|
yuuji@0
|
3756
|
yuuji@0
|
3757 /* Print raw sized text
|
yuuji@0
|
3758 * Accepts: sizedtext
|
yuuji@0
|
3759 */
|
yuuji@0
|
3760
|
yuuji@0
|
3761 void ptext (SIZEDTEXT *txt,STRING *st)
|
yuuji@0
|
3762 {
|
yuuji@0
|
3763 unsigned char c,*s;
|
yuuji@0
|
3764 unsigned long i = txt->size;
|
yuuji@0
|
3765 if (s = txt->data) while (i && ((PBOUT ((c = *s++) ? c : 0x80) != EOF))) --i;
|
yuuji@0
|
3766 else if (st) while (i && (PBOUT ((c = SNX (st)) ? c : 0x80) != EOF)) --i;
|
yuuji@0
|
3767 /* failed to complete? */
|
yuuji@0
|
3768 if (i) ioerror (stdout,"writing text");
|
yuuji@0
|
3769 }
|
yuuji@0
|
3770
|
yuuji@0
|
3771 /* Print thread
|
yuuji@0
|
3772 * Accepts: thread
|
yuuji@0
|
3773 */
|
yuuji@0
|
3774
|
yuuji@0
|
3775 void pthread (THREADNODE *thr)
|
yuuji@0
|
3776 {
|
yuuji@0
|
3777 THREADNODE *t;
|
yuuji@0
|
3778 while (thr) { /* for each branch */
|
yuuji@0
|
3779 PBOUT ('('); /* open branch */
|
yuuji@0
|
3780 if (thr->num) { /* first node message number */
|
yuuji@0
|
3781 pnum (thr->num);
|
yuuji@0
|
3782 if (t = thr->next) { /* any subsequent nodes? */
|
yuuji@0
|
3783 PBOUT (' ');
|
yuuji@0
|
3784 while (t) { /* for each subsequent node */
|
yuuji@0
|
3785 if (t->branch) { /* branches? */
|
yuuji@0
|
3786 pthread (t); /* yes, recurse to do branch */
|
yuuji@0
|
3787 t = NIL; /* done */
|
yuuji@0
|
3788 }
|
yuuji@0
|
3789 else { /* just output this number */
|
yuuji@0
|
3790 pnum (t->num);
|
yuuji@0
|
3791 t = t->next; /* and do next message */
|
yuuji@0
|
3792 }
|
yuuji@0
|
3793 if (t) PBOUT (' '); /* delimit if more to come */
|
yuuji@0
|
3794 }
|
yuuji@0
|
3795 }
|
yuuji@0
|
3796 }
|
yuuji@0
|
3797 else pthread (thr->next); /* nest for dummy */
|
yuuji@0
|
3798 PBOUT (')'); /* done with this branch */
|
yuuji@0
|
3799 thr = thr->branch; /* do next branch */
|
yuuji@0
|
3800 }
|
yuuji@0
|
3801 }
|
yuuji@0
|
3802
|
yuuji@0
|
3803 /* Print capabilities
|
yuuji@0
|
3804 * Accepts: option flag
|
yuuji@0
|
3805 */
|
yuuji@0
|
3806
|
yuuji@0
|
3807 void pcapability (long flag)
|
yuuji@0
|
3808 {
|
yuuji@0
|
3809 unsigned long i;
|
yuuji@0
|
3810 char *s;
|
yuuji@0
|
3811 struct stat sbuf;
|
yuuji@0
|
3812 AUTHENTICATOR *auth;
|
yuuji@0
|
3813 THREADER *thr = (THREADER *) mail_parameters (NIL,GET_THREADERS,NIL);
|
yuuji@0
|
3814 /* always output protocol level */
|
yuuji@0
|
3815 PSOUT ("CAPABILITY IMAP4REV1 I18NLEVEL=1 LITERAL+");
|
yuuji@0
|
3816 #ifdef NETSCAPE_BRAIN_DAMAGE
|
yuuji@0
|
3817 PSOUT (" X-NETSCAPE");
|
yuuji@0
|
3818 #endif
|
yuuji@0
|
3819 if (flag >= 0) { /* want post-authentication capabilities? */
|
yuuji@0
|
3820 PSOUT (" IDLE UIDPLUS NAMESPACE CHILDREN MAILBOX-REFERRALS BINARY UNSELECT ESEARCH WITHIN SCAN SORT");
|
yuuji@0
|
3821 while (thr) { /* threaders */
|
yuuji@0
|
3822 PSOUT (" THREAD=");
|
yuuji@0
|
3823 PSOUT (thr->name);
|
yuuji@0
|
3824 thr = thr->next;
|
yuuji@0
|
3825 }
|
yuuji@0
|
3826 if (!anonymous) PSOUT (" MULTIAPPEND");
|
yuuji@0
|
3827 }
|
yuuji@0
|
3828 if (flag <= 0) { /* want pre-authentication capabilities? */
|
yuuji@0
|
3829 PSOUT (" SASL-IR LOGIN-REFERRALS");
|
yuuji@0
|
3830 if (s = ssl_start_tls (NIL)) fs_give ((void **) &s);
|
yuuji@0
|
3831 else PSOUT (" STARTTLS");
|
yuuji@0
|
3832 /* disable plaintext */
|
yuuji@0
|
3833 if (!(i = !mail_parameters (NIL,GET_DISABLEPLAINTEXT,NIL)))
|
yuuji@0
|
3834 PSOUT (" LOGINDISABLED");
|
yuuji@0
|
3835 for (auth = mail_lookup_auth (1); auth; auth = auth->next)
|
yuuji@0
|
3836 if (auth->server && !(auth->flags & AU_DISABLE) &&
|
yuuji@0
|
3837 !(auth->flags & AU_HIDE) && (i || (auth->flags & AU_SECURE))) {
|
yuuji@0
|
3838 PSOUT (" AUTH=");
|
yuuji@0
|
3839 PSOUT (auth->name);
|
yuuji@0
|
3840 }
|
yuuji@0
|
3841 if (!stat (ANOFILE,&sbuf)) PSOUT (" AUTH=ANONYMOUS");
|
yuuji@0
|
3842 }
|
yuuji@0
|
3843 }
|
yuuji@0
|
3844
|
yuuji@0
|
3845 /* Anonymous users may only use these mailboxes in these namespaces */
|
yuuji@0
|
3846
|
yuuji@0
|
3847 char *oktab[] = {"#news.", "#ftp/", "#public/", 0};
|
yuuji@0
|
3848
|
yuuji@0
|
3849
|
yuuji@0
|
3850 /* Check if mailbox name is OK
|
yuuji@0
|
3851 * Accepts: reference name
|
yuuji@0
|
3852 * mailbox name
|
yuuji@0
|
3853 */
|
yuuji@0
|
3854
|
yuuji@0
|
3855 long nameok (char *ref,char *name)
|
yuuji@0
|
3856 {
|
yuuji@0
|
3857 int i;
|
yuuji@0
|
3858 unsigned char *s,*t;
|
yuuji@0
|
3859 if (!name) return NIL; /* failure if missing name */
|
yuuji@0
|
3860 if (!anonymous) return T; /* otherwise OK if not anonymous */
|
yuuji@0
|
3861 /* validate reference */
|
yuuji@0
|
3862 if (ref && ((*ref == '#') || (*ref == '{')))
|
yuuji@0
|
3863 for (i = 0; oktab[i]; i++) {
|
yuuji@0
|
3864 for (s = ref, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
|
yuuji@0
|
3865 if (!*t) { /* reference OK */
|
yuuji@0
|
3866 if (*name == '#') break;/* check name if override */
|
yuuji@0
|
3867 else return T; /* otherwise done */
|
yuuji@0
|
3868 }
|
yuuji@0
|
3869 }
|
yuuji@0
|
3870 /* ordinary names are OK */
|
yuuji@0
|
3871 if ((*name != '#') && (*name != '{')) return T;
|
yuuji@0
|
3872 for (i = 0; oktab[i]; i++) { /* validate mailbox */
|
yuuji@0
|
3873 for (s = name, t = oktab[i]; *t && !compare_uchar (*s,*t); s++, t++);
|
yuuji@0
|
3874 if (!*t) return T; /* name is OK */
|
yuuji@0
|
3875 }
|
yuuji@0
|
3876 response = "%.80s NO Anonymous may not %.80s this name\015\012";
|
yuuji@0
|
3877 return NIL;
|
yuuji@0
|
3878 }
|
yuuji@0
|
3879
|
yuuji@0
|
3880
|
yuuji@0
|
3881 /* Convert possible BBoard name to actual name
|
yuuji@0
|
3882 * Accepts: command
|
yuuji@0
|
3883 * mailbox name
|
yuuji@0
|
3884 * Returns: maibox name
|
yuuji@0
|
3885 */
|
yuuji@0
|
3886
|
yuuji@0
|
3887 char *bboardname (char *cmd,char *name)
|
yuuji@0
|
3888 {
|
yuuji@0
|
3889 if (cmd[0] == 'B') { /* want bboard? */
|
yuuji@0
|
3890 char *s = litstk[litsp++] = (char *) fs_get (strlen (name) + 9);
|
yuuji@0
|
3891 sprintf (s,"#public/%s",(*name == '/') ? name+1 : name);
|
yuuji@0
|
3892 name = s;
|
yuuji@0
|
3893 }
|
yuuji@0
|
3894 return name;
|
yuuji@0
|
3895 }
|
yuuji@0
|
3896
|
yuuji@0
|
3897 /* Test if name is news proxy
|
yuuji@0
|
3898 * Accepts: name
|
yuuji@0
|
3899 * Returns: T if news proxy, NIL otherwise
|
yuuji@0
|
3900 */
|
yuuji@0
|
3901
|
yuuji@0
|
3902 long isnewsproxy (char *name)
|
yuuji@0
|
3903 {
|
yuuji@0
|
3904 return (nntpproxy && (name[0] == '#') &&
|
yuuji@0
|
3905 ((name[1] == 'N') || (name[1] == 'n')) &&
|
yuuji@0
|
3906 ((name[2] == 'E') || (name[2] == 'e')) &&
|
yuuji@0
|
3907 ((name[3] == 'W') || (name[3] == 'w')) &&
|
yuuji@0
|
3908 ((name[4] == 'S') || (name[4] == 's')) && (name[5] == '.')) ?
|
yuuji@0
|
3909 LONGT : NIL;
|
yuuji@0
|
3910 }
|
yuuji@0
|
3911
|
yuuji@0
|
3912
|
yuuji@0
|
3913 /* News proxy generate canonical pattern
|
yuuji@0
|
3914 * Accepts: reference
|
yuuji@0
|
3915 * pattern
|
yuuji@0
|
3916 * buffer to return canonical pattern
|
yuuji@0
|
3917 * Returns: T on success with pattern in buffer, NIL on failure
|
yuuji@0
|
3918 */
|
yuuji@0
|
3919
|
yuuji@0
|
3920 long newsproxypattern (char *ref,char *pat,char *pattern,long flag)
|
yuuji@0
|
3921 {
|
yuuji@0
|
3922 if (!nntpproxy) return NIL;
|
yuuji@0
|
3923 if (strlen (ref) > NETMAXMBX) {
|
yuuji@0
|
3924 sprintf (pattern,"Invalid reference specification: %.80s",ref);
|
yuuji@0
|
3925 mm_log (pattern,ERROR);
|
yuuji@0
|
3926 return NIL;
|
yuuji@0
|
3927 }
|
yuuji@0
|
3928 if (strlen (pat) > NETMAXMBX) {
|
yuuji@0
|
3929 sprintf (pattern,"Invalid pattern specification: %.80s",pat);
|
yuuji@0
|
3930 mm_log (pattern,ERROR);
|
yuuji@0
|
3931 return NIL;
|
yuuji@0
|
3932 }
|
yuuji@0
|
3933 if (flag) { /* prepend proxy specifier */
|
yuuji@0
|
3934 sprintf (pattern,"{%.300s/nntp}",nntpproxy);
|
yuuji@0
|
3935 pattern += strlen (pattern);
|
yuuji@0
|
3936 }
|
yuuji@0
|
3937 if (*ref) { /* have a reference */
|
yuuji@0
|
3938 strcpy (pattern,ref); /* copy reference to pattern */
|
yuuji@0
|
3939 /* # overrides mailbox field in reference */
|
yuuji@0
|
3940 if (*pat == '#') strcpy (pattern,pat);
|
yuuji@0
|
3941 /* pattern starts, reference ends, with . */
|
yuuji@0
|
3942 else if ((*pat == '.') && (pattern[strlen (pattern) - 1] == '.'))
|
yuuji@0
|
3943 strcat (pattern,pat + 1); /* append, omitting one of the period */
|
yuuji@0
|
3944 else strcat (pattern,pat); /* anything else is just appended */
|
yuuji@0
|
3945 }
|
yuuji@0
|
3946 else strcpy (pattern,pat); /* just have basic name */
|
yuuji@0
|
3947 return isnewsproxy (pattern);
|
yuuji@0
|
3948 }
|
yuuji@0
|
3949
|
yuuji@0
|
3950 /* IMAP4rev1 Authentication responder
|
yuuji@0
|
3951 * Accepts: challenge
|
yuuji@0
|
3952 * length of challenge
|
yuuji@0
|
3953 * pointer to response length return location if non-NIL
|
yuuji@0
|
3954 * Returns: response
|
yuuji@0
|
3955 */
|
yuuji@0
|
3956
|
yuuji@0
|
3957 #define RESPBUFLEN 8*MAILTMPLEN
|
yuuji@0
|
3958
|
yuuji@0
|
3959 char *imap_responder (void *challenge,unsigned long clen,unsigned long *rlen)
|
yuuji@0
|
3960 {
|
yuuji@0
|
3961 unsigned long i,j;
|
yuuji@0
|
3962 unsigned char *t,resp[RESPBUFLEN];
|
yuuji@0
|
3963 if (initial) { /* initial response given? */
|
yuuji@0
|
3964 if (clen) return NIL; /* not permitted */
|
yuuji@0
|
3965 /* set up response */
|
yuuji@0
|
3966 i = strlen ((char *) (t = initial));
|
yuuji@0
|
3967 initial = NIL; /* no more initial response */
|
yuuji@0
|
3968 if ((*t == '=') && !t[1]) { /* SASL-IR does this for 0-length response */
|
yuuji@0
|
3969 if (rlen) *rlen = 0; /* set length zero if empty */
|
yuuji@0
|
3970 return cpystr (""); /* and return empty string as response */
|
yuuji@0
|
3971 }
|
yuuji@0
|
3972 }
|
yuuji@0
|
3973 else { /* issue challenge, get response */
|
yuuji@0
|
3974 PSOUT ("+ ");
|
yuuji@0
|
3975 for (t = rfc822_binary ((void *) challenge,clen,&i),j = 0; j < i; j++)
|
yuuji@0
|
3976 if (t[j] > ' ') PBOUT (t[j]);
|
yuuji@0
|
3977 fs_give ((void **) &t);
|
yuuji@0
|
3978 CRLF;
|
yuuji@0
|
3979 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
3980 /* slurp response buffer */
|
yuuji@0
|
3981 slurp ((char *) resp,RESPBUFLEN,INPUTTIMEOUT);
|
yuuji@0
|
3982 if (!(t = (unsigned char *) strchr ((char *) resp,'\012')))
|
yuuji@0
|
3983 return (char *) flush ();
|
yuuji@0
|
3984 if (t[-1] == '\015') --t; /* remove CR */
|
yuuji@0
|
3985 *t = '\0'; /* tie off buffer */
|
yuuji@0
|
3986 if (resp[0] == '*') {
|
yuuji@0
|
3987 cancelled = T;
|
yuuji@0
|
3988 return NIL;
|
yuuji@0
|
3989 }
|
yuuji@0
|
3990 i = t - resp; /* length of response */
|
yuuji@0
|
3991 t = resp; /* set up for return call */
|
yuuji@0
|
3992 }
|
yuuji@0
|
3993 return (i % 4) ? NIL : /* return if valid BASE64 */
|
yuuji@0
|
3994 (char *) rfc822_base64 (t,i,rlen ? rlen : &i);
|
yuuji@0
|
3995 }
|
yuuji@0
|
3996
|
yuuji@0
|
3997 /* Proxy copy across mailbox formats
|
yuuji@0
|
3998 * Accepts: mail stream
|
yuuji@0
|
3999 * sequence to copy on this stream
|
yuuji@0
|
4000 * destination mailbox
|
yuuji@0
|
4001 * option flags
|
yuuji@0
|
4002 * Returns: T if success, else NIL
|
yuuji@0
|
4003 */
|
yuuji@0
|
4004
|
yuuji@0
|
4005 long proxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
|
yuuji@0
|
4006 {
|
yuuji@0
|
4007 MAILSTREAM *ts;
|
yuuji@0
|
4008 STRING st;
|
yuuji@0
|
4009 MSGDATA md;
|
yuuji@0
|
4010 SEARCHSET *set;
|
yuuji@0
|
4011 char tmp[MAILTMPLEN];
|
yuuji@0
|
4012 unsigned long i,j;
|
yuuji@0
|
4013 md.stream = stream;
|
yuuji@0
|
4014 md.msgno = 0;
|
yuuji@0
|
4015 md.flags = md.date = NIL;
|
yuuji@0
|
4016 md.message = &st;
|
yuuji@0
|
4017 /* Currently ignores CP_MOVE and CP_DEBUG */
|
yuuji@0
|
4018 if (!((options & CP_UID) ? /* validate sequence */
|
yuuji@0
|
4019 mail_uid_sequence (stream,sequence) : mail_sequence (stream,sequence)))
|
yuuji@0
|
4020 return NIL;
|
yuuji@0
|
4021 response = win; /* cancel previous errors */
|
yuuji@0
|
4022 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
4023 /* c-client clobbers sequence, use spare */
|
yuuji@0
|
4024 for (i = 1,j = 0,set = mail_newsearchset (); i <= nmsgs; i++)
|
yuuji@0
|
4025 if (mail_elt (stream,i)->spare = mail_elt (stream,i)->sequence) {
|
yuuji@0
|
4026 mail_append_set (set,mail_uid (stream,i));
|
yuuji@0
|
4027 if (!j) md.msgno = (j = i) - 1;
|
yuuji@0
|
4028 }
|
yuuji@0
|
4029 /* only if at least one message to copy */
|
yuuji@0
|
4030 if (j && !mail_append_multiple (NIL,mailbox,proxy_append,(void *) &md)) {
|
yuuji@0
|
4031 response = trycreate ? losetry : lose;
|
yuuji@0
|
4032 if (set) mail_free_searchset (&set);
|
yuuji@0
|
4033 return NIL;
|
yuuji@0
|
4034 }
|
yuuji@0
|
4035 if (caset) csset = set; /* set for return value now */
|
yuuji@0
|
4036 else if (set) mail_free_searchset (&set);
|
yuuji@0
|
4037 response = win; /* stomp any previous babble */
|
yuuji@0
|
4038 if (md.msgno) { /* get new driver name if was dummy */
|
yuuji@0
|
4039 sprintf (tmp,"Cross-format (%.80s -> %.80s) COPY completed",
|
yuuji@0
|
4040 stream->dtb->name,(ts = mail_open (NIL,mailbox,OP_PROTOTYPE)) ?
|
yuuji@0
|
4041 ts->dtb->name : "unknown");
|
yuuji@0
|
4042 mm_log (tmp,NIL);
|
yuuji@0
|
4043 }
|
yuuji@0
|
4044 return LONGT;
|
yuuji@0
|
4045 }
|
yuuji@0
|
4046
|
yuuji@0
|
4047 /* Proxy append message callback
|
yuuji@0
|
4048 * Accepts: MAIL stream
|
yuuji@0
|
4049 * append data package
|
yuuji@0
|
4050 * pointer to return initial flags
|
yuuji@0
|
4051 * pointer to return message internal date
|
yuuji@0
|
4052 * pointer to return stringstruct of message or NIL to stop
|
yuuji@0
|
4053 * Returns: T if success (have message or stop), NIL if error
|
yuuji@0
|
4054 */
|
yuuji@0
|
4055
|
yuuji@0
|
4056 long proxy_append (MAILSTREAM *stream,void *data,char **flags,char **date,
|
yuuji@0
|
4057 STRING **message)
|
yuuji@0
|
4058 {
|
yuuji@0
|
4059 MESSAGECACHE *elt;
|
yuuji@0
|
4060 unsigned long i;
|
yuuji@0
|
4061 char *s,*t,tmp[MAILTMPLEN];
|
yuuji@0
|
4062 MSGDATA *md = (MSGDATA *) data;
|
yuuji@0
|
4063 if (md->flags) fs_give ((void **) &md->flags);
|
yuuji@0
|
4064 if (md->date) fs_give ((void **) &md->date);
|
yuuji@0
|
4065 *message = NIL; /* assume all done */
|
yuuji@0
|
4066 *flags = *date = NIL;
|
yuuji@0
|
4067 while (++md->msgno <= nmsgs)
|
yuuji@0
|
4068 if ((elt = mail_elt (md->stream,md->msgno))->spare) {
|
yuuji@0
|
4069 if (!(elt->valid && elt->day)) {
|
yuuji@0
|
4070 sprintf (tmp,"%lu",md->msgno);
|
yuuji@0
|
4071 mail_fetch_fast (md->stream,tmp,NIL);
|
yuuji@0
|
4072 }
|
yuuji@0
|
4073 memset (s = tmp,0,MAILTMPLEN);
|
yuuji@0
|
4074 /* copy flags */
|
yuuji@0
|
4075 if (elt->seen) strcat (s," \\Seen");
|
yuuji@0
|
4076 if (elt->deleted) strcat (s," \\Deleted");
|
yuuji@0
|
4077 if (elt->flagged) strcat (s," \\Flagged");
|
yuuji@0
|
4078 if (elt->answered) strcat (s," \\Answered");
|
yuuji@0
|
4079 if (elt->draft) strcat (s," \\Draft");
|
yuuji@0
|
4080 if (i = elt->user_flags) do
|
yuuji@0
|
4081 if ((t = md->stream->user_flags[find_rightmost_bit (&i)]) && *t &&
|
yuuji@0
|
4082 (strlen (t) < ((size_t) (MAILTMPLEN-((s += strlen (s))+2-tmp))))) {
|
yuuji@0
|
4083 *s++ = ' '; /* space delimiter */
|
yuuji@0
|
4084 strcpy (s,t);
|
yuuji@0
|
4085 } while (i); /* until no more user flags */
|
yuuji@0
|
4086 *message = md->message; /* set up return values */
|
yuuji@0
|
4087 *flags = md->flags = cpystr (tmp + 1);
|
yuuji@0
|
4088 *date = md->date = cpystr (mail_date (tmp,elt));
|
yuuji@0
|
4089 INIT (md->message,msg_string,(void *) md,elt->rfc822_size);
|
yuuji@0
|
4090 break; /* process this message */
|
yuuji@0
|
4091 }
|
yuuji@0
|
4092 return LONGT;
|
yuuji@0
|
4093 }
|
yuuji@0
|
4094
|
yuuji@0
|
4095 /* Append message callback
|
yuuji@0
|
4096 * Accepts: MAIL stream
|
yuuji@0
|
4097 * append data package
|
yuuji@0
|
4098 * pointer to return initial flags
|
yuuji@0
|
4099 * pointer to return message internal date
|
yuuji@0
|
4100 * pointer to return stringstruct of message or NIL to stop
|
yuuji@0
|
4101 * Returns: T if success (have message or stop), NIL if error
|
yuuji@0
|
4102 */
|
yuuji@0
|
4103
|
yuuji@0
|
4104 long append_msg (MAILSTREAM *stream,void *data,char **flags,char **date,
|
yuuji@0
|
4105 STRING **message)
|
yuuji@0
|
4106 {
|
yuuji@0
|
4107 unsigned long i,j;
|
yuuji@0
|
4108 char *t;
|
yuuji@0
|
4109 APPENDDATA *ad = (APPENDDATA *) data;
|
yuuji@0
|
4110 unsigned char *arg = ad->arg;
|
yuuji@0
|
4111 /* flush text of previous message */
|
yuuji@0
|
4112 if (t = ad->flags) fs_give ((void **) &ad->flags);
|
yuuji@0
|
4113 if (t = ad->date) fs_give ((void **) &ad->date);
|
yuuji@0
|
4114 if (t = ad->msg) fs_give ((void **) &ad->msg);
|
yuuji@0
|
4115 *flags = *date = NIL; /* assume no flags or date */
|
yuuji@0
|
4116 if (t) { /* have previous message? */
|
yuuji@0
|
4117 if (!*arg) { /* if least one message, and no more coming */
|
yuuji@0
|
4118 *message = NIL; /* set stop */
|
yuuji@0
|
4119 return LONGT; /* return success */
|
yuuji@0
|
4120 }
|
yuuji@0
|
4121 else if (*arg++ != ' ') { /* must have a delimiter to next argument */
|
yuuji@0
|
4122 response = misarg; /* oops */
|
yuuji@0
|
4123 return NIL;
|
yuuji@0
|
4124 }
|
yuuji@0
|
4125 }
|
yuuji@0
|
4126 *message = ad->message; /* return pointer to message stringstruct */
|
yuuji@0
|
4127 if (*arg == '(') { /* parse optional flag list */
|
yuuji@0
|
4128 t = ++arg; /* pointer to flag list contents */
|
yuuji@0
|
4129 while (*arg && (*arg != ')')) arg++;
|
yuuji@0
|
4130 if (*arg) *arg++ = '\0';
|
yuuji@0
|
4131 if (*arg == ' ') arg++;
|
yuuji@0
|
4132 *flags = ad->flags = cpystr (t);
|
yuuji@0
|
4133 }
|
yuuji@0
|
4134 /* parse optional date */
|
yuuji@0
|
4135 if (*arg == '"') *date = ad->date = cpystr (snarf (&arg));
|
yuuji@0
|
4136 if (!arg || (*arg != '{')) /* parse message */
|
yuuji@0
|
4137 response = "%.80s BAD Missing literal in %.80s\015\012";
|
yuuji@0
|
4138 else if (!isdigit (arg[1]))
|
yuuji@0
|
4139 response = "%.80s BAD Missing message to %.80s\015\012";
|
yuuji@0
|
4140 else if (!(i = strtoul (arg+1,&t,10)))
|
yuuji@0
|
4141 response = "%.80s NO Empty message to %.80s\015\012";
|
yuuji@0
|
4142 else if (i > MAXAPPENDTXT) /* maybe relax this a little */
|
yuuji@0
|
4143 response = "%.80s NO Excessively large message to %.80s\015\012";
|
yuuji@0
|
4144 else if (((*t == '+') && (t[1] == '}') && !t[2]) || ((*t == '}') && !t[1])) {
|
yuuji@0
|
4145 /* get a literal buffer */
|
yuuji@0
|
4146 inliteral (ad->msg = (char *) fs_get (i+1),i);
|
yuuji@0
|
4147 /* get new command tail */
|
yuuji@0
|
4148 slurp (ad->arg,CMDLEN - (ad->arg - cmdbuf),INPUTTIMEOUT);
|
yuuji@0
|
4149 if (strchr (ad->arg,'\012')) {
|
yuuji@0
|
4150 /* reset strtok mechanism, tie off if done */
|
yuuji@0
|
4151 if (!strtok (ad->arg,"\015\012")) *ad->arg = '\0';
|
yuuji@0
|
4152 /* possible LITERAL+? */
|
yuuji@0
|
4153 if (((j = strlen (ad->arg)) > 3) && (ad->arg[j - 1] == '}') &&
|
yuuji@0
|
4154 (ad->arg[j - 2] == '+') && isdigit (ad->arg[j - 3])) {
|
yuuji@0
|
4155 /* back over possible count */
|
yuuji@0
|
4156 for (j -= 4; j && isdigit (ad->arg[j]); j--);
|
yuuji@0
|
4157 if (ad->arg[j] == '{') {/* found a literal? */
|
yuuji@0
|
4158 litplus.ok = T; /* yes, note LITERAL+ in effect, set size */
|
yuuji@0
|
4159 litplus.size = strtoul (ad->arg + j + 1,NIL,10);
|
yuuji@0
|
4160 }
|
yuuji@0
|
4161 }
|
yuuji@0
|
4162 /* initialize stringstruct */
|
yuuji@0
|
4163 INIT (ad->message,mail_string,(void *) ad->msg,i);
|
yuuji@0
|
4164 return LONGT; /* ready to go */
|
yuuji@0
|
4165 }
|
yuuji@0
|
4166 flush (); /* didn't find end of line? */
|
yuuji@0
|
4167 fs_give ((void **) &ad->msg);
|
yuuji@0
|
4168 }
|
yuuji@0
|
4169 else response = badarg; /* not a literal */
|
yuuji@0
|
4170 return NIL; /* error */
|
yuuji@0
|
4171 }
|
yuuji@0
|
4172
|
yuuji@0
|
4173 /* Got COPY UID data
|
yuuji@0
|
4174 * Accepts: MAIL stream
|
yuuji@0
|
4175 * mailbox name
|
yuuji@0
|
4176 * UID validity
|
yuuji@0
|
4177 * source set of UIDs
|
yuuji@0
|
4178 * destination set of UIDs
|
yuuji@0
|
4179 */
|
yuuji@0
|
4180
|
yuuji@0
|
4181 void copyuid (MAILSTREAM *stream,char *mailbox,unsigned long uidvalidity,
|
yuuji@0
|
4182 SEARCHSET *sourceset,SEARCHSET *destset)
|
yuuji@0
|
4183 {
|
yuuji@0
|
4184 if (cauidvalidity) fatal ("duplicate COPYUID/APPENDUID data");
|
yuuji@0
|
4185 cauidvalidity = uidvalidity;
|
yuuji@0
|
4186 csset = sourceset;
|
yuuji@0
|
4187 caset = destset;
|
yuuji@0
|
4188 }
|
yuuji@0
|
4189
|
yuuji@0
|
4190
|
yuuji@0
|
4191 /* Got APPEND UID data
|
yuuji@0
|
4192 * Accepts: mailbox name
|
yuuji@0
|
4193 * UID validity
|
yuuji@0
|
4194 * destination set of UIDs
|
yuuji@0
|
4195 */
|
yuuji@0
|
4196
|
yuuji@0
|
4197 void appenduid (char *mailbox,unsigned long uidvalidity,SEARCHSET *set)
|
yuuji@0
|
4198 {
|
yuuji@0
|
4199 copyuid (NIL,mailbox,uidvalidity,NIL,set);
|
yuuji@0
|
4200 }
|
yuuji@0
|
4201
|
yuuji@0
|
4202
|
yuuji@0
|
4203 /* Got a referral
|
yuuji@0
|
4204 * Accepts: MAIL stream
|
yuuji@0
|
4205 * URL
|
yuuji@0
|
4206 * referral type code
|
yuuji@0
|
4207 */
|
yuuji@0
|
4208
|
yuuji@0
|
4209 char *referral (MAILSTREAM *stream,char *url,long code)
|
yuuji@0
|
4210 {
|
yuuji@0
|
4211 if (lstref) fs_give ((void **) &lstref);
|
yuuji@0
|
4212 lstref = cpystr (url); /* set referral */
|
yuuji@0
|
4213 /* set error if not a logged in referral */
|
yuuji@0
|
4214 if (code != REFAUTH) response = lose;
|
yuuji@0
|
4215 if (!lsterr) lsterr = cpystr ("Try referral URL");
|
yuuji@0
|
4216 return NIL; /* don't chase referrals for now */
|
yuuji@0
|
4217 }
|
yuuji@0
|
4218
|
yuuji@0
|
4219 /* Co-routines from MAIL library */
|
yuuji@0
|
4220
|
yuuji@0
|
4221
|
yuuji@0
|
4222 /* Message matches a search
|
yuuji@0
|
4223 * Accepts: MAIL stream
|
yuuji@0
|
4224 * message number
|
yuuji@0
|
4225 */
|
yuuji@0
|
4226
|
yuuji@0
|
4227 void mm_searched (MAILSTREAM *s,unsigned long msgno)
|
yuuji@0
|
4228 {
|
yuuji@0
|
4229 /* nothing to do here */
|
yuuji@0
|
4230 }
|
yuuji@0
|
4231
|
yuuji@0
|
4232
|
yuuji@0
|
4233 /* Message exists (i.e. there are that many messages in the mailbox)
|
yuuji@0
|
4234 * Accepts: MAIL stream
|
yuuji@0
|
4235 * message number
|
yuuji@0
|
4236 */
|
yuuji@0
|
4237
|
yuuji@0
|
4238 void mm_exists (MAILSTREAM *s,unsigned long number)
|
yuuji@0
|
4239 {
|
yuuji@0
|
4240 /* note change in number of messages */
|
yuuji@0
|
4241 if ((s != tstream) && (nmsgs != number)) {
|
yuuji@0
|
4242 nmsgs = number; /* always update number of messages */
|
yuuji@0
|
4243 if (quell_events) existsquelled = T;
|
yuuji@0
|
4244 else {
|
yuuji@0
|
4245 PSOUT ("* ");
|
yuuji@0
|
4246 pnum (nmsgs);
|
yuuji@0
|
4247 PSOUT (" EXISTS\015\012");
|
yuuji@0
|
4248 }
|
yuuji@0
|
4249 recent = 0xffffffff; /* make sure update recent too */
|
yuuji@0
|
4250 }
|
yuuji@0
|
4251 }
|
yuuji@0
|
4252
|
yuuji@0
|
4253
|
yuuji@0
|
4254 /* Message expunged
|
yuuji@0
|
4255 * Accepts: MAIL stream
|
yuuji@0
|
4256 * message number
|
yuuji@0
|
4257 */
|
yuuji@0
|
4258
|
yuuji@0
|
4259 void mm_expunged (MAILSTREAM *s,unsigned long number)
|
yuuji@0
|
4260 {
|
yuuji@0
|
4261 if (quell_events) fatal ("Impossible EXPUNGE event");
|
yuuji@0
|
4262 if (s != tstream) {
|
yuuji@0
|
4263 PSOUT ("* ");
|
yuuji@0
|
4264 pnum (number);
|
yuuji@0
|
4265 PSOUT (" EXPUNGE\015\012");
|
yuuji@0
|
4266 }
|
yuuji@0
|
4267 nmsgs--;
|
yuuji@0
|
4268 existsquelled = T; /* do EXISTS when command done */
|
yuuji@0
|
4269 }
|
yuuji@0
|
4270
|
yuuji@0
|
4271
|
yuuji@0
|
4272 /* Message status changed
|
yuuji@0
|
4273 * Accepts: MAIL stream
|
yuuji@0
|
4274 * message number
|
yuuji@0
|
4275 */
|
yuuji@0
|
4276
|
yuuji@0
|
4277 void mm_flags (MAILSTREAM *s,unsigned long number)
|
yuuji@0
|
4278 {
|
yuuji@0
|
4279 if (s != tstream) mail_elt (s,number)->spare2 = T;
|
yuuji@0
|
4280 }
|
yuuji@0
|
4281
|
yuuji@0
|
4282 /* Mailbox found
|
yuuji@0
|
4283 * Accepts: hierarchy delimiter
|
yuuji@0
|
4284 * mailbox name
|
yuuji@0
|
4285 * attributes
|
yuuji@0
|
4286 */
|
yuuji@0
|
4287
|
yuuji@0
|
4288 void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
|
yuuji@0
|
4289 {
|
yuuji@0
|
4290 mm_list_work ("LIST",delimiter,name,attributes);
|
yuuji@0
|
4291 }
|
yuuji@0
|
4292
|
yuuji@0
|
4293
|
yuuji@0
|
4294 /* Subscribed mailbox found
|
yuuji@0
|
4295 * Accepts: hierarchy delimiter
|
yuuji@0
|
4296 * mailbox name
|
yuuji@0
|
4297 * attributes
|
yuuji@0
|
4298 */
|
yuuji@0
|
4299
|
yuuji@0
|
4300 void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
|
yuuji@0
|
4301 {
|
yuuji@0
|
4302 mm_list_work ("LSUB",delimiter,name,attributes);
|
yuuji@0
|
4303 }
|
yuuji@0
|
4304
|
yuuji@0
|
4305
|
yuuji@0
|
4306 /* Mailbox status
|
yuuji@0
|
4307 * Accepts: MAIL stream
|
yuuji@0
|
4308 * mailbox name
|
yuuji@0
|
4309 * mailbox status
|
yuuji@0
|
4310 */
|
yuuji@0
|
4311
|
yuuji@0
|
4312 void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
|
yuuji@0
|
4313 {
|
yuuji@0
|
4314 if (!quell_events) {
|
yuuji@0
|
4315 char tmp[MAILTMPLEN];
|
yuuji@0
|
4316 tmp[0] = tmp[1] = '\0';
|
yuuji@0
|
4317 if (status->flags & SA_MESSAGES)
|
yuuji@0
|
4318 sprintf (tmp + strlen (tmp)," MESSAGES %lu",status->messages);
|
yuuji@0
|
4319 if (status->flags & SA_RECENT)
|
yuuji@0
|
4320 sprintf (tmp + strlen (tmp)," RECENT %lu",status->recent);
|
yuuji@0
|
4321 if (status->flags & SA_UNSEEN)
|
yuuji@0
|
4322 sprintf (tmp + strlen (tmp)," UNSEEN %lu",status->unseen);
|
yuuji@0
|
4323 if (status->flags & SA_UIDNEXT)
|
yuuji@0
|
4324 sprintf (tmp + strlen (tmp)," UIDNEXT %lu",status->uidnext);
|
yuuji@0
|
4325 if (status->flags & SA_UIDVALIDITY)
|
yuuji@0
|
4326 sprintf (tmp + strlen(tmp)," UIDVALIDITY %lu",status->uidvalidity);
|
yuuji@0
|
4327 PSOUT ("* STATUS ");
|
yuuji@0
|
4328 pastring (mailbox);
|
yuuji@0
|
4329 PSOUT (" (");
|
yuuji@0
|
4330 PSOUT (tmp+1);
|
yuuji@0
|
4331 PBOUT (')');
|
yuuji@0
|
4332 CRLF;
|
yuuji@0
|
4333 }
|
yuuji@0
|
4334 }
|
yuuji@0
|
4335
|
yuuji@0
|
4336 /* Worker routine for LIST and LSUB
|
yuuji@0
|
4337 * Accepts: name of response
|
yuuji@0
|
4338 * hierarchy delimiter
|
yuuji@0
|
4339 * mailbox name
|
yuuji@0
|
4340 * attributes
|
yuuji@0
|
4341 */
|
yuuji@0
|
4342
|
yuuji@0
|
4343 void mm_list_work (char *what,int delimiter,char *name,long attributes)
|
yuuji@0
|
4344 {
|
yuuji@0
|
4345 char *s;
|
yuuji@0
|
4346 if (!quell_events) {
|
yuuji@0
|
4347 char tmp[MAILTMPLEN];
|
yuuji@0
|
4348 if (finding) {
|
yuuji@0
|
4349 PSOUT ("* MAILBOX ");
|
yuuji@0
|
4350 PSOUT (name);
|
yuuji@0
|
4351 }
|
yuuji@0
|
4352 /* new form */
|
yuuji@0
|
4353 else if ((cmd[0] == 'R') || !(attributes & LATT_REFERRAL)) {
|
yuuji@0
|
4354 PSOUT ("* ");
|
yuuji@0
|
4355 PSOUT (what);
|
yuuji@0
|
4356 PSOUT (" (");
|
yuuji@0
|
4357 tmp[0] = tmp[1] = '\0';
|
yuuji@0
|
4358 if (attributes & LATT_NOINFERIORS) strcat (tmp," \\NoInferiors");
|
yuuji@0
|
4359 if (attributes & LATT_NOSELECT) strcat (tmp," \\NoSelect");
|
yuuji@0
|
4360 if (attributes & LATT_MARKED) strcat (tmp," \\Marked");
|
yuuji@0
|
4361 if (attributes & LATT_UNMARKED) strcat (tmp," \\UnMarked");
|
yuuji@0
|
4362 if (attributes & LATT_HASCHILDREN) strcat (tmp," \\HasChildren");
|
yuuji@0
|
4363 if (attributes & LATT_HASNOCHILDREN) strcat (tmp," \\HasNoChildren");
|
yuuji@0
|
4364 PSOUT (tmp+1);
|
yuuji@0
|
4365 switch (delimiter) {
|
yuuji@0
|
4366 case '\\': /* quoted delimiter */
|
yuuji@0
|
4367 case '"':
|
yuuji@0
|
4368 PSOUT (") \"\\");
|
yuuji@0
|
4369 PBOUT (delimiter);
|
yuuji@0
|
4370 PBOUT ('"');
|
yuuji@0
|
4371 break;
|
yuuji@0
|
4372 case '\0': /* no delimiter */
|
yuuji@0
|
4373 PSOUT (") NIL");
|
yuuji@0
|
4374 break;
|
yuuji@0
|
4375 default: /* unquoted delimiter */
|
yuuji@0
|
4376 PSOUT (") \"");
|
yuuji@0
|
4377 PBOUT (delimiter);
|
yuuji@0
|
4378 PBOUT ('"');
|
yuuji@0
|
4379 break;
|
yuuji@0
|
4380 }
|
yuuji@0
|
4381 PBOUT (' ');
|
yuuji@0
|
4382 /* output mailbox name */
|
yuuji@0
|
4383 if (proxylist && (s = strchr (name,'}'))) pastring (s+1);
|
yuuji@0
|
4384 else pastring (name);
|
yuuji@0
|
4385 }
|
yuuji@0
|
4386 CRLF;
|
yuuji@0
|
4387 }
|
yuuji@0
|
4388 }
|
yuuji@0
|
4389
|
yuuji@0
|
4390 /* Notification event
|
yuuji@0
|
4391 * Accepts: MAIL stream
|
yuuji@0
|
4392 * string to log
|
yuuji@0
|
4393 * error flag
|
yuuji@0
|
4394 */
|
yuuji@0
|
4395
|
yuuji@0
|
4396 void mm_notify (MAILSTREAM *stream,char *string,long errflg)
|
yuuji@0
|
4397 {
|
yuuji@0
|
4398 SIZEDTEXT msg;
|
yuuji@0
|
4399 char *s,*code;
|
yuuji@0
|
4400 if (!quell_events && (!tstream || (stream != tstream))) {
|
yuuji@0
|
4401 switch (errflg) {
|
yuuji@0
|
4402 case NIL: /* information message, set as OK response */
|
yuuji@0
|
4403 if ((string[0] == '[') &&
|
yuuji@0
|
4404 ((string[1] == 'T') || (string[1] == 't')) &&
|
yuuji@0
|
4405 ((string[2] == 'R') || (string[2] == 'r')) &&
|
yuuji@0
|
4406 ((string[3] == 'Y') || (string[3] == 'y')) &&
|
yuuji@0
|
4407 ((string[4] == 'C') || (string[4] == 'c')) &&
|
yuuji@0
|
4408 ((string[5] == 'R') || (string[5] == 'r')) &&
|
yuuji@0
|
4409 ((string[6] == 'E') || (string[6] == 'e')) &&
|
yuuji@0
|
4410 ((string[7] == 'A') || (string[7] == 'a')) &&
|
yuuji@0
|
4411 ((string[8] == 'T') || (string[8] == 't')) &&
|
yuuji@0
|
4412 ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']'))
|
yuuji@0
|
4413 trycreate = T;
|
yuuji@0
|
4414 case BYE: /* some other server signing off */
|
yuuji@0
|
4415 case PARSE: /* parse glitch, output unsolicited OK */
|
yuuji@0
|
4416 code = "* OK ";
|
yuuji@0
|
4417 break;
|
yuuji@0
|
4418 case WARN: /* warning, output unsolicited NO (kludge!) */
|
yuuji@0
|
4419 code = "* NO ";
|
yuuji@0
|
4420 break;
|
yuuji@0
|
4421 case ERROR: /* error that broke command */
|
yuuji@0
|
4422 default: /* default should never happen */
|
yuuji@0
|
4423 code = "* BAD ";
|
yuuji@0
|
4424 break;
|
yuuji@0
|
4425 }
|
yuuji@0
|
4426 PSOUT (code);
|
yuuji@0
|
4427 msg.size = (s = strpbrk ((char *) (msg.data = (unsigned char *) string),
|
yuuji@0
|
4428 "\015\012")) ?
|
yuuji@0
|
4429 (s - string) : strlen (string);
|
yuuji@0
|
4430 PSOUTR (&msg);
|
yuuji@0
|
4431 CRLF;
|
yuuji@0
|
4432 PFLUSH (); /* let client see it immediately */
|
yuuji@0
|
4433 }
|
yuuji@0
|
4434 }
|
yuuji@0
|
4435
|
yuuji@0
|
4436 /* Log an event for the user to see
|
yuuji@0
|
4437 * Accepts: string to log
|
yuuji@0
|
4438 * error flag
|
yuuji@0
|
4439 */
|
yuuji@0
|
4440
|
yuuji@0
|
4441 void mm_log (char *string,long errflg)
|
yuuji@0
|
4442 {
|
yuuji@0
|
4443 SIZEDTEXT msg;
|
yuuji@0
|
4444 char *s;
|
yuuji@0
|
4445 msg.size =
|
yuuji@0
|
4446 (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
|
yuuji@0
|
4447 (s - string) : strlen (string);
|
yuuji@0
|
4448 switch (errflg) {
|
yuuji@0
|
4449 case NIL: /* information message, set as OK response */
|
yuuji@0
|
4450 if (response == win) { /* only if no other response yet */
|
yuuji@0
|
4451 if (lsterr) { /* if there was a previous message */
|
yuuji@0
|
4452 if (!quell_events) {
|
yuuji@0
|
4453 PSOUT ("* OK "); /* blat it out */
|
yuuji@0
|
4454 PSOUT (lsterr);
|
yuuji@0
|
4455 CRLF;
|
yuuji@0
|
4456 PFLUSH (); /* let client see it immediately */
|
yuuji@0
|
4457 }
|
yuuji@0
|
4458 fs_give ((void **) &lsterr);
|
yuuji@0
|
4459 }
|
yuuji@0
|
4460 lsterr = cpystr (string); /* copy string for later use */
|
yuuji@0
|
4461 if (s) lsterr[s - string] = NIL;
|
yuuji@0
|
4462 }
|
yuuji@0
|
4463 break;
|
yuuji@0
|
4464 case PARSE: /* parse glitch, output unsolicited OK */
|
yuuji@0
|
4465 if (!quell_events) {
|
yuuji@0
|
4466 PSOUT ("* OK [PARSE] ");
|
yuuji@0
|
4467 PSOUTR (&msg);
|
yuuji@0
|
4468 CRLF;
|
yuuji@0
|
4469 PFLUSH (); /* let client see it immediately */
|
yuuji@0
|
4470 }
|
yuuji@0
|
4471 break;
|
yuuji@0
|
4472 case WARN: /* warning, output unsolicited NO */
|
yuuji@0
|
4473 /* ignore "Mailbox is empty" (KLUDGE!) */
|
yuuji@0
|
4474 if (strcmp (string,"Mailbox is empty")) {
|
yuuji@0
|
4475 if (lstwrn) { /* have previous warning? */
|
yuuji@0
|
4476 if (!quell_events) {
|
yuuji@0
|
4477 PSOUT ("* NO ");
|
yuuji@0
|
4478 PSOUT (lstwrn);
|
yuuji@0
|
4479 CRLF;
|
yuuji@0
|
4480 PFLUSH (); /* make sure client sees it immediately */
|
yuuji@0
|
4481 }
|
yuuji@0
|
4482 fs_give ((void **) &lstwrn);
|
yuuji@0
|
4483 }
|
yuuji@0
|
4484 lstwrn = cpystr (string); /* note last warning */
|
yuuji@0
|
4485 if (s) lstwrn[s - string] = NIL;
|
yuuji@0
|
4486 }
|
yuuji@0
|
4487 break;
|
yuuji@0
|
4488 case ERROR: /* error that broke command */
|
yuuji@0
|
4489 default: /* default should never happen */
|
yuuji@0
|
4490 response = trycreate ? losetry : lose;
|
yuuji@0
|
4491 if (lsterr) fs_give ((void **) &lsterr);
|
yuuji@0
|
4492 lsterr = cpystr (string); /* note last error */
|
yuuji@0
|
4493 if (s) lsterr[s - string] = NIL;
|
yuuji@0
|
4494 break;
|
yuuji@0
|
4495 }
|
yuuji@0
|
4496 }
|
yuuji@0
|
4497
|
yuuji@0
|
4498 /* Return last error
|
yuuji@0
|
4499 */
|
yuuji@0
|
4500
|
yuuji@0
|
4501 char *lasterror (void)
|
yuuji@0
|
4502 {
|
yuuji@0
|
4503 if (lsterr) return lsterr;
|
yuuji@0
|
4504 if (lstwrn) return lstwrn;
|
yuuji@0
|
4505 return "<unknown>";
|
yuuji@0
|
4506 }
|
yuuji@0
|
4507
|
yuuji@0
|
4508
|
yuuji@0
|
4509 /* Log an event to debugging telemetry
|
yuuji@0
|
4510 * Accepts: string to log
|
yuuji@0
|
4511 */
|
yuuji@0
|
4512
|
yuuji@0
|
4513 void mm_dlog (char *string)
|
yuuji@0
|
4514 {
|
yuuji@0
|
4515 mm_log (string,WARN); /* shouldn't happen normally */
|
yuuji@0
|
4516 }
|
yuuji@0
|
4517
|
yuuji@0
|
4518 /* Get user name and password for this host
|
yuuji@0
|
4519 * Accepts: parse of network user name
|
yuuji@0
|
4520 * where to return user name
|
yuuji@0
|
4521 * where to return password
|
yuuji@0
|
4522 * trial count
|
yuuji@0
|
4523 */
|
yuuji@0
|
4524
|
yuuji@0
|
4525 void mm_login (NETMBX *mb,char *username,char *password,long trial)
|
yuuji@0
|
4526 {
|
yuuji@0
|
4527 /* set user name */
|
yuuji@0
|
4528 strncpy (username,*mb->user ? mb->user : (char *) user,NETMAXUSER);
|
yuuji@0
|
4529 strncpy (password,pass,256); /* and password */
|
yuuji@0
|
4530 }
|
yuuji@0
|
4531
|
yuuji@0
|
4532
|
yuuji@0
|
4533 /* About to enter critical code
|
yuuji@0
|
4534 * Accepts: stream
|
yuuji@0
|
4535 */
|
yuuji@0
|
4536
|
yuuji@0
|
4537 void mm_critical (MAILSTREAM *s)
|
yuuji@0
|
4538 {
|
yuuji@0
|
4539 ++critical;
|
yuuji@0
|
4540 }
|
yuuji@0
|
4541
|
yuuji@0
|
4542
|
yuuji@0
|
4543 /* About to exit critical code
|
yuuji@0
|
4544 * Accepts: stream
|
yuuji@0
|
4545 */
|
yuuji@0
|
4546
|
yuuji@0
|
4547 void mm_nocritical (MAILSTREAM *s)
|
yuuji@0
|
4548 {
|
yuuji@0
|
4549 /* go non-critical, pending death? */
|
yuuji@0
|
4550 if (!--critical && (state == LOGOUT)) {
|
yuuji@0
|
4551 /* clean up iff needed */
|
yuuji@0
|
4552 if (s && (stream != s) && !s->lock && (s->dtb->flags & DR_XPOINT))
|
yuuji@0
|
4553 s = mail_close (s);
|
yuuji@0
|
4554 longjmp (jmpenv,1); /* die now */
|
yuuji@0
|
4555 }
|
yuuji@0
|
4556 }
|
yuuji@0
|
4557
|
yuuji@0
|
4558 /* Disk error found
|
yuuji@0
|
4559 * Accepts: stream
|
yuuji@0
|
4560 * system error code
|
yuuji@0
|
4561 * flag indicating that mailbox may be clobbered
|
yuuji@0
|
4562 * Returns: abort flag
|
yuuji@0
|
4563 */
|
yuuji@0
|
4564
|
yuuji@0
|
4565 long mm_diskerror (MAILSTREAM *s,long errcode,long serious)
|
yuuji@0
|
4566 {
|
yuuji@0
|
4567 if (serious) { /* try your damnest if clobberage likely */
|
yuuji@0
|
4568 mm_notify (s,"Retrying to fix probable mailbox damage!",ERROR);
|
yuuji@0
|
4569 PFLUSH (); /* dump output buffer */
|
yuuji@0
|
4570 syslog (LOG_ALERT,
|
yuuji@0
|
4571 "Retrying after disk error user=%.80s host=%.80s mbx=%.80s: %.80s",
|
yuuji@0
|
4572 user ? (char *) user : "???",tcp_clienthost (),
|
yuuji@0
|
4573 (stream && stream->mailbox) ? stream->mailbox : "???",
|
yuuji@0
|
4574 strerror (errcode));
|
yuuji@0
|
4575 settimeout (0); /* make damn sure timeout disabled */
|
yuuji@0
|
4576 sleep (60); /* give it some time to clear up */
|
yuuji@0
|
4577 return NIL;
|
yuuji@0
|
4578 }
|
yuuji@0
|
4579 if (!quell_events) { /* otherwise die before more damage is done */
|
yuuji@0
|
4580 PSOUT ("* NO Disk error: ");
|
yuuji@0
|
4581 PSOUT (strerror (errcode));
|
yuuji@0
|
4582 CRLF;
|
yuuji@0
|
4583 }
|
yuuji@0
|
4584 return T;
|
yuuji@0
|
4585 }
|
yuuji@0
|
4586
|
yuuji@0
|
4587
|
yuuji@0
|
4588 /* Log a fatal error event
|
yuuji@0
|
4589 * Accepts: string to log
|
yuuji@0
|
4590 */
|
yuuji@0
|
4591
|
yuuji@0
|
4592 void mm_fatal (char *string)
|
yuuji@0
|
4593 {
|
yuuji@0
|
4594 SIZEDTEXT msg;
|
yuuji@0
|
4595 char *s;
|
yuuji@0
|
4596 msg.size =
|
yuuji@0
|
4597 (s = strpbrk ((char *) (msg.data = (unsigned char *) string),"\015\012")) ?
|
yuuji@0
|
4598 (s - string) : strlen (string);
|
yuuji@0
|
4599 if (!quell_events) {
|
yuuji@0
|
4600 PSOUT ("* BYE [ALERT] IMAP4rev1 server crashing: ");
|
yuuji@0
|
4601 PSOUTR (&msg);
|
yuuji@0
|
4602 CRLF;
|
yuuji@0
|
4603 PFLUSH ();
|
yuuji@0
|
4604 }
|
yuuji@0
|
4605 syslog (LOG_ALERT,"Fatal error user=%.80s host=%.80s mbx=%.80s: %.80s",
|
yuuji@0
|
4606 user ? (char *) user : "???",tcp_clienthost (),
|
yuuji@0
|
4607 (stream && stream->mailbox) ? stream->mailbox : "???",string);
|
yuuji@0
|
4608 }
|