Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/imap.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2012 The Regents of the University of California an*
5
* *
6
* Redistribution and use in source and binary forms, with or *
7
* without modification, are permitted provided that the following *
8
* conditions are met: *
9
* *
10
* 1. Redistributions of source code must retain the above *
11
* copyright notice, this list of conditions and the *
12
* following disclaimer. *
13
* *
14
* 2. Redistributions in binary form must reproduce the above *
15
* copyright notice, this list of conditions and the *
16
* following disclaimer in the documentation and/or other *
17
* materials provided with the distribution. *
18
* *
19
* 3. Neither the name of The Regents of the University of California*
20
* names of its contributors may be used to endorse or *
21
* promote products derived from this software without *
22
* specific prior written permission. *
23
* *
24
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
25
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
26
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
27
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
28
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS *
29
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
30
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED *
31
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, *
32
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON *
33
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
34
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY *
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
36
* POSSIBILITY OF SUCH DAMAGE. *
37
* *
38
* Redistribution and use in source and binary forms, with or without *
39
* modification, are permitted provided that the following conditions *
40
* are met: *
41
* 1. Redistributions of source code must retain the above copyright *
42
* notice, this list of conditions and the following disclaimer. *
43
* 2. Redistributions in binary form must reproduce the above copyright *
44
* notice, this list of conditions and the following disclaimer in *
45
* the documentation and/or other materials provided with the *
46
* distribution. *
47
* 3. Neither the name of the University nor the names of its *
48
* contributors may be used to endorse or promote products derived *
49
* from this software without specific prior written permission. *
50
* *
51
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" *
52
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
53
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
54
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS *
55
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *
56
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
57
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *
58
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
59
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *
60
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *
61
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *
62
* SUCH DAMAGE. *
63
* *
64
* Kurt Shoens (UCB) *
65
* gsf *
66
* *
67
***********************************************************************/
68
#pragma prototyped
69
/*
70
* IMAP4rev1 client
71
*
72
* Glenn Fowler
73
* AT&T Research
74
*/
75
76
#include "mailx.h"
77
78
#if _PACKAGE_ast
79
80
#include <css.h>
81
#include <tm.h>
82
#include <vmalloc.h>
83
84
#define imapfree(sp,op) ((op)->state=(op)->retain=0)
85
86
#define IMAP ((Imap_t*)state.msg.imap.state)
87
88
#define IMAP_VERSION 4 /* at least this */
89
#define IMAP_REVISION 1 /* at least this */
90
91
#define IMAP_data 1 /* data item */
92
#define IMAP_list 2 /* list item */
93
#define IMAP_name 3 /* name (atom) item */
94
#define IMAP_number 4 /* number item */
95
#define IMAP_string 5 /* string item */
96
97
struct Imaparg_s; typedef struct Imaparg_s Imaparg_t;
98
struct Imapblock_s; typedef struct Imapblock_s Imapblock_t;
99
struct Imappart_s; typedef struct Imappart_s Imappart_t;
100
101
typedef struct { /* IMAP_data item */
102
char* buffer;
103
size_t length;
104
} Imapdata_t;
105
106
typedef struct { /* IMAP_list item */
107
Imaparg_t* head;
108
Imaparg_t* tail;
109
} Imaplist_t;
110
111
typedef union { /* list item value */
112
Imapdata_t data;
113
Imaplist_t list;
114
long number;
115
char* name;
116
char* string;
117
} Imapvalue_t;
118
119
struct Imaparg_s { /* response arg list element */
120
Imaparg_t* next;
121
int type;
122
Imapvalue_t value;
123
};
124
125
typedef struct { /* flag table element */
126
const char* name; /* canonical flag name */
127
int code; /* IMAP_* code */
128
int flag; /* mailx M* flag */
129
} Imapflag_t;
130
131
typedef struct { /* lex table element */
132
const char* name; /* canonical name */
133
int code; /* IMAP_* code */
134
} Imaplex_t;
135
136
typedef struct { /* IMAP Msg_t.m_info info */
137
char* from; /* from address */
138
char* date; /* localized date */
139
char* subject; /* subject */
140
Imappart_t* parts; /* multipart parts */
141
int attachments; /* attachment count */
142
} Imapmsg_t;
143
144
struct Imappart_s { /* message part */
145
Imappart_t* next; /* next part */
146
short content; /* IMAP_CONTENT_* type */
147
short attachment; /* attachment ordinal */
148
int lines; /* # lines */
149
size_t size; /* # octets */
150
char* id; /* IMAP BODY[id] */
151
char* type; /* MIME type/subtype */
152
char* encoding; /* MIME encoding */
153
char* name; /* attachment name */
154
char* options; /* encoding options */
155
Imapmsg_t* msg; /* IMAP_CONTENT_message info */
156
};
157
158
typedef struct { /* imap_BODYSTRUCTURE discipline*/
159
Imappart_t* lastpart; /* part list tail */
160
int prestack; /* one before stack is ok */
161
int idstack[64]; /* for nested message ids */
162
int* id; /* top of idstack */
163
} Imapbody_t;
164
165
struct Imapblock_s { /* multiline response cache */
166
Imapblock_t* next; /* next in list */
167
size_t length; /* length of this block */
168
char* data; /* data for this block */
169
};
170
171
typedef struct { /* response info */
172
int state; /* IMAP_OP_* state */
173
int code; /* IMAP_* code */
174
int count; /* optional count prefix */
175
int retain; /* retain until imapfree() */
176
char msg[32]; /* first part of last message */
177
Vmalloc_t* vm; /* local store */
178
Imaplist_t args; /* arg list */
179
Imapblock_t* blocks; /* multiline cache */
180
} Imapop_t;
181
182
typedef struct
183
{
184
Imapop_t op[8]; /* pending ops */
185
Sfio_t* mp; /* tmp message stream */
186
Sfio_t* np; /* tmp normalization stream */
187
Sfio_t* rp; /* IMAP service recv stream */
188
Sfio_t* sp; /* IMAP service send stream */
189
Sfio_t* tp; /* tmp string stream */
190
Vmdisc_t vmdisc; /* vmopen() discipline */
191
Vmalloc_t* gm; /* IMAP global store */
192
Vmalloc_t* vm; /* IMAP mailbox store */
193
int auth; /* IMAP_AUTH_* methods */
194
int authenticated; /* connection authenticated */
195
int connected; /* connected to server */
196
int exiting; /* attempting to logout */
197
char* host; /* IMAP server host */
198
Msg_t* index; /* last SEARCH message index */
199
char* meth; /* IMAP authentication method */
200
int revision; /* IMAP service revision */
201
int selected; /* mailbox selected */
202
int tag; /* last op tag (index) */
203
char* user; /* IMAP user name */
204
int version; /* IMAP service version */
205
206
struct
207
{
208
Sfio_t* fp; /* output stream */
209
char* prefix; /* line prefix */
210
short prefixlen; /* line prefix length */
211
short emptylen; /* empty line prefix length */
212
} copy; /* imap_copy() state for FETCH */
213
214
struct
215
{
216
int delimiter; /* mailbox hierarchy delimiter */
217
int exists; /* # messages */
218
int recent; /* first recent message */
219
int read_only; /* no updates */
220
int trycreate; /* op may work after CREATE */
221
int uidnext; /* next expected message UID */
222
int uidvalidity; /* UID validation */
223
int unseen; /* first unseen message */
224
} mailbox; /* mailbox state */
225
} Imap_t;
226
227
/*
228
* imap dump <item> table
229
*/
230
231
#define IMAP_DUMP_FLAGS 1
232
#define IMAP_DUMP_FOLDER 2
233
#define IMAP_DUMP_MESSAGE 3
234
#define IMAP_DUMP_QUEUE 4
235
#define IMAP_DUMP_STATE 5
236
237
static const Imaplex_t imapdump[] =
238
{
239
"fl[ags]", IMAP_DUMP_FLAGS,
240
"f[older]", IMAP_DUMP_FOLDER,
241
"m[essage]", IMAP_DUMP_MESSAGE,
242
"q[ueue]", IMAP_DUMP_QUEUE,
243
"s[tate]", IMAP_DUMP_STATE,
244
};
245
246
/*
247
* operation states
248
*/
249
250
#define IMAP_STATE_free 0 /* must be 0 */
251
#define IMAP_STATE_sent 1
252
#define IMAP_STATE_more 2
253
#define IMAP_STATE_done 3
254
255
static const Imaplex_t imapstate[] =
256
{
257
"free", IMAP_STATE_free,
258
"sent", IMAP_STATE_sent,
259
"more", IMAP_STATE_more,
260
"done", IMAP_STATE_done,
261
};
262
263
/*
264
* part content types
265
*/
266
267
#define IMAP_CONTENT_data 0 /* must be 0 */
268
#define IMAP_CONTENT_attachment 1 /* part is an attachment */
269
#define IMAP_CONTENT_header 2 /* part is a header */
270
#define IMAP_CONTENT_message 3 /* part is an embeded message */
271
#define IMAP_CONTENT_multipart 4 /* part is an embeded message */
272
#define IMAP_CONTENT_text 5 /* part is plain text */
273
274
static const Imaplex_t imapcontent[] =
275
{
276
"attachment", IMAP_CONTENT_attachment,
277
"data", IMAP_CONTENT_data,
278
"header", IMAP_CONTENT_header,
279
"message", IMAP_CONTENT_message,
280
"multipart", IMAP_CONTENT_multipart,
281
"text", IMAP_CONTENT_text,
282
};
283
284
/*
285
* message flags
286
*/
287
288
static const Imaplex_t imapmflags[] =
289
{
290
"DELETE", MDELETE,
291
"INIT", MINIT,
292
"MARK", MMARK,
293
"MBOX", MBOX,
294
"MODIFY", MODIFY,
295
"NEW", MNEW,
296
"NONE", MNONE,
297
"PRESERVE", MPRESERVE,
298
"READ", MREAD,
299
"SAVE", MSAVE,
300
"SCAN", MSCAN,
301
"SPAM", MSPAM,
302
"STATUS", MSTATUS,
303
"TOUCH", MTOUCH,
304
"USED", MUSED,
305
"ZOMBIE", MZOMBIE,
306
};
307
308
/*
309
* the rest define the parts of the protocol used by this implementation
310
*/
311
312
#define IMAP_UNKNOWN 0 /* must be 0 */
313
#define IMAP_OK 1
314
#define IMAP_NO 2
315
#define IMAP_BAD 3
316
#define IMAP_BYE 4
317
#define IMAP_CAPABILITY 5
318
#define IMAP_COPY 6
319
#define IMAP_EXISTS 7
320
#define IMAP_EXPUNGE 8
321
#define IMAP_FETCH 9
322
#define IMAP_FLAGS 10
323
#define IMAP_LIST 11
324
#define IMAP_LSUB 12
325
#define IMAP_MORE 13
326
#define IMAP_PREAUTH 14
327
#define IMAP_RECENT 15
328
#define IMAP_SEARCH 16
329
330
static const Imaplex_t imapresponse[] =
331
{
332
"+", IMAP_MORE,
333
"BAD", IMAP_BAD,
334
"BYE", IMAP_BYE,
335
"CAPABILITY", IMAP_CAPABILITY,
336
"COPY", IMAP_COPY,
337
"EXISTS", IMAP_EXISTS,
338
"EXPUNGE", IMAP_EXPUNGE,
339
"FETCH", IMAP_FETCH,
340
"FLAGS", IMAP_FLAGS,
341
"LIST", IMAP_LIST,
342
"LSUB", IMAP_LSUB,
343
"NO", IMAP_NO,
344
"OK", IMAP_OK,
345
"PREAUTH", IMAP_PREAUTH,
346
"RECENT", IMAP_RECENT,
347
"SEARCH", IMAP_SEARCH,
348
"STORE", IMAP_FETCH,
349
"UNKNOWN", IMAP_UNKNOWN,
350
};
351
352
#define IMAP_AUTH_ANONYMOUS (1<<0)
353
#define IMAP_AUTH_KERBEROS_V4 (1<<1)
354
#define IMAP_AUTH_LOGIN (1<<2)
355
#define IMAP_AUTH_XFILE (1<<3)
356
357
static const Imaplex_t imapauth[] =
358
{
359
"ANONYMOUS", IMAP_AUTH_ANONYMOUS,
360
"KERBEROS_V4", IMAP_AUTH_KERBEROS_V4,
361
"LOGIN", IMAP_AUTH_LOGIN,
362
"XFILE", IMAP_AUTH_XFILE,
363
};
364
365
#define IMAP_STATUS_ALERT 1
366
#define IMAP_STATUS_NEWNAME 2
367
#define IMAP_STATUS_PARSE 3
368
#define IMAP_STATUS_PERMANENTFLAGS 4
369
#define IMAP_STATUS_READ_ONLY 5
370
#define IMAP_STATUS_READ_WRITE 6
371
#define IMAP_STATUS_TRYCREATE 7
372
#define IMAP_STATUS_UIDNEXT 8
373
#define IMAP_STATUS_UIDVALIDITY 9
374
#define IMAP_STATUS_UNSEEN 10
375
376
static const Imaplex_t imapstatus[] =
377
{
378
"ALERT", IMAP_STATUS_ALERT,
379
"NEWNAME", IMAP_STATUS_NEWNAME,
380
"PARSE", IMAP_STATUS_PARSE,
381
"PERMANENTFLAGS", IMAP_STATUS_PERMANENTFLAGS,
382
"READ-ONLY", IMAP_STATUS_READ_ONLY,
383
"READ-WRITE", IMAP_STATUS_READ_WRITE,
384
"TRYCREATE", IMAP_STATUS_TRYCREATE,
385
"UIDNEXT", IMAP_STATUS_UIDNEXT,
386
"UIDVALIDITY", IMAP_STATUS_UIDVALIDITY,
387
"UNSEEN", IMAP_STATUS_UNSEEN,
388
};
389
390
#define IMAP_FETCH_BODY 1
391
#define IMAP_FETCH_BODYSTRUCTURE 2
392
#define IMAP_FETCH_ENVELOPE 3
393
#define IMAP_FETCH_FLAGS 4
394
#define IMAP_FETCH_INTERNALDATE 5
395
#define IMAP_FETCH_RFC822_SIZE 6
396
#define IMAP_FETCH_UID 7
397
398
static const Imaplex_t imapfetch[] =
399
{
400
"BODY", IMAP_FETCH_BODY,
401
"BODYSTRUCTURE", IMAP_FETCH_BODYSTRUCTURE,
402
"ENVELOPE", IMAP_FETCH_ENVELOPE,
403
"FLAGS", IMAP_FETCH_FLAGS,
404
"INTERNALDATE", IMAP_FETCH_INTERNALDATE,
405
"RFC822.SIZE", IMAP_FETCH_RFC822_SIZE,
406
"UID", IMAP_FETCH_UID,
407
};
408
409
#define IMAP_FLAG_APPLICABLE (1<<0)
410
#define IMAP_FLAG_PERMANENT (1<<1)
411
412
#define IMAP_FLAG_ANSWERED (1<<2)
413
#define IMAP_FLAG_DELETED (1<<3)
414
#define IMAP_FLAG_DRAFT (1<<4)
415
#define IMAP_FLAG_FLAGGED (1<<5)
416
#define IMAP_FLAG_RECENT (1<<6)
417
#define IMAP_FLAG_SCAN (1<<7)
418
#define IMAP_FLAG_SEEN (1<<8)
419
#define IMAP_FLAG_SPAM (1<<9)
420
421
#define IMAP_FLAG_LOCAL (IMAP_FLAG_SCAN|IMAP_FLAG_SPAM)
422
423
static Imapflag_t imapflags[] =
424
{
425
"/Scan", IMAP_FLAG_SCAN, MSCAN,
426
"/Spam", IMAP_FLAG_SPAM, MSPAM,
427
"\\*", IMAP_FLAG_LOCAL, 0,
428
"\\Answered", IMAP_FLAG_ANSWERED, 0,
429
"\\Deleted", IMAP_FLAG_DELETED, MDELETE|MZOMBIE,
430
"\\Draft", IMAP_FLAG_DRAFT, 0,
431
"\\Flagged", IMAP_FLAG_FLAGGED, 0,
432
"\\Recent", IMAP_FLAG_RECENT, MNEW,
433
"\\Seen", IMAP_FLAG_SEEN, MREAD,
434
};
435
436
static int imapconnect(Imap_t*);
437
438
/*
439
* return the name for the given code in table tab with n elements
440
*/
441
442
static char*
443
imapname(register const Imaplex_t* tab, int n, int code)
444
{
445
register const Imaplex_t* end;
446
447
for (end = tab + n; tab < end; tab++)
448
if (code == tab->code)
449
return (char*)tab->name;
450
return "[ERROR]";
451
}
452
453
/*
454
* return the list of flag names set in flags in the string stream sp
455
*/
456
457
static char*
458
imapflagnames(register Sfio_t* sp, register const Imaplex_t* tab, int n, register int flags)
459
{
460
register const Imaplex_t* end;
461
register int sep;
462
463
sep = 0;
464
for (end = tab + n; tab < end; tab++)
465
if (flags & tab->code)
466
{
467
if (sep)
468
sfputc(sp, ' ');
469
else
470
sep = 1;
471
sfputr(sp, tab->name, -1);
472
}
473
if (!sep)
474
sfputc(sp, '0');
475
return struse(sp);
476
}
477
478
/*
479
* list arg value on sfstdout
480
*/
481
482
static void
483
imapdumparg(Imap_t* imap, Imaparg_t* ap, int level)
484
{
485
switch (ap->type)
486
{
487
case IMAP_data:
488
sfprintf(sfstdout, " {%d}%.*s", ap->value.data.length, ap->value.data.length, ap->value.data.buffer);
489
break;
490
case IMAP_list:
491
if (ap = ap->value.list.head)
492
{
493
sfprintf(sfstdout, " (");
494
do {
495
imapdumparg(imap, ap, level + 1);
496
} while (ap = ap->next);
497
sfprintf(sfstdout, " )");
498
}
499
else
500
sfprintf(sfstdout, " NIL");
501
break;
502
case IMAP_name:
503
sfprintf(sfstdout, " %s", ap->value.name);
504
break;
505
case IMAP_number:
506
sfprintf(sfstdout, " %ld", ap->value.number);
507
break;
508
case IMAP_string:
509
sfprintf(sfstdout, " \"%s\"", ap->value.string);
510
break;
511
}
512
if (!level)
513
sfprintf(sfstdout, "\n");
514
}
515
516
/*
517
* set mailbox flag capabilities
518
*/
519
520
static void
521
imapset(Imap_t* imap, register Imaparg_t* ap, int code)
522
{
523
register int i;
524
register Imapflag_t* fp;
525
526
for (i = 0; i < elementsof(imapflags); i++)
527
imapflags[i].code &= ~code;
528
for (; ap; ap = ap->next)
529
if (ap->type == IMAP_name && (fp = (Imapflag_t*)strsearch(imapflags, elementsof(imapflags), sizeof(imapflags[0]), stracmp, ap->value.name, NiL)))
530
{
531
if (fp->code == IMAP_FLAG_LOCAL)
532
for (i = 0; i < elementsof(imapflags); i++)
533
if (imapflags[i].code & IMAP_FLAG_LOCAL)
534
imapflags[i].code |= code;
535
fp->code |= code;
536
}
537
}
538
539
/*
540
* parse a response arg
541
*/
542
543
static char*
544
imapgetarg(Imap_t* imap, register Imapop_t* op, register Imaplist_t* lp, int* ep, register char* s)
545
{
546
register int c;
547
register char* b;
548
register Imaparg_t* ap;
549
register int eol;
550
int m;
551
char* e;
552
553
eol = ep ? *ep : 0;
554
for (;;)
555
{
556
for (; isspace(*s); s++);
557
if (*s)
558
{
559
if (*s == eol)
560
{
561
*ep = 0;
562
return s + 1;
563
}
564
if (!(ap = vmnewof(op->vm, 0, Imaparg_t, 1, 0)))
565
note(FATAL|SYSTEM, "Out of space [imap token]");
566
if (lp->tail)
567
lp->tail->next = ap;
568
else
569
lp->head = ap;
570
lp->tail = ap;
571
if (!eol && op->code <= IMAP_BYE && *s != '[')
572
{
573
ap->type = IMAP_string;
574
ap->value.string = s;
575
return 0;
576
}
577
switch (*s)
578
{
579
case '{':
580
ap->type = IMAP_data;
581
ap->value.data.length = strtol(s + 1, &e, 10);
582
if (*e != '}' || !op->blocks || !(op->blocks = op->blocks->next))
583
goto name;
584
ap->value.data.buffer = op->blocks->data;
585
return e + 1;
586
case '"':
587
for (b = ++s; *s && *s != '"'; s++);
588
if (*s)
589
*s++ = 0;
590
ap->type = IMAP_string;
591
ap->value.string = b;
592
return s;
593
case '[':
594
m = ']';
595
goto list;
596
case '(':
597
m = ')';
598
list:
599
ap->type = IMAP_list;
600
ap->value.list.head = ap->value.list.tail = 0;
601
s++;
602
while ((s = imapgetarg(imap, op, &ap->value.list, &m, s)) && m);
603
return s;
604
case '0':
605
case '1':
606
case '2':
607
case '3':
608
case '4':
609
case '5':
610
case '6':
611
case '7':
612
case '8':
613
case '9':
614
ap->type = IMAP_number;
615
ap->value.number = strtol(s, &e, 10);
616
return e;
617
case 'N':
618
if (*(s + 1) == 'I' && *(s + 2) == 'L' && !isalnum(*(s + 3)))
619
{
620
ap->type = IMAP_list;
621
ap->value.list.head = 0;
622
return s + 3;
623
}
624
/*FALLTHROUGH*/
625
default:
626
name:
627
m = 0;
628
for (b = s; (c = *s); s++)
629
if (c == '[')
630
m = c;
631
else if (c == ']')
632
m = 0;
633
else if (m == 0)
634
{
635
if (c == eol)
636
{
637
*ep = 0;
638
break;
639
}
640
else if (isspace(c))
641
break;
642
}
643
if (c)
644
*s++ = 0;
645
ap->type = IMAP_name;
646
ap->value.name = b;
647
return s;
648
}
649
}
650
if (!op->blocks || !(op->blocks = op->blocks->next))
651
break;
652
s = op->blocks->data;
653
}
654
return 0;
655
}
656
657
/*
658
* output an arg value converting \r\n => \n
659
*/
660
661
static void
662
imapputarg(register Imap_t* imap, register Imaparg_t* ap)
663
{
664
register char* e;
665
register char* s;
666
register char* t;
667
register int n;
668
669
switch (ap->type)
670
{
671
case IMAP_data:
672
t = s = ap->value.data.buffer;
673
e = s + ap->value.data.length;
674
while (t = (char*)memchr(t, '\r', e - t))
675
{
676
if (++t >= e)
677
break;
678
if (*t != '\n')
679
continue;
680
*(t - 1) = '\n';
681
n = t - s;
682
if (imap->copy.prefix)
683
sfwrite(imap->copy.fp, imap->copy.prefix, n > 1 ? imap->copy.prefixlen : imap->copy.emptylen);
684
sfwrite(imap->copy.fp, s, n);
685
if ((s = ++t) >= e)
686
break;
687
}
688
if ((n = e - s) > 0)
689
{
690
if (imap->copy.prefix)
691
sfwrite(imap->copy.fp, imap->copy.prefix, n > 1 ? imap->copy.prefixlen : imap->copy.emptylen);
692
sfwrite(imap->copy.fp, s, e - s);
693
}
694
break;
695
case IMAP_string:
696
sfputr(imap->copy.fp, ap->value.string, '\n');
697
break;
698
}
699
}
700
701
/*
702
* read and parse an IMAP server response
703
*/
704
705
static Imapop_t*
706
imapop(register Imap_t* imap)
707
{
708
register char* s;
709
register char* z;
710
register size_t n;
711
register Imapop_t* op;
712
register Imapblock_t* bp;
713
register Imapblock_t* bt;
714
char* b;
715
char* e;
716
Imapblock_t* bh;
717
Imaplex_t* xp;
718
719
/*
720
* read the first (possibly only) line of the response
721
*/
722
723
if (!(s = sfgetr(imap->rp, '\n', 1)))
724
return 0;
725
if ((z = s + sfvalue(imap->rp)) > s && --z > s && *(z - 1) == '\r')
726
*--z = 0;
727
if (TRACING('m'))
728
note(ERROR, "imap: mesg %s", s);
729
730
/*
731
* tag or *
732
*/
733
734
for (; isspace(*s); s++);
735
if (*s == '*')
736
{
737
for (; *++s && !isspace(*s););
738
op = imap->op;
739
op->state = IMAP_STATE_sent;
740
}
741
else
742
{
743
n = (int)strtol(s, &e, 10);
744
s = e;
745
op = imap->op + n;
746
}
747
if (op->state != IMAP_STATE_sent)
748
note(ERROR, "imap: %d: internal error -- unknown operation", n);
749
750
/*
751
* clear/allocate the local response vm
752
*/
753
754
if (op->vm)
755
vmclear(op->vm);
756
else if (!(op->vm = vmopen(&imap->vmdisc, Vmlast, 0)))
757
note(FATAL|SYSTEM, "Out of space [imap vm]");
758
759
/*
760
* optional count
761
*/
762
763
for (; isspace(*s); s++);
764
if (isdigit(*s))
765
{
766
n = (int)strtol(s, &e, 10);
767
for (s = e; isspace(*s); s++);
768
}
769
else
770
n = 0;
771
772
/*
773
* response code
774
*/
775
776
for (b = s; *s && !isspace(*s); s++);
777
if (*s)
778
for (*s++ = 0; isspace(*s); s++);
779
op->code = (xp = (Imaplex_t*)strsearch(imapresponse, elementsof(imapresponse), sizeof(Imaplex_t), stracmp, b, NiL)) ? xp->code : IMAP_UNKNOWN;
780
op->count = n;
781
if (TRACING('r'))
782
note(ERROR, "imap: recv %d %s %d %s", op - imap->op, imapname(imapresponse, elementsof(imapresponse), op->code), op->count, s);
783
784
/*
785
* if the message has imbedded literals there's
786
* more input to consume and we have to cache what's
787
* already been read
788
*/
789
790
bt = 0;
791
while (z > s && *(z - 1) == '}')
792
{
793
n = z - s + 1;
794
if (!(bp = vmnewof(op->vm, 0, Imapblock_t, 1, n)))
795
note(FATAL|SYSTEM, "Out of space [imap block]");
796
memcpy(bp->data = (char*)(bp + 1), s, n);
797
if (bt)
798
bt->next = bp;
799
else
800
bh = bp;
801
bt = bp;
802
for (z -= 2; z > s && *z != '{'; z--);
803
n = (size_t)strtol(z + 1, NiL, 10);
804
if (!(bp = vmnewof(op->vm, 0, Imapblock_t, 1, n)))
805
note(FATAL|SYSTEM, "Out of space [imap block]");
806
if ((bp->length = n) && sfread(imap->rp, bp->data = (char*)(bp + 1), n) != n)
807
return 0;
808
bt = bt->next = bp;
809
if (!(s = sfgetr(imap->rp, '\n', 1)))
810
return 0;
811
if ((z = s + sfvalue(imap->rp)) > s && --z > s && *(z - 1) == '\r')
812
*--z = 0;
813
if (TRACING('m'))
814
note(ERROR, "imap: mesg %s", s);
815
}
816
if (bt)
817
{
818
if (!(bp = vmnewof(op->vm, 0, Imapblock_t, 1, n)))
819
note(FATAL|SYSTEM, "Out of space [imap block]");
820
bp->length = z - s;
821
bp->data = s;
822
bt->next = bp;
823
s = bh->data;
824
}
825
else
826
bh = 0;
827
op->blocks = bh;
828
op->args.head = op->args.tail = 0;
829
while (s = imapgetarg(imap, op, &op->args, NiL, s));
830
return op;
831
}
832
833
/*
834
* handle EXPUNGE response
835
*/
836
837
static void
838
imap_EXPUNGE(Imap_t* imap, register Imapop_t* op)
839
{
840
}
841
842
/*
843
* consume ENVELOPE response
844
*/
845
846
static void
847
imap_ENVELOPE(register Imap_t* imap, register Imaparg_t* vp, Msg_t* mp)
848
{
849
register Imapmsg_t* ip;
850
register char* s;
851
852
ip = (Imapmsg_t*)mp->m_info;
853
if (!vp)
854
return;
855
if (s = vp->value.string)
856
{
857
s = fmttime("%a %b %e %H:%M %Z %Y", tmdate(s, NiL, NiL));
858
if (ip->date)
859
strcpy(ip->date, s);
860
else
861
ip->date = vmstrdup(imap->vm, s);
862
}
863
if (!(vp = vp->next))
864
return;
865
if (s = vp->value.string)
866
ip->subject = vmstrdup(imap->vm, s);
867
if (!(vp = vp->next) ||
868
!(vp = vp->value.list.head) ||
869
!(vp = vp->value.list.head) ||
870
!(vp = vp->next) ||
871
!(vp = vp->next))
872
return;
873
ip->from = vp->value.string;
874
if (!(vp = vp->next))
875
return;
876
if ((s = vp->value.string) && state.var.local)
877
s = localize(s);
878
if (s && *s)
879
{
880
sfprintf(imap->tp, "%s@%s", ip->from, s);
881
ip->from = struse(imap->tp);
882
}
883
ip->from = vmstrdup(imap->vm, ip->from);
884
}
885
886
/*
887
* consume BODYSTRUCTURE response
888
*/
889
890
static void
891
imap_BODYSTRUCTURE(register Imap_t* imap, register Imaparg_t* ap, register Msg_t* mp, register Imapbody_t* bp)
892
{
893
register char* s;
894
register Imappart_t* pp;
895
register Imaparg_t* vp;
896
int* id;
897
898
if (!ap)
899
return;
900
if (!(pp = vmnewof(imap->vm, 0, Imappart_t, 1, 0)))
901
note(FATAL, "out of space [imap part]");
902
if (bp->lastpart)
903
bp->lastpart->next = pp;
904
else
905
((Imapmsg_t*)mp->m_info)->parts = pp;
906
bp->lastpart = pp;
907
(*bp->id)++;
908
if (ap->type == IMAP_list)
909
{
910
pp->content = IMAP_CONTENT_multipart;
911
if (bp->id >= &bp->idstack[elementsof(bp->idstack) - 1])
912
note(FATAL, "imap: multipart nesting too deep -- %d max", elementsof(bp->idstack));
913
*++bp->id = 0;
914
do {
915
imap_BODYSTRUCTURE(imap, ap->value.list.head, mp, bp);
916
} while ((ap = ap->next) && ap->type == IMAP_list);
917
bp->id--;
918
sfputr(imap->tp, "multipart", -1);
919
if (ap && ap->type == IMAP_string)
920
sfprintf(imap->tp, "/%s", strlower(ap->value.string));
921
pp->type = vmstrdup(imap->vm, struse(imap->tp));
922
pp->id = "";
923
}
924
else
925
{
926
id = bp->idstack;
927
sfprintf(imap->tp, "%d", *id);
928
while (++id <= bp->id)
929
sfprintf(imap->tp, ".%d", *id);
930
pp->id = vmstrdup(imap->vm, struse(imap->tp));
931
if (s = ap->value.string)
932
{
933
s = strlower(s);
934
if (streq(s, "text"))
935
pp->content = IMAP_CONTENT_text;
936
else if (streq(s, "message"))
937
pp->content = IMAP_CONTENT_message;
938
else
939
pp->content = IMAP_CONTENT_data;
940
sfputr(imap->tp, s, -1);
941
}
942
if ((ap = ap->next) && (s = ap->value.string))
943
sfprintf(imap->tp, "/%s", strlower(s));
944
pp->type = vmstrdup(imap->vm, struse(imap->tp));
945
if (!ap || !(ap = ap->next))
946
return;
947
if (vp = ap->value.list.head)
948
do {
949
s = vp->value.string;
950
if (!(vp = vp->next))
951
break;
952
if (s && streq(s, "NAME") && vp->value.string)
953
{
954
pp->name = vmstrdup(imap->vm, vp->value.string);
955
break;
956
}
957
} while (vp = vp->next);
958
if (!(ap = ap->next) ||
959
!(ap = ap->next) ||
960
!(ap = ap->next))
961
return;
962
if (ap->type == IMAP_string && ap->value.string)
963
pp->encoding = vmstrdup(imap->vm, strlower(ap->value.string));
964
if (!(ap = ap->next))
965
return;
966
pp->size = ap->value.number;
967
if (!(ap = ap->next))
968
return;
969
switch (pp->content)
970
{
971
case IMAP_CONTENT_message:
972
if (bp->id >= &bp->idstack[elementsof(bp->idstack) - 1])
973
note(FATAL, "imap: message nesting too deep -- %d max", elementsof(bp->idstack));
974
*++bp->id = 0;
975
imap_ENVELOPE(imap, ap->value.list.head, mp);
976
ap = ap->next;
977
imap_BODYSTRUCTURE(imap, ap->value.list.head, mp, bp);
978
bp->id--;
979
break;
980
case IMAP_CONTENT_text:
981
pp->lines = ap->value.number;
982
if (!pp->name)
983
{
984
if (!(ap = ap->next) || !(ap = ap->next))
985
return;
986
if (ap->type == IMAP_list &&
987
(ap = ap->value.list.head) &&
988
(s = ap->value.string) &&
989
streq(s, "ATTACHMENT") &&
990
(ap = ap->next) &&
991
(ap = ap->value.list.head))
992
while ((s = ap->value.name) &&
993
(ap = ap->next))
994
{
995
if (streq(s, "FILENAME"))
996
{
997
pp->name = vmstrdup(imap->vm, ap->value.string);
998
break;
999
}
1000
if (!(ap = ap->next))
1001
break;
1002
}
1003
}
1004
break;
1005
case IMAP_CONTENT_data:
1006
if (!pp->name)
1007
{
1008
sfprintf(imap->tp, "%d.att", ((Imapmsg_t*)mp->m_info)->attachments + 1);
1009
pp->name = vmstrdup(imap->vm, struse(imap->tp));
1010
}
1011
break;
1012
}
1013
if (pp->name)
1014
{
1015
mp->m_lines += 2;
1016
pp->content = IMAP_CONTENT_attachment;
1017
pp->attachment = ++((Imapmsg_t*)mp->m_info)->attachments;
1018
}
1019
else
1020
mp->m_lines += pp->lines;
1021
}
1022
}
1023
1024
/*
1025
* handle FETCH response
1026
*/
1027
1028
static void
1029
imap_FETCH(Imap_t* imap, register Imapop_t* op)
1030
{
1031
register Msg_t* mp;
1032
register Imaparg_t* ap;
1033
register Imaparg_t* vp;
1034
register Imapmsg_t* ip;
1035
Imaplex_t* xp;
1036
Imapflag_t* fp;
1037
char* s;
1038
Imapbody_t body;
1039
1040
if (op->count > state.msg.count)
1041
note(FATAL, "imap: %d: unknown message -- expected %d max", op->count, state.msg.count);
1042
mp = state.msg.list + op->count - 1;
1043
ip = (Imapmsg_t*)mp->m_info;
1044
if (ap = op->args.head)
1045
{
1046
if (ap->type == IMAP_list)
1047
ap = ap->value.list.head;
1048
while (ap)
1049
{
1050
if (ap->type != IMAP_name)
1051
xp = 0;
1052
else if (!(xp = (Imaplex_t*)strsearch(imapfetch, elementsof(imapfetch), sizeof(Imaplex_t), stracmp, ap->value.name, NiL)))
1053
{
1054
if (s = strchr(ap->value.name, '['))
1055
{
1056
*s = 0;
1057
xp = (Imaplex_t*)strsearch(imapfetch, elementsof(imapfetch), sizeof(Imaplex_t), stracmp, ap->value.name, NiL);
1058
*s = '[';
1059
}
1060
if ((TRACING('u')) && !xp)
1061
note(ERROR, "imap: %s: unknown response", ap->value.name);
1062
}
1063
if (ap = ap->next)
1064
{
1065
if (xp) switch (xp->code)
1066
{
1067
case IMAP_FETCH_BODY:
1068
imapputarg(imap, ap);
1069
break;
1070
case IMAP_FETCH_BODYSTRUCTURE:
1071
memset(&body, 0, sizeof(body));
1072
body.id = body.idstack - 1;
1073
*body.idstack = 1;
1074
imap_BODYSTRUCTURE(imap, ap->value.list.head, mp, &body);
1075
break;
1076
case IMAP_FETCH_ENVELOPE:
1077
imap_ENVELOPE(imap, ap->value.list.head, mp);
1078
break;
1079
case IMAP_FETCH_FLAGS:
1080
for (vp = ap->value.list.head; vp; vp = vp->next)
1081
if (vp->type == IMAP_name && (fp = (Imapflag_t*)strsearch(imapflags, elementsof(imapflags), sizeof(imapflags[0]), stracmp, vp->value.name, NiL)))
1082
mp->m_flag |= fp->flag;
1083
break;
1084
case IMAP_FETCH_INTERNALDATE:
1085
if (!ip->date && (s = ap->value.string))
1086
ip->date = vmstrdup(imap->vm, fmttime("%a %b %e %H:%M %Z %Y", tmdate(s, NiL, NiL)));
1087
break;
1088
case IMAP_FETCH_RFC822_SIZE:
1089
mp->m_size = ap->value.number;
1090
break;
1091
}
1092
else if (TRACING('u'))
1093
note(ERROR, "imap: %s: unknown FETCH response", ap->value.name);
1094
ap = ap->next;
1095
}
1096
}
1097
}
1098
}
1099
1100
/*
1101
* sync the state.msg.list from the server
1102
*/
1103
1104
static void
1105
imapsync(register Imap_t* imap)
1106
{
1107
register Msg_t* mp;
1108
int dot;
1109
1110
dot = state.msg.dot ? (state.msg.dot - state.msg.list) : 0;
1111
while (state.msg.count < imap->mailbox.exists)
1112
{
1113
mp = newmsg(0);
1114
if (state.msg.count < imap->mailbox.unseen || !imap->mailbox.unseen)
1115
mp->m_flag = MUSED|MREAD;
1116
}
1117
if (imap->mailbox.unseen < 0)
1118
imap->mailbox.unseen = 1;
1119
else if (imap->mailbox.unseen > state.msg.count)
1120
imap->mailbox.unseen = state.msg.count;
1121
state.msg.dot = state.msg.list + dot;
1122
}
1123
1124
/*
1125
* consume IMAP server info until tag is complete
1126
* tag<0 for any tag completed
1127
*/
1128
1129
static Imapop_t*
1130
imaprecv(register Imap_t* imap, register Imapop_t* wp)
1131
{
1132
register int n;
1133
register Imapop_t* op;
1134
register Imaparg_t* ap;
1135
register Imaparg_t* sp;
1136
Imaplex_t* xp;
1137
int to;
1138
int i;
1139
char* s;
1140
1141
if (!wp || wp == imap->op)
1142
{
1143
for (op = imap->op + 1;; op++)
1144
if (op >= &imap->op[elementsof(imap->op)])
1145
return 0;
1146
else if (op->state == IMAP_STATE_sent)
1147
break;
1148
}
1149
if (!wp)
1150
to = 0;
1151
else if (wp->state != IMAP_STATE_sent && wp != imap->op)
1152
{
1153
if (wp->code)
1154
return wp;
1155
note(ERROR, "imap: %d: operation never sent", wp - imap->op);
1156
return 0;
1157
}
1158
else
1159
to = -1;
1160
for (;;)
1161
{
1162
switch (sfpoll(&imap->rp, 1, to))
1163
{
1164
case 0:
1165
if (wp)
1166
note(ERROR, "imap: %d: operation not completed", wp - imap->op);
1167
break;
1168
case 1:
1169
if (op = imapop(imap))
1170
{
1171
if (TRACING('a'))
1172
{
1173
note(ERROR, "imap: exec op %d count %d", op->code, op->count);
1174
for (ap = op->args.head; ap; ap = ap->next)
1175
imapdumparg(imap, ap, 0);
1176
}
1177
switch (op->code)
1178
{
1179
case IMAP_BAD:
1180
case IMAP_NO:
1181
n = 1;
1182
goto status;
1183
case IMAP_BYE:
1184
n = !imap->exiting;
1185
imap->connected = 0;
1186
wp = op;
1187
goto status;
1188
case IMAP_CAPABILITY:
1189
for (ap = op->args.head; ap; ap = ap->next)
1190
if (ap->type == IMAP_name)
1191
{
1192
s = ap->value.name;
1193
/*UNDENT...*/
1194
1195
if (strneq(s, "IMAP", 4))
1196
{
1197
s += 4;
1198
if ((i = (int)strtol(s, &s, 10)) >= imap->version)
1199
{
1200
if (i > imap->version)
1201
imap->revision = 0;
1202
imap->version = i;
1203
for (; *s && !isdigit(*s); s++);
1204
i = (int)strtol(s, NiL, 10);
1205
if (i > imap->revision)
1206
imap->revision = i;
1207
}
1208
}
1209
else if (strneq(s, "AUTH=", 5))
1210
if (xp = (Imaplex_t*)strsearch(imapauth, elementsof(imapauth), sizeof(Imaplex_t), stracmp, s + 5, NiL))
1211
imap->auth |= xp->code;
1212
1213
/*...INDENT*/
1214
}
1215
break;
1216
case IMAP_EXISTS:
1217
if (imap->mailbox.exists < 0)
1218
imap->mailbox.exists = op->count;
1219
else
1220
{
1221
n = op->count - imap->mailbox.exists;
1222
if (n > 0)
1223
note(ERROR, "(NOTE %d new message%s incorporated)", n, n == 1 ? "" : "s");
1224
else if (n < 0)
1225
{
1226
n = -n;
1227
note(ERROR, "(NOTE %d message%s delected by another session)", n, n == 1 ? "" : "s");
1228
vmclear(imap->vm);
1229
state.msg.count = 0;
1230
}
1231
imap->mailbox.exists = op->count;
1232
imapsync(imap);
1233
}
1234
break;
1235
case IMAP_EXPUNGE:
1236
imap_EXPUNGE(imap, op);
1237
break;
1238
case IMAP_FETCH:
1239
imap_FETCH(imap, op);
1240
break;
1241
case IMAP_FLAGS:
1242
if ((ap = op->args.head) && ap->type == IMAP_list)
1243
imapset(imap, ap->value.list.head, IMAP_FLAG_APPLICABLE);
1244
break;
1245
case IMAP_LIST:
1246
ap = op->args.head;
1247
ap = ap->next;
1248
ap = ap->next;
1249
sfprintf(sfstdout, "@%s\n", ap->value.string);
1250
break;
1251
case IMAP_OK:
1252
n = 0;
1253
status:
1254
/*
1255
* check for status
1256
*/
1257
if (ap = op->args.head)
1258
{
1259
if (ap->type == IMAP_list && (sp = ap->value.list.head) && sp->type == IMAP_name && (xp = (Imaplex_t*)strsearch(imapstatus, elementsof(imapstatus), sizeof(Imaplex_t), stracmp, sp->value.name, NiL)))
1260
{
1261
/*UNDENT...*/
1262
1263
ap = ap->next;
1264
sp = sp->next;
1265
switch (xp->code)
1266
{
1267
case IMAP_STATUS_ALERT:
1268
case IMAP_STATUS_NEWNAME:
1269
case IMAP_STATUS_PARSE:
1270
n = 1;
1271
break;
1272
case IMAP_STATUS_PERMANENTFLAGS:
1273
if (sp && sp->type == IMAP_list)
1274
imapset(imap, sp->value.list.head, IMAP_FLAG_PERMANENT);
1275
break;
1276
case IMAP_STATUS_READ_ONLY:
1277
imap->mailbox.read_only = 1;
1278
break;
1279
case IMAP_STATUS_READ_WRITE:
1280
imap->mailbox.read_only = 0;
1281
break;
1282
case IMAP_STATUS_TRYCREATE:
1283
imap->mailbox.trycreate = 1;
1284
break;
1285
case IMAP_STATUS_UIDNEXT:
1286
imap->mailbox.uidnext = sp && sp->type == IMAP_number ? sp->value.number : 0;
1287
break;
1288
case IMAP_STATUS_UIDVALIDITY:
1289
imap->mailbox.uidvalidity = sp && sp->type == IMAP_number ? sp->value.number : 0;
1290
break;
1291
case IMAP_STATUS_UNSEEN:
1292
imap->mailbox.unseen = sp && sp->type == IMAP_number ? sp->value.number : 0;
1293
break;
1294
}
1295
1296
/*...INDENT*/
1297
}
1298
if (n && ap && ap->type == IMAP_string)
1299
note(ERROR, "imap: %s", ap->value.string);
1300
}
1301
break;
1302
case IMAP_PREAUTH:
1303
imap->authenticated = 1;
1304
break;
1305
case IMAP_RECENT:
1306
imap->mailbox.recent = op->count;
1307
break;
1308
case IMAP_SEARCH:
1309
for (ap = op->args.head; ap; ap = ap->next)
1310
if (ap->type == IMAP_number)
1311
{
1312
imap->index->m_index = ap->value.number;
1313
imap->index++;
1314
}
1315
break;
1316
}
1317
if (!op->retain)
1318
imapfree(imap, op);
1319
if (wp == op || wp == imap->op)
1320
return op;
1321
}
1322
continue;
1323
default:
1324
note(ERROR, "imap: lost server connection");
1325
break;
1326
}
1327
break;
1328
}
1329
return 0;
1330
}
1331
1332
/*
1333
* va_list version of imapsend
1334
*/
1335
1336
static Imapop_t*
1337
imapvsend(register Imap_t* imap, int retain, const char* fmt, va_list ap)
1338
{
1339
register int i;
1340
register int c;
1341
register int q;
1342
register char* s;
1343
va_list oap;
1344
1345
va_copy(oap, ap);
1346
1347
/*
1348
* autologout may have dropped the connection
1349
* the autoreconnect is here
1350
*/
1351
1352
if (!imap->connected && imapconnect(imap))
1353
return 0;
1354
1355
/*
1356
* grab a free tag
1357
* this might mean waiting for a previous one to finish
1358
*/
1359
1360
if (!(i = imap->tag))
1361
i = 1;
1362
c = i;
1363
while (imap->op[i].state)
1364
{
1365
if (++i >= elementsof(imap->op))
1366
i = 1;
1367
if (i == c)
1368
imaprecv(imap, imap->op);
1369
}
1370
imap->tag = i;
1371
1372
/*
1373
* write the op
1374
*/
1375
1376
sfprintf(imap->np, "%d ", i);
1377
sfvprintf(imap->np, fmt, ap);
1378
s = struse(imap->np);
1379
q = 0;
1380
for (;;)
1381
{
1382
switch (c = *s++)
1383
{
1384
case 0:
1385
break;
1386
case '"':
1387
case '\'':
1388
sfputc(imap->tp, c);
1389
if (!q)
1390
q = c;
1391
continue;
1392
case ' ':
1393
case '\n':
1394
case '\r':
1395
case '\t':
1396
if (!q)
1397
{
1398
for (; (c = *s) && (c == ' ' || c == '\n' || c == '\r' || c == '\t'); s++);
1399
if (c && c != ')')
1400
sfputc(imap->tp, ' ');
1401
}
1402
else
1403
sfputc(imap->tp, c);
1404
continue;
1405
case '(':
1406
sfputc(imap->tp, c);
1407
if (!q)
1408
for (; (c = *s) && (c == ' ' || c == '\n' || c == '\r' || c == '\t'); s++);
1409
continue;
1410
case ')':
1411
sfputc(imap->tp, c);
1412
if (!q)
1413
{
1414
for (; (c = *s) && (c == ' ' || c == '\n' || c == '\r' || c == '\t'); s++);
1415
if (c && c != ']')
1416
sfputc(imap->tp, ' ');
1417
}
1418
continue;
1419
default:
1420
sfputc(imap->tp, c);
1421
continue;
1422
}
1423
break;
1424
}
1425
s = struse(imap->tp);
1426
if (TRACING('s'))
1427
note(ERROR, "imap: send %s", s);
1428
if (sfprintf(imap->sp, "%s\r\n", s) < 0 || sfsync(imap->sp) < 0)
1429
{
1430
note(ERROR|SYSTEM, "imap: server send error");
1431
i = 0;
1432
}
1433
if (!i)
1434
return 0;
1435
imap->op[i].state = IMAP_STATE_sent;
1436
imap->op[i].retain = retain;
1437
sfvsprintf(imap->op[i].msg, sizeof(imap->op[i].msg) - 1, fmt, oap);
1438
return imap->op + i;
1439
}
1440
1441
/*
1442
* send a tagged imap op
1443
*/
1444
1445
static Imapop_t*
1446
imapsend(Imap_t* imap, int retain, const char* fmt, ...)
1447
{
1448
Imapop_t* op;
1449
va_list ap;
1450
1451
va_start(ap, fmt);
1452
op = imapvsend(imap, retain, fmt, ap);
1453
va_end(ap);
1454
return op;
1455
}
1456
1457
/*
1458
* send and recv a tagged imap op
1459
*/
1460
1461
static int
1462
imapexec(Imap_t* imap, const char* fmt, ...)
1463
{
1464
register Imapop_t* op;
1465
va_list ap;
1466
1467
va_start(ap, fmt);
1468
op = imapvsend(imap, 1, fmt, ap);
1469
va_end(ap);
1470
if (!op || !imaprecv(imap, op))
1471
return 0;
1472
imapfree(imap, op);
1473
return op->code == IMAP_OK ? 0 : -1;
1474
}
1475
1476
/*
1477
* connect and authenticate to the IMAP service
1478
*/
1479
1480
static int
1481
imapconnect(register Imap_t* imap)
1482
{
1483
register char* svc;
1484
int fd;
1485
1486
/*
1487
* imap->connected<0 means we got here from one of
1488
* the imapexec()'s in this function -- not good
1489
*/
1490
1491
if (imap->connected < 0)
1492
{
1493
note(FATAL, "imap: connection/authentication error");
1494
return -1;
1495
}
1496
1497
/*
1498
* the old connection if any is useless now
1499
*/
1500
1501
if (imap->sp)
1502
{
1503
sfclose(imap->sp);
1504
imap->sp = 0;
1505
}
1506
if (imap->rp)
1507
{
1508
sfclose(imap->rp);
1509
imap->rp = 0;
1510
}
1511
if (!imap->host || !imap->user || !imap->meth)
1512
{
1513
register char* host;
1514
register char* user;
1515
register char* meth;
1516
Sfio_t* fp;
1517
1518
/*
1519
* get the host and authentication info from the imap file
1520
*
1521
* <host> [ <user> [ <meth> [ <arg> ... ] ] ]
1522
*/
1523
1524
if (fp = fileopen(state.var.imap, "IRXr"))
1525
{
1526
while (host = sfgetr(fp, '\n', 1))
1527
{
1528
for (; isspace(*host); host++);
1529
if (!*host || *host == '#')
1530
continue;
1531
for (user = host; *user && !isspace(*user); user++);
1532
if (*user)
1533
*user++ = 0;
1534
if (!imap->host || streq(imap->host, host))
1535
{
1536
for (; isspace(*user); user++);
1537
for (meth = user; *meth && !isspace(*meth); meth++);
1538
if (*meth)
1539
*meth++ = 0;
1540
if (!imap->user || streq(imap->user, user))
1541
{
1542
for (; isspace(*meth); meth++);
1543
goto match;
1544
}
1545
}
1546
}
1547
host = 0;
1548
user = 0;
1549
meth = 0;
1550
}
1551
else
1552
{
1553
host = imap->host;
1554
user = imap->user;
1555
meth = imap->meth;
1556
}
1557
match:
1558
if (host && (!*host || streq(host, "*")))
1559
host = 0;
1560
if (!imap->host || host && !streq(host, imap->host))
1561
imap->host = host ? vmstrdup(imap->gm, host) : "local";
1562
if (user && (!*user || streq(user, "*")))
1563
user = 0;
1564
if (!imap->user || user && !streq(user, imap->user))
1565
imap->user = user ? vmstrdup(imap->gm, user) : state.var.user;
1566
if (meth && (!*meth || streq(meth, "*")))
1567
meth = 0;
1568
if (!imap->meth || meth && !streq(meth, imap->meth))
1569
{
1570
if (!meth)
1571
{
1572
if (streq(imap->user, "anonymous"))
1573
{
1574
sfprintf(imap->tp, "LOGIN ANONYMOUS %s", state.var.user);
1575
if (state.var.domain)
1576
sfprintf(imap->tp, "@%s", state.var.domain);
1577
meth = struse(imap->tp);
1578
}
1579
else
1580
meth = "AUTHENTICATE XFILE";
1581
}
1582
imap->meth = vmstrdup(imap->gm, meth);
1583
}
1584
if (fp)
1585
sfclose(fp);
1586
}
1587
1588
/*
1589
* connect to the service
1590
*/
1591
1592
sfprintf(imap->tp, "/dev/tcp/%s/inet.imap", imap->host);
1593
svc = struse(imap->tp);
1594
if ((fd = csopen(&cs, svc, 0)) < 0)
1595
{
1596
note(ERROR|SYSTEM, "imap: %s: cannot connect to service", svc);
1597
return -1;
1598
}
1599
if (!(imap->sp = sfnew(NiL, NiL, SF_UNBOUND, fd, SF_WRITE)) ||
1600
!(imap->rp = sfnew(NiL, NiL, SF_UNBOUND, fd, SF_READ)))
1601
{
1602
if (imap->sp)
1603
{
1604
sfclose(imap->sp);
1605
imap->sp = 0;
1606
}
1607
else
1608
close(fd);
1609
note(ERROR|SYSTEM, "imap: %s: cannot buffer service", svc);
1610
return -1;
1611
}
1612
imap->connected = -1;
1613
if (imapexec(imap, "CAPABILITY") || imap->version < IMAP_VERSION || imap->version == IMAP_VERSION && imap->revision < IMAP_REVISION)
1614
{
1615
if (imap->version)
1616
note(ERROR|SYSTEM, "imap: %s: service version %d.%d must be at least %d.%d", svc, imap->version, imap->revision, IMAP_VERSION, IMAP_REVISION);
1617
else
1618
note(ERROR|SYSTEM, "imap: %s: service connect error", svc);
1619
goto drop;
1620
}
1621
if (!imap->authenticated)
1622
{
1623
/*
1624
* do the authentication
1625
* imap->authenticated<0 is a hint to the
1626
* IMAP_MORE response to consult imap->meth
1627
*/
1628
1629
imap->authenticated = -1;
1630
if (imapexec(imap, "%s", imap->meth))
1631
{
1632
note(ERROR|SYSTEM, "imap: %s: service authentication error", svc);
1633
goto drop;
1634
}
1635
imap->authenticated = 1;
1636
}
1637
imap->connected = 1;
1638
return 0;
1639
drop:
1640
if (imap->sp)
1641
{
1642
sfclose(imap->sp);
1643
imap->sp = 0;
1644
}
1645
if (imap->rp)
1646
{
1647
sfclose(imap->rp);
1648
imap->rp = 0;
1649
}
1650
imap->connected = 0;
1651
return -1;
1652
}
1653
1654
/*
1655
* print message and fail on VM_BADADDR,VM_NOMEM
1656
*/
1657
1658
static int
1659
vmexcept(Vmalloc_t* region, int type, void* obj, Vmdisc_t* disc)
1660
{
1661
Vmstat_t st;
1662
1663
switch (type)
1664
{
1665
case VM_BADADDR:
1666
note(ERROR, "imap: invalid pointer %p passed to free or realloc", obj);
1667
imap_exit(1);
1668
return -1;
1669
case VM_NOMEM:
1670
vmstat(region, &st);
1671
note(ERROR, "imap: storage allocator out of space on %lu byte request ( region %lu segments %lu busy %lu:%lu:%lu free %lu:%lu:%lu )", (size_t)obj, st.extent, st.n_seg, st.n_busy, st.s_busy, st.m_busy, st.n_free, st.s_free, st.m_free);
1672
imap_exit(1);
1673
return -1;
1674
}
1675
return 0;
1676
}
1677
1678
/*
1679
* initialize the IMAP service state
1680
*/
1681
1682
static Imap_t*
1683
imapinit(void)
1684
{
1685
register Imap_t* imap;
1686
1687
if (!(imap = newof(0, Imap_t, 1, 0)))
1688
{
1689
note(ERROR|SYSTEM, "Out of space [imap]");
1690
return 0;
1691
}
1692
imap->vmdisc = *Vmdcheap;
1693
imap->vmdisc.exceptf = vmexcept;
1694
if (!(imap->gm = vmopen(&imap->vmdisc, Vmlast, 0)) || !(imap->vm = vmopen(&imap->vmdisc, Vmlast, 0)))
1695
{
1696
note(ERROR|SYSTEM, "Out of space [imap state vm]");
1697
if (imap->gm)
1698
vmclose(imap->gm);
1699
free(imap);
1700
return 0;
1701
}
1702
if (!(imap->mp = sfstropen()) || !(imap->tp = sfstropen()) || !(imap->np = sfstropen()))
1703
{
1704
note(ERROR|SYSTEM, "Out of space [imap tmp string stream]");
1705
vmclose(imap->gm);
1706
vmclose(imap->vm);
1707
free(imap);
1708
return 0;
1709
}
1710
imap->copy.fp = sfstdout;
1711
return state.msg.imap.state = (void*)imap;
1712
}
1713
1714
/*
1715
* flush IMAP_STATE_sent requests and CLOSE
1716
*/
1717
1718
static int
1719
imapclose(register Imap_t* imap)
1720
{
1721
register Msg_t* mp;
1722
register int n;
1723
1724
while (imaprecv(imap, imap->op));
1725
imap->selected = 0;
1726
if (state.folder == FIMAP)
1727
{
1728
n = 0;
1729
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++)
1730
if (!(mp->m_flag & MDELETE))
1731
n++;
1732
if (n)
1733
note(ERROR, "Held %d message%s in %s", n, n == 1 ? "" : "s", state.path.mail);
1734
else
1735
note(ERROR, "No more messages in %s", state.path.mail);
1736
}
1737
return imapexec(imap, "CLOSE");
1738
}
1739
1740
/*
1741
* IMAP state.msg.list initialization
1742
*/
1743
1744
int
1745
imap_setptr(char* name, int isedit)
1746
{
1747
register Imap_t* imap;
1748
register char* s;
1749
register char* t;
1750
1751
if (!(imap = IMAP) && (!name || !(imap = imapinit())))
1752
return -1;
1753
if (imap->selected && imapclose(imap))
1754
return -1;
1755
if (!name)
1756
return 0;
1757
vmclear(imap->vm);
1758
state.folder = FIMAP;
1759
memset(&imap->mailbox, sizeof(imap->mailbox), 0);
1760
imap->mailbox.exists = -1;
1761
1762
/*
1763
* determine the service info
1764
* info from previous folders is cached
1765
* to preserve authentication
1766
* ${IMAP} provides defaults
1767
*/
1768
1769
if (streq(name, "@"))
1770
name = "@inbox";
1771
strncopy(state.path.prev, state.path.mail, sizeof(state.path.prev));
1772
if (name != state.path.mail)
1773
strncopy(state.path.mail, name, sizeof(state.path.mail));
1774
if (*name == '@')
1775
name++;
1776
else if (strneq(name, "imap://", 7))
1777
{
1778
sfprintf(imap->tp, "%s", name + 7);
1779
s = struse(imap->tp);
1780
if (!(t = strchr(s, '/')))
1781
{
1782
note(ERROR, "imap: %s: user name expected", name);
1783
return -1;
1784
}
1785
*t++ = 0;
1786
if (!imap->host || !streq(imap->host, s))
1787
{
1788
imap->host = vmstrdup(imap->gm, s);
1789
imap->connected = 0;
1790
imap->meth = 0;
1791
}
1792
if (s = strchr(t, '/'))
1793
*s++ = 0;
1794
if (!imap->user || !streq(imap->user, t))
1795
{
1796
imap->user = vmstrdup(imap->gm, t);
1797
imap->connected = 0;
1798
imap->meth = 0;
1799
}
1800
if (!s || !*s)
1801
name = "inbox";
1802
else
1803
name += (s - sfstrbase(imap->tp)) + 7;
1804
}
1805
if (!imap->connected && imapconnect(imap))
1806
return -1;
1807
if (imapexec(imap, "%s %s", state.readonly ? "EXAMINE" : "SELECT", name))
1808
return -1;
1809
imap->selected = 1;
1810
state.msg.count = 0;
1811
imapsync(imap);
1812
state.msg.dot = state.msg.list + imap->mailbox.unseen - 1;
1813
return 0;
1814
}
1815
1816
/*
1817
* return message pointer given number
1818
*/
1819
1820
static Msg_t*
1821
imap_msg(int m)
1822
{
1823
register Imap_t* imap;
1824
register Msg_t* mp;
1825
1826
mp = state.msg.list + m - 1;
1827
if (!mp->m_info)
1828
{
1829
imap = IMAP;
1830
if (!(mp->m_info = (void*)vmnewof(imap->vm, 0, Imapmsg_t, 1, 0)))
1831
note(FATAL, "out of space [imap msg info]");
1832
if (imapexec(imap, "FETCH %d (RFC822.SIZE FLAGS ENVELOPE BODYSTRUCTURE INTERNALDATE)", m))
1833
note(FATAL, "imap: %d: cannot fetch message info", m);
1834
if (state.var.spam && !(mp->m_flag & (MSCAN|MSPAM)))
1835
setinput(mp);
1836
if (TRACING('b'))
1837
{
1838
register Imapmsg_t* ip;
1839
register Imappart_t* pp;
1840
1841
ip = (Imapmsg_t*)mp->m_info;
1842
for (pp = ip->parts; pp; pp = pp->next)
1843
note(ERROR, "imap: message %d id=%s content=%s type=%s encoding=%s name=%s size=%d/%d", m, pp->id, imapname(imapcontent, elementsof(imapcontent), pp->content), pp->type, pp->encoding, pp->name, pp->lines, pp->size);
1844
}
1845
}
1846
return mp;
1847
}
1848
1849
/*
1850
* IMAP setinput()
1851
*/
1852
1853
Sfio_t*
1854
imap_setinput(register Msg_t* mp)
1855
{
1856
register Imap_t* imap = IMAP;
1857
int m = mp - state.msg.list + 1;
1858
1859
imap->copy.fp = imap->mp;
1860
sfstrseek(imap->mp, 0, SEEK_SET);
1861
if (imapexec(imap, "FETCH %d (BODY.PEEK[HEADER])", m))
1862
note(FATAL, "imap: %d: cannot fetch message header", m);
1863
imap->copy.fp = sfstdout;
1864
imap->mp->_endb = imap->mp->_next;
1865
sfseek(imap->mp, (Sfoff_t)0, SEEK_SET);
1866
return imap->mp;
1867
}
1868
1869
/*
1870
/*
1871
* IMAP command()
1872
*/
1873
1874
int
1875
imap_command(char* s)
1876
{
1877
register Imap_t* imap = IMAP;
1878
register int n;
1879
int items;
1880
char* e;
1881
Imaplex_t* xp;
1882
unsigned long trace;
1883
1884
if (strneq(s, "dump", 4) && (!(n = *(s + 4)) || isspace(n)))
1885
{
1886
for (s += 4; isspace(*s); s++);
1887
if (!*s)
1888
{
1889
list:
1890
sfprintf(sfstdout, "dump items are:");
1891
for (n = 0; n < elementsof(imapdump); n++)
1892
sfprintf(sfstdout, " %s", imapdump[n].name);
1893
sfprintf(sfstdout, "\n");
1894
}
1895
else for (items = 0;;)
1896
{
1897
for (; isspace(*s); s++);
1898
if (!*s)
1899
break;
1900
if (items++)
1901
sfprintf(sfstdout, "\n");
1902
if (!(xp = (Imaplex_t*)strpsearch(imapdump, elementsof(imapdump), sizeof(imapdump[0]), s, &e)))
1903
{
1904
note(ERROR, "%s: unknown dump item", s);
1905
goto list;
1906
}
1907
for (s = e; isspace(*s); s++);
1908
if (isdigit(*s))
1909
{
1910
n = strtol(s, &e, 0);
1911
s = e;
1912
}
1913
else if (*s == '*')
1914
{
1915
s++;
1916
n = -1;
1917
}
1918
else
1919
n = 0;
1920
switch (xp->code)
1921
{
1922
case IMAP_DUMP_FLAGS:
1923
for (n = 0; n < elementsof(imapflags); n++)
1924
sfprintf(sfstdout, "%15s%s%s\n", imapflags[n].name, (imapflags[n].code & IMAP_FLAG_APPLICABLE) ? " APPLICABLE" : "", (imapflags[n].code & IMAP_FLAG_PERMANENT) ? " PERMANENT" : "");
1925
break;
1926
case IMAP_DUMP_FOLDER:
1927
sfprintf(sfstdout, " name %s\n", state.path.mail);
1928
sfprintf(sfstdout, " delimiter %s\n", imap->mailbox.delimiter);
1929
sfprintf(sfstdout, " exists %d\n", imap->mailbox.exists);
1930
sfprintf(sfstdout, " recent %d\n", imap->mailbox.recent);
1931
sfprintf(sfstdout, " read_only %d\n", imap->mailbox.recent);
1932
sfprintf(sfstdout, " trycreate %d\n", imap->mailbox.trycreate);
1933
sfprintf(sfstdout, " uidnext %d\n", imap->mailbox.uidnext);
1934
sfprintf(sfstdout, " uidvalidity %d\n", imap->mailbox.uidvalidity);
1935
sfprintf(sfstdout, " unseen %d\n", imap->mailbox.unseen);
1936
break;
1937
case IMAP_DUMP_MESSAGE:
1938
{
1939
register Msg_t* mp;
1940
register Msg_t* ep;
1941
register Imapmsg_t* ip;
1942
register Imappart_t* pp;
1943
1944
if (n > state.msg.count)
1945
{
1946
sfprintf(sfstdout, "%d: invalid message", n);
1947
break;
1948
}
1949
if (n < 0)
1950
{
1951
mp = state.msg.list;
1952
ep = state.msg.list + state.msg.count;
1953
}
1954
else if (n > 0)
1955
{
1956
ep = state.msg.list + n;
1957
mp = ep - 1;
1958
}
1959
else
1960
{
1961
mp = state.msg.dot;
1962
ep = mp + 1;
1963
}
1964
for (; mp < ep; mp++)
1965
{
1966
sfprintf(sfstdout, "message %4d %s\n", mp - state.msg.list + 1, imapflagnames(imap->tp, imapmflags, elementsof(imapmflags), mp->m_flag));
1967
if (ip = (Imapmsg_t*)mp->m_info)
1968
for (pp = ip->parts; pp; pp = pp->next)
1969
sfprintf(sfstdout, "%12s content=%s type=%s encoding=%s name=%s size=%d/%d\n", pp->id, imapname(imapcontent, elementsof(imapcontent), pp->content), pp->type, pp->encoding, pp->name, pp->lines, pp->size);
1970
}
1971
break;
1972
}
1973
case IMAP_DUMP_QUEUE:
1974
{
1975
register Imapop_t* op;
1976
1977
for (op = imap->op + 1; op < &imap->op[elementsof(imap->op)]; op++)
1978
sfprintf(sfstdout, " [%d] %s %s %d %s%s\n", op - imap->op, imapname(imapstate, elementsof(imapstate), op->state), imapname(imapresponse, elementsof(imapresponse), op->code), op->count, op->msg, op->retain ? " retain" : "");
1979
break;
1980
}
1981
case IMAP_DUMP_STATE:
1982
sfprintf(sfstdout, " host %s\n", imap->host);
1983
sfprintf(sfstdout, " user %s\n", imap->user);
1984
sfprintf(sfstdout, " auth %s\n", imapflagnames(imap->tp, imapauth, elementsof(imapauth), imap->auth));
1985
sfprintf(sfstdout, " authenticated %d\n", imap->authenticated);
1986
sfprintf(sfstdout, " connected %d\n", imap->connected);
1987
sfprintf(sfstdout, " exiting %d\n", imap->exiting);
1988
sfprintf(sfstdout, " selected %d\n", imap->selected);
1989
sfprintf(sfstdout, " tag %d\n", imap->tag);
1990
sfprintf(sfstdout, " version %d.%d\n", imap->version, imap->revision);
1991
break;
1992
}
1993
}
1994
n = 0;
1995
}
1996
else
1997
{
1998
trace = state.trace;
1999
TRACE('r');
2000
n = imapexec(imap, "%s", s);
2001
state.trace = trace;
2002
}
2003
return n;
2004
}
2005
2006
/*
2007
* IMAP copy()
2008
*/
2009
2010
int
2011
imap_copy(register struct msg* mp, Sfio_t* op, Dt_t** ignore, char* prefix, unsigned long flags)
2012
{
2013
register Imap_t* imap = IMAP;
2014
register Imapmsg_t* ip;
2015
register Imappart_t* pp;
2016
register char* s;
2017
register int i;
2018
struct name* np;
2019
2020
/*
2021
* Compute the prefix string, without trailing whitespace
2022
*/
2023
2024
i = mp - state.msg.list + 1;
2025
mp = imap_msg(i);
2026
ip = (Imapmsg_t*)mp->m_info;
2027
imap->copy.fp = op;
2028
if (imap->copy.prefix = prefix)
2029
{
2030
imap->copy.prefixlen = strlen(prefix);
2031
s = prefix + imap->copy.prefixlen;
2032
while (--s >= prefix && isspace(*s));
2033
imap->copy.emptylen = (s + 1) - prefix;
2034
sfputr(op, imap->copy.prefix, -1);
2035
}
2036
sfprintf(op, "From %s %s\n", ip->from, ip->date);
2037
sfprintf(imap->tp, "FETCH %d (BODY[HEADER", i);
2038
if (ignore && *ignore)
2039
{
2040
if (ignore == &state.ignoreall)
2041
sfprintf(imap->tp, ".FIELDS NIL");
2042
else
2043
{
2044
if (dictflags(ignore) & RETAIN)
2045
{
2046
i = RETAIN;
2047
sfprintf(imap->tp, ".FIELDS (");
2048
s = "";
2049
}
2050
else
2051
{
2052
i = IGNORE;
2053
sfprintf(imap->tp, ".FIELDS.NOT (From");
2054
s = " ";
2055
}
2056
for (np = (struct name*)dtfirst(*ignore); np; np = (struct name*)dtnext(*ignore, np))
2057
if (np->flags & i)
2058
{
2059
sfprintf(imap->tp, "%s%s", s, np->name);
2060
s = " ";
2061
}
2062
sfprintf(imap->tp, ")");
2063
}
2064
sfprintf(imap->tp, "]");
2065
for (pp = ip->parts; pp; pp = pp->next)
2066
if (pp->content == IMAP_CONTENT_text)
2067
sfprintf(imap->tp, " BODY[%s]", pp->id);
2068
}
2069
else
2070
sfprintf(imap->tp, "] BODY[TEXT]");
2071
sfprintf(imap->tp, ")");
2072
if (imapexec(IMAP, struse(imap->tp)))
2073
note(FATAL, "imap: %d: cannot fetch message info", i);
2074
imap->copy.fp = sfstdout;
2075
imap->copy.prefix = 0;
2076
if (ignore && *ignore)
2077
for (pp = ip->parts; pp; pp = pp->next)
2078
if (pp->content == IMAP_CONTENT_attachment)
2079
sfprintf(op, "\n(attachment %3d %s %18s \"%s\")\n", pp->attachment, counts(1, pp->lines, pp->size), pp->type, pp->name);
2080
sfprintf(op, "\n");
2081
return 0;
2082
}
2083
2084
/*
2085
* IMAP mkdir()
2086
*/
2087
2088
int
2089
imap_mkdir(char* s)
2090
{
2091
if (*s == '@')
2092
s++;
2093
return imapexec(IMAP, "CREATE %s", s);
2094
}
2095
2096
/*
2097
* IMAP rename()
2098
*/
2099
2100
int
2101
imap_rename(char* f, char* t)
2102
{
2103
if (*f == '@')
2104
f++;
2105
if (*t == '@')
2106
t++;
2107
return imapexec(IMAP, "RENAME %s %s", f, t);
2108
}
2109
2110
/*
2111
* IMAP rmdir()
2112
*/
2113
2114
int
2115
imap_rmdir(char* s)
2116
{
2117
if (*s == '@')
2118
s++;
2119
return imapexec(IMAP, "DELETE %s", s);
2120
}
2121
2122
/*
2123
* IMAP exit()
2124
*/
2125
2126
void
2127
imap_exit(int code)
2128
{
2129
register Imap_t* imap = IMAP;
2130
register Msg_t* mp;
2131
2132
if (state.folder == FIMAP)
2133
for (mp = state.msg.list; mp < state.msg.list + state.msg.count; mp++)
2134
if ((mp->m_flag & (MDELETE|MZOMBIE)) == MDELETE)
2135
imap_msgflags(mp, 0, MDELETE);
2136
imap->selected = 0;
2137
if (imap->connected)
2138
imap_quit();
2139
exit(code);
2140
}
2141
2142
/*
2143
* IMAP folders()
2144
*/
2145
2146
int
2147
imap_folders(void)
2148
{
2149
return imapexec(IMAP, "LIST \"\" *");
2150
}
2151
2152
/*
2153
* IMAP getatt()
2154
*/
2155
2156
static int
2157
imap_getatt(Msg_t* mp, register Imappart_t* pp, register char* name, unsigned long flags, off_t* lines, off_t* chars)
2158
{
2159
register Imap_t* imap = IMAP;
2160
register char* s;
2161
register int n;
2162
char* cmd;
2163
2164
if (!(cmd = iscmd(name)))
2165
{
2166
if (state.var.attachments && name == pp->name)
2167
{
2168
sfprintf(state.path.temp, "%s/%s", state.var.attachments, name);
2169
name = struse(state.path.temp);
2170
}
2171
if (!(name = expand(name, 1)))
2172
return 1;
2173
}
2174
if (pp->encoding && mime(1) && mimeview(state.part.mime, "encoding", name, pp->type, pp->options))
2175
pp->encoding = pp->type;
2176
if (pp->encoding && !isdigit(pp->encoding[0]))
2177
{
2178
sfprintf(state.path.temp, "uudecode -h -x %s", pp->encoding);
2179
if (!mimecmp("text", pp->type, NiL))
2180
sfprintf(state.path.temp, " -t");
2181
if (cmd)
2182
sfprintf(state.path.temp, " -o - | %s", cmd);
2183
else if (filestd(name, "w"))
2184
sfprintf(state.path.temp, " | %s", state.var.pager);
2185
else
2186
{
2187
sfprintf(state.path.temp, " -o ", name);
2188
shquote(state.path.temp, name);
2189
}
2190
s = struse(state.path.temp);
2191
n = 1;
2192
}
2193
else if (cmd)
2194
{
2195
s = cmd;
2196
n = -1;
2197
}
2198
else if (filestd(name, "w"))
2199
{
2200
s = state.var.pager;
2201
n = -1;
2202
}
2203
else
2204
{
2205
s = name;
2206
n = 0;
2207
}
2208
if (state.var.debug)
2209
{
2210
note(DEBUG, "%s `%s'", n ? "pipe" : "file", s);
2211
if ((flags & GMIME) &&
2212
mime(1) &&
2213
(s = mimeview(state.part.mime, NiL, name, pp->type, pp->options)))
2214
note(DEBUG, "mimeview `%s'", s);
2215
return 0;
2216
}
2217
if (!(imap->copy.fp = n ? pipeopen(s, "w") : fileopen(s, "ERw"))) {
2218
imap->copy.fp = sfstdout;
2219
return 1;
2220
}
2221
if (lines)
2222
*lines = pp->lines;
2223
if (chars)
2224
*chars = pp->size;
2225
n = mp - state.msg.list + 1;
2226
if (imapexec(imap, "FETCH %d (BODY[%s])", n, pp->id))
2227
note(FATAL, "imap: %d: cannot fetch message info", n);
2228
imap->copy.fp = sfstdout;
2229
fileclose(imap->copy.fp);
2230
if (flags & GDISPLAY)
2231
note(ERROR, "\"%s\" %s", name, counts(1, pp->lines, pp->size));
2232
if ((flags & GMIME) &&
2233
mime(1) &&
2234
(s = mimeview(state.part.mime, NiL, name, pp->type, pp->options)) &&
2235
(n = start_command(state.var.shell, SIG_REG_EXEC, -1, -1, "-c", s, NiL)) >= 0)
2236
wait_command(n);
2237
return 0;
2238
}
2239
2240
/*
2241
* IMAP get1()
2242
*/
2243
2244
int
2245
imap_get1(char** argv, unsigned long flags)
2246
{
2247
register Imappart_t* pp;
2248
register int i;
2249
register char* s;
2250
Msg_t* mp;
2251
Imapmsg_t* ip;
2252
char* name;
2253
char* a;
2254
char* e;
2255
int n;
2256
int r;
2257
2258
if (state.msg.dot < state.msg.list || state.msg.dot >= state.msg.list + state.msg.count)
2259
{
2260
note(ERROR, "No current message");
2261
return 1;
2262
}
2263
mp = imap_msg(state.msg.dot - state.msg.list + 1);
2264
ip = (Imapmsg_t*)mp->m_info;
2265
if (!ip->attachments || !(pp = ip->parts))
2266
{
2267
note(ERROR, "No attachments in current message");
2268
return 1;
2269
}
2270
if (!*argv)
2271
{
2272
do {
2273
if (pp->content == IMAP_CONTENT_attachment)
2274
sfprintf(sfstdout, "(attachment %3d %s %18s \"%s\")\n", pp->attachment, counts(1, pp->lines, pp->size), pp->type, pp->name);
2275
} while (pp = pp->next);
2276
return 0;
2277
}
2278
if (!(a = newof(0, char, ip->attachments, 1)))
2279
note(PANIC, "Out of space [imap attachments]");
2280
s = *argv++;
2281
r = 0;
2282
for (;;)
2283
{
2284
while (isspace(*s))
2285
s++;
2286
if (!*s)
2287
break;
2288
else if (*s == ',')
2289
{
2290
s++;
2291
r = 0;
2292
}
2293
else if (*s == '*')
2294
{
2295
if (!r)
2296
r = 1;
2297
for (i = r; i <= ip->attachments; i++)
2298
a[i] = 1;
2299
r = 0;
2300
}
2301
else if (*s == '-')
2302
{
2303
s++;
2304
r = 1;
2305
}
2306
else
2307
{
2308
n = strtol(s, &e, 0);
2309
if (n > 0 && n <= ip->attachments)
2310
{
2311
if (r)
2312
{
2313
for (i = r; i <= n; i++)
2314
a[i] = 1;
2315
r = 0;
2316
}
2317
else
2318
a[n] = 1;
2319
}
2320
else
2321
{
2322
note(ERROR, "%s: invalid attachment number", s);
2323
while (*e && !isspace(*e))
2324
e++;
2325
}
2326
s = e;
2327
if (*s == '-')
2328
{
2329
s++;
2330
r = n;
2331
}
2332
}
2333
}
2334
r = 0;
2335
for (i = 1; i <= ip->attachments; i++)
2336
if (a[i])
2337
{
2338
while (pp->attachment != i)
2339
if (!(pp = pp->next))
2340
{
2341
note(ERROR, "%d: attachment number out of range", i);
2342
r = 1;
2343
goto done;
2344
}
2345
if (pp->content != IMAP_CONTENT_attachment)
2346
{
2347
note(ERROR, "%d: not an attachment", i);
2348
continue;
2349
}
2350
if (name = *argv)
2351
argv++;
2352
else
2353
name = pp->name;
2354
if (imap_getatt(mp, pp, name, flags, NiL, NiL))
2355
r = 1;
2356
}
2357
done:
2358
free(a);
2359
return r;
2360
}
2361
2362
/*
2363
* IMAP printhead()
2364
*/
2365
2366
void
2367
imap_printhead(int m, int who)
2368
{
2369
register Msg_t* mp;
2370
register Imapmsg_t* ip;
2371
char* sizes;
2372
int subjlen;
2373
int current;
2374
int disposition;
2375
2376
mp = imap_msg(m);
2377
if (mp->m_flag & (MDELETE|MNONE))
2378
return;
2379
ip = (Imapmsg_t*)mp->m_info;
2380
current = state.msg.dot == mp && !state.var.justheaders ? '>' : ' ';
2381
if (mp->m_flag & MBOX)
2382
disposition = 'M';
2383
else if (mp->m_flag & MPRESERVE)
2384
disposition = 'P';
2385
else if (mp->m_flag & MSAVE)
2386
disposition = '*';
2387
else if (mp->m_flag & MSPAM)
2388
disposition = 'X';
2389
else if (!(mp->m_flag & (MREAD|MNEW)))
2390
disposition = 'U';
2391
else if ((mp->m_flag & (MREAD|MNEW)) == MNEW)
2392
disposition = 'N';
2393
else
2394
disposition = who ? 'R' : ' ';
2395
if (who)
2396
{
2397
if (!state.var.domain || strchr(ip->from, '@'))
2398
printf("%c %s\n", disposition, ip->from);
2399
else
2400
printf("%c %s@%s\n", disposition, ip->from, state.var.domain);
2401
}
2402
else
2403
{
2404
sizes = counts(!!state.var.news, mp->m_lines, mp->m_size);
2405
subjlen = state.screenwidth - 50 - strlen(sizes);
2406
if (ip->subject && subjlen >= 0)
2407
printf("%c%c%3d %-20.20s %16.16s %s %.*s\n",
2408
current, disposition, m, ip->from, ip->date, sizes,
2409
subjlen, ip->subject);
2410
else
2411
printf("%c%c%3d %-20.20s %16.16s %s\n",
2412
current, disposition, m, ip->from, ip->date, sizes);
2413
}
2414
}
2415
2416
/*
2417
* imap_msgflags() support
2418
*/
2419
2420
static void
2421
imap_flags(register Imap_t* imap, Msg_t* mp, register int flags, char* op)
2422
{
2423
register int i;
2424
register int c;
2425
2426
if (TRACING('z'))
2427
note(ERROR, "IMAP: smap_flags msg=%d flags=0x%04x op=%s", mp - state.msg.list + 1, flags, op);
2428
sfprintf(imap->tp, "STORE %d %sFLAGS.SILENT ", mp - state.msg.list + 1, op);
2429
c = '(';
2430
for (i = 0; i < elementsof(imapflags); i++)
2431
if ((imapflags[i].flag & flags) && (imapflags[i].code & IMAP_FLAG_PERMANENT))
2432
{
2433
sfprintf(imap->tp, "%c%s", c, imapflags[i].name);
2434
c = ' ';
2435
}
2436
if (c == ' ')
2437
{
2438
sfprintf(imap->tp, ")");
2439
if (!imapsend(imap, 0, "%s", struse(imap->tp)))
2440
note(ERROR, "%d: message flags not updated", mp - state.msg.list + 1);
2441
}
2442
else
2443
sfstrseek(imap->tp, 0, SEEK_SET);
2444
}
2445
2446
/*
2447
* IMAP message flag update
2448
*/
2449
2450
void
2451
imap_msgflags(register Msg_t* mp, int set, int clr)
2452
{
2453
register Imap_t* imap = IMAP;
2454
register int flags;
2455
2456
if (mp->m_flag & ~clr & MDELETE)
2457
return;
2458
if (flags = mp->m_flag & clr)
2459
{
2460
mp->m_flag &= ~clr;
2461
if (flags & (MDELETE|MREAD|MSCAN|MSPAM))
2462
imap_flags(imap, mp, flags, "-");
2463
}
2464
if (flags = ~mp->m_flag & set)
2465
{
2466
mp->m_flag |= set;
2467
if (flags & (MDELETE|MREAD|MSCAN|MSPAM))
2468
imap_flags(imap, mp, flags, "+");
2469
}
2470
}
2471
2472
/*
2473
* IMAP message search/list
2474
* '(' IMAP SEARCH expression ')'
2475
*/
2476
2477
int
2478
imap_msglist(register char* s)
2479
{
2480
register Imap_t* imap = IMAP;
2481
register char* t;
2482
2483
for (; isspace(*s); s++);
2484
if (*s != '(')
2485
{
2486
note(ERROR, "imap: %s: invalid SEARCH expression", s);
2487
return 0;
2488
}
2489
s++;
2490
t = s + strlen(s);
2491
for (; t > s && isspace(*--t) && *t != ')';);
2492
imap->index = state.msg.list;
2493
if (imapexec(imap, "SEARCH %*.*s", t - s, t - s, s))
2494
return 0;
2495
imap->index->m_index = 0;
2496
return imap->index - state.msg.list;
2497
}
2498
2499
/*
2500
* IMAP quit()
2501
*/
2502
2503
void
2504
imap_quit(void)
2505
{
2506
register Imap_t* imap = IMAP;
2507
register int i;
2508
2509
state.msg.imap.state = 0;
2510
if (imap->selected)
2511
imapclose(imap);
2512
imap->exiting = 1;
2513
imapexec(imap, "LOGOUT");
2514
for (i = 0; i < elementsof(imap->op); i++)
2515
if (imap->op[i].vm)
2516
vmclose(imap->op[i].vm);
2517
if (imap->gm)
2518
vmclose(imap->gm);
2519
if (imap->vm)
2520
vmclose(imap->vm);
2521
if (imap->np)
2522
sfclose(imap->np);
2523
if (imap->sp)
2524
sfclose(imap->sp);
2525
if (imap->rp)
2526
sfclose(imap->rp);
2527
if (imap->mp)
2528
sfclose(imap->mp);
2529
if (imap->tp)
2530
sfclose(imap->tp);
2531
}
2532
2533
/*
2534
* IMAP save()
2535
*/
2536
2537
int
2538
imap_save(register Msg_t* mp, char* folder)
2539
{
2540
return imapexec(IMAP, "COPY %d %s", mp - state.msg.list + 1, folder + 1);
2541
}
2542
2543
#else
2544
2545
/*
2546
* stubs when IMAP can't fly
2547
*/
2548
2549
int
2550
imap_setptr(char* name, int isedit)
2551
{
2552
note(ERROR, "imap: support not enabled");
2553
return -1;
2554
}
2555
2556
#endif
2557
2558