ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/mymap/mymapd
Revision: 1.2
Committed: Wed Aug 27 01:38:42 2003 UTC (20 years, 9 months ago) by pcg
Branch: MAIN
Changes since 1.1: +12 -8 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #!/usr/bin/perl
2
3 use Coro;
4 use Coro::Event;
5 use Coro::Socket;
6
7 BEGIN { require "mymap.pl" };
8
9 $/ = $CRLF;
10
11 my $port = new Coro::Socket
12 LocalPort => "imap",
13 ReuseAddr => 1,
14 Listen => 1,
15 or die;
16
17 sub slog {
18 my $level = shift;
19 my $format = shift;
20 printf "---: $format\n", @_;
21 }
22
23 sub resp {
24 slog 9, ">>> @_";
25 my $r;
26 $r .= "* $_[$_]$CRLF" for 2 .. $#_-1;
27 $r .= "$_[1] $_[$#_]$CRLF";
28 print {$_[0]} $r;
29 }
30
31 $RE_pgrp = qr{
32 (?:
33 [^()]*
34 | \( (??{ $RE_pgrp }) \)
35 )*
36 }x;
37
38 sub expand_list {
39 my @r;
40 $_[0] =~ s/\*/$_[1]/g;
41 for (split /,/, $_[0]) {
42 if (/(\d+):(\d+)/) {
43 push @r, $_ for $1 .. $2;
44 } else {
45 push @r, $_;
46 }
47 }
48 \@r;
49 }
50
51 sub split_pgrp {
52 my @r;
53 while ($_[0] =~ /\G(?:([^( ]+)|\(($RE_pgrp)\)) ?/mg) {
54 push @r, defined $1 ? $1 : $2;
55 }
56 \@r;
57 }
58
59 sub literal {
60 "{" . (length $_[0]) . "}\015\012$_[0]";
61 }
62
63 sub handle_connection {
64 my $fh = shift;
65
66 eval {
67
68 my $uid; # user-id
69 my $bid; # mailbox-id
70 my $readonly; # mailbox read-only?
71 my @mid; # message numbers
72
73 my $expunge = sub {
74 my $st = sql_exec \my($id),
75 "select id from msg
76 where flags & ($F_D|$F_deleted) = ($F_deleted)
77 and bid = ?",
78 $bid;
79 while ($st->fetch) {
80 my %rev;
81 @rev{@mid} = (1..@mid);
82 my $idx = $rev{$id}
83 or next;
84 print $fh "* $idx EXPUNGE\015\012";
85 splice @mid, $idx-1, 1;
86 sql_exec "update msg set flags = flags | $F_D where id = ?", $id;
87 }
88 };
89
90 my $close = sub {
91 return unless $bid;
92 sql_exec "update box set ctime = ? where id = ?", time, $bid;
93 $bid = 0;
94 @mid = ();
95 };
96
97 #resp $fh, "*", "OK IMAP4 service ready.";
98 resp $fh, "*", "PREAUTH IMAP4 service ready."; #$uid = 1;
99 CMD:
100 while (<$fh>) {
101 /^(\S+)(?: (\S+)([^\015\012]*))?$CRLF$/os or last;
102 my ($tag, $cmd, $arg) = ($1, $2, $3);
103 my @arg;
104 $cmd = lc $cmd;
105 while () {
106 if ($arg =~ /\G "((?:[^"\\]*|\\.))"/gc
107 #|| $arg =~ /\G ([^"(){ \x00-\x1f*%\\]+)/gc # ] is allowed...
108 || $arg =~ /\G ([^"(){ \x00-\x1f%\\]+)/gc # evolution-fix
109 || $arg =~ /\G \(($RE_pgrp)\)/gc) {
110 push @arg, $1;
111 } elsif ($arg =~ /\G {(\d+)}$/gc) {
112 resp $fh, $tag, "literals not yet implemented";
113 next CMD;
114 } elsif ($arg =~ /\G$/gc) {
115 last;
116 } elsif ($arg =~ /\G(.*)$/gc) {
117 $arg = $1;
118 $arg =~ y/\015\012//d;
119 resp $fh, $tag, "BAD parse error at '$arg' (@arg)";
120 next CMD;
121 } else {
122 die;
123 }
124 }
125
126 slog 9, "<<< $tag|$cmd|@arg";#d#
127 if ($cmd eq "capability") {
128 resp $fh, $tag, "CAPABILITY IMAP4", "OK";
129 } elsif ($cmd eq "noop") {
130 resp $fh, $tag, "OK";
131 # bah!
132 } elsif ($cmd eq "login" && !$uid) {
133 my $st = sql_exec \$uid, "select id from user where name = ? and pass = ?", $arg[0], $arg[1];
134 if ($st->fetch) {
135 resp $fh, $tag, "OK";
136 } else {
137 resp $fh, $tag, "NO user unknown or authenticitation failure";
138 }
139 } elsif ($cmd eq "authenticitate" && !$uid) {
140 resp $fh, "NO";
141 } elsif ($cmd eq "logout") {
142 resp $fh, $tag, "BYE", "OK";
143 last CMD;
144 } elsif ($uid) {
145 my $U;
146
147 if ($cmd eq "uid") {
148 $cmd = lc shift @arg;
149 $U = 1;
150 }
151
152 if ($cmd eq "select" || $cmd eq "examine") {
153 &$close;
154
155 $arg[0] = "INBOX" if lc $arg[0] eq "inbox";
156
157 my $st = sql_exec \$bid, \my($ctime, $uidv),
158 "select id, unix_timestamp(ctime), unix_timestamp(uidvalidity)
159 from box where uid = ? and name = ?",
160 $uid, $arg[0];
161 if ($st->fetch) {
162 my $r = 0;
163 $st = sql_exec \my($mid, $ntime),
164 "select id, ntime
165 from msg
166 where bid = ? and flags & $F_D = 0",
167 $bid;
168 while ($st->fetch) {
169 $r++ if $ntime >= $ctime;
170 push @mid, $mid;
171 }
172 $readonly = $cmd eq "examine";
173 resp $fh, $tag,
174 (scalar@mid) . " EXISTS",
175 "$r RECENT",
176 "FLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
177 "PERMANENTFLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
178 "OK [UID-VALIDITY $uidv]",
179 "OK [READ-".($readonly ? "ONLY]" : "WRITE]");
180 } else {
181 resp $fh, $tag, "NO mailbox does not exist";
182 }
183
184 # mailbox management
185 } elsif ($cmd eq "create") {
186 $arg[0] =~ s/\///;
187 eval {
188 sql_exec "insert into box (uid, name) values (?, ?)", $uid, $arg[0];
189 };
190 if ($@) {
191 $@ =~ y/\015\012//d;
192 resp $fh, $tag, "NO $@";
193 } else {
194 resp $fh, $tag, "OK";
195 }
196 } elsif ($cmd eq "delete") {
197 if (lc $arg[0] ne "inbox" and sql_exec "delete from box where uid = ? and name = ?", $uid, $arg[0]) {
198 resp $fh, $tag, "OK";
199 } else {
200 resp $fh, $tag, "NO";
201 }
202 } elsif ($cmd eq "rename") {
203 eval {
204 sql_exec "update box set name = ? where uid = ? and name = ?", $arg[1], $uid, $arg[0];
205 };
206 if ($@) {
207 $@ =~ y/\015\012//d;
208 resp $fh, $tag, "NO $@";
209 } else {
210 resp $fh, $tag, "OK";
211 }
212 } elsif ($cmd eq "subscribe" || $cmd eq "unsubscribe" || $cmd eq "lsub") {
213 resp $fh, $tag, "NO not supported";
214 } elsif ($cmd eq "list") {
215 if ($arg[0] != "") {
216 resp $fh, $tag, "NO reference not supported";
217 } else {
218 #$arg[1] =~ y/%/%/; #% not supported yet
219 $arg[1] =~ y/*/%/;
220 my $st = sql_exec \my($name),
221 "select name from box
222 where uid = ? and name like ?",
223 $uid, $arg[1];
224 my @r;
225 while ($st->fetch) {
226 push @r, "LIST () \"/\" $name";
227 }
228 resp $fh, $tag, @r, "OK";
229 }
230 } elsif ($cmd eq "append") {
231 resp $fh, $tag, "BAD not supported yet!";
232
233 # message handling
234 } elsif ($bid) {
235 if ($cmd eq "close") {
236 &$expunge;
237 &$close;
238 resp $fh, $tag, "OK";
239 } elsif ($cmd eq "expunge") {
240 die if $U;
241 &$expunge;
242 resp $fh, $tag, "OK";
243 } elsif ($cmd eq "fetch") {
244 my $list = expand_list($arg[0], scalar @mid);
245 my ($mid, $flags, $date, $head, $body);
246 my @exec;
247 my ($sql_body, $sql_head) = (0, 0);
248 my $sel = split_pgrp lc $arg[1];
249
250 while (my $s = shift @$sel) {
251 if ($s eq "all") {
252 push @$sel, qw(flags internaldate rfc822.size); next;
253 } elsif ($s eq "full") {
254 push @$sel, qw(flags internaldate rfc822.size envelope body); next;
255 } elsif ($s eq "uid") {
256 $s = sub { "UID $mid" };
257 } elsif ($s eq "flags") {
258 $s = sub {
259 "FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")";
260 };
261 } elsif ($s eq "internaldate") {
262 $s = sub { "INTERNALDATE \"$date\"" };
263 } elsif ($s eq "rfc822.size") {
264 $sql_body = "body";
265 $s = sub { "RFC822.SIZE ".length $body };
266 } elsif ($s eq "rfc822.header") {
267 $sql_head = "header";
268 $s = sub { "RFC822.HEADER ".literal($head) };
269 } elsif ($s eq "rfc822") {
270 $sql_body = "body"; $sql_head = "header";
271 $s = sub { "RFC822 ".literal($head.$CRLF.$body) };
272 } elsif ($s eq "body.peek[0]") {#d# evolution-bug
273 $sql_head = "header";
274 $s = sub { "BODY[0] ".literal($head) };
275 } elsif ($s eq "rfc822.header.lines") {
276 $sql_head = "header";
277 my $re;
278 $re = '(?i:^((?:'
279 . (join "|", @{split_pgrp shift @$sel})
280 . '):[^\012\015]*))';
281 $re = qr($re);
282
283 $s = sub {
284 "RFC822.HEADER.LINES ".literal (join $CRLF, $head =~ /$re/mg);
285 };
286 } else {
287 $s = sub { '""' };
288 }
289 push @exec, $s;
290 }
291
292 my %rev;
293 unless ($U) {
294 for (@$list) {
295 $mid = $mid[$_ - 1];
296 $rev{$mid} = $_;
297 $_ = $mid;
298 }
299 }
300
301 my $st = sql_exec \($mid, $flags, $date, $head, $body),
302 "select id, flags, date_format(ntime, '%d-%m-%Y %h:%i:%s +0000'), $sql_head, $sql_body
303 from msg
304 where id in (".(join ",", @$list).")";
305
306 my $r;
307 while ($st->fetch) {
308 my $id = $U ? $mid : $rev{$mid};
309 $r .= "* $id FETCH (".(join " ", map &$_, @exec).")\015\12";
310 if (length $r > 32768) {
311 print $fh $r;
312 $r = "";
313 }
314 }
315 print $fh "$r$tag OK\015\012";
316 #resp $fh, $tag, "OK";
317
318 } elsif ($cmd eq "store") {
319 my $list = expand_list($arg[0], scalar @mid);
320 my $cmd = lc $arg[1];
321 my $flags = flags2bitmask(@{split_pgrp $arg[2]});
322
323 my %rev;
324 unless ($U) {
325 for (@$list) {
326 $mid = $mid[$_ - 1];
327 $rev{$mid} = $_;
328 $_ = $mid;
329 }
330 }
331
332 my $ids = join ",", @$list;
333
334 my $silent = $cmd =~ s/\.silent$//;
335
336 if ($cmd eq "flags") {
337 sql_exec "update msg set flags = ? where id in ($ids)", $flags;
338 } elsif ($cmd eq "+flags") {
339 sql_exec "update msg set flags = flags | ? where id in ($ids)", $flags;
340 } elsif ($cmd eq "-flags") {
341 sql_exec "update msg set flags = flags & ~? where id in ($ids)", $flags;
342 }
343
344 if ($silent) {
345 resp $fh, $tag, "OK";
346 } else {
347 my $st = sql_exec \($mid, $flags),
348 "select id, flags
349 from msg
350 where id in ($ids)";
351
352 my $r;
353 while ($st->fetch) {
354 my $id = $U ? $mid : $rev{$mid};
355 $r .= "* $id FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")\015\012";
356 }
357 print $fh "$r$tag OK\015\012";
358 #resp $fh, $tag, "OK";
359 }
360
361 } else {
362 resp $fh, $tag, "BAD command not understood (nocommand)";
363 }
364
365 # well...
366 } else {
367 resp $fh, $tag, "BAD command not understood (notmailbox)";
368 }
369
370 } else {
371 resp $fh, $tag, "BAD command not understood (notauth)";
372 }
373 }
374 };
375 if ($@) {
376 resp $fh, "*", "BYE";
377 slog 0, "$@";
378 }
379 slog 3, "$fh: closed connection\n";#d#
380 }
381
382 async {
383 slog 1, "accepting connections";
384 while () {
385 my $fh = $port->accept;
386 slog 3, "accepted $fh on $port";
387 async \&handle_connection, $fh;
388 undef $fh;
389 }
390 };
391
392 loop;
393