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

# User Rev Content
1 root 1.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