ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/mymap/mymapd
Revision: 1.3
Committed: Sat Nov 15 03:53:10 2003 UTC (20 years, 6 months ago) by pcg
Branch: MAIN
CVS Tags: rel-2_5, rel-4_91, rel-4_22, rel-4_21, rel-4_0, rel-4_3, rel-3_41, rel-5_151, rel-4_13, rel-4_11, rel-5_1, rel-5_0, rel-6_0, rel-6_5, rel-4_748, rel-3_55, rel-4_8, rel-4_9, rel-3_51, rel-4_741, rel-4_743, rel-4_742, rel-6_10, rel-4_744, rel-4_747, rel-6_13, rel-4_01, rel-4_03, rel-4_02, rel-2_0, rel-2_1, rel-1_1, rel-1_0, rel-1_9, rel-1_2, rel-3_6, rel-3_62, rel-3_63, rel-3_61, rel-1_5, rel-1_4, rel-1_7, rel-1_6, rel-3_4, rel-6_09, rel-6_08, rel-6_07, rel-6_06, rel-6_05, rel-6_04, rel-6_03, rel-6_02, rel-6_01, rel-5_161, rel-3_1, rel-4_74, rel-4_71, rel-4_72, rel-4_73, rel-5_371, rel-5_372, rel-6_512, rel-6_513, rel-6_511, rel-6_514, rel-5_22, rel-5_23, rel-5_24, rel-5_25, rel-6_32, rel-6_33, rel-6_31, rel-6_36, rel-6_37, rel-5_162, rel-5_2, rel-6_38, rel-6_39, rel-4_802, rel-4_803, rel-3_5, rel-4_801, rel-3_3, rel-3_2, rel-4_804, rel-3_0, rel-5_37, rel-5_36, rel-4_479, rel-6_23, rel-3_01, rel-6_29, rel-6_28, rel-6_46, rel-4_50, rel-4_51, rel-6_45, rel-4_4, rel-3_11, rel-1_31, rel-4_45, rel-6_51, rel-6_52, rel-6_53, rel-6_54, rel-6_55, rel-6_56, rel-6_57, rel-4_745, rel-4_901, rel-4_49, rel-4_48, rel-4_1, rel-4_2, rel-4_746, rel-5_11, rel-5_12, rel-5_15, rel-5_14, rel-5_17, rel-5_16, stack_sharing, rel-4_47, rel-4_46, rel-4_7, rel-3_501, rel-6_43, rel-6_42, rel-6_41, rel-6_47, rel-5_132, rel-5_131, rel-6_44, rel-6_49, rel-6_48, rel-4_911, rel-4_912, rel-4_31, rel-4_32, rel-4_33, rel-4_34, rel-4_35, rel-4_36, rel-4_37, HEAD
Changes since 1.2: +1 -0 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 warn $_;#d#
102 /^(\S+)(?: (\S+)([^\015\012]*))?$CRLF$/os or last;
103 my ($tag, $cmd, $arg) = ($1, $2, $3);
104 my @arg;
105 $cmd = lc $cmd;
106 while () {
107 if ($arg =~ /\G "((?:[^"\\]*|\\.))"/gc
108 #|| $arg =~ /\G ([^"(){ \x00-\x1f*%\\]+)/gc # ] is allowed...
109 || $arg =~ /\G ([^"(){ \x00-\x1f%\\]+)/gc # evolution-fix
110 || $arg =~ /\G \(($RE_pgrp)\)/gc) {
111 push @arg, $1;
112 } elsif ($arg =~ /\G {(\d+)}$/gc) {
113 resp $fh, $tag, "literals not yet implemented";
114 next CMD;
115 } elsif ($arg =~ /\G$/gc) {
116 last;
117 } elsif ($arg =~ /\G(.*)$/gc) {
118 $arg = $1;
119 $arg =~ y/\015\012//d;
120 resp $fh, $tag, "BAD parse error at '$arg' (@arg)";
121 next CMD;
122 } else {
123 die;
124 }
125 }
126
127 slog 9, "<<< $tag|$cmd|@arg";#d#
128 if ($cmd eq "capability") {
129 resp $fh, $tag, "CAPABILITY IMAP4", "OK";
130 } elsif ($cmd eq "noop") {
131 resp $fh, $tag, "OK";
132 # bah!
133 } elsif ($cmd eq "login" && !$uid) {
134 my $st = sql_exec \$uid, "select id from user where name = ? and pass = ?", $arg[0], $arg[1];
135 if ($st->fetch) {
136 resp $fh, $tag, "OK";
137 } else {
138 resp $fh, $tag, "NO user unknown or authenticitation failure";
139 }
140 } elsif ($cmd eq "authenticitate" && !$uid) {
141 resp $fh, "NO";
142 } elsif ($cmd eq "logout") {
143 resp $fh, $tag, "BYE", "OK";
144 last CMD;
145 } elsif ($uid) {
146 my $U;
147
148 if ($cmd eq "uid") {
149 $cmd = lc shift @arg;
150 $U = 1;
151 }
152
153 if ($cmd eq "select" || $cmd eq "examine") {
154 &$close;
155
156 $arg[0] = "INBOX" if lc $arg[0] eq "inbox";
157
158 my $st = sql_exec \$bid, \my($ctime, $uidv),
159 "select id, unix_timestamp(ctime), unix_timestamp(uidvalidity)
160 from box where uid = ? and name = ?",
161 $uid, $arg[0];
162 if ($st->fetch) {
163 my $r = 0;
164 $st = sql_exec \my($mid, $ntime),
165 "select id, ntime
166 from msg
167 where bid = ? and flags & $F_D = 0",
168 $bid;
169 while ($st->fetch) {
170 $r++ if $ntime >= $ctime;
171 push @mid, $mid;
172 }
173 $readonly = $cmd eq "examine";
174 resp $fh, $tag,
175 (scalar@mid) . " EXISTS",
176 "$r RECENT",
177 "FLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
178 "PERMANENTFLAGS (\\D \\Answered \\Flagged \\Deleted \\Seen \\Draft)",
179 "OK [UID-VALIDITY $uidv]",
180 "OK [READ-".($readonly ? "ONLY]" : "WRITE]");
181 } else {
182 resp $fh, $tag, "NO mailbox does not exist";
183 }
184
185 # mailbox management
186 } elsif ($cmd eq "create") {
187 $arg[0] =~ s/\///;
188 eval {
189 sql_exec "insert into box (uid, name) values (?, ?)", $uid, $arg[0];
190 };
191 if ($@) {
192 $@ =~ y/\015\012//d;
193 resp $fh, $tag, "NO $@";
194 } else {
195 resp $fh, $tag, "OK";
196 }
197 } elsif ($cmd eq "delete") {
198 if (lc $arg[0] ne "inbox" and sql_exec "delete from box where uid = ? and name = ?", $uid, $arg[0]) {
199 resp $fh, $tag, "OK";
200 } else {
201 resp $fh, $tag, "NO";
202 }
203 } elsif ($cmd eq "rename") {
204 eval {
205 sql_exec "update box set name = ? where uid = ? and name = ?", $arg[1], $uid, $arg[0];
206 };
207 if ($@) {
208 $@ =~ y/\015\012//d;
209 resp $fh, $tag, "NO $@";
210 } else {
211 resp $fh, $tag, "OK";
212 }
213 } elsif ($cmd eq "subscribe" || $cmd eq "unsubscribe" || $cmd eq "lsub") {
214 resp $fh, $tag, "NO not supported";
215 } elsif ($cmd eq "list") {
216 if ($arg[0] != "") {
217 resp $fh, $tag, "NO reference not supported";
218 } else {
219 #$arg[1] =~ y/%/%/; #% not supported yet
220 $arg[1] =~ y/*/%/;
221 my $st = sql_exec \my($name),
222 "select name from box
223 where uid = ? and name like ?",
224 $uid, $arg[1];
225 my @r;
226 while ($st->fetch) {
227 push @r, "LIST () \"/\" $name";
228 }
229 resp $fh, $tag, @r, "OK";
230 }
231 } elsif ($cmd eq "append") {
232 resp $fh, $tag, "BAD not supported yet!";
233
234 # message handling
235 } elsif ($bid) {
236 if ($cmd eq "close") {
237 &$expunge;
238 &$close;
239 resp $fh, $tag, "OK";
240 } elsif ($cmd eq "expunge") {
241 die if $U;
242 &$expunge;
243 resp $fh, $tag, "OK";
244 } elsif ($cmd eq "fetch") {
245 my $list = expand_list($arg[0], scalar @mid);
246 my ($mid, $flags, $date, $head, $body);
247 my @exec;
248 my ($sql_body, $sql_head) = (0, 0);
249 my $sel = split_pgrp lc $arg[1];
250
251 while (my $s = shift @$sel) {
252 if ($s eq "all") {
253 push @$sel, qw(flags internaldate rfc822.size); next;
254 } elsif ($s eq "full") {
255 push @$sel, qw(flags internaldate rfc822.size envelope body); next;
256 } elsif ($s eq "uid") {
257 $s = sub { "UID $mid" };
258 } elsif ($s eq "flags") {
259 $s = sub {
260 "FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")";
261 };
262 } elsif ($s eq "internaldate") {
263 $s = sub { "INTERNALDATE \"$date\"" };
264 } elsif ($s eq "rfc822.size") {
265 $sql_body = "body";
266 $s = sub { "RFC822.SIZE ".length $body };
267 } elsif ($s eq "rfc822.header") {
268 $sql_head = "header";
269 $s = sub { "RFC822.HEADER ".literal($head) };
270 } elsif ($s eq "rfc822") {
271 $sql_body = "body"; $sql_head = "header";
272 $s = sub { "RFC822 ".literal($head.$CRLF.$body) };
273 } elsif ($s eq "body.peek[0]") {#d# evolution-bug
274 $sql_head = "header";
275 $s = sub { "BODY[0] ".literal($head) };
276 } elsif ($s eq "rfc822.header.lines") {
277 $sql_head = "header";
278 my $re;
279 $re = '(?i:^((?:'
280 . (join "|", @{split_pgrp shift @$sel})
281 . '):[^\012\015]*))';
282 $re = qr($re);
283
284 $s = sub {
285 "RFC822.HEADER.LINES ".literal (join $CRLF, $head =~ /$re/mg);
286 };
287 } else {
288 $s = sub { '""' };
289 }
290 push @exec, $s;
291 }
292
293 my %rev;
294 unless ($U) {
295 for (@$list) {
296 $mid = $mid[$_ - 1];
297 $rev{$mid} = $_;
298 $_ = $mid;
299 }
300 }
301
302 my $st = sql_exec \($mid, $flags, $date, $head, $body),
303 "select id, flags, date_format(ntime, '%d-%m-%Y %h:%i:%s +0000'), $sql_head, $sql_body
304 from msg
305 where id in (".(join ",", @$list).")";
306
307 my $r;
308 while ($st->fetch) {
309 my $id = $U ? $mid : $rev{$mid};
310 $r .= "* $id FETCH (".(join " ", map &$_, @exec).")\015\12";
311 if (length $r > 32768) {
312 print $fh $r;
313 $r = "";
314 }
315 }
316 print $fh "$r$tag OK\015\012";
317 #resp $fh, $tag, "OK";
318
319 } elsif ($cmd eq "store") {
320 my $list = expand_list($arg[0], scalar @mid);
321 my $cmd = lc $arg[1];
322 my $flags = flags2bitmask(@{split_pgrp $arg[2]});
323
324 my %rev;
325 unless ($U) {
326 for (@$list) {
327 $mid = $mid[$_ - 1];
328 $rev{$mid} = $_;
329 $_ = $mid;
330 }
331 }
332
333 my $ids = join ",", @$list;
334
335 my $silent = $cmd =~ s/\.silent$//;
336
337 if ($cmd eq "flags") {
338 sql_exec "update msg set flags = ? where id in ($ids)", $flags;
339 } elsif ($cmd eq "+flags") {
340 sql_exec "update msg set flags = flags | ? where id in ($ids)", $flags;
341 } elsif ($cmd eq "-flags") {
342 sql_exec "update msg set flags = flags & ~? where id in ($ids)", $flags;
343 }
344
345 if ($silent) {
346 resp $fh, $tag, "OK";
347 } else {
348 my $st = sql_exec \($mid, $flags),
349 "select id, flags
350 from msg
351 where id in ($ids)";
352
353 my $r;
354 while ($st->fetch) {
355 my $id = $U ? $mid : $rev{$mid};
356 $r .= "* $id FLAGS (" . (join " ", map "\\" . ucfirst $_, split /,/, $flags) . ")\015\012";
357 }
358 print $fh "$r$tag OK\015\012";
359 #resp $fh, $tag, "OK";
360 }
361
362 } else {
363 resp $fh, $tag, "BAD command not understood (nocommand)";
364 }
365
366 # well...
367 } else {
368 resp $fh, $tag, "BAD command not understood (notmailbox)";
369 }
370
371 } else {
372 resp $fh, $tag, "BAD command not understood (notauth)";
373 }
374 }
375 };
376 if ($@) {
377 resp $fh, "*", "BYE";
378 slog 0, "$@";
379 }
380 slog 3, "$fh: closed connection\n";#d#
381 }
382
383 async {
384 slog 1, "accepting connections";
385 while () {
386 my $fh = $port->accept;
387 slog 3, "accepted $fh on $port";
388 async \&handle_connection, $fh;
389 undef $fh;
390 }
391 };
392
393 loop;
394