ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/mymap/mymapd
Revision: 1.1
Committed: Thu Aug 9 02:57:54 2001 UTC (22 years, 11 months ago) by root
Branch: MAIN
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 => "imap2",
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
108 || $arg =~ /\G \(($RE_pgrp)\)/gc) {
109 push @arg, $1;
110 } elsif ($arg =~ /\G {(\d+)}$/gc) {
111 resp $fh, $tag, "literals not yet implemented";
112 next CMD;
113 } elsif ($arg =~ /\G$/gc) {
114 last;
115 } elsif ($arg =~ /\G(.*)$/gc) {
116 $arg = $1;
117 $arg =~ y/\015\012//d;
118 resp $fh, $tag, "BAD parse error at '$arg' (@arg)";
119 next CMD;
120 } else {
121 die;
122 }
123 }
124
125 slog 9,"<<< $tag|$cmd|@arg";#d#
126 if ($cmd eq "capability") {
127 resp $fh, $tag, "CAPABILITY IMAP4", "OK";
128 } elsif ($cmd eq "noop") {
129 resp $fh, $tag, "OK";
130 # bah!
131 } elsif ($cmd eq "login" && !$uid) {
132 my $st = sql_exec \$uid, "select id from user where name = ? and pass = ?", $arg[0], $arg[1];
133 if ($st->fetch) {
134 resp $fh, $tag, "OK";
135 } else {
136 resp $fh, $tag, "NO user unknown or authenticitation failure";
137 }
138 } elsif ($cmd eq "authenticitate" && !$uid) {
139 resp $fh, "NO";
140 } elsif ($cmd eq "logout") {
141 resp $fh, $tag, "BYE", "OK";
142 last CMD;
143 } elsif ($uid) {
144 my $U;
145
146 if ($cmd eq "uid") {
147 $cmd = lc shift @arg;
148 $U = 1;
149 }
150
151 if ($cmd eq "select" || $cmd eq "examine") {
152 &$close;
153
154 $arg[0] = "INBOX" if lc $arg[0] eq "inbox";
155
156 my $st = sql_exec \$bid, \my($ctime, $uidv),
157 "select id, unix_timestamp(ctime), unix_timestamp(uidvalidity)
158 from box where uid = ? and name = ?",
159 $uid, $arg[0];
160 if ($st->fetch) {
161 my $r = 0;
162 $st = sql_exec \my($mid, $ntime),
163 "select id, ntime
164 from msg
165 where bid = ? and flags & $F_D = 0",
166 $bid;
167 while ($st->fetch) {
168 $r++ if $ntime >= $ctime;
169 push @mid, $mid;
170 }
171 $readonly = $cmd eq "examine";
172 resp $fh, $tag,
173 (scalar@mid) . " EXISTS",
174 "$r RECENT",
175 "FLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
176 "PERMANENTFLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
177 "OK [UID-VALIDITY $uidv]",
178 "OK [READ-".($readonly ? "ONLY]" : "WRITE]");
179 } else {
180 resp $fh, $tag, "NO mailbox does not exist";
181 }
182
183 # mailbox management
184 } elsif ($cmd eq "create") {
185 $arg[0] =~ s/\///;
186 eval {
187 sql_exec "insert into box (uid, name) values (?, ?)", $uid, $arg[0];
188 };
189 if ($@) {
190 $@ =~ y/\015\012//d;
191 resp $fh, $tag, "NO $@";
192 } else {
193 resp $fh, $tag, "OK";
194 }
195 } elsif ($cmd eq "delete") {
196 if (lc $arg[0] ne "inbox" and sql_exec "delete from box where uid = ? and name = ?", $uid, $arg[0]) {
197 resp $fh, $tag, "OK";
198 } else {
199 resp $fh, $tag, "NO";
200 }
201 } elsif ($cmd eq "rename") {
202 eval {
203 sql_exec "update box set name = ? where uid = ? and name = ?", $arg[1], $uid, $arg[0];
204 };
205 if ($@) {
206 $@ =~ y/\015\012//d;
207 resp $fh, $tag, "NO $@";
208 } else {
209 resp $fh, $tag, "OK";
210 }
211 } elsif ($cmd eq "subscribe" || $cmd eq "unsubscribe" || $cmd eq "lsub") {
212 resp $fh, $tag, "NO not supported";
213 } elsif ($cmd eq "list") {
214 if ($arg[0] != "") {
215 resp $fh, $tag, "NO reference not supported";
216 } else {
217 #$arg[1] =~ y/%/%/; #% not supported yet
218 $arg[1] =~ y/*/%/;
219 my $st = sql_exec \my($name),
220 "select name from box
221 where uid = ? and name like ?",
222 $uid, $arg[1];
223 my @r;
224 while ($st->fetch) {
225 push @r, "LIST () \"/\" $name";
226 }
227 resp $fh, $tag, @r, "OK";
228 }
229 } elsif ($cmd eq "append") {
230 resp $fh, $tag, "BAD not supported yet!";
231
232 # message handling
233 } elsif ($bid) {
234 if ($cmd eq "close") {
235 &$expunge;
236 &$close;
237 resp $fh, $tag, "OK";
238 } elsif ($cmd eq "expunge") {
239 die if $U;
240 &$expunge;
241 resp $fh, $tag, "OK";
242 } elsif ($cmd eq "fetch") {
243 my $list = expand_list($arg[0], scalar @mid);
244 my ($mid, $flags, $date, $head, $body);
245 my @exec;
246 my ($sql_body, $sql_head) = (0, 0);
247 my $sel = split_pgrp lc $arg[1];
248
249 while (my $s = shift @$sel) {
250 if ($s eq "all") {
251 push @$sel, qw(flags internaldate rfc822.size); next;
252 } elsif ($s eq "full") {
253 push @$sel, qw(flags internaldate rfc822.size envelope body); next;
254 } elsif ($s eq "uid") {
255 $s = sub { "UID $mid" };
256 } elsif ($s eq "flags") {
257 $s = sub {
258 "FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")";
259 };
260 } elsif ($s eq "internaldate") {
261 $s = sub { "INTERNALDATE \"$date\"" };
262 } elsif ($s eq "rfc822.size") {
263 $sql_body = "body";
264 $s = sub { "RFC822.SIZE ".length $body };
265 } elsif ($s eq "rfc822.header") {
266 $sql_head = "header";
267 $s = sub { "RFC822.HEADER ".literal($head) };
268 } elsif ($s eq "rfc822") {
269 $sql_body = "body"; $sql_head = "header";
270 $s = sub { "RFC822 ".literal($head.$CRLF.$body) };
271 } elsif ($s eq "rfc822.header.lines") {
272 $sql_head = "header";
273 my $re;
274 $re = '(?i:^((?:'
275 . (join "|", @{split_pgrp shift @$sel})
276 . '):[^\012\015]*))';
277 $re = qr($re);
278
279 $s = sub {
280 "RFC822.HEADER.LINES ".literal (join $CRLF, $head =~ /$re/mg);
281 };
282 } else {
283 $s = sub { '""' };
284 }
285 push @exec, $s;
286 }
287
288 my %rev;
289 unless ($U) {
290 for (@$list) {
291 $mid = $mid[$_ - 1];
292 $rev{$mid} = $_;
293 $_ = $mid;
294 }
295 }
296
297 my $st = sql_exec \($mid, $flags, $date, $head, $body),
298 "select id, flags, date_format(ntime, '%d-%m-%Y %h:%i:%s +0000'), $sql_head, $sql_body
299 from msg
300 where id in (".(join ",", @$list).")";
301
302 my $r;
303 while ($st->fetch) {
304 my $id = $U ? $mid : $rev{$mid};
305 $r .= "* $id FETCH (".(join " ", map &$_, @exec).")\015\12";
306 if (length $r > 32768) {
307 print $fh $r;
308 $r = "";
309 }
310 }
311 print $fh "$r$tag OK\015\012";
312 #resp $fh, $tag, "OK";
313
314 } elsif ($cmd eq "store") {
315 my $list = expand_list($arg[0], scalar @mid);
316 my $cmd = lc $arg[1];
317 my $flags = flags2bitmask(@{split_pgrp $arg[2]});
318
319 my %rev;
320 unless ($U) {
321 for (@$list) {
322 $mid = $mid[$_ - 1];
323 $rev{$mid} = $_;
324 $_ = $mid;
325 }
326 }
327
328 my $ids = join ",", @$list;
329
330 my $silent = $cmd =~ s/\.silent$//;
331
332 if ($cmd eq "flags") {
333 sql_exec "update msg set flags = ? where id in ($ids)", $flags;
334 } elsif ($cmd eq "+flags") {
335 sql_exec "update msg set flags = flags | ? where id in ($ids)", $flags;
336 } elsif ($cmd eq "-flags") {
337 sql_exec "update msg set flags = flags & ~? where id in ($ids)", $flags;
338 }
339
340 if ($silent) {
341 resp $fh, $tag, "OK";
342 } else {
343 my $st = sql_exec \($mid, $flags),
344 "select id, flags
345 from msg
346 where id in ($ids)";
347
348 my $r;
349 while ($st->fetch) {
350 my $id = $U ? $mid : $rev{$mid};
351 $r .= "* $id FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")\015\012";
352 }
353 print $fh "$r$tag OK\015\012";
354 #resp $fh, $tag, "OK";
355 }
356
357 } else {
358 resp $fh, $tag, "BAD command not understood";
359 }
360
361 # well...
362 } else {
363 resp $fh, $tag, "BAD command not understood";
364 }
365
366 } else {
367 resp $fh, $tag, "BAD command not understood";
368 }
369 }
370 };
371 if ($@) {
372 resp $fh, "*", "BYE";
373 slog 0, "$@";
374 }
375 slog 3, "$fh: closed connection\n";#d#
376 }
377
378 async {
379 slog 1, "accepting connections";
380 while () {
381 my $fh = $port->accept;
382 slog 3, "accepted $fh on $port";
383 async \&handle_connection, $fh;
384 undef $fh;
385 }
386 };
387
388 loop;
389