xref: /plan9/sys/src/cmd/ip/imap4d/search.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6 
7 static int	dateCmp(char *date, Search *s);
8 static int	addrSearch(MAddr *a, char *s);
9 static int	fileSearch(Msg *m, char *file, char *pat);
10 static int	headerSearch(Msg *m, char *hdr, char *pat);
11 
12 /*
13  * free to exit, parseErr, since called before starting any client reply
14  *
15  * the header and envelope searches should convert mime character set escapes.
16  */
17 int
18 searchMsg(Msg *m, Search *s)
19 {
20 	MsgSet *ms;
21 	int ok;
22 
23 	if(!msgStruct(m, 1) || m->expunged)
24 		return 0;
25 	for(ok = 1; ok && s != nil; s = s->next){
26 		switch(s->key){
27 		default:
28 			ok = 0;
29 			break;
30 		case SKNot:
31 			ok = !searchMsg(m, s->left);
32 			break;
33 		case SKOr:
34 			ok = searchMsg(m, s->left) || searchMsg(m, s->right);
35 			break;
36 		case SKAll:
37 			ok = 1;
38 			break;
39 		case SKAnswered:
40 			ok = (m->flags & MAnswered) == MAnswered;
41 			break;
42 		case SKDeleted:
43 			ok = (m->flags & MDeleted) == MDeleted;
44 			break;
45 		case SKDraft:
46 			ok = (m->flags & MDraft) == MDraft;
47 			break;
48 		case SKFlagged:
49 			ok = (m->flags & MFlagged) == MFlagged;
50 			break;
51 		case SKKeyword:
52 			ok = (m->flags & s->num) == s->num;
53 			break;
54 		case SKNew:
55 			ok = (m->flags & (MRecent|MSeen)) == MRecent;
56 			break;
57 		case SKOld:
58 			ok = (m->flags & MRecent) != MRecent;
59 			break;
60 		case SKRecent:
61 			ok = (m->flags & MRecent) == MRecent;
62 			break;
63 		case SKSeen:
64 			ok = (m->flags & MSeen) == MSeen;
65 			break;
66 		case SKUnanswered:
67 			ok = (m->flags & MAnswered) != MAnswered;
68 			break;
69 		case SKUndeleted:
70 			ok = (m->flags & MDeleted) != MDeleted;
71 			break;
72 		case SKUndraft:
73 			ok = (m->flags & MDraft) != MDraft;
74 			break;
75 		case SKUnflagged:
76 			ok = (m->flags & MFlagged) != MFlagged;
77 			break;
78 		case SKUnkeyword:
79 			ok = (m->flags & s->num) != s->num;
80 			break;
81 		case SKUnseen:
82 			ok = (m->flags & MSeen) != MSeen;
83 			break;
84 
85 		case SKLarger:
86 			ok = msgSize(m) > s->num;
87 			break;
88 		case SKSmaller:
89 			ok = msgSize(m) < s->num;
90 			break;
91 
92 		case SKBcc:
93 			ok = addrSearch(m->bcc, s->s);
94 			break;
95 		case SKCc:
96 			ok = addrSearch(m->cc, s->s);
97 			break;
98 		case SKFrom:
99 			ok = addrSearch(m->from, s->s);
100 			break;
101 		case SKTo:
102 			ok = addrSearch(m->to, s->s);
103 			break;
104 		case SKSubject:
105 			ok = cistrstr(m->info[ISubject], s->s) != nil;
106 			break;
107 
108 		case SKBefore:
109 			ok = dateCmp(m->unixDate, s) < 0;
110 			break;
111 		case SKOn:
112 			ok = dateCmp(m->unixDate, s) == 0;
113 			break;
114 		case SKSince:
115 			ok = dateCmp(m->unixDate, s) > 0;
116 			break;
117 		case SKSentBefore:
118 			ok = dateCmp(m->info[IDate], s) < 0;
119 			break;
120 		case SKSentOn:
121 			ok = dateCmp(m->info[IDate], s) == 0;
122 			break;
123 		case SKSentSince:
124 			ok = dateCmp(m->info[IDate], s) > 0;
125 			break;
126 
127 		case SKUid:
128 		case SKSet:
129 			for(ms = s->set; ms != nil; ms = ms->next)
130 				if(s->key == SKUid && m->uid >= ms->from && m->uid <= ms->to
131 				|| s->key == SKSet && m->seq >= ms->from && m->seq <= ms->to)
132 					break;
133 			ok = ms != nil;
134 			break;
135 
136 		case SKHeader:
137 			ok = headerSearch(m, s->hdr, s->s);
138 			break;
139 
140 		case SKBody:
141 		case SKText:
142 			if(s->key == SKText && cistrstr(m->head.buf, s->s)){
143 				ok = 1;
144 				break;
145 			}
146 			ok = fileSearch(m, "body", s->s);
147 			break;
148 		}
149 	}
150 	return ok;
151 }
152 
153 static int
154 fileSearch(Msg *m, char *file, char *pat)
155 {
156 	char buf[BufSize + 1];
157 	int n, nbuf, npat, fd, ok;
158 
159 	npat = strlen(pat);
160 	if(npat >= BufSize / 2)
161 		return 0;
162 
163 	fd = msgFile(m, file);
164 	if(fd < 0)
165 		return 0;
166 	ok = 0;
167 	nbuf = 0;
168 	for(;;){
169 		n = read(fd, &buf[nbuf], BufSize - nbuf);
170 		if(n <= 0)
171 			break;
172 		nbuf += n;
173 		buf[nbuf] = '\0';
174 		if(cistrstr(buf, pat) != nil){
175 			ok = 1;
176 			break;
177 		}
178 		if(nbuf > npat){
179 			memmove(buf, &buf[nbuf - npat], npat);
180 			nbuf = npat;
181 		}
182 	}
183 	close(fd);
184 	return ok;
185 }
186 
187 static int
188 headerSearch(Msg *m, char *hdr, char *pat)
189 {
190 	SList hdrs;
191 	char *s, *t;
192 	int ok, n;
193 
194 	n = m->head.size + 3;
195 	s = emalloc(n);
196 	hdrs.next = nil;
197 	hdrs.s = hdr;
198 	ok = 0;
199 	if(selectFields(s, n, m->head.buf, &hdrs, 1) > 0){
200 		t = strchr(s, ':');
201 		if(t != nil && cistrstr(t+1, pat) != nil)
202 			ok = 1;
203 	}
204 	free(s);
205 	return ok;
206 }
207 
208 static int
209 addrSearch(MAddr *a, char *s)
210 {
211 	char *ok, *addr;
212 
213 	for(; a != nil; a = a->next){
214 		addr = maddrStr(a);
215 		ok = cistrstr(addr, s);
216 		free(addr);
217 		if(ok != nil)
218 			return 1;
219 	}
220 	return 0;
221 }
222 
223 static int
224 dateCmp(char *date, Search *s)
225 {
226 	Tm tm;
227 
228 	date2tm(&tm, date);
229 	if(tm.year < s->year)
230 		return -1;
231 	if(tm.year > s->year)
232 		return 1;
233 	if(tm.mon < s->mon)
234 		return -1;
235 	if(tm.mon > s->mon)
236 		return 1;
237 	if(tm.mday < s->mday)
238 		return -1;
239 	if(tm.mday > s->mday)
240 		return 1;
241 	return 0;
242 }
243