Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/mailx/head.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the BSD package *
4
*Copyright (c) 1978-2011 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
* Mail -- a mail program
71
*
72
* Routines for processing and detecting headlines.
73
*/
74
75
#include "mailx.h"
76
77
static const struct lab fields[] = {
78
"newsgroups", GNEWS,
79
"article-id", GNEWS,
80
"reply-to", GNEWS|GREPLY,
81
"original-from", GNEWS|GREPLY,
82
"from", GNEWS|GREPLY,
83
"sender", GNEWS,
84
"apparently-to", GTO,
85
"original-to", GTO,
86
"to", GTO,
87
"cc", GCC,
88
"bcc", GBCC,
89
"subject", GSUB,
90
"subj", GSUB,
91
"status", GSTATUS,
92
"references", GREFERENCES,
93
"message-id", GMESSAGEID,
94
"received", 0,
95
"return-path", 0,
96
"importance", 0,
97
"in-reply-to", 0,
98
"priority", 0,
99
"x-", 0,
100
};
101
102
/*
103
* Data setup for mimehead().
104
*/
105
106
#define CONTENT_boundary 1
107
#define CONTENT_disposition 2
108
#define CONTENT_encoding 3
109
#define CONTENT_filename 4
110
#define CONTENT_name 5
111
#define CONTENT_type 6
112
113
struct content {
114
const char* name;
115
int index;
116
};
117
118
static const struct content contents[] = {
119
"boundary", CONTENT_boundary,
120
"disposition", CONTENT_disposition,
121
"filename", CONTENT_filename,
122
"name", CONTENT_name,
123
"transfer-encoding", CONTENT_encoding,
124
"type", CONTENT_type,
125
};
126
127
/*
128
* Called by mimehead() to set content data values.
129
*/
130
static int
131
content(Mime_t* mp, void* entry, char* data, size_t size, Mimedisc_t* disc)
132
{
133
register struct content* cp = (struct content*)entry;
134
register struct part* ap;
135
register struct bound* bp;
136
char* s;
137
char* e;
138
139
note(DEBUG, "content multi=%d %s `%-.*s'", state.part.in.multi, cp->name, size, data);
140
if (!(ap = ((struct state_part*)disc)->head))
141
ap = &state.part.global;
142
switch (cp->index) {
143
case CONTENT_boundary:
144
if (state.part.in.multi) {
145
if (!(bp = newof(0, struct bound, 1, size + 1))) {
146
note(ERROR|SYSTEM, "Out of space");
147
break;
148
}
149
bp->size = size;
150
memcpy(bp->data, data, size);
151
bp->next = state.part.in.boundary;
152
state.part.in.boundary = bp;
153
note(DEBUG, "content boundary=`%s'", bp->data);
154
}
155
break;
156
case CONTENT_disposition:
157
ap->flags |= PART_disposition;
158
if (!mimecmp("inline", data, NiL))
159
ap->flags |= PART_inline;
160
/*FALLTHROUGH*/
161
case CONTENT_type:
162
if (!mimecmp("multipart", data, NiL))
163
state.part.in.multi = 1;
164
else if (!*ap->type || !strchr(ap->type, '/') || cp->index == CONTENT_type && (ap->flags & PART_disposition)) {
165
ap->flags &= ~PART_disposition;
166
if (size >= sizeof(ap->type))
167
size = sizeof(ap->type) - 1;
168
strncopy(ap->type, data, size + 1);
169
strlower(ap->type);
170
s = data + size;
171
if (!(e = strchr(s, '\n')))
172
e = s + strlen(s);
173
if ((size = (e - s)) >= sizeof(ap->opts))
174
size = sizeof(ap->opts) - 1;
175
strncopy(ap->opts, s, size + 1);
176
if (!mimecmp("text/plain", data, NiL) || !mimecmp("text/enriched", data, NiL))
177
{
178
ap->flags |= PART_text;
179
if (!strncmp(ap->code, "bin", 3))
180
*ap->code = 0;
181
}
182
else if (!mimecmp("message", data, NiL))
183
ap->flags |= PART_message;
184
else
185
ap->flags |= PART_application;
186
}
187
break;
188
case CONTENT_encoding:
189
if (size >= sizeof(ap->code))
190
size = sizeof(ap->code) - 1;
191
strncopy(ap->code, data, size + 1);
192
strlower(ap->code);
193
if ((ap->flags & PART_text) && !strncmp(ap->code, "bin", 3))
194
*ap->code = 0;
195
break;
196
case CONTENT_name:
197
if (*ap->name)
198
break;
199
/*FALLTHROUGH*/
200
case CONTENT_filename:
201
if (size >= sizeof(ap->name))
202
size = sizeof(ap->name) - 1;
203
strncopy(ap->name, data, size + 1);
204
break;
205
}
206
return 0;
207
}
208
209
/*
210
* Open a mime handle and return non-0 if ok to mime.
211
* op==0 for header parsing, op==1 for viewing.
212
*/
213
int
214
mime(int op)
215
{
216
if (state.part.init < 0)
217
return 0;
218
if (!state.part.mime) {
219
state.part.disc.version = MIME_VERSION;
220
state.part.disc.flags = 0;
221
state.part.disc.errorf = 0;
222
state.part.disc.valuef = content;
223
if (!(state.part.mime = mimeopen(&state.part.disc))) {
224
state.part.init = -1;
225
return 0;
226
}
227
}
228
if (state.part.init < op) {
229
mimeload(state.part.mime, state.var.mailcap, MIME_LIST);
230
state.part.init = op;
231
}
232
return 1;
233
}
234
235
/*
236
* Set up a message for header parsing.
237
*/
238
int
239
headset(register struct parse* pp, struct msg* mp, FILE* fp, struct header* hp, Dt_t** ignore, unsigned long flags)
240
{
241
int r;
242
struct bound* bp;
243
244
while (bp = state.part.in.boundary) {
245
state.part.in.boundary = bp->next;
246
free(bp);
247
}
248
pp->fp = mp ? setinput(mp) : fp;
249
pp->mp = mp;
250
if ((flags & GMIME) && !mime(0))
251
flags &= ~GMIME;
252
if (pp->hp = hp) {
253
if (flags & GRULE)
254
headclear(hp, flags);
255
else
256
hp->h_clear = flags;
257
}
258
pp->count = mp ? mp->m_size : 0;
259
pp->flags = flags & (GDISPLAY|GNL);
260
pp->ignore = 0;
261
if (hp || (flags & GFROM))
262
r = 1;
263
else if ((r = headget(pp)) && (pp->flags & GDISPLAY) && pp->length > 6) {
264
register char* s;
265
register char* t;
266
struct name* np;
267
int c;
268
269
/*
270
* Pun on GMETOO -- if from me then don't ignore To:
271
*/
272
for (s = t = pp->buf + 5; *t && !isspace(*t); t++);
273
c = *t;
274
*t = 0;
275
if (streq(s, state.var.user) || (np = dictsearch(&state.aliases, s, LOOKUP)) && streq(np->name, state.var.user))
276
pp->flags |= GMETOO;
277
*t = c;
278
}
279
pp->ignore = ignore;
280
pp->flags |= flags;
281
pp->type = 0;
282
if (state.part.in.head) {
283
state.part.in.count = 0;
284
do {
285
state.part.in.tail = state.part.in.head->next;
286
free(state.part.in.head);
287
} while (state.part.in.head = state.part.in.tail);
288
}
289
state.part.in.multi = 0;
290
if (state.part.global.flags)
291
memset(&state.part.global, 0, sizeof(state.part.global));
292
return r;
293
}
294
295
/*
296
* Generate the message status field.
297
*/
298
static void
299
status(struct parse* pp)
300
{
301
register char* s;
302
303
pp->flags &= ~GSTATUS;
304
s = strcopy(pp->buf, "Status");
305
pp->separator = s;
306
*s++ = (pp->flags & GDISPLAY) ? ':' : 0;
307
*s++ = ' ';
308
if (pp->mp->m_flag & MSPAM)
309
*s++ = 'X';
310
if (pp->mp->m_flag & MREAD)
311
*s++ = 'R';
312
if (!(pp->mp->m_flag & MNEW))
313
*s++ = 'O';
314
if (*(s - 1) == ' ')
315
s--;
316
*s++ = '\n';
317
*s = 0;
318
pp->length = s - pp->buf;
319
if (!(pp->flags & GNL))
320
pp->buf[pp->length - 1] = 0;
321
}
322
323
/*
324
* Attach part ap to the current message.
325
*/
326
static void
327
attach(register struct part* ap)
328
{
329
register int c;
330
register int p;
331
register char* s;
332
register char* t;
333
334
if (!(ap->flags & (PART_application|PART_message|PART_text)))
335
ap->flags |= PART_text;
336
if ((ap->flags & PART_text) && ((ap->flags & PART_inline) || !ap->name[0]))
337
ap->flags |= PART_body;
338
if (!(ap->flags & PART_body))
339
ap->count = ++state.part.in.count;
340
if (!ap->name[0])
341
sfsprintf(ap->name, sizeof(ap->name), "%d.att", ap->count);
342
else {
343
s = t = ap->name;
344
for (p = 0; c = *s++; p = c) {
345
if (c == '/' || c == '\\') {
346
c = 0;
347
t = ap->name;
348
}
349
else {
350
if (isspace(c) || iscntrl(c) || !isprint(c) || c == '#' || c == '$' || c == '&' || c == '*' || c == '(' || c == ')' || c == '"' || c == '\'' || c == '`' || c == '<' || c == '>' || c == '?' || c == '[' || c == ']' || c == '{' || c == '}')
351
c = '_';
352
if (c != '_' && c != '-' || p != '_' && p != '-')
353
*t++ = c;
354
}
355
}
356
*t = 0;
357
}
358
if (!*ap->type)
359
strcpy(ap->type, "unknown");
360
note(DEBUG, "part %d offset=%ld size=%ld lines=%ld flags=0x%08x type=%s name=%s", ap->count, (long)ap->offset, (long)ap->size, (long)ap->lines, ap->flags, ap->type, ap->name);
361
if (state.part.in.tail)
362
state.part.in.tail->next = ap;
363
else
364
state.part.in.head = ap;
365
state.part.in.tail = ap;
366
}
367
368
/*
369
* Read the next header line.
370
*/
371
static char*
372
headline(register struct parse* pp)
373
{
374
register char* s;
375
register char* e;
376
register char* t;
377
register int c;
378
register int n;
379
380
if (pp->mp && pp->count <= 0 || (pp->flags & GDONE) || !fgets(pp->buf, sizeof(pp->buf), pp->fp)) {
381
pp->flags |= GDONE;
382
if (pp->hp)
383
pp->hp->h_clear = 0;
384
return 0;
385
}
386
pp->length = n = strlen(pp->buf);
387
s = pp->buf + n;
388
e = pp->buf + sizeof(pp->buf) - 1;
389
pp->count -= n;
390
if (*pp->buf != '\n' && (*pp->buf != '\r' || *(pp->buf + 1) != '\n')) {
391
while ((!pp->mp || pp->count > 0) && (c = fgetc(pp->fp)) != EOF) {
392
if (c == '\r' || c == '\n' || !isspace(c)) {
393
ungetc(c, pp->fp);
394
break;
395
}
396
t = s;
397
if (s < e) {
398
*(s - 1) = ' ';
399
*s++ = ' ';
400
}
401
n = 1;
402
while ((c = fgetc(pp->fp)) != EOF) {
403
n++;
404
if (s < e)
405
*s++ = c;
406
if (c == '\n')
407
break;
408
}
409
pp->count -= n;
410
pp->length += s - t;
411
}
412
}
413
*s = 0;
414
return pp->buf;
415
}
416
417
/*
418
* Get mime multipart content.
419
*/
420
static void
421
multipart(register struct parse* pp)
422
{
423
register char* s;
424
unsigned long count;
425
off_t offset;
426
int n;
427
int header;
428
struct part* ap;
429
struct bound* bp;
430
431
if (!state.part.in.boundary)
432
return;
433
pp->flags &= ~GMIME;
434
offset = ftell(pp->fp) - pp->length;
435
count = pp->count + pp->length;
436
ap = 0;
437
header = 0;
438
while (s = headline(pp)) {
439
if (state.part.in.boundary && (pp->length == (state.part.in.boundary->size + 3) || pp->length == (state.part.in.boundary->size + 5)) && s[0] == '-' && s[1] == '-' && !strncmp(s + 2, state.part.in.boundary->data, state.part.in.boundary->size)) {
440
if (ap) {
441
ap->raw.size = ftell(pp->fp) - ap->raw.offset - pp->length;
442
attach(ap);
443
note(DEBUG, "raw offset=%ld size=%ld", (long)ap->raw.offset, (long)ap->raw.size);
444
}
445
if (!(ap = newof(0, struct part, 1, 0))) {
446
note(ERROR|SYSTEM, "Out of space");
447
break;
448
}
449
ap->raw.offset = ftell(pp->fp);
450
header = 1;
451
if (s[state.part.in.boundary->size + 2] == '-' && s[state.part.in.boundary->size + 3] == '-') {
452
bp = state.part.in.boundary->next;
453
free(state.part.in.boundary);
454
if (!(state.part.in.boundary = bp)) {
455
header = 0;
456
while (pp->count > 0 && (n = getc(pp->fp)) == '\n')
457
pp->count--;
458
if (n != EOF)
459
ungetc(n, pp->fp);
460
ap->offset = ftell(pp->fp);
461
}
462
}
463
}
464
else if (header) {
465
state.part.head = ap;
466
if (!mimehead(state.part.mime, (void*)contents, elementsof(contents), sizeof(*contents), s)) {
467
if (ap->flags & PART_message) {
468
ap->flags &= ~PART_message;
469
header = 1;
470
}
471
else
472
header = strchr(s, ':') != 0;
473
ap->offset = ftell(pp->fp);
474
}
475
state.part.head = 0;
476
}
477
else if (ap) {
478
ap->size += pp->length;
479
ap->lines++;
480
}
481
}
482
if (ap) {
483
if (header)
484
free(ap);
485
else
486
attach(ap);
487
}
488
fseek(pp->fp, offset, SEEK_SET);
489
pp->count = count;
490
}
491
492
/*
493
* Get the next message header.
494
*/
495
int
496
headget(register struct parse* pp)
497
{
498
register char* s;
499
register char* t;
500
register int n;
501
register const struct lab* lp;
502
off_t body;
503
off_t text;
504
int i;
505
int separator;
506
507
while (s = headline(pp)) {
508
/*
509
* A blank line separates the headers from the body.
510
*/
511
if ((n = *s) == '\n' || n == '\r' && (n = *(s + 1)) == '\n') {
512
if (state.var.headerbotch && pp->fp != stdin && (body = ftell(pp->fp)) > 0) {
513
/*
514
* If the next batch of lines up to
515
* a blank line look like headers then
516
* treat them as such rather than
517
* message body. This compensates
518
* for those mailers that botch the
519
* message manual of style.
520
*/
521
text = body;
522
separator = 1;
523
i = 0;
524
for (;;) {
525
if (!(s = fgets(pp->buf, sizeof(pp->buf), pp->fp)))
526
goto done;
527
if ((n = *s) == '\n' || n == '\r' && (n = *(s + 1)) == '\n') {
528
if (!separator)
529
break;
530
text = ftell(pp->fp);
531
}
532
else if (isspace(n)) {
533
if (!i)
534
goto done;
535
separator = 0;
536
}
537
else if (isalpha(n) || n == '>') {
538
if (n != '>' || strncmp(++s, "From ", 5)) {
539
t = s;
540
do {
541
if (!*s || isspace(*s))
542
goto done;
543
} while (*s++ != ':');
544
if (!i) {
545
n = s - t - 1;
546
if (n > 2 && (t[0] == 'X' || t[0] == 'x') && t[1] == '-')
547
i = 1;
548
else {
549
i = -1;
550
do {
551
if (++i >= elementsof(fields))
552
goto done;
553
} while (strncasecmp(fields[i].name, t, n));
554
}
555
}
556
}
557
separator = 0;
558
}
559
else if (isprint(n)) {
560
while (*++s == n);
561
if (*s != '\n' && (*s != '\r' || *(s + 1) != '\n'))
562
goto done;
563
if (separator)
564
text = ftell(pp->fp);
565
}
566
else
567
goto done;
568
}
569
n = fseek(pp->fp, text, SEEK_SET);
570
pp->count -= text - body;
571
continue;
572
done:
573
n = fseek(pp->fp, body, SEEK_SET);
574
s = pp->buf;
575
*s++ = '\n';
576
*s = 0;
577
}
578
pp->flags |= GDONE;
579
break;
580
}
581
if (isspace(n))
582
continue;
583
if (!isalnum(n) && isprint(n)) {
584
t = s;
585
while (*++t == n);
586
if (*t == '\n' || *t == '\r' && *(t + 1) == '\n') {
587
if (n == '-' && (t - s) > 2) {
588
/*
589
* Edit rule treated like a blank line.
590
*/
591
break;
592
}
593
/*
594
* Treat rules as ignored header lines.
595
*/
596
continue;
597
}
598
}
599
if ((pp->flags & GMIME) && mimehead(state.part.mime, (void*)contents, elementsof(contents), sizeof(*contents), s))
600
multipart(pp);
601
else {
602
i = 0;
603
while (*s == '>') {
604
s++;
605
i = 1;
606
}
607
pp->name = s;
608
for (n = 0; *s && !isspace(*s) && *s != ':'; n = *s++) {
609
if (isupper(*s)) {
610
if (isalnum(n))
611
*s = tolower(*s);
612
}
613
else if (islower(*s)) {
614
if (!isalnum(n))
615
*s = toupper(*s);
616
}
617
}
618
if ((separator = *(pp->separator = s)) != ':' && i)
619
continue;
620
*s = 0;
621
if (!pp->ignore || !ignored(pp->ignore, pp->name) || (pp->flags & GMETOO) && streq(pp->name, "To") && ((pp->flags &= ~GMETOO), 1)) {
622
while (isspace(*++s));
623
pp->data = s;
624
pp->type = 0;
625
if (pp->flags & GCOMPOSE) {
626
for (lp = state.hdrtab; lp->name; lp++)
627
if (pp->flags & (lp->type|GMISC)) {
628
s = pp->name;
629
t = (char*)lp->name;
630
if (upper(*s) == *t) {
631
while (lower(*++s) == *++t);
632
if (!*s && *t == ':') {
633
if (pp->flags & lp->type) {
634
pp->flags &= ~lp->type;
635
pp->type = lp->type;
636
if (lp->type & GSTATUS)
637
status(pp);
638
}
639
break;
640
}
641
}
642
}
643
if (!pp->type && (pp->flags & GMISC)) {
644
pp->type = GMISC;
645
*pp->separator = separator;
646
pp->data = pp->name;
647
}
648
}
649
if (!(pp->flags & GNL))
650
pp->buf[pp->length - 1] = 0;
651
if (pp->type && pp->hp)
652
extract(pp->hp, pp->type, pp->data);
653
if (pp->flags & GDISPLAY)
654
*pp->separator = separator;
655
return 1;
656
}
657
}
658
}
659
if (pp->flags & GSTATUS) {
660
status(pp);
661
return 1;
662
}
663
if (state.part.in.head) {
664
fseek(pp->fp, state.part.in.head->offset, SEEK_SET);
665
pp->count = state.part.in.head->size;
666
}
667
return 0;
668
}
669
670
/*
671
* Fetch the field info by name from the passed message.
672
*/
673
static char*
674
grabname(struct parse* pp, struct msg* mp, char* name, unsigned long type)
675
{
676
register char* r = 0;
677
register int n = 0;
678
register int i;
679
register int u;
680
681
if (headset(pp, mp, NiL, NiL, NiL, 0)) {
682
u = (type & GUSER) ? strlen(state.var.user) : 0;
683
while (headget(pp)) {
684
if (!strcasecmp(pp->name, name)) {
685
if (!(type & GLAST))
686
return savestr(pp->data);
687
if (u > 0 && !strncasecmp(pp->data, state.var.user, u) && pp->data[u] == '@')
688
u = -u;
689
else if (u < 0) {
690
if (!*pp->data)
691
return savestr(pp->data);
692
continue;
693
}
694
i = strlen(pp->data) + 1;
695
if (i > n) {
696
n = i;
697
r = savestr(pp->data);
698
}
699
else
700
strcpy(r, pp->data);
701
}
702
}
703
}
704
return r;
705
}
706
707
/*
708
* Fetch the field info by type from the passed message.
709
*/
710
static char*
711
grabtype(struct parse* pp, register struct msg* mp, unsigned long type)
712
{
713
register char* s;
714
register char* e;
715
register char* t;
716
register FILE* fp;
717
struct sender* sp;
718
struct sendor* op;
719
struct sendand* ap;
720
int i;
721
int first = 1;
722
char namebuf[LINESIZE];
723
724
if (type & (GREPLY|GSENDER)) {
725
for (sp = state.sender; sp; sp = sp->next)
726
for (op = &sp->sendor; op; op = op->next)
727
for (ap = &op->sendand; ap;) {
728
if (!(s = grabname(pp, mp, ap->head, ap->flags)) || !strmatch(s, ap->pattern))
729
break;
730
if (!(ap = ap->next))
731
return sp->address;
732
}
733
if (type & GSENDER)
734
return 0;
735
}
736
e = namebuf + sizeof(namebuf);
737
for (i = 0; i < elementsof(fields); i++)
738
if ((fields[i].type & type) &&
739
(s = grabname(pp, mp, (char*)fields[i].name, type)) &&
740
((type & (GNEWS|GSUB)) || (s = skin(s, type))) &&
741
yankword(s, namebuf)) {
742
if ((type & GNEWS) && (t = strchr(s, ',')))
743
*t = 0;
744
if (type & (GNEWS|GSUB))
745
for (e = s, i = 0; *e; e++)
746
if (isspace(*e))
747
{
748
if (i || *e != ' ')
749
{
750
t = e;
751
while (i = *e++)
752
{
753
if (isspace(i))
754
for (i = ' '; isspace(*e); e++);
755
*t++ = i;
756
}
757
*t = 0;
758
break;
759
}
760
i = 1;
761
}
762
else
763
i = 0;
764
return s;
765
}
766
#if 0
767
if (!(type & (GREPLY|GTO)) || (type & GLAST))
768
#else
769
if (!(type & (GREPLY|GTO)))
770
#endif
771
return 0;
772
fp = setinput(mp);
773
namebuf[0] = 0;
774
if (readline(fp, pp->buf, sizeof(pp->buf)) >= 0) {
775
again:
776
for (;;) {
777
for (s = pp->buf; *s && *s != ' '; s++);
778
for (; *s == ' ' || *s == '\t'; s++) ;
779
for (t = &namebuf[strlen(namebuf)]; *s && *s != ' ' && *s != '\t' && t < namebuf + sizeof(namebuf) - 1;)
780
*t++ = *s++;
781
*t = 0;
782
if (readline(fp, pp->buf, sizeof(pp->buf)) < 0)
783
break;
784
if (!(s = strchr(pp->buf, 'F')))
785
break;
786
if (strncmp(s, "From", 4))
787
break;
788
while (s = strchr(s, 'r')) {
789
if (!strncmp(s, "remote", 6)) {
790
if (!(s = strchr(s, 'f')))
791
break;
792
if (strncmp(s, "from", 4))
793
break;
794
if (!(s = strchr(s, ' ')))
795
break;
796
s++;
797
if (first) {
798
first = 0;
799
s = strncopy(namebuf, s, e - namebuf);
800
}
801
else if (t = strrchr(namebuf, '!')) {
802
t++;
803
s = strncopy(t, s, e - t);
804
strncopy(s, "!", e - s);
805
}
806
goto again;
807
}
808
s++;
809
}
810
}
811
}
812
return skin(savestr(namebuf), type);
813
}
814
815
/*
816
* Grab field/address header from this message.
817
* If name!=0 then it contains the header name,
818
* otherwise grab by type.
819
*/
820
char*
821
grab(register struct msg* mp, unsigned long type, char* name)
822
{
823
register char* s;
824
struct parse pp;
825
826
if (!name)
827
s = grabtype(&pp, mp, type);
828
else if ((s = grabname(&pp, mp, name, type)) && (type & (GCOMPARE|GDISPLAY)))
829
s = normalize(s, type, NiL, 0);
830
return s;
831
}
832
833
/*
834
* Collect a liberal (space, tab delimited) word into b.
835
* Update p to point to the next word.
836
*/
837
char*
838
wordnext(char** p, char* b)
839
{
840
register char* s;
841
register char* t;
842
register int c;
843
844
if (!(s = *p) || !*s)
845
return 0;
846
while (isspace(*s))
847
s++;
848
t = b;
849
while ((c = *s++) && !isspace(c)) {
850
*t++ = c;
851
if (c == '"') {
852
do {
853
if (!(c = *s++)) {
854
s--;
855
break;
856
}
857
} while ((*t++ = c) != '"');
858
}
859
}
860
if (!c)
861
s--;
862
else
863
while (isspace(*s))
864
s++;
865
*p = s;
866
*t = 0;
867
return *b ? b : (char*)0;
868
}
869
870
/*
871
* Split a headline into its useful components.
872
* Copy the line into dynamic string space, then set
873
* pointers into the copied line in the passed headline
874
* structure. Actually, it scans.
875
*/
876
void
877
parse(struct msg* mp, char* line, register struct headline* hl, char* pbuf, size_t psize)
878
{
879
register char* s;
880
register char* t;
881
register char* e;
882
struct parse pp;
883
884
hl->l_from = 0;
885
hl->l_info = 0;
886
hl->l_date = 0;
887
t = pbuf;
888
e = t + psize;
889
if (mp && state.folder == FMH && strncmp(line, "From ", 5)) {
890
hl->l_from = grabname(&pp, mp, "From", 0);
891
hl->l_date = grabname(&pp, mp, "Date", 0);
892
}
893
else if (wordnext(&line, pp.buf) && (s = wordnext(&line, pp.buf))) {
894
if (state.var.news) {
895
t = strncopy(hl->l_info = t, s, e - t);
896
if (mp) {
897
hl->l_from = grabname(&pp, mp, "From", 0);
898
hl->l_date = grabname(&pp, mp, "Date", 0);
899
}
900
}
901
else {
902
t = strncopy(hl->l_from = t, s, e - t - 1) + 1;
903
if (*(s = line)) {
904
if (s[0] == 't' && s[1] == 't' && s[2] == 'y') {
905
t = strncopy(hl->l_info = t, s, e - t);
906
s = wordnext(&line, pp.buf);
907
}
908
if (*(s = line))
909
t = strncopy(hl->l_date = t, s, e - t);
910
}
911
}
912
}
913
}
914
915
/*
916
* See if the passed line buffer is a mail header.
917
* Return true if yes. Note the extreme pains to
918
* accomodate all funny formats.
919
*/
920
int
921
ishead(char* linebuf, int inhead)
922
{
923
register char* cp;
924
struct headline hl;
925
char parbuf[LINESIZE];
926
927
928
if (state.var.news) {
929
cp = linebuf;
930
if (*cp++ == 'A' && *cp++ == 'r' && *cp++ == 't' &&
931
*cp++ == 'i' && *cp++ == 'c' && *cp++ == 'l' &&
932
*cp++ == 'e' && (*cp == ' ' || *cp == ':'))
933
return 1;
934
}
935
cp = linebuf;
936
if (*cp++ != 'F' || *cp++ != 'r' || *cp++ != 'o' || *cp++ != 'm' ||
937
*cp++ != ' ')
938
return 0;
939
parse(NiL, linebuf, &hl, parbuf, sizeof(parbuf));
940
if (!hl.l_from || !hl.l_date || !isdate(hl.l_date)) {
941
note(DEBUG, "\"%s\": not a header: no from or date field", linebuf);
942
return 0;
943
}
944
return 1;
945
}
946
947