merging erik quanstros nupas

This commit is contained in:
cinap_lenrek 2017-03-12 17:15:03 +01:00
parent 8177d20fb2
commit 963cfc9a6f
194 changed files with 22221 additions and 15366 deletions

View File

@ -1,4 +0,0 @@
Mail stored
plumb /mail/box/$user/names
mail -'x' someaddress
mkbox /mail/box/$user/new_box

View File

@ -1,11 +0,0 @@
#!/bin/rc
for(i){
if(! test -e $i){
if(cp /dev/null $i){
chmod 600 $i
chmod +al $i
}
}
if not echo $i already exists
}

View File

@ -1,57 +0,0 @@
The Acme Mail program uses upas/fs to parse the mail box, and then
presents a file-browser-like user interface to reading and sending
messages. The Mail window presents each numbered message like the
contents of a directory presented one per line. If a message has a
Subject: line, that is shown indented on the following line.
Multipart MIME-encoded messages are presented in the obvious
hierarchical format.
Mail uses upas/fs to access the mail box. By default it reads "mbox",
the standard user mail box. If Mail is given an argument, it is
passed to upas/fs as the name of the mail box (or upas/fs directory)
to open.
Although Mail works if the plumber is not running, it's designed to be
run with plumbing enabled and many of its features work best if it is.
The mailbox window has a few commands: Put writes back the mailbox;
Mail creates a new window in which to compose a message; and Delmesg
deletes messages by number. The number may be given as argument or
indicated by selecting the header line in the mailbox window.
(Delmesg does not expand null selections, in the interest of safety.)
Clicking the right button on a message number opens it; clicking on
any of the subparts of a message opens that (and also opens the
message itself). Each message window has a few commands in the tag
with obvious names: Reply, Delmsg, etc. "Reply" replies to the single
sender of the message, "Reply all" or "Replyall" replies to everyone
in the From:, To:, and CC: lines.
Message parts with recognized MIME types such as image/jpeg are sent
to the plumber for further dispatch. Acme Mail also listens to
messages on the seemail and showmail plumbing ports, to report the
arrival of new messages (highlighting the entry; right-click on the
entry to open the message) and open them if you right-click on the
face in the faces window.
When composing a mail message or replying to a message, the first line
of the text is a list of recipients of the message. To:, and CC:, and BCC:
lines are interpreted in the usual way. Two other header lines are
special to Acme Mail:
Include: file places a copy of file in the message as an
inline MIME attachment.
Attach: file places a copy of file in the message as a regular
MIME attachment.
Acme Mail uses these conventions when replying to messages,
constructing headers for the default behavior. You may edit these to
change behavior. Most important, when replying to a message Mail will
always Include: the original message; delete that line if you don't
want to include it.
If the mailbox
/mail/box/$user/outgoing
exists, Acme Mail will save your a copy of your outgoing messages
there. Attachments are described in the copy but not included.
The -m mntpoint flag specifies a different mount point for /upas/fs.

View File

@ -5,6 +5,5 @@ none:VQ:
all install clean nuke installall update:V:
@{cd bin/source; mk $target}
@{cd mail/src; mk $target}
@{cd news/src; mk $target}
@{cd wiki/src; mk $target}

View File

@ -40,6 +40,7 @@ ALL=\
spin\
port\
colophon\
nupas/nupas\
ALLPS=${ALL:%=%.ps}
HTML=${ALL:%=%.html} release3.html release4.html

92
sys/doc/nupas/macros.ms Normal file
View File

@ -0,0 +1,92 @@
.de F1
.nr OI \\n(.iu
.nr PW 1v
.KF
.sp 0.3v
..
.de T1
.F1
..
.de F2
.ds Fp Figure\ \\n(Fi
.ds Fn Figure\ \\n+(Fi
.ds Fq \\*(Fp
.F0
..
.de T2
.ds Tp Table\ \\n(Ti
.ds Tn Table\ \\n+(Ti
.ds Tq \\*(Tp
.T0
..
.de F0
.nr BD 1
.if t .ps \\n(PS-1
.ie \\n(VS>=41 .vs \\n(VSu-1p
.el .vs \\n(VSp-1p
.ft 1
.di DD
.ll \\n(.lu*3u/4u
.in 0
.fi
.ad b
.sp 0.5v
\f3\\*(Fq\f1\ \ \c
..
.de T0
.nr BD 1
.if t .ps \\n(PS-1
.ie \\n(VS>=41 .vs \\n(VSu-1p
.el .vs \\n(VSp-1p
.ft 1
.di DD
.ll \\n(.lu*3u/4u
.in 0
.fi
.ad b
.sp 0.5v
\f3\\*(Tq\f1\ \ \c
..
.de F3
.sp 0.5v
.di
.br
.ll \\n(.lu*4u/3u
.if \\n(dl>\\n(BD .nr BD \\n(dl
.if \\n(BD<\\n(.l .in (\\n(.lu-\\n(BDu)/2u
.nf
.DD
.in \\n(OIu
.nr BD 0
.fi
.KE
.ie \\n(VS>=41 .vs \\n(VSu
.el .vs \\n(VSp
..
.de T3
.F3
..
.de EX
.P1
\s-4
..
.de EE
\s+4
.P2
..
.nr Fi 1 +1
.nr Ti 1 +1
.ds Fn Figure\ \\n(Fi
.ds Tn Table\ \\n(Ti
.nr XP 2 \" delta point size for program
.nr XV 2p \" delta vertical for programs
.nr XT 4 \" delta tab stop for programs
.nr DV .5v \" space before start of program
.FP lucidasans
.nr PS 11
.nr VS 13
.nr LL 6.6i
.nr PI 0 \" paragraph indent
.nr PD 4p \" extra space between paragraphs
.pl 11i
.rm CH

20
sys/doc/nupas/mkfile Normal file
View File

@ -0,0 +1,20 @@
TARG=nupas.ps nupas.pdf
all:V: $TARG
%.ps:DQ: %.ms
eval `{doctype macros.ms $stem.ms} | \
lp -m.9 -dstdout >$target
%.pdf:DQ: %.ps
cat ../docfonts $stem.ps >_$stem.ps
ps2pdf _$stem.ps $stem.pdf && rm -f _$stem.ps
%.show:VQ: %.ps
page -w $stem.ps
#install:VQ: fs4.man fs.man fsrecover.man fsconfig.man
# cp fs4.man /sys/man/4/fs
# cp fs.man /sys/man/8/fs
# cp fsconfig.man /sys/man/8/fsconfig
# cp fsrecover.man /sys/man/8/fsrecover

354
sys/doc/nupas/nupas.ms Normal file
View File

@ -0,0 +1,354 @@
.EQ
delim $$
.EN
.TL
Scaling Upas
.AU
Erik Quanstrom
quanstro@coraid.com
.AB
The Plan 9 email system, Upas, uses traditional methods of delivery to
.UX
mail boxes while using a user-level file system, Upas/fs, to
translate mail boxes of various formats into a single, convenient format for access.
Unfortunately, it does not do so efficiently. Upas/fs
reads entire folders into core. When deleting email from mail boxes,
the entire mail box is rewritten. I describe how Upas/fs has been
adapted to use caching, indexing and a new mail box format (mdir) to
limit I/O, reduce core size and eliminate the need to rewrite mail
boxes.
.AE
.NH
Introduction
.LP
.DS I
Chained at his root two scion demons dwell
.br
Erasmus Darwin, The Botanic Garden
.DE
.LP
At Coraid, email is the largest resource user in the system by orders
of magnitude. As of July, 2007, rewriting mail boxes was using
300MB/day on the WORM and several users required more than 400MB of
core. As of July, 2008, rewriting mail boxes was using 800MB/day on
the WORM and several users required more than 1.2GB of core to read
email. Clearly these are difficult to sustain levels of growth, even
without growth of the company. We needed to limit the amount of disk
space used and, more urgently, reduce Upas/fs' core size.
.LP
The techniques employed are simple. Mail is now stored in a directory
with one message per file. This eliminates the need to rewrite mail
boxes. Upas/fs now maintains an index which allows it to present
complete message summaries without reading indexed messages.
Combining the two techniques allows Upas/fs to read only new or
referenced messages. Finally, caching limits both the total number of
in-core messages and their total size.
.NH
Mdir Format
.LP
In addition to meeting our urgent operational requirements of reducing
memory and disk footprint, to meet the expectations of our users we
require a solution that is able to handle folders up to ten thousand
messages, open folders quickly, list the contents of folders quickly
and support the current set of mail readers.
.LP
There are several potential styles of mail boxes. The maildir[1] format
has some attractive properties. Mail can be delivered to or deleted
from a mail box without locking. New mail or deleted mail may be
detected with a directory scan. When used with WORM storage, the
amount of storage required is no more than the size of new mail
received. Mbox format can require that a new copy of the inbox be
stored every day. Even with storage that coalesces duplicate blocks
such as Venti, deleting a message will generally require new storage
since messages are not disk-block aligned. Maildir does not reduce
the cost of the common task of a summary listing of mail such as
generated by acme Mail.
.LP
The mails[2] format proposes a directory per mail. A copy of
the mail as delivered is stored and each mime part is decoded
in such a way that a mail reader could display the file directly.
Command line tools in the style of MH[3] are used to display and
process mail. Upas/fs is not necessary for reading local mail.
Mails has the potential to reduce memory footprint below that
offered by mdirs for native email reading. However all of the
largest mail boxes at our site are served exclusively through IMAP.
The preformatting by mails would be unnecessary for such accounts.
.LP
Other mail servers such as Lotus Notes[4] store email in a custom
database format which allows for fielded and full-text searching
of mail folders. Such a format provides very quick mail
listings and good search capabilities. Such a solution would not
lend itself well to a tool-based environment, nor would it be simple.
.LP
Maildir format seemed the best basic format but its particulars are
tied to the
.UX
environment; mdir is a descendant. A mdir folder
is a directory with the name of the folder. Messages in the mdir
folder are stored in a file named
.I "utime.seq" .
.I Utime
is defined as the decimal
.UX
seconds when the message was added to
the folder. For the inbox, this time will correspond to the
.UX
“From ” line.
.I Seq
is a two-digit sequence number starting with
.CW "00."
The lowest available sequence number is used to store the message.
Thus, the first email possible would be named
.CW "0.00."
To prevent accidents, message files are stored with
the append-only and exclusive-access bits turned on.
The message is stored in the same format it would be in mbox
format; each message is a valid mbox folder with a single message.
.NH
Indexing
.LP
When upas/fs finds an unindexed message, it is added to the index.
The index is a file named
.I "foldername" .idx
and consists a signature and one line per MIME part. Each line
contains the SHA1 checksum of the message (or a place holder for
subparts), one field per entry in the
.I "messageid/info"
file, flags and the number of subparts. The flags are currently a
superset of the standard IMAP flags. They provide the similar
functionality to maildir's modified file names. Thus the `S'
(answered) flag remains set between invocations of mail readers.
Other mutable information about a message may be stored in a similar
way.
.LP
Since the
.I info
file is read by all the mail readers to produce mail listings,
mail boxes may be listed without opening any mail files when no new
mail has arrived. Similarly, opening a new mail box requires reading
the index and checking new mail. Index files are typically between
0.5% and 5% the size of the full mail box. Each time the index is
generated, it is fully rewritten.
.NH
Caching
.LP
Upas/fs stores each message in a
.CW "Message"
structure. To enable caching, this structure was split
into four parts: The
.CW "Idx"
(or index), message subparts, information on the cache state of the
message and a set of pointers into the processed header and body.
Only the pointers to the processed header and body are subject to
caching. The available cache states are
.CW "Cidx" ,
.CW "Cheader"
and
.CW "Cbody" .
.LP
When the header and body are not present, the average message with
subparts takes roughly 3KB of memory. Thus a 10,000 message mail box
would require roughly 30MB of core in addition to any cached
messages. Reads of the
.CW "info"
or
.CW "subject"
files can be satisfied from the information in the
.CW "Idx"
structure.
.LP
Since there are a fair number of very large messages, requests that
can be satisfied by reading the message headers do not result in the
full message being read. Reads of the
.CW "header"
or
.CW "rawheader"
files of top-level messages are satisfied in this way. Reading the
same files for subparts, however, results in the entire message being
read. Caching the header results in the
.CW "Cheader"
cache state.
.LP
Similarly, reading the
.CW "body"
requires the body to be read, processed and results in
the
.CW "Cbody"
cache state. Reading from MIME subparts also results
in the
.CW "Cbody"
cache state.
.LP
The cache has a simple LRU replacement policy. Each time a cached
member of a message is accessed, it is moved to the head of the list.
The cache contains a maximum number of messages and a maximum size.
While the maximum number of messages may not be exceeded, the maximum
cache size may be exceeded if the sum of all the currently referenced
messages is greater than the size of the cache. In this case all
unreferenced messages will be freed. When removing a message
from the cache all of the cacheable information is freed.
.NH
Collateral damage
.LP
.DS I
Each new user of a new system uncovers a new class of bugs.
.br
— Brian Kernighan
.DE
.LP
In addition to upas/fs, programs that have assumptions about how
mail boxes are structured needed to be modified. Programs which
deliver mail to mail boxes (deliver, marshal, ml, smtp) and append messages to
folders were given a common (nedmail) function to call. Since this
was done by modifying functions in the Upas common library, this
presented a problem for programs not traditionally part of Upas
such as acme Mail and imap4d. Rather than fold these programs
into Upas, a new program, mbappend, was added to Upas.
.LP
Imap4d also requires the ability to rename and remove folders.
While an external program would work for this as well, that
approach has some drawbacks. Most importantly, IMAP folders
can't be moved or renamed in the same way without reimplementing
functionality that is already in upas/fs. It also emphasises the
asymmetry between reading and deleting email and other folder
actions. Folder renaming and removal were added to upas/fs.
It is intended that mbappend will be removed soon
and replaced with equivalent upas/fs functionality —
at least for non-delivery programs.
.LP
Mdirs also expose an oddity about file permissions. An append-only
file that is mode
.CW 0622
may be appended to by anyone, but is readable only by the owner.
With a directory, such a setup is not directly possible as write permission
to a directory implies permission to remove. There are a number of
solutions to this problem. Delivery could be made asymmetrical—incoming
files could be written to a mbox. Or, following the example of the outbound
mail queue, each user could deliver to a directory owned by that user.
In many BSD-derived
.UX
systems, the “sticky bit” on directories is used to modify
the meaning of the
.CW w
bit for users matching only the other bits. For them, the
.CW w
bit gives permission to create but not to remove.
.LP
While this is somewhat of a one-off situation, I chose to implement
a version of the “sticky bit” using the existing append-only bit on our
file server. This was implemented as an extra permission check when
removing files. Fewer than 10 lines of code were required.
.NH
Performance
.LP
A representative local mail box was used to generate some rough
performance numbers. The mail box is 110MB and contains 868 messages.
These figures are shown in table 1. In the worse case—an unindexed
mail box—the new upas/fs uses 18% of the memory of the original while
using 13% more cpu. In the best case, it uses only 5% of the memory
while using only 13% of the cpu. Clearly, a larger mail box will make
these ratios more attractive. In the two months since the snapshot was
taken, that same mail box has grown to 220MB and contains 1814
messages.
.ps -2
.DS C
.TS
box, tab(:);
c s s s s
c | c | c | c | c
a | n | n | n | n.
Table 1 Performance
_
action:user:system:real:core size:
:s:s:s:MB:
_
old fs read:1.69:0.84:6.07:135
_
initial read:1.65:0.90:6.90:25
_
indexed read:0.64:0.03:0.77:6.5
.TE
.DE
.NL
.NH
Future Work
.LP
While Upas' memory usage has been drastically reduced,
it is still a work-in-progress. Caching and indexing are
adequate but primitive. Upas/fs is still inconsistently
bypassed for appending messages to mail boxes. There
are also some features which remain incomplete. Finally,
the small increase in scale brings some new questions about
the organization of email.
.LP
It may be useful for mail boxes with very large numbers
of messages to divide the index into fixed-size chunks.
Then messages could be read into a fixed-sized pool of
structures as needed. However it is currently hard to
see how clients could easily interface a mail box large
enough for this technique to be useful. Currently, all
clients assume that it is reasonable to allocate an
in-core data structure for each message in a mail box.
To take advantage of a chunked index, clients (or the
server) would need a way of limiting the number of
messages considered at a time. Also, for such large
mail boxes, it would be important to separate the
incoming messages from older messages to limit the work
required to scan for new messages.
.LP
Caching is particularly unsatisfactory. Files should
be read in fixed-sized buffers so maximum memory usage
does not depend on the size of the largest file in the
mail box. Unfortunately, current data structures do not readily
support this. In practice, this limitation has not yet
been noticeable.
.LP
There are also a few features that need to be completed.
Tracking of references has been added to marshal and
upas/fs. In addition, the index provides a place to store
mutable information about a message. These capabilities
should be built upon to provide general threading and
tagging capabilities.
.NH
Speculation
.LP
Freed from the limitation that all messages in a
mail box must be read and stored in memory before a
single message may be accessed, it is interesting to
speculate on a few further possibilites.
.LP
For example, it may be
useful to replace separate mail boxes with a single
collection of messages assigned to one or more virtual
mail boxes. The association between a message and a
mail box would be a “tag.” A message could be added to
or removed from one or more mail boxes without modifying
the mdir file. If threads were implemented by tagging
each message with its references, it would be possible
to follow threads across mail boxes, even to messages
removed from all mail boxes, provided the underlying
file were not also removed. If a facility for adding
arbitrary, automatic tags were enabled, it would be
possible to tag messages with the email address in
the SMTP From line.
.NH
References
.IP [1]
D. Bernstein, “Using maildir format”,
published online at
.br
http://cr.yp.to/proto/maildir.html
.IP [2]
F. Ballesteros
.IR mails (1),
published online at
http://lsub.org/magic/man2html/1/mails
.IP [3]
MH Wikipedia entry,
http://en.wikipedia.org/wiki/MH_Message_Handling_System
.IP [4]
Lotus Notes Wikipedia entry,
http://en.wikipedia.org/wiki/Lotus_Notes
.IP [5]
D. Presotto, “Upas—a Simpler Approach to Network Mail”,
Proceedings of the 10th Usenix conference, 1985.

View File

@ -20,7 +20,7 @@ pop3, imap4d \- Internet mail servers
.B -p
]
.PP
.B ip/imap4d
.B upas/imap4d
.RB [ -acpv ]
.RB [ -d
.IR smtpdomain ]
@ -142,7 +142,7 @@ debugging output
.SH SOURCE
.B /sys/src/cmd/upas/pop3
.br
.B /sys/src/cmd/ip/imap4d
.B /sys/src/cmd/upas/imap4d
.SH "SEE ALSO"
.IR aliasmail (8),
.IR faces (1),

View File

@ -1,184 +0,0 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <libsec.h>
#include <bio.h>
#include "imap4d.h"
/*
* hack to allow smtp forwarding.
* hide the peer IP address under a rock in the ratifier FS.
*/
void
enableForwarding(void)
{
char buf[64], peer[64], *p;
static ulong last;
ulong now;
int fd;
if(remote == nil)
return;
now = time(0);
if(now < last + 5*60)
return;
last = now;
fd = open("/srv/ratify", ORDWR);
if(fd < 0)
return;
if(!mount(fd, -1, "/mail/ratify", MBEFORE, "")){
close(fd);
return;
}
close(fd);
strncpy(peer, remote, sizeof(peer));
peer[sizeof(peer) - 1] = '\0';
p = strchr(peer, '!');
if(p != nil)
*p = '\0';
snprint(buf, sizeof(buf), "/mail/ratify/trusted/%s#32", peer);
/*
* if the address is already there and the user owns it,
* remove it and recreate it to give him a new time quanta.
*/
if(access(buf, 0) >= 0 && remove(buf) < 0)
return;
fd = create(buf, OREAD, 0666);
if(fd >= 0)
close(fd);
}
void
setupuser(AuthInfo *ai)
{
Waitmsg *w;
int pid;
if(ai){
strecpy(username, username+sizeof username, ai->cuid);
if(auth_chuid(ai, nil) < 0)
bye("user auth failed: %r");
auth_freeAI(ai);
}else
strecpy(username, username+sizeof username, getuser());
if(newns(username, 0) < 0)
bye("user login failed: %r");
/*
* hack to allow access to outgoing smtp forwarding
*/
enableForwarding();
snprint(mboxDir, MboxNameLen, "/mail/box/%s", username);
if(myChdir(mboxDir) < 0)
bye("can't open user's mailbox");
switch(pid = fork()){
case -1:
bye("can't initialize mail system");
break;
case 0:
execl("/bin/upas/fs", "upas/fs", "-np", nil);
_exits("rob1");
_exits(0);
break;
default:
break;
}
if((w=wait()) == nil || w->pid != pid || w->msg[0] != '\0')
bye("can't initialize mail system");
free(w);
}
static char*
authresp(void)
{
char *s, *t;
int n;
t = Brdline(&bin, '\n');
n = Blinelen(&bin);
if(n < 2)
return nil;
n--;
if(t[n-1] == '\r')
n--;
t[n] = '\0';
if(n == 0 || strcmp(t, "*") == 0)
return nil;
s = binalloc(&parseBin, n + 1, 0);
n = dec64((uchar*)s, n, t, n);
s[n] = '\0';
return s;
}
/*
* rfc 2195 cram-md5 authentication
*/
char*
cramauth(void)
{
AuthInfo *ai;
Chalstate *cs;
char *s, *t;
if((cs = auth_challenge("proto=cram role=server")) == nil)
return "couldn't get cram challenge";
Bprint(&bout, "+ %.*[\r\n", cs->nchal, cs->chal);
if(Bflush(&bout) < 0)
writeErr();
s = authresp();
if(s == nil)
return "client cancelled authentication";
t = strchr(s, ' ');
if(t == nil)
bye("bad auth response");
*t++ = '\0';
strncpy(username, s, UserNameLen);
username[UserNameLen-1] = '\0';
cs->user = username;
cs->resp = t;
cs->nresp = strlen(t);
if((ai = auth_response(cs)) == nil)
return "login failed";
auth_freechal(cs);
setupuser(ai);
return nil;
}
AuthInfo*
passLogin(char *user, char *secret)
{
AuthInfo *ai;
Chalstate *cs;
uchar digest[MD5dlen];
char response[2*MD5dlen+1];
if((cs = auth_challenge("proto=cram role=server")) == nil)
return nil;
hmac_md5((uchar*)cs->chal, strlen(cs->chal),
(uchar*)secret, strlen(secret), digest,
nil);
snprint(response, sizeof(response), "%.*H", MD5dlen, digest);
cs->user = user;
cs->resp = response;
cs->nresp = strlen(response);
ai = auth_response(cs);
auth_freechal(cs);
return ai;
}

View File

@ -1,259 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <libsec.h>
#include "imap4d.h"
static int saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n);
static int saveb(int fd, DigestState *dstate, char *buf, int nr, int nw);
static long appSpool(Biobuf *bout, Biobuf *bin, long n);
/*
* check if the message exists
*/
int
copyCheck(Box *box, Msg *m, int uids, void *v)
{
int fd;
USED(box);
USED(uids);
USED(v);
if(m->expunged)
return 0;
fd = msgFile(m, "raw");
if(fd < 0){
msgDead(m);
return 0;
}
close(fd);
return 1;
}
int
copySave(Box *box, Msg *m, int uids, void *vs)
{
Dir *d;
Biobuf b;
vlong length;
char *head;
int ok, hfd, bfd, nhead;
USED(box);
USED(uids);
if(m->expunged)
return 0;
hfd = msgFile(m, "unixheader");
if(hfd < 0){
msgDead(m);
return 0;
}
head = readFile(hfd);
if(head == nil){
close(hfd);
return 0;
}
/*
* clear out the header if it doesn't end in a newline,
* since it is a runt and the "header" will show up in the raw file.
*/
nhead = strlen(head);
if(nhead > 0 && head[nhead-1] != '\n')
nhead = 0;
bfd = msgFile(m, "raw");
close(hfd);
if(bfd < 0){
msgDead(m);
return 0;
}
d = dirfstat(bfd);
if(d == nil){
close(bfd);
return 0;
}
length = d->length;
free(d);
Binit(&b, bfd, OREAD);
ok = saveMsg(vs, m->info[IDigest], m->flags, head, nhead, &b, length);
Bterm(&b);
close(bfd);
return ok;
}
/*
* first spool the input into a temorary file,
* and massage the input in the process.
* then save to real box.
*/
int
appendSave(char *mbox, int flags, char *head, Biobuf *b, long n)
{
Biobuf btmp;
int fd, ok;
fd = imapTmp();
if(fd < 0)
return 0;
Bprint(&bout, "+ Ready for literal data\r\n");
if(Bflush(&bout) < 0)
writeErr();
Binit(&btmp, fd, OWRITE);
n = appSpool(&btmp, b, n);
Bterm(&btmp);
if(n < 0){
close(fd);
return 0;
}
seek(fd, 0, 0);
Binit(&btmp, fd, OREAD);
ok = saveMsg(mbox, nil, flags, head, strlen(head), &btmp, n);
Bterm(&btmp);
close(fd);
return ok;
}
/*
* copy from bin to bout,
* mapping "\r\n" to "\n" and "\nFrom " to "\n From "
* return the number of bytes in the mapped file.
*
* exactly n bytes must be read from the input,
* unless an input error occurs.
*/
static long
appSpool(Biobuf *bout, Biobuf *bin, long n)
{
int i, c;
c = '\n';
while(n > 0){
if(c == '\n' && n >= STRLEN("From ")){
for(i = 0; i < STRLEN("From "); i++){
c = Bgetc(bin);
if(c != "From "[i]){
if(c < 0)
return -1;
Bungetc(bin);
break;
}
n--;
}
if(i == STRLEN("From "))
Bputc(bout, ' ');
Bwrite(bout, "From ", i);
}
c = Bgetc(bin);
n--;
if(c == '\r' && n-- > 0){
c = Bgetc(bin);
if(c != '\n')
Bputc(bout, '\r');
}
if(c < 0)
return -1;
if(Bputc(bout, c) < 0)
return -1;
}
if(c != '\n')
Bputc(bout, '\n');
if(Bflush(bout) < 0)
return -1;
return Boffset(bout);
}
static int
saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n)
{
DigestState *dstate;
MbLock *ml;
uchar shadig[SHA1dlen];
char buf[BufSize + 1], digbuf[NDigest + 1];
int i, fd, nr, nw, ok;
ml = mbLock();
if(ml == nil)
return 0;
fd = openLocked(mboxDir, dst, OWRITE);
if(fd < 0){
mbUnlock(ml);
return 0;
}
seek(fd, 0, 2);
dstate = nil;
if(digest == nil)
dstate = sha1(nil, 0, nil, nil);
if(!saveb(fd, dstate, head, nhead, nhead)){
if(dstate != nil)
sha1(nil, 0, shadig, dstate);
mbUnlock(ml);
close(fd);
return 0;
}
ok = 1;
if(n == 0)
ok = saveb(fd, dstate, "\n", 0, 1);
while(n > 0){
nr = n;
if(nr > BufSize)
nr = BufSize;
nr = Bread(b, buf, nr);
if(nr <= 0){
saveb(fd, dstate, "\n\n", 0, 2);
ok = 0;
break;
}
n -= nr;
nw = nr;
if(n == 0){
if(buf[nw - 1] != '\n')
buf[nw++] = '\n';
buf[nw++] = '\n';
}
if(!saveb(fd, dstate, buf, nr, nw)){
ok = 0;
break;
}
mbLockRefresh(ml);
}
close(fd);
if(dstate != nil){
digest = digbuf;
sha1(nil, 0, shadig, dstate);
for(i = 0; i < SHA1dlen; i++)
snprint(digest+2*i, NDigest+1-2*i, "%2.2ux", shadig[i]);
}
if(ok){
fd = cdOpen(mboxDir, impName(dst), OWRITE);
if(fd < 0)
fd = emptyImp(dst);
if(fd >= 0){
seek(fd, 0, 2);
wrImpFlags(buf, flags, 1);
fprint(fd, "%.*s %.*lud %s\n", NDigest, digest, NUid, 0UL, buf);
close(fd);
}
}
mbUnlock(ml);
return 1;
}
static int
saveb(int fd, DigestState *dstate, char *buf, int nr, int nw)
{
if(dstate != nil)
sha1((uchar*)buf, nr, nil, dstate);
if(write(fd, buf, nw) != nw)
return 0;
return 1;
}

View File

@ -1,108 +0,0 @@
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <bio.h>
#include "imap4d.h"
void
debuglog(char *fmt, ...)
{
va_list arg;
static int logfd;
if(debug == 0)
return;
if(logfd == 0)
logfd = open("/sys/log/imap4d", OWRITE);
if(logfd > 0){
va_start(arg, fmt);
fprint(logfd, "%s: ", username);
vfprint(logfd, fmt, arg);
va_end(arg);
}
}
void
boxVerify(Box *box)
{
Msg *m;
ulong seq, uid, recent;
if(box == nil)
return;
recent = 0;
seq = 0;
uid = 0;
for(m = box->msgs; m != nil; m = m->next){
if(m->seq == 0)
fprint(2, "m->seq == 0: m->seq=%lud\n", m->seq);
else if(m->seq <= seq)
fprint(2, "m->seq=%lud out of order: last=%lud\n", m->seq, seq);
seq = m->seq;
if(m->uid == 0)
fprint(2, "m->uid == 0: m->seq=%lud\n", m->seq);
else if(m->uid <= uid)
fprint(2, "m->uid=%lud out of order: last=%lud\n", m->uid, uid);
uid = m->uid;
if(m->flags & MRecent)
recent++;
}
if(seq != box->max)
fprint(2, "max=%lud, should be %lud\n", box->max, seq);
if(uid >= box->uidnext)
fprint(2, "uidnext=%lud, maxuid=%lud\n", box->uidnext, uid);
if(recent != box->recent)
fprint(2, "recent=%lud, should be %lud\n", box->recent, recent);
}
void
openfiles(void)
{
Dir *d;
int i;
for(i = 0; i < 20; i++){
d = dirfstat(i);
if(d != nil){
fprint(2, "fd[%d]='%s' type=%c dev=%d user='%s group='%s'\n", i, d->name, d->type, d->dev, d->uid, d->gid);
free(d);
}
}
}
void
ls(char *file)
{
Dir *d;
int fd, i, nd;
fd = open(file, OREAD);
if(fd < 0)
return;
/*
* read box to find all messages
* each one has a directory, and is in numerical order
*/
d = dirfstat(fd);
if(d == nil){
close(fd);
return;
}
if(!(d->mode & DMDIR)){
fprint(2, "file %s\n", file);
free(d);
close(fd);
return;
}
free(d);
while((nd = dirread(fd, &d)) > 0){
for(i = 0; i < nd; i++){
fprint(2, "%s/%s %c\n", file, d[i].name, "-d"[(d[i].mode & DMDIR) == DMDIR]);
}
free(d);
}
close(fd);
}

View File

@ -1,125 +0,0 @@
/*
* sorted by 4,/^$/|sort -bd +1
*/
int fqid(int fd, Qid *qid);
int BNList(Biobuf *b, NList *nl, char *sep);
int BSList(Biobuf *b, SList *sl, char *sep);
int BimapMimeParams(Biobuf *b, MimeHdr *mh);
int Bimapaddr(Biobuf *b, MAddr *a);
int Bimapdate(Biobuf *b, Tm *tm);
int Bimapstr(Biobuf *b, char *s);
int Brfc822date(Biobuf *b, Tm *tm);
int appendSave(char *mbox, int flags, char *head, Biobuf *b, long n);
void bye(char *fmt, ...);
int cdCreate(char *dir, char *file, int mode, ulong perm);
int cdExists(char *dir, char *file);
Dir *cdDirstat(char *dir, char *file);
int cdDirwstat(char *dir, char *file, Dir *d);
int cdOpen(char *dir, char *file, int mode);
int cdRemove(char *dir, char *file);
MbLock *checkBox(Box *box, int imped);
int ciisprefix(char *pre, char *s);
int cistrcmp(char*, char*);
int cistrncmp(char*, char*, int);
char *cistrstr(char *s, char *sub);
void closeBox(Box *box, int opened);
void closeImp(Box *box, MbLock *ml);
int copyBox(char *from, char *to, int doremove);
int copyCheck(Box *box, Msg *m, int uids, void *v);
int copySave(Box *box, Msg *m, int uids, void *vs);
char *cramauth(void);
int createBox(char *mbox, int dir);
Tm *date2tm(Tm *tm, char *date);
int decmutf7(char *out, int nout, char *in);
int deleteMsgs(Box *box);
void debuglog(char *fmt, ...);
void *emalloc(ulong);
int emptyImp(char *mbox);
void enableForwarding(void);
int encmutf7(char *out, int nout, char *in);
void *erealloc(void*, ulong);
char *estrdup(char*);
int expungeMsgs(Box *box, int send);
void *ezmalloc(ulong);
void fetchBodyFill(ulong n);
void fetchBody(Msg *m, Fetch *f);
Pair fetchBodyPart(Fetch *f, ulong size);
void fetchBodyStr(Fetch *f, char *buf, ulong size);
void fetchBodyStruct(Msg *m, Header *h, int extensions);
void fetchEnvelope(Msg *m);
int fetchMsg(Box *box, Msg *m, int uids, void *fetch);
Msg *fetchSect(Msg *m, Fetch *f);
int fetchSeen(Box *box, Msg *m, int uids, void *vf);
void fetchStructExt(Header *h);
Msg *findMsgSect(Msg *m, NList *sect);
int forMsgs(Box *box, MsgSet *ms, ulong max, int uids, int (*f)(Box*, Msg*, int, void*), void *rock);
void freeMsg(Msg *m);
ulong imap4DateTime(char *date);
int imap4Date(Tm *tm, char *date);
int imap4date(char *s, int n, Tm *tm);
int imapTmp(void);
char *impName(char *name);
int infoIsNil(char *s);
int isdotdot(char*);
int isprefix(char *pre, char *s);
int issuffix(char *suf, char *s);
int listBoxes(char *cmd, char *ref, char *pat);
char *loginauth(void);
int lsubBoxes(char *cmd, char *ref, char *pat);
char *maddrStr(MAddr *a);
ulong mapFlag(char *name);
ulong mapInt(NamedInt *map, char *name);
void mbLockRefresh(MbLock *ml);
int mbLocked(void);
MbLock *mbLock(void);
void mbUnlock(MbLock *ml);
char *mboxName(char*);
Fetch *mkFetch(int op, Fetch *next);
NList *mkNList(ulong n, NList *next);
SList *mkSList(char *s, SList *next);
Store *mkStore(int sign, int op, int flags);
int moveBox(char *from, char *to);
void msgDead(Msg *m);
int msgFile(Msg *m, char *f);
int msgInfo(Msg *m);
int msgIsMulti(Header *h);
int msgIsRfc822(Header *h);
int msgSeen(Box *box, Msg *m);
ulong msgSize(Msg *m);
int msgStruct(Msg *m, int top);
char *mutf7str(char*);
int myChdir(char *dir);
int okMbox(char *mbox);
Box *openBox(char *name, char *fsname, int writable);
int openLocked(char *dir, char *file, int mode);
void parseErr(char *msg);
AuthInfo *passLogin(char*, char*);
char *readFile(int fd);
void resetCurDir(void);
Fetch *revFetch(Fetch *f);
NList *revNList(NList *s);
SList *revSList(SList *s);
int rfc822date(char *s, int n, Tm *tm);
int searchMsg(Msg *m, Search *s);
long selectFields(char *dst, long n, char *hdr, SList *fields, int matches);
void sendFlags(Box *box, int uids);
void setFlags(Box *box, Msg *m, int f);
void setupuser(AuthInfo*);
int storeMsg(Box *box, Msg *m, int uids, void *fetch);
char *strmutf7(char*);
void strrev(char *s, char *e);
int subscribe(char *mbox, int how);
void wrImpFlags(char *buf, int flags, int killRecent);
void writeErr(void);
void writeFlags(Biobuf *b, Msg *m, int recentOk);
#pragma varargck argpos bye 1
#pragma varargck argpos debuglog 1
#define MK(t) ((t*)emalloc(sizeof(t)))
#define MKZ(t) ((t*)ezmalloc(sizeof(t)))
#define MKN(t,n) ((t*)emalloc((n)*sizeof(t)))
#define MKNZ(t,n) ((t*)ezmalloc((n)*sizeof(t)))
#define MKNA(t,at,n) ((t*)emalloc(sizeof(t) + (n)*sizeof(at)))
#define STRLEN(cs) (sizeof(cs)-1)

View File

@ -1,398 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
static int copyData(int ffd, int tfd, MbLock *ml);
static MbLock mLock =
{
.fd = -1
};
static char curDir[MboxNameLen];
void
resetCurDir(void)
{
curDir[0] = '\0';
}
int
myChdir(char *dir)
{
if(strcmp(dir, curDir) == 0)
return 0;
if(dir[0] != '/' || strlen(dir) > MboxNameLen)
return -1;
strcpy(curDir, dir);
if(chdir(dir) < 0){
werrstr("mychdir failed: %r");
return -1;
}
return 0;
}
int
cdCreate(char *dir, char *file, int mode, ulong perm)
{
if(myChdir(dir) < 0)
return -1;
return create(file, mode, perm);
}
Dir*
cdDirstat(char *dir, char *file)
{
if(myChdir(dir) < 0)
return nil;
return dirstat(file);
}
int
cdExists(char *dir, char *file)
{
Dir *d;
d = cdDirstat(dir, file);
if(d == nil)
return 0;
free(d);
return 1;
}
int
cdDirwstat(char *dir, char *file, Dir *d)
{
if(myChdir(dir) < 0)
return -1;
return dirwstat(file, d);
}
int
cdOpen(char *dir, char *file, int mode)
{
if(myChdir(dir) < 0)
return -1;
return open(file, mode);
}
int
cdRemove(char *dir, char *file)
{
if(myChdir(dir) < 0)
return -1;
return remove(file);
}
/*
* open the one true mail lock file
*/
MbLock*
mbLock(void)
{
int i;
if(mLock.fd >= 0)
bye("mail lock deadlock");
for(i = 0; i < 5; i++){
mLock.fd = openLocked(mboxDir, "L.mbox", OREAD);
if(mLock.fd >= 0)
return &mLock;
sleep(1000);
}
return nil;
}
void
mbUnlock(MbLock *ml)
{
if(ml != &mLock)
bye("bad mail unlock");
if(ml->fd < 0)
bye("mail unlock when not locked");
close(ml->fd);
ml->fd = -1;
}
void
mbLockRefresh(MbLock *ml)
{
char buf[1];
seek(ml->fd, 0, 0);
read(ml->fd, buf, 1);
}
int
mbLocked(void)
{
return mLock.fd >= 0;
}
char*
impName(char *name)
{
char *s;
int n;
if(cistrcmp(name, "inbox") == 0)
if(access("msgs", AEXIST) == 0)
name = "msgs";
else
name = "mbox";
n = strlen(name) + STRLEN(".imp") + 1;
s = binalloc(&parseBin, n, 0);
if(s == nil)
return nil;
snprint(s, n, "%s.imp", name);
return s;
}
/*
* massage the mailbox name into something valid
* eliminates all .', and ..',s, redundatant and trailing /'s.
*/
char *
mboxName(char *s)
{
char *ss;
ss = mutf7str(s);
if(ss == nil)
return nil;
cleanname(ss);
return ss;
}
char *
strmutf7(char *s)
{
char *m;
int n;
n = strlen(s) * MUtf7Max + 1;
m = binalloc(&parseBin, n, 0);
if(m == nil)
return nil;
if(encmutf7(m, n, s) < 0)
return nil;
return m;
}
char *
mutf7str(char *s)
{
char *m;
int n;
/*
* n = strlen(s) * UTFmax / (2.67) + 1
* UTFMax / 2.67 == 3 / (8/3) == 9 / 8
*/
n = strlen(s);
n = (n * 9 + 7) / 8 + 1;
m = binalloc(&parseBin, n, 0);
if(m == nil)
return nil;
if(decmutf7(m, n, s) < 0)
return nil;
return m;
}
void
splitr(char *s, int c, char **left, char **right)
{
char *d;
int n;
n = strlen(s);
d = binalloc(&parseBin, n + 1, 0);
if(d == nil)
parseErr("out of memory");
strcpy(d, s);
s = strrchr(d, c);
if(s != nil){
*left = d;
*s++ = '\0';
*right = s;
}else{
*right = d;
*left = d + n;
}
}
/*
* create the mailbox and all intermediate components
* a trailing / implies the new mailbox is a directory;
* otherwise, it's a file.
*
* return with the file open for write, or directory open for read.
*/
int
createBox(char *mbox, int dir)
{
char *m;
int fd;
fd = -1;
for(m = mbox; *m; m++){
if(*m == '/'){
*m = '\0';
if(access(mbox, AEXIST) < 0){
if(fd >= 0)
close(fd);
fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
if(fd < 0)
return -1;
}
*m = '/';
}
}
if(dir)
fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
else
fd = cdCreate(mboxDir, mbox, OWRITE, 0664);
return fd;
}
/*
* move one mail folder to another
* destination mailbox doesn't exist.
* the source folder may be a directory or a mailbox,
* and may be in the same directory as the destination,
* or a completely different directory.
*/
int
moveBox(char *from, char *to)
{
Dir *d;
char *fd, *fe, *td, *te, *fimp;
splitr(from, '/', &fd, &fe);
splitr(to, '/', &td, &te);
/*
* in the same directory: try rename
*/
d = cdDirstat(mboxDir, from);
if(d == nil)
return 0;
if(strcmp(fd, td) == 0){
nulldir(d);
d->name = te;
if(cdDirwstat(mboxDir, from, d) >= 0){
fimp = impName(from);
d->name = impName(te);
cdDirwstat(mboxDir, fimp, d);
free(d);
return 1;
}
}
/*
* directory copy is too hard for now
*/
if(d->mode & DMDIR)
return 0;
free(d);
return copyBox(from, to, 1);
}
/*
* copy the contents of one mailbox to another
* either truncates or removes the source box if it succeeds.
*/
int
copyBox(char *from, char *to, int doremove)
{
MbLock *ml;
char *fimp, *timp;
int ffd, tfd, ok;
if(cistrcmp(from, "inbox") == 0)
if(access("msgs", AEXIST) == 0)
from = "msgs";
else
from = "mbox";
ml = mbLock();
if(ml == nil)
return 0;
ffd = openLocked(mboxDir, from, OREAD);
if(ffd < 0){
mbUnlock(ml);
return 0;
}
tfd = createBox(to, 0);
if(tfd < 0){
mbUnlock(ml);
close(ffd);
return 0;
}
ok = copyData(ffd, tfd, ml);
close(ffd);
close(tfd);
if(!ok){
mbUnlock(ml);
return 0;
}
fimp = impName(from);
timp = impName(to);
if(fimp != nil && timp != nil){
ffd = cdOpen(mboxDir, fimp, OREAD);
if(ffd >= 0){
tfd = cdCreate(mboxDir, timp, OWRITE, 0664);
if(tfd >= 0){
copyData(ffd, tfd, ml);
close(tfd);
}
close(ffd);
}
}
cdRemove(mboxDir, fimp);
if(doremove)
cdRemove(mboxDir, from);
else
close(cdOpen(mboxDir, from, OWRITE|OTRUNC));
mbUnlock(ml);
return 1;
}
/*
* copies while holding the mail lock,
* then tries to copy permissions and group ownership
*/
static int
copyData(int ffd, int tfd, MbLock *ml)
{
Dir *fd, td;
char buf[BufSize];
int n;
for(;;){
n = read(ffd, buf, BufSize);
if(n <= 0){
if(n < 0)
return 0;
break;
}
if(write(tfd, buf, n) != n)
return 0;
mbLockRefresh(ml);
}
fd = dirfstat(ffd);
if(fd != nil){
nulldir(&td);
td.mode = fd->mode;
if(dirfwstat(tfd, &td) >= 0){
nulldir(&td);
td.gid = fd->gid;
dirfwstat(tfd, &td);
}
}
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,378 +0,0 @@
/*
* mailbox and message representations
*
* these structures are allocated with emalloc and must be explicitly freed
*/
typedef struct Box Box;
typedef struct Header Header;
typedef struct MAddr MAddr;
typedef struct MbLock MbLock;
typedef struct MimeHdr MimeHdr;
typedef struct Msg Msg;
typedef struct NamedInt NamedInt;
typedef struct Pair Pair;
enum
{
StrAlloc = 32, /* characters allocated at a time */
BufSize = 8*1024, /* size of transfer block */
NDigest = 40, /* length of digest string */
NUid = 10, /* length of .imp uid string */
NFlags = 8, /* length of .imp flag string */
LockSecs = 5 * 60, /* seconds to wait for acquiring a locked file */
MboxNameLen = 256, /* max. length of upas/fs mbox name */
MsgNameLen = 32, /* max. length of a file in a upas/fs mbox */
UserNameLen = 64, /* max. length of user's name */
MUtf7Max = 6, /* max length for a modified utf7 character: &bbbb- */
/*
* message flags
*/
MSeen = 1 << 0,
MAnswered = 1 << 1,
MFlagged = 1 << 2,
MDeleted = 1 << 3,
MDraft = 1 << 4,
MRecent = 1 << 5,
/*
* message bogus flags
*/
NotBogus = 0, /* the message is displayable */
BogusHeader = 1, /* the header had bad characters */
BogusBody = 2, /* the body had bad characters */
BogusTried = 4, /* attempted to open the fake message */
};
struct Box
{
char *name; /* path name of mailbox */
char *fs; /* fs name of mailbox */
char *fsDir; /* /mail/fs/box->fs */
char *imp; /* path name of .imp file */
uchar writable; /* can write back messages? */
uchar dirtyImp; /* .imp file needs to be written? */
uchar sendFlags; /* need flags update */
Qid qid; /* qid of fs mailbox */
Qid impQid; /* qid of .imp when last synched */
long mtime; /* file mtime when last read */
ulong max; /* maximum msgs->seq, same as number of messages */
ulong toldMax; /* last value sent to client */
ulong recent; /* number of recently received messaged */
ulong toldRecent; /* last value sent to client */
ulong uidnext; /* next uid value assigned to a message */
ulong uidvalidity; /* uid of mailbox */
Msg *msgs;
};
/*
* fields of Msg->info
*/
enum
{
/*
* read from upasfs
*/
IFrom,
ITo,
ICc,
IReplyTo,
IUnixDate,
ISubject,
IType,
IDisposition,
IFilename,
IDigest,
IBcc,
IInReplyTo, /* aka internal date */
IDate,
ISender,
IMessageId,
ILines, /* number of lines of raw body */
IMax
};
struct Header
{
char *buf; /* header, including terminating \r\n */
ulong size; /* strlen(buf) */
ulong lines; /* number of \n characters in buf */
/*
* pre-parsed mime headers
*/
MimeHdr *type; /* content-type */
MimeHdr *id; /* content-id */
MimeHdr *description; /* content-description */
MimeHdr *encoding; /* content-transfer-encoding */
MimeHdr *md5; /* content-md5 */
MimeHdr *disposition; /* content-disposition */
MimeHdr *language; /* content-language */
};
struct Msg
{
Msg *next;
Msg *prev;
Msg *kids;
Msg *parent;
char *fsDir; /* box->fsDir of enclosing message */
Header head; /* message header */
Header mime; /* mime header from enclosing multipart spec */
int flags;
uchar sendFlags; /* flags value needs to be sent to client */
uchar expunged; /* message actually expunged, but not yet reported to client */
uchar matched; /* search succeeded? */
uchar bogus; /* implies the message is invalid, ie contains nulls; see flags above */
ulong uid; /* imap unique identifier */
ulong seq; /* position in box; 1 is oldest */
ulong id; /* number of message directory in upas/fs */
char *fs; /* name of message directory */
char *efs; /* pointer after / in fs; enough space for file name */
ulong size; /* size of fs/rawbody, in bytes, with \r added before \n */
ulong lines; /* number of lines in rawbody */
char *iBuf;
char *info[IMax]; /* all info about message */
char *unixDate;
MAddr *unixFrom;
MAddr *to; /* parsed out address lines */
MAddr *from;
MAddr *replyTo;
MAddr *sender;
MAddr *cc;
MAddr *bcc;
};
/*
* pre-parsed header lines
*/
struct MAddr
{
char *personal;
char *box;
char *host;
MAddr *next;
};
struct MimeHdr
{
char *s;
char *t;
MimeHdr *next;
};
/*
* mapping of integer & names
*/
struct NamedInt
{
char *name;
int v;
};
/*
* lock for all mail file operations
*/
struct MbLock
{
int fd;
};
/*
* parse nodes for imap4rev1 protocol
*
* important: all of these items are allocated
* in one can, so they can be tossed out at the same time.
* this allows leakless parse error recovery by simply tossing the can away.
* however, it means these structures cannot be mixed with the mailbox structures
*/
typedef struct Fetch Fetch;
typedef struct NList NList;
typedef struct SList SList;
typedef struct MsgSet MsgSet;
typedef struct Store Store;
typedef struct Search Search;
/*
* parse tree for fetch command
*/
enum
{
FEnvelope,
FFlags,
FInternalDate,
FRfc822,
FRfc822Head,
FRfc822Size,
FRfc822Text,
FBodyStruct,
FUid,
FBody, /* BODY */
FBodySect, /* BODY [...] */
FBodyPeek,
FMax
};
enum
{
FPAll,
FPHead,
FPHeadFields,
FPHeadFieldsNot,
FPMime,
FPText,
FPMax
};
struct Fetch
{
uchar op; /* F.* operator */
uchar part; /* FP.* subpart for body[] & body.peek[]*/
uchar partial; /* partial fetch? */
long start; /* partial fetch amounts */
long size;
NList *sect;
SList *hdrs;
Fetch *next;
};
/*
* status items
*/
enum{
SMessages = 1 << 0,
SRecent = 1 << 1,
SUidNext = 1 << 2,
SUidValidity = 1 << 3,
SUnseen = 1 << 4,
};
/*
* parse tree for store command
*/
enum
{
STFlags,
STFlagsSilent,
STMax
};
struct Store
{
uchar sign;
uchar op;
int flags;
};
/*
* parse tree for search command
*/
enum
{
SKNone,
SKCharset,
SKAll,
SKAnswered,
SKBcc,
SKBefore,
SKBody,
SKCc,
SKDeleted,
SKDraft,
SKFlagged,
SKFrom,
SKHeader,
SKKeyword,
SKLarger,
SKNew,
SKNot,
SKOld,
SKOn,
SKOr,
SKRecent,
SKSeen,
SKSentBefore,
SKSentOn,
SKSentSince,
SKSet,
SKSince,
SKSmaller,
SKSubject,
SKText,
SKTo,
SKUid,
SKUnanswered,
SKUndeleted,
SKUndraft,
SKUnflagged,
SKUnkeyword,
SKUnseen,
SKMax
};
struct Search
{
int key;
char *s;
char *hdr;
ulong num;
int year;
int mon;
int mday;
MsgSet *set;
Search *left;
Search *right;
Search *next;
};
struct NList
{
ulong n;
NList *next;
};
struct SList
{
char *s;
SList *next;
};
struct MsgSet
{
ulong from;
ulong to;
MsgSet *next;
};
struct Pair
{
ulong start;
ulong stop;
};
#include "bin.h"
extern Bin *parseBin;
extern Biobuf bout;
extern Biobuf bin;
extern char username[UserNameLen];
extern char mboxDir[MboxNameLen];
extern char *fetchPartNames[FPMax];
extern char *site;
extern char *remote;
extern int debug;
#include "fns.h"

View File

@ -1,412 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
#define SUBSCRIBED "imap.subscribed"
static int matches(char *ref, char *pat, char *name);
static int mayMatch(char *pat, char *name, int star);
static int checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir);
static int listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime);
static int listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm);
static int mkSubscribed(void);
static long
listMtime(char *file)
{
Dir *d;
long mtime;
d = cdDirstat(mboxDir, file);
if(d == nil)
return 0;
mtime = d->mtime;
free(d);
return mtime;
}
/*
* check for subscribed mailboxes
* each line is either a comment starting with #
* or is a subscribed mailbox name
*/
int
lsubBoxes(char *cmd, char *ref, char *pat)
{
MbLock *mb;
Dir *d;
Biobuf bin;
char *s;
long mtime;
int fd, ok, isdir;
mb = mbLock();
if(mb == nil)
return 0;
fd = cdOpen(mboxDir, SUBSCRIBED, OREAD);
if(fd < 0)
fd = mkSubscribed();
if(fd < 0){
mbUnlock(mb);
return 0;
}
ok = 0;
Binit(&bin, fd, OREAD);
while(s = Brdline(&bin, '\n')){
s[Blinelen(&bin) - 1] = '\0';
if(s[0] == '#')
continue;
isdir = 1;
if(cistrcmp(s, "INBOX") == 0){
if(access("msgs", AEXIST) == 0)
mtime = listMtime("msgs");
else
mtime = listMtime("mbox");
isdir = 0;
}else{
d = cdDirstat(mboxDir, s);
if(d != nil){
mtime = d->mtime;
if(!(d->mode & DMDIR))
isdir = 0;
free(d);
}else
mtime = 0;
}
ok |= checkMatch(cmd, ref, pat, s, mtime, isdir);
}
Bterm(&bin);
close(fd);
mbUnlock(mb);
return ok;
}
static int
mkSubscribed(void)
{
int fd;
fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664);
if(fd < 0)
return -1;
fprint(fd, "#imap4 subscription list\nINBOX\n");
seek(fd, 0, 0);
return fd;
}
/*
* either subscribe or unsubscribe to a mailbox
*/
int
subscribe(char *mbox, int how)
{
MbLock *mb;
char *s, *in, *ein;
int fd, tfd, ok, nmbox;
if(cistrcmp(mbox, "inbox") == 0)
mbox = "INBOX";
mb = mbLock();
if(mb == nil)
return 0;
fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR);
if(fd < 0)
fd = mkSubscribed();
if(fd < 0){
mbUnlock(mb);
return 0;
}
in = readFile(fd);
if(in == nil){
mbUnlock(mb);
return 0;
}
nmbox = strlen(mbox);
s = strstr(in, mbox);
while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n'))
s = strstr(s+1, mbox);
ok = 0;
if(how == 's' && s == nil){
if(fprint(fd, "%s\n", mbox) > 0)
ok = 1;
}else if(how == 'u' && s != nil){
ein = strchr(s, '\0');
memmove(s, &s[nmbox+1], ein - &s[nmbox+1]);
ein -= nmbox+1;
tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC);
if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in)
ok = 1;
if(tfd > 0)
close(tfd);
}else
ok = 1;
close(fd);
mbUnlock(mb);
return ok;
}
/*
* stupidly complicated so that % doesn't read entire directory structure
* yet * works
* note: in most places, inbox is case-insensitive,
* but here INBOX is checked for a case-sensitve match.
*/
int
listBoxes(char *cmd, char *ref, char *pat)
{
int ok;
ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0);
return ok | listMatch(cmd, ref, pat, ref, pat);
}
/*
* look for all messages which may match the pattern
* punt when a * is reached
*/
static int
listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm)
{
Dir *dir, *dirs;
char *mdir, *m, *mb, *wc;
long mode;
int c, i, nmb, nmdir, nd, ok, fd;
mdir = nil;
for(m = mm; c = *m; m++){
if(c == '%' || c == '*'){
if(mdir == nil){
fd = cdOpen(mboxDir, ".", OREAD);
if(fd < 0)
return 0;
mbox = "";
nmdir = 0;
}else{
*mdir = '\0';
fd = cdOpen(mboxDir, mbox, OREAD);
*mdir = '/';
nmdir = mdir - mbox + 1;
if(fd < 0)
return 0;
dir = dirfstat(fd);
if(dir == nil){
close(fd);
return 0;
}
mode = dir->mode;
free(dir);
if(!(mode & DMDIR))
break;
}
wc = m;
for(; c = *m; m++)
if(c == '/')
break;
nmb = nmdir + strlen(m) + MboxNameLen + 3;
mb = emalloc(nmb);
strncpy(mb, mbox, nmdir);
ok = 0;
while((nd = dirread(fd, &dirs)) > 0){
for(i = 0; i < nd; i++){
if(strcmp(mbox, "") == 0 &&
!okMbox(dirs[i].name))
continue;
/* Safety: ignore message dirs */
if(strstr(dirs[i].name, "mails") != 0 ||
strcmp(dirs[i].name, "out") == 0 ||
strcmp(dirs[i].name, "obox") == 0 ||
strcmp(dirs[i].name, "ombox") == 0)
continue;
if(strcmp(dirs[i].name, "msgs") == 0)
dirs[i].mode &= ~DMDIR;
if(*wc == '*' && dirs[i].mode & DMDIR &&
mayMatch(mm, dirs[i].name, 1)){
snprint(mb+nmdir, nmb-nmdir,
"%s", dirs[i].name);
ok |= listAll(cmd, ref, pat, mb,
dirs[i].mtime);
}else if(mayMatch(mm, dirs[i].name, 0)){
snprint(mb+nmdir, nmb-nmdir,
"%s%s", dirs[i].name, m);
if(*m == '\0')
ok |= checkMatch(cmd,
ref, pat, mb,
dirs[i].mtime,
dirs[i].mode &
DMDIR);
else if(dirs[i].mode & DMDIR)
ok |= listMatch(cmd,
ref, pat, mb, mb
+ nmdir + strlen(
dirs[i].name));
}
}
free(dirs);
}
close(fd);
free(mb);
return ok;
}
if(c == '/'){
mdir = m;
mm = m + 1;
}
}
m = mbox;
if(*mbox == '\0')
m = ".";
dir = cdDirstat(mboxDir, m);
if(dir == nil)
return 0;
ok = checkMatch(cmd, ref, pat, mbox, dir->mtime, (dir->mode & DMDIR) == DMDIR);
free(dir);
return ok;
}
/*
* too hard: recursively list all files rooted at mbox,
* and list checkMatch figure it out
*/
static int
listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime)
{
Dir *dirs;
char *mb;
int i, nmb, nd, ok, fd;
ok = checkMatch(cmd, ref, pat, mbox, mtime, 1);
fd = cdOpen(mboxDir, mbox, OREAD);
if(fd < 0)
return ok;
nmb = strlen(mbox) + MboxNameLen + 2;
mb = emalloc(nmb);
while((nd = dirread(fd, &dirs)) > 0){
for(i = 0; i < nd; i++){
snprint(mb, nmb, "%s/%s", mbox, dirs[i].name);
/* safety: do not recurr */
if(0 && dirs[i].mode & DMDIR)
ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime);
else
ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0);
}
free(dirs);
}
close(fd);
free(mb);
return ok;
}
static int
mayMatch(char *pat, char *name, int star)
{
Rune r;
int i, n;
for(; *pat && *pat != '/'; pat += n){
r = *(uchar*)pat;
if(r < Runeself)
n = 1;
else
n = chartorune(&r, pat);
if(r == '*' || r == '%'){
pat += n;
if(r == '*' && star || *pat == '\0' || *pat == '/')
return 1;
while(*name){
if(mayMatch(pat, name, star))
return 1;
name += chartorune(&r, name);
}
return 0;
}
for(i = 0; i < n; i++)
if(name[i] != pat[i])
return 0;
name += n;
}
if(*name == '\0')
return 1;
return 0;
}
/*
* mbox is a mailbox name which might match pat.
* verify the match
* generates response
*/
static int
checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir)
{
char *s, *flags;
if(!matches(ref, pat, mbox) || !okMbox(mbox))
return 0;
if(strcmp(mbox, ".") == 0)
mbox = "";
if(isdir)
flags = "(\\Noselect)";
else{
s = impName(mbox);
if(s != nil && listMtime(s) < mtime)
flags = "(\\Noinferiors \\Marked)";
else
flags = "(\\Noinferiors)";
}
s = strmutf7(mbox);
if(s != nil)
Bprint(&bout, "* %s %s \"/\" \"%s\"\r\n", cmd, flags, s);
return 1;
}
static int
matches(char *ref, char *pat, char *name)
{
Rune r;
int i, n;
while(ref != pat)
if(*name++ != *ref++)
return 0;
for(; *pat; pat += n){
r = *(uchar*)pat;
if(r < Runeself)
n = 1;
else
n = chartorune(&r, pat);
if(r == '*'){
pat += n;
if(*pat == '\0')
return 1;
while(*name){
if(matches(pat, pat, name))
return 1;
name += chartorune(&r, name);
}
return 0;
}
if(r == '%'){
pat += n;
while(*name && *name != '/'){
if(matches(pat, pat, name))
return 1;
name += chartorune(&r, name);
}
pat -= n;
continue;
}
for(i = 0; i < n; i++)
if(name[i] != pat[i])
return 0;
name += n;
}
if(*name == '\0')
return 1;
return 0;
}

View File

@ -1,863 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
static NamedInt flagChars[NFlags] =
{
{"s", MSeen},
{"a", MAnswered},
{"f", MFlagged},
{"D", MDeleted},
{"d", MDraft},
{"r", MRecent},
};
static int fsCtl = -1;
static void boxFlags(Box *box);
static int createImp(Box *box, Qid *qid);
static void fsInit(void);
static void mboxGone(Box *box);
static MbLock *openImp(Box *box, int new);
static int parseImp(Biobuf *b, Box *box);
static int readBox(Box *box);
static ulong uidRenumber(Msg *m, ulong uid, int force);
static int impFlags(Box *box, Msg *m, char *flags);
/*
* strategy:
* every mailbox file has an associated .imp file
* which maps upas/fs message digests to uids & message flags.
*
* the .imp files are locked by /mail/fs/usename/L.mbox.
* whenever the flags can be modified, the lock file
* should be opened, thereby locking the uid & flag state.
* for example, whenever new uids are assigned to messages,
* and whenever flags are changed internally, the lock file
* should be open and locked. this means the file must be
* opened during store command, and when changing the \seen
* flag for the fetch command.
*
* if no .imp file exists, a null one must be created before
* assigning uids.
*
* the .imp file has the following format
* imp : "imap internal mailbox description\n"
* uidvalidity " " uidnext "\n"
* messageLines
*
* messageLines :
* | messageLines digest " " uid " " flags "\n"
*
* uid, uidnext, and uidvalidity are 32 bit decimal numbers
* printed right justified in a field NUid characters long.
* the 0 uid implies that no uid has been assigned to the message,
* but the flags are valid. note that message lines are in mailbox
* order, except possibly for 0 uid messages.
*
* digest is an ascii hex string NDigest characters long.
*
* flags has a character for each of NFlag flag fields.
* if the flag is clear, it is represented by a "-".
* set flags are represented as a unique single ascii character.
* the currently assigned flags are, in order:
* MSeen s
* MAnswered a
* MFlagged f
* MDeleted D
* MDraft d
*/
Box*
openBox(char *name, char *fsname, int writable)
{
Box *box;
MbLock *ml;
int n, new;
if(cistrcmp(name, "inbox") == 0)
if(access("msgs", AEXIST) == 0)
name = "msgs";
else
name = "mbox";
fsInit();
debuglog("imap4d open %s %s\n", name, fsname);
if(fprint(fsCtl, "open '/mail/box/%s/%s' %s", username, name, fsname) < 0){
//ZZZ
char err[ERRMAX];
rerrstr(err, sizeof err);
if(strstr(err, "file does not exist") == nil)
fprint(2,
"imap4d at %lud: upas/fs open %s/%s as %s failed: '%s' %s",
time(nil), username, name, fsname, err,
ctime(time(nil))); /* NB: ctime result ends with \n */
fprint(fsCtl, "close %s", fsname);
return nil;
}
/*
* read box to find all messages
* each one has a directory, and is in numerical order
*/
box = MKZ(Box);
box->writable = writable;
n = strlen(name) + 1;
box->name = emalloc(n);
strcpy(box->name, name);
n += STRLEN(".imp");
box->imp = emalloc(n);
snprint(box->imp, n, "%s.imp", name);
n = strlen(fsname) + 1;
box->fs = emalloc(n);
strcpy(box->fs, fsname);
n = STRLEN("/mail/fs/") + strlen(fsname) + 1;
box->fsDir = emalloc(n);
snprint(box->fsDir, n, "/mail/fs/%s", fsname);
box->uidnext = 1;
new = readBox(box);
if(new >= 0){
ml = openImp(box, new);
if(ml != nil){
closeImp(box, ml);
return box;
}
}
closeBox(box, 0);
return nil;
}
/*
* check mailbox
* returns fd of open .imp file if imped.
* otherwise, return value is insignificant
*
* careful: called by idle polling proc
*/
MbLock*
checkBox(Box *box, int imped)
{
MbLock *ml;
Dir *d;
int new;
if(box == nil)
return nil;
/*
* if stat fails, mailbox must be gone
*/
d = cdDirstat(box->fsDir, ".");
if(d == nil){
mboxGone(box);
return nil;
}
new = 0;
if(box->qid.path != d->qid.path || box->qid.vers != d->qid.vers
|| box->mtime != d->mtime){
new = readBox(box);
if(new < 0){
free(d);
return nil;
}
}
free(d);
ml = openImp(box, new);
if(ml == nil)
box->writable = 0;
else if(!imped){
closeImp(box, ml);
ml = nil;
}
return ml;
}
/*
* mailbox is unreachable, so mark all messages expunged
* clean up .imp files as well.
*/
static void
mboxGone(Box *box)
{
Msg *m;
if(cdExists(mboxDir, box->name) < 0)
cdRemove(mboxDir, box->imp);
for(m = box->msgs; m != nil; m = m->next)
m->expunged = 1;
box->writable = 0;
}
/*
* read messages in the mailbox
* mark message that no longer exist as expunged
* returns -1 for failure, 0 if no new messages, 1 if new messages.
*/
static int
readBox(Box *box)
{
Msg *msgs, *m, *last;
Dir *d;
char *s;
long max, id;
int i, nd, fd, new;
fd = cdOpen(box->fsDir, ".", OREAD);
if(fd < 0){
syslog(0, "mail",
"imap4d at %lud: upas/fs stat of %s/%s aka %s failed: %r",
time(nil), username, box->name, box->fsDir);
mboxGone(box);
return -1;
}
/*
* read box to find all messages
* each one has a directory, and is in numerical order
*/
d = dirfstat(fd);
if(d == nil){
close(fd);
return -1;
}
box->mtime = d->mtime;
box->qid = d->qid;
last = nil;
msgs = box->msgs;
max = 0;
new = 0;
free(d);
while((nd = dirread(fd, &d)) > 0){
for(i = 0; i < nd; i++){
s = d[i].name;
id = strtol(s, &s, 10);
if(id <= max || *s != '\0'
|| (d[i].mode & DMDIR) != DMDIR)
continue;
max = id;
while(msgs != nil){
last = msgs;
msgs = msgs->next;
if(last->id == id)
goto continueDir;
last->expunged = 1;
}
new = 1;
m = MKZ(Msg);
m->id = id;
m->fsDir = box->fsDir;
m->fs = emalloc(2 * (MsgNameLen + 1));
m->efs = seprint(m->fs, m->fs + (MsgNameLen + 1), "%lud/", id);
m->size = ~0UL;
m->lines = ~0UL;
m->prev = last;
m->flags = MRecent;
if(!msgInfo(m))
freeMsg(m);
else{
if(last == nil)
box->msgs = m;
else
last->next = m;
last = m;
}
continueDir:;
}
free(d);
}
close(fd);
for(; msgs != nil; msgs = msgs->next)
msgs->expunged = 1;
/*
* make up the imap message sequence numbers
*/
id = 1;
for(m = box->msgs; m != nil; m = m->next){
if(m->seq && m->seq != id)
bye("internal error assigning message numbers");
m->seq = id++;
}
box->max = id - 1;
return new;
}
/*
* read in the .imp file, or make one if it doesn't exist.
* make sure all flags and uids are consistent.
* return the mailbox lock.
*/
#define IMPMAGIC "imap internal mailbox description\n"
static MbLock*
openImp(Box *box, int new)
{
Qid qid;
Biobuf b;
MbLock *ml;
int fd;
//ZZZZ
int once;
ml = mbLock();
if(ml == nil)
return nil;
fd = cdOpen(mboxDir, box->imp, OREAD);
once = 0;
ZZZhack:
if(fd < 0 || fqid(fd, &qid) < 0){
if(fd < 0){
char buf[ERRMAX];
errstr(buf, sizeof buf);
if(cistrstr(buf, "does not exist") == nil)
fprint(2, "imap4d at %lud: imp open failed: %s\n", time(nil), buf);
if(!once && cistrstr(buf, "locked") != nil){
once = 1;
fprint(2, "imap4d at %lud: imp %s/%s %s locked when it shouldn't be; spinning\n", time(nil), username, box->name, box->imp);
fd = openLocked(mboxDir, box->imp, OREAD);
goto ZZZhack;
}
}
if(fd >= 0)
close(fd);
fd = createImp(box, &qid);
if(fd < 0){
mbUnlock(ml);
return nil;
}
box->dirtyImp = 1;
if(box->uidvalidity == 0)
box->uidvalidity = box->mtime;
box->impQid = qid;
new = 1;
}else if(qid.path != box->impQid.path || qid.vers != box->impQid.vers){
Binit(&b, fd, OREAD);
if(!parseImp(&b, box)){
box->dirtyImp = 1;
if(box->uidvalidity == 0)
box->uidvalidity = box->mtime;
}
Bterm(&b);
box->impQid = qid;
new = 1;
}
if(new)
boxFlags(box);
close(fd);
return ml;
}
/*
* close the .imp file, after writing out any changes
*/
void
closeImp(Box *box, MbLock *ml)
{
Msg *m;
Qid qid;
Biobuf b;
char buf[NFlags+1];
int fd;
if(ml == nil)
return;
if(!box->dirtyImp){
mbUnlock(ml);
return;
}
fd = cdCreate(mboxDir, box->imp, OWRITE, 0664);
if(fd < 0){
mbUnlock(ml);
return;
}
Binit(&b, fd, OWRITE);
box->dirtyImp = 0;
Bprint(&b, "%s", IMPMAGIC);
Bprint(&b, "%.*lud %.*lud\n", NUid, box->uidvalidity, NUid, box->uidnext);
for(m = box->msgs; m != nil; m = m->next){
if(m->expunged)
continue;
wrImpFlags(buf, m->flags, strcmp(box->fs, "imap") == 0);
Bprint(&b, "%.*s %.*lud %s\n", NDigest, m->info[IDigest], NUid, m->uid, buf);
}
Bterm(&b);
if(fqid(fd, &qid) >= 0)
box->impQid = qid;
close(fd);
mbUnlock(ml);
}
void
wrImpFlags(char *buf, int flags, int killRecent)
{
int i;
for(i = 0; i < NFlags; i++){
if((flags & flagChars[i].v)
&& (flagChars[i].v != MRecent || !killRecent))
buf[i] = flagChars[i].name[0];
else
buf[i] = '-';
}
buf[i] = '\0';
}
int
emptyImp(char *mbox)
{
Dir *d;
long mode;
int fd;
fd = cdCreate(mboxDir, impName(mbox), OWRITE, 0664);
if(fd < 0)
return -1;
d = cdDirstat(mboxDir, mbox);
if(d == nil){
close(fd);
return -1;
}
fprint(fd, "%s%.*lud %.*lud\n", IMPMAGIC, NUid, d->mtime, NUid, 1UL);
mode = d->mode & 0777;
nulldir(d);
d->mode = mode;
dirfwstat(fd, d);
free(d);
return fd;
}
/*
* try to match permissions with mbox
*/
static int
createImp(Box *box, Qid *qid)
{
Dir *d;
long mode;
int fd;
fd = cdCreate(mboxDir, box->imp, OREAD, 0664);
if(fd < 0)
return -1;
d = cdDirstat(mboxDir, box->name);
if(d != nil){
mode = d->mode & 0777;
nulldir(d);
d->mode = mode;
dirfwstat(fd, d);
free(d);
}
if(fqid(fd, qid) < 0){
close(fd);
return -1;
}
return fd;
}
/*
* read or re-read a .imp file.
* this is tricky:
* messages can be deleted by another agent
* we might still have a Msg for an expunged message,
* because we haven't told the client yet.
* we can have a Msg without a .imp entry.
* flag information is added at the end of the .imp by copy & append
* there can be duplicate messages (same digests).
*
* look up existing messages based on uid.
* look up new messages based on in order digest matching.
*
* note: in the face of duplicate messages, one of which is deleted,
* two active servers may decide different ones are valid, and so return
* different uids for the messages. this situation will stablize when the servers exit.
*/
static int
parseImp(Biobuf *b, Box *box)
{
Msg *m, *mm;
char *s, *t, *toks[3];
ulong uid, u;
int match, n;
m = box->msgs;
s = Brdline(b, '\n');
if(s == nil || Blinelen(b) != STRLEN(IMPMAGIC)
|| strncmp(s, IMPMAGIC, STRLEN(IMPMAGIC)) != 0)
return 0;
s = Brdline(b, '\n');
if(s == nil || Blinelen(b) != 2*NUid + 2)
return 0;
s[2*NUid + 1] = '\0';
u = strtoul(s, &t, 10);
if(u != box->uidvalidity && box->uidvalidity != 0)
return 0;
box->uidvalidity = u;
if(*t != ' ' || t != s + NUid)
return 0;
t++;
u = strtoul(t, &t, 10);
if(box->uidnext > u)
return 0;
box->uidnext = u;
if(t != s + 2*NUid+1 || box->uidnext == 0)
return 0;
uid = ~0;
while(m != nil){
s = Brdline(b, '\n');
if(s == nil)
break;
n = Blinelen(b) - 1;
if(n != NDigest + NUid + NFlags + 2
|| s[NDigest] != ' ' || s[NDigest + NUid + 1] != ' ')
return 0;
toks[0] = s;
s[NDigest] = '\0';
toks[1] = s + NDigest + 1;
s[NDigest + NUid + 1] = '\0';
toks[2] = s + NDigest + NUid + 2;
s[n] = '\0';
t = toks[1];
u = strtoul(t, &t, 10);
if(*t != '\0' || uid != ~0 && (uid >= u && u || u && !uid))
return 0;
uid = u;
/*
* zero uid => added by append or copy, only flags valid
* can only match messages without uids, but this message
* may not be the next one, and may have been deleted.
*/
if(!uid){
for(; m != nil && m->uid; m = m->next)
;
for(mm = m; mm != nil; mm = mm->next){
if(mm->info[IDigest] != nil &&
strcmp(mm->info[IDigest], toks[0]) == 0){
if(!mm->uid)
mm->flags = 0;
if(!impFlags(box, mm, toks[2]))
return 0;
m = mm->next;
break;
}
}
continue;
}
/*
* ignore expunged messages,
* and messages already assigned uids which don't match this uid.
* such messages must have been deleted by another imap server,
* which updated the mailbox and .imp file since we read the mailbox,
* or because upas/fs got confused by consecutive duplicate messages,
* the first of which was deleted by another imap server.
*/
for(; m != nil && (m->expunged || m->uid && m->uid < uid); m = m->next)
;
if(m == nil)
break;
/*
* only check for digest match on the next message,
* since it comes before all other messages, and therefore
* must be in the .imp file if they should be.
*/
match = m->info[IDigest] != nil &&
strcmp(m->info[IDigest], toks[0]) == 0;
if(uid && (m->uid == uid || !m->uid && match)){
if(!match)
bye("inconsistent uid");
/*
* wipe out recent flag if some other server saw this new message.
* it will be read from the .imp file if is really should be set,
* ie the message was only seen by a status command.
*/
if(!m->uid)
m->flags = 0;
if(!impFlags(box, m, toks[2]))
return 0;
m->uid = uid;
m = m->next;
}
}
return 1;
}
/*
* parse .imp flags
*/
static int
impFlags(Box *box, Msg *m, char *flags)
{
int i, f;
f = 0;
for(i = 0; i < NFlags; i++){
if(flags[i] == '-')
continue;
if(flags[i] != flagChars[i].name[0])
return 0;
f |= flagChars[i].v;
}
/*
* recent flags are set until the first time message's box is selected or examined.
* it may be stored in the file as a side effect of a status or subscribe command;
* if so, clear it out.
*/
if((f & MRecent) && strcmp(box->fs, "imap") == 0)
box->dirtyImp = 1;
f |= m->flags & MRecent;
/*
* all old messages with changed flags should be reported to the client
*/
if(m->uid && m->flags != f){
box->sendFlags = 1;
m->sendFlags = 1;
}
m->flags = f;
return 1;
}
/*
* assign uids to any new messages
* which aren't already in the .imp file.
* sum up totals for flag values.
*/
static void
boxFlags(Box *box)
{
Msg *m;
box->recent = 0;
for(m = box->msgs; m != nil; m = m->next){
if(m->uid == 0){
box->dirtyImp = 1;
box->uidnext = uidRenumber(m, box->uidnext, 0);
}
if(m->flags & MRecent)
box->recent++;
}
}
static ulong
uidRenumber(Msg *m, ulong uid, int force)
{
for(; m != nil; m = m->next){
if(!force && m->uid != 0)
bye("uid renumbering with a valid uid");
m->uid = uid++;
}
return uid;
}
void
closeBox(Box *box, int opened)
{
Msg *m, *next;
/*
* make sure to leave the mailbox directory so upas/fs can close the mailbox
*/
myChdir(mboxDir);
if(box->writable){
deleteMsgs(box);
if(expungeMsgs(box, 0))
closeImp(box, checkBox(box, 1));
}
if(fprint(fsCtl, "close %s", box->fs) < 0 && opened)
bye("can't talk to mail server");
for(m = box->msgs; m != nil; m = next){
next = m->next;
freeMsg(m);
}
free(box->name);
free(box->fs);
free(box->fsDir);
free(box->imp);
free(box);
}
int
deleteMsgs(Box *box)
{
Msg *m;
char buf[BufSize], *p, *start;
int ok;
if(!box->writable)
return 0;
/*
* first pass: delete messages; gang the writes together for speed.
*/
ok = 1;
start = seprint(buf, buf + sizeof(buf), "delete %s", box->fs);
p = start;
for(m = box->msgs; m != nil; m = m->next){
if((m->flags & MDeleted) && !m->expunged){
m->expunged = 1;
p = seprint(p, buf + sizeof(buf), " %lud", m->id);
if(p + 32 >= buf + sizeof(buf)){
if(write(fsCtl, buf, p - buf) < 0)
bye("can't talk to mail server");
p = start;
}
}
}
if(p != start && write(fsCtl, buf, p - buf) < 0)
bye("can't talk to mail server");
return ok;
}
/*
* second pass: remove the message structure,
* and renumber message sequence numbers.
* update messages counts in mailbox.
* returns true if anything changed.
*/
int
expungeMsgs(Box *box, int send)
{
Msg *m, *next, *last;
ulong n;
n = 0;
last = nil;
for(m = box->msgs; m != nil; m = next){
m->seq -= n;
next = m->next;
if(m->expunged){
if(send)
Bprint(&bout, "* %lud expunge\r\n", m->seq);
if(m->flags & MRecent)
box->recent--;
n++;
if(last == nil)
box->msgs = next;
else
last->next = next;
freeMsg(m);
}else
last = m;
}
if(n){
box->max -= n;
box->dirtyImp = 1;
}
return n;
}
static void
fsInit(void)
{
if(fsCtl >= 0)
return;
fsCtl = open("/mail/fs/ctl", ORDWR);
if(fsCtl < 0)
bye("can't open mail file system");
if(fprint(fsCtl, "close mbox") < 0)
bye("can't initialize mail file system");
}
static char *stoplist[] =
{
"mbox",
"pipeto",
"forward",
"names",
"pipefrom",
"headers",
"imap.ok",
0
};
enum {
Maxokbytes = 4096,
Maxfolders = Maxokbytes / 4,
};
static char *folders[Maxfolders];
static char *folderbuff;
static void
readokfolders(void)
{
int fd, nr;
fd = open("imap.ok", OREAD);
if(fd < 0)
return;
folderbuff = malloc(Maxokbytes);
if(folderbuff == nil) {
close(fd);
return;
}
nr = read(fd, folderbuff, Maxokbytes-1); /* once is ok */
close(fd);
if(nr < 0){
free(folderbuff);
folderbuff = nil;
return;
}
folderbuff[nr] = 0;
tokenize(folderbuff, folders, nelem(folders));
}
/*
* reject bad mailboxes based on mailbox name
*/
int
okMbox(char *path)
{
char *name;
int i;
if(folderbuff == nil && access("imap.ok", AREAD) == 0)
readokfolders();
name = strrchr(path, '/');
if(name == nil)
name = path;
else
name++;
if(folderbuff != nil){
for(i = 0; i < nelem(folders) && folders[i] != nil; i++)
if(cistrcmp(folders[i], name) == 0)
return 1;
return 0;
}
if(strlen(name) + STRLEN(".imp") >= MboxNameLen)
return 0;
for(i = 0; stoplist[i]; i++)
if(strcmp(name, stoplist[i]) == 0)
return 0;
if(isprefix("L.", name) || isprefix("imap-tmp.", name)
|| issuffix(".imp", name)
|| strcmp("imap.subscribed", name) == 0
|| isdotdot(name) || name[0] == '/')
return 0;
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,213 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
/*
* iterated over all of the items in the message set.
* errors are accumulated, but processing continues.
* if uids, then ignore non-existent messages.
* otherwise, that's an error
*/
int
forMsgs(Box *box, MsgSet *ms, ulong max, int uids, int (*f)(Box*, Msg*, int, void*), void *rock)
{
Msg *m;
ulong id;
int ok, rok;
ok = 1;
for(; ms != nil; ms = ms->next){
id = ms->from;
rok = 0;
for(m = box->msgs; m != nil && m->seq <= max; m = m->next){
if(!uids && m->seq > id
|| uids && m->uid > ms->to)
break;
if(!uids && m->seq == id
|| uids && m->uid >= id){
if(!(*f)(box, m, uids, rock))
ok = 0;
if(uids)
id = m->uid;
if(id >= ms->to){
rok = 1;
break;
}
if(ms->to == ~0UL)
rok = 1;
id++;
}
}
if(!uids && !rok)
ok = 0;
}
return ok;
}
Store *
mkStore(int sign, int op, int flags)
{
Store *st;
st = binalloc(&parseBin, sizeof(Store), 1);
if(st == nil)
parseErr("out of memory");
st->sign = sign;
st->op = op;
st->flags = flags;
return st;
}
Fetch *
mkFetch(int op, Fetch *next)
{
Fetch *f;
f = binalloc(&parseBin, sizeof(Fetch), 1);
if(f == nil)
parseErr("out of memory");
f->op = op;
f->next = next;
return f;
}
Fetch*
revFetch(Fetch *f)
{
Fetch *last, *next;
last = nil;
for(; f != nil; f = next){
next = f->next;
f->next = last;
last = f;
}
return last;
}
NList*
mkNList(ulong n, NList *next)
{
NList *nl;
nl = binalloc(&parseBin, sizeof(NList), 0);
if(nl == nil)
parseErr("out of memory");
nl->n = n;
nl->next = next;
return nl;
}
NList*
revNList(NList *nl)
{
NList *last, *next;
last = nil;
for(; nl != nil; nl = next){
next = nl->next;
nl->next = last;
last = nl;
}
return last;
}
SList*
mkSList(char *s, SList *next)
{
SList *sl;
sl = binalloc(&parseBin, sizeof(SList), 0);
if(sl == nil)
parseErr("out of memory");
sl->s = s;
sl->next = next;
return sl;
}
SList*
revSList(SList *sl)
{
SList *last, *next;
last = nil;
for(; sl != nil; sl = next){
next = sl->next;
sl->next = last;
last = sl;
}
return last;
}
int
BNList(Biobuf *b, NList *nl, char *sep)
{
char *s;
int n;
s = "";
n = 0;
for(; nl != nil; nl = nl->next){
n += Bprint(b, "%s%lud", s, nl->n);
s = sep;
}
return n;
}
int
BSList(Biobuf *b, SList *sl, char *sep)
{
char *s;
int n;
s = "";
n = 0;
for(; sl != nil; sl = sl->next){
n += Bprint(b, "%s", s);
n += Bimapstr(b, sl->s);
s = sep;
}
return n;
}
int
Bimapdate(Biobuf *b, Tm *tm)
{
char buf[64];
if(tm == nil)
tm = localtime(time(nil));
imap4date(buf, sizeof(buf), tm);
return Bimapstr(b, buf);
}
int
Brfc822date(Biobuf *b, Tm *tm)
{
char buf[64];
if(tm == nil)
tm = localtime(time(nil));
rfc822date(buf, sizeof(buf), tm);
return Bimapstr(b, buf);
}
int
Bimapstr(Biobuf *b, char *s)
{
char *t;
int c;
if(s == nil)
return Bprint(b, "NIL");
for(t = s; ; t++){
c = *t;
if(c == '\0')
return Bprint(b, "\"%s\"", s);
if(t - s > 64 || c >= 0x7f || strchr("\"\\\r\n", c) != nil)
break;
}
return Bprint(b, "{%lud}\r\n%s", strlen(s), s);
}

View File

@ -1,244 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
static int dateCmp(char *date, Search *s);
static int addrSearch(MAddr *a, char *s);
static int fileSearch(Msg *m, char *file, char *pat);
static int headerSearch(Msg *m, char *hdr, char *pat);
/*
* free to exit, parseErr, since called before starting any client reply
*
* the header and envelope searches should convert mime character set escapes.
*/
int
searchMsg(Msg *m, Search *s)
{
MsgSet *ms;
int ok;
if(!msgStruct(m, 1) || m->expunged)
return 0;
for(ok = 1; ok && s != nil; s = s->next){
switch(s->key){
default:
ok = 0;
break;
case SKNot:
ok = !searchMsg(m, s->left);
break;
case SKOr:
ok = searchMsg(m, s->left) || searchMsg(m, s->right);
break;
case SKAll:
ok = 1;
break;
case SKAnswered:
ok = (m->flags & MAnswered) == MAnswered;
break;
case SKDeleted:
ok = (m->flags & MDeleted) == MDeleted;
break;
case SKDraft:
ok = (m->flags & MDraft) == MDraft;
break;
case SKFlagged:
ok = (m->flags & MFlagged) == MFlagged;
break;
case SKKeyword:
ok = (m->flags & s->num) == s->num;
break;
case SKNew:
ok = (m->flags & (MRecent|MSeen)) == MRecent;
break;
case SKOld:
ok = (m->flags & MRecent) != MRecent;
break;
case SKRecent:
ok = (m->flags & MRecent) == MRecent;
break;
case SKSeen:
ok = (m->flags & MSeen) == MSeen;
break;
case SKUnanswered:
ok = (m->flags & MAnswered) != MAnswered;
break;
case SKUndeleted:
ok = (m->flags & MDeleted) != MDeleted;
break;
case SKUndraft:
ok = (m->flags & MDraft) != MDraft;
break;
case SKUnflagged:
ok = (m->flags & MFlagged) != MFlagged;
break;
case SKUnkeyword:
ok = (m->flags & s->num) != s->num;
break;
case SKUnseen:
ok = (m->flags & MSeen) != MSeen;
break;
case SKLarger:
ok = msgSize(m) > s->num;
break;
case SKSmaller:
ok = msgSize(m) < s->num;
break;
case SKBcc:
ok = addrSearch(m->bcc, s->s);
break;
case SKCc:
ok = addrSearch(m->cc, s->s);
break;
case SKFrom:
ok = addrSearch(m->from, s->s);
break;
case SKTo:
ok = addrSearch(m->to, s->s);
break;
case SKSubject:
ok = 0;
if(m->info[ISubject])
ok = cistrstr(m->info[ISubject], s->s) != nil;
break;
case SKBefore:
ok = dateCmp(m->unixDate, s) < 0;
break;
case SKOn:
ok = dateCmp(m->unixDate, s) == 0;
break;
case SKSince:
ok = dateCmp(m->unixDate, s) > 0;
break;
case SKSentBefore:
ok = dateCmp(m->info[IDate], s) < 0;
break;
case SKSentOn:
ok = dateCmp(m->info[IDate], s) == 0;
break;
case SKSentSince:
ok = dateCmp(m->info[IDate], s) > 0;
break;
case SKUid:
case SKSet:
for(ms = s->set; ms != nil; ms = ms->next)
if(s->key == SKUid && m->uid >= ms->from && m->uid <= ms->to
|| s->key == SKSet && m->seq >= ms->from && m->seq <= ms->to)
break;
ok = ms != nil;
break;
case SKHeader:
ok = headerSearch(m, s->hdr, s->s);
break;
case SKBody:
case SKText:
if(s->key == SKText && cistrstr(m->head.buf, s->s)){
ok = 1;
break;
}
ok = fileSearch(m, "body", s->s);
break;
}
}
return ok;
}
static int
fileSearch(Msg *m, char *file, char *pat)
{
char buf[BufSize + 1];
int n, nbuf, npat, fd, ok;
npat = strlen(pat);
if(npat >= BufSize / 2)
return 0;
fd = msgFile(m, file);
if(fd < 0)
return 0;
ok = 0;
nbuf = 0;
for(;;){
n = read(fd, &buf[nbuf], BufSize - nbuf);
if(n <= 0)
break;
nbuf += n;
buf[nbuf] = '\0';
if(cistrstr(buf, pat) != nil){
ok = 1;
break;
}
if(nbuf > npat){
memmove(buf, &buf[nbuf - npat], npat);
nbuf = npat;
}
}
close(fd);
return ok;
}
static int
headerSearch(Msg *m, char *hdr, char *pat)
{
SList hdrs;
char *s, *t;
int ok, n;
n = m->head.size + 3;
s = emalloc(n);
hdrs.next = nil;
hdrs.s = hdr;
ok = 0;
if(selectFields(s, n, m->head.buf, &hdrs, 1) > 0){
t = strchr(s, ':');
if(t != nil && cistrstr(t+1, pat) != nil)
ok = 1;
}
free(s);
return ok;
}
static int
addrSearch(MAddr *a, char *s)
{
char *ok, *addr;
for(; a != nil; a = a->next){
addr = maddrStr(a);
ok = cistrstr(addr, s);
free(addr);
if(ok != nil)
return 1;
}
return 0;
}
static int
dateCmp(char *date, Search *s)
{
Tm tm;
date2tm(&tm, date);
if(tm.year < s->year)
return -1;
if(tm.year > s->year)
return 1;
if(tm.mon < s->mon)
return -1;
if(tm.mon > s->mon)
return 1;
if(tm.mday < s->mday)
return -1;
if(tm.mday > s->mday)
return 1;
return 0;
}

View File

@ -1,127 +0,0 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include "imap4d.h"
static NamedInt flagMap[] =
{
{"\\Seen", MSeen},
{"\\Answered", MAnswered},
{"\\Flagged", MFlagged},
{"\\Deleted", MDeleted},
{"\\Draft", MDraft},
{"\\Recent", MRecent},
{nil, 0}
};
int
storeMsg(Box *box, Msg *m, int uids, void *vst)
{
Store *st;
int f, flags;
USED(uids);
if(m->expunged)
return uids;
st = vst;
flags = st->flags;
f = m->flags;
if(st->sign == '+')
f |= flags;
else if(st->sign == '-')
f &= ~flags;
else
f = flags;
/*
* not allowed to change the recent flag
*/
f = (f & ~MRecent) | (m->flags & MRecent);
setFlags(box, m, f);
if(st->op != STFlagsSilent){
m->sendFlags = 1;
box->sendFlags = 1;
}
return 1;
}
/*
* update flags & global flag counts in box
*/
void
setFlags(Box *box, Msg *m, int f)
{
if(f == m->flags)
return;
box->dirtyImp = 1;
if((f & MRecent) != (m->flags & MRecent)){
if(f & MRecent)
box->recent++;
else
box->recent--;
}
m->flags = f;
}
void
sendFlags(Box *box, int uids)
{
Msg *m;
if(!box->sendFlags)
return;
box->sendFlags = 0;
for(m = box->msgs; m != nil; m = m->next){
if(!m->expunged && m->sendFlags){
Bprint(&bout, "* %lud FETCH (", m->seq);
if(uids)
Bprint(&bout, "uid %lud ", m->uid);
Bprint(&bout, "FLAGS (");
writeFlags(&bout, m, 1);
Bprint(&bout, "))\r\n");
m->sendFlags = 0;
}
}
}
void
writeFlags(Biobuf *b, Msg *m, int recentOk)
{
char *sep;
int f;
sep = "";
for(f = 0; flagMap[f].name != nil; f++){
if((m->flags & flagMap[f].v)
&& (flagMap[f].v != MRecent || recentOk)){
Bprint(b, "%s%s", sep, flagMap[f].name);
sep = " ";
}
}
}
int
msgSeen(Box *box, Msg *m)
{
if(m->flags & MSeen)
return 0;
m->flags |= MSeen;
box->sendFlags = 1;
m->sendFlags = 1;
box->dirtyImp = 1;
return 1;
}
ulong
mapFlag(char *name)
{
return mapInt(flagMap, name);
}

View File

@ -27,7 +27,7 @@ TARG = 6in4\
socksd\
wol\
DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp imap4d snoopy
DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy
BIN=/$objtype/bin/ip
HFILES=dhcp.h arp.h glob.h icmp.h telnet.h

View File

@ -118,6 +118,7 @@ extern void rewritembox(Window*, Message*);
extern void mkreply(Message*, char*, char*, Plumbattr*, char*);
extern void delreply(Message*);
extern int write2(int, int, char*, int, int);
extern int mesgadd(Message*, char*, Dir*, char*);
extern void mesgmenu(Window*, Message*);
@ -162,3 +163,4 @@ extern char *user;
extern char deleted[];
extern int wctlfd;
extern int shortmenu;
extern int altmenu;

View File

@ -34,7 +34,8 @@ void plumbthread(void);
void plumbshowthread(void*);
void plumbsendthread(void*);
int shortmenu;
int shortmenu;
int altmenu;
void
usage(void)
@ -57,12 +58,13 @@ removeupasfs(void)
int
ismaildir(char *s)
{
char buf[256];
char *path;
Dir *d;
int ret;
snprint(buf, sizeof buf, "%s%s", maildir, s);
d = dirstat(buf);
path = smprint("%s%s", maildir, s);
d = dirstat(path);
free(path);
if(d == nil)
return 0;
ret = d->qid.type & QTDIR;
@ -87,6 +89,7 @@ threadmain(int argc, char *argv[])
plumbshowmailfd = plumbopen("showmail", OREAD|OCEXEC);
shortmenu = 0;
altmenu = 0;
ARGBEGIN{
case 's':
shortmenu = 1;
@ -94,6 +97,9 @@ threadmain(int argc, char *argv[])
case 'S':
shortmenu = 2;
break;
case 'A':
altmenu = 1;
break;
case 'o':
outgoing = EARGF(usage());
break;
@ -183,13 +189,15 @@ threadmain(int argc, char *argv[])
wbox = newwindow();
winname(wbox, mbox.name);
wintagwrite(wbox, "Put Mail Delmesg ", 3+1+4+1+7+1);
wintagwrite(wbox, "Put Mail Delmesg Save ", 3+1+4+1+7+1+4+1);
threadcreate(mainctl, wbox, STACK);
fmtstrinit(&fmt);
fmtprint(&fmt, "Mail");
if(shortmenu)
fmtprint(&fmt, " -%c", "sS"[shortmenu-1]);
if(altmenu)
fmtprint(&fmt, " -A");
if(outgoing)
fmtprint(&fmt, " -o %s", outgoing);
fmtprint(&fmt, " %s", name);
@ -309,6 +317,32 @@ delmesg(char *name, char *digest, int dodel)
}
}
extern int mesgsave(Message*, char*);
void
savemesg(char *box, char *name, char *digest)
{
char *s;
int ok;
Message *m;
m = mesglookupfile(&mbox, name, digest);
if(!m || m->isreply)
return;
s = estrdup("\t[saved");
if(!box[0])
ok = mesgsave(m, "stored");
else{
ok = mesgsave(m, box);
s = eappend(s, " ", box);
}
if(ok){
s = egrow(s, "]", nil);
mesgmenumark(mbox.w, m->name, s);
}
free(s);
}
void
plumbthread(void)
{
@ -363,7 +397,7 @@ plumbsendthread(void*)
int
mboxcommand(Window *w, char *s)
{
char *args[10], **targs;
char *args[10], **targs, *r, *box;
Message *m, *next;
int ok, nargs, i, j;
char buf[128];
@ -443,6 +477,42 @@ mboxcommand(Window *w, char *s)
free(targs);
return 1;
}
if(strncmp(args[0], "Save", 4) == 0){
box = "";
i = 1;
if(nargs > 1 && !mesglookupfile(&mbox, args[1], nil)){
box = args[1];
i++;
nargs--;
}
if(nargs > 1){
for(; i<nargs; i++){
snprint(buf, sizeof buf, "%s%s", mbox.name, args[i]);
savemesg(box, buf, nil);
}
}
s = winselection(w);
if(s == nil)
return 1;
nargs = 1;
for(i=0; s[i]; i++)
if(s[i] == '\n')
nargs++;
targs = emalloc(nargs*sizeof(char*)); /* could be too many for a local array */
nargs = getfields(s, targs, nargs, 1, "\n");
for(i=0; i<nargs; i++){
if(!isdigit(targs[i][0]))
continue;
j = strtoul(targs[i], &r, 10);
if(j == 0 || *r != '/')
continue;
snprint(buf, sizeof buf, "%s%d", mbox.name, j);
savemesg(box, buf, nil);
}
free(s);
free(targs);
return 1;
}
return 0;
}

View File

@ -22,7 +22,7 @@ struct{
char *port;
char *suffix;
} ports[] = {
"text/", "edit", ".txt",
"text/", "edit", ".txt", /* must be first for plumbport() */
/* text must be first for plumbport() */
"image/gif", "image", ".gif",
"image/jpeg", "image", ".jpg",
@ -30,9 +30,9 @@ struct{
"image/png", "image", ".png",
"image/tiff", "image", ".tif",
"application/postscript", "postscript", ".ps",
"application/pdf", "postscript", ".pdf",
"application/msword", "msword", ".doc",
"application/rtf", "msword", ".rtf",
"application/pdf", "postscript", ".pdf",
"application/msword", "msword", ".doc",
"application/rtf", "msword", ".rtf",
"audio/x-wav", "wav", ".wav",
nil, nil
};
@ -90,55 +90,52 @@ line(char *data, char **pp)
return q;
}
void
scanheaders(Message *m, char *dir)
char*
fc(Message *m, char *s)
{
char *s, *t, *u, *f;
char *r;
s = f = readfile(dir, "header", nil);
if(s != nil)
while(*s){
t = line(s, &s);
if(strncmp(t, "From: ", 6) == 0){
m->fromcolon = estrdup(t+6);
/* remove all quotes; they're ugly and irregular */
for(u=m->fromcolon; *u; u++)
if(*u == '"')
memmove(u, u+1, strlen(u));
}
if(strncmp(t, "Subject: ", 9) == 0)
m->subject = estrdup(t+9);
free(t);
}
if(m->fromcolon == nil)
m->fromcolon = estrdup(m->from);
free(f);
if(*s && *m->from){
r = smprint("%s <%s>", s, m->from);
free(s);
return r;
}else if(*s)
return s;
else if(*m->from)
return estrdup(m->from);
return estrdup("??");
}
int
loadinfo(Message *m, char *dir)
{
int n;
char *data, *p, *s;
char *data, *p;
data = readfile(dir, "info", &n);
if(data == nil)
return 0;
m->from = line(data, &p);
scanheaders(m, dir); /* depends on m->from being set */
m->to = line(p, &p);
m->cc = line(p, &p);
m->replyto = line(p, &p);
m->date = line(p, &p);
s = line(p, &p);
if(m->subject == nil)
m->subject = s;
else
free(s);
m->subject = line(p, &p);
m->type = line(p, &p);
m->disposition = line(p, &p);
m->filename = line(p, &p);
m->digest = line(p, &p);
/* m->bcc = */ free(line(p, &p));
/* m->inreplyto = */ free(line(p, &p));
/* m->date = */ free(line(p, &p));
/* m->sender = */ free(line(p, &p));
/* m->messageid = */ free(line(p, &p));
/* m->lines = */ free(line(p, &p));
/* m->size = */ free(line(p, &p));
/* m->flags = */ free(line(p, &p));
/* m->fileid = */ free(line(p, &p));
m->fromcolon = fc(m, line(p, &p));
free(data);
return 1;
}
@ -293,6 +290,34 @@ readfile(char *dir, char *name, int *np)
return data;
}
int
writefile(char *dir, char *name, char *s)
{
char *e, *file;
int fd, n;
file = estrstrdup(dir, name);
// fprint(2, "writefile %s [%s]\n", file, s);
fd = open(file, OWRITE);
if(fd < 0)
return -1;
for(e = s + strlen(s); e - s > 0; s += n)
if((n = write(fd, s, e - s)) <= 0)
break;
close(fd);
return s == e? 0: -1;
}
void
setflags(Message *m, char *f)
{
char *t;
t = smprint("%s/%s", mbox.name, m->name);
writefile(t, "flags", f);
free(t);
}
char*
info(Message *m, int ind, int ogf)
{
@ -306,6 +331,24 @@ info(Message *m, int ind, int ogf)
else
p=m->fromcolon;
if(ind==0 && altmenu){
len = 12;
lens = 20;
if(ind==0 && m->subject[0]=='\0'){
snprint(fmt, sizeof fmt,
"\t%%-%d.%ds\t%%-12.12s\t", len, len);
snprint(s, sizeof s, fmt, p, stripdate(m->date) + 4);
}else{
snprint(fmt, sizeof fmt,
"\t%%-%d.%ds\t%%-12.12s\t%%-%d.%ds", len, len, lens, lens);
snprint(s, sizeof s, fmt, p, stripdate(m->date) + 4, m->subject);
}
i = estrdup(s);
return i;
}
if(ind==0 && shortmenu){
len = 30;
lens = 30;
@ -555,44 +598,58 @@ mesgdel(Message *mbox, Message *m)
mesgfreeparts(m);
}
int
deliver(char *folder, char *file)
{
char *av[4];
int pid, wpid, nz;
Waitmsg *w;
pid = fork();
switch(pid){
case -1:
return -1;
case 0:
av[0] = "mbappend";
av[1] = folder;
av[2] = file;
av[3] = 0;
exec("/bin/upas/mbappend", av);
_exits("b0rked");
return -1;
default:
while(w = wait()){
nz = !w->msg || !w->msg[0];
if(!nz)
werrstr("%s", w->msg);
wpid = w->pid;
free(w);
if(wpid == pid)
return nz? 0: -1;
}
return -1;
}
}
int
mesgsave(Message *m, char *s)
{
int ofd, n, k, ret;
char *t, *raw, *unixheader, *all;
char *t;
int ret;
t = estrstrdup(mbox.name, m->name);
raw = readfile(t, "raw", &n);
unixheader = readfile(t, "unixheader", &k);
if(raw==nil || unixheader==nil){
fprint(2, "Mail: can't read %s: %r\n", t);
free(t);
return 0;
}
free(t);
all = emalloc(n+k+1);
memmove(all, unixheader, k);
memmove(all+k, raw, n);
memmove(all+k+n, "\n", 1);
n = k+n+1;
free(unixheader);
free(raw);
ret = 1;
s = estrdup(s);
t = smprint("%s/%s/rawunix", mbox.name, m->name);
if(s[0] != '/')
s = egrow(estrdup(mailboxdir), "/", s);
ofd = open(s, OWRITE);
if(ofd < 0){
fprint(2, "Mail: can't open %s: %r\n", s);
ret = 0;
}else if(seek(ofd, 0LL, 2)<0 || write(ofd, all, n)!=n){
s = estrdup(s);
else
s = smprint("%s/%s", mailboxdir, s);
ret = 1;
if(deliver(s, t) == -1){
fprint(2, "Mail: save failed: can't write %s: %r\n", s);
ret = 0;
}
free(all);
close(ofd);
setflags(m, "S");
free(s);
free(t);
return ret;
}
@ -627,6 +684,7 @@ mesgcommand(Message *m, char *cmd)
mesgmenumark(mbox.w, m->name, s);
}
free(s);
setflags(m, "S");
goto Return;
}
if(strcmp(args[0], "Reply")==0){
@ -634,6 +692,7 @@ mesgcommand(Message *m, char *cmd)
mkreply(m, "Replyall", nil, nil, nil);
else
mkreply(m, "Reply", nil, nil, nil);
// setflags(m, "a");
goto Return;
}
if(strcmp(args[0], "Q") == 0){
@ -642,6 +701,7 @@ mesgcommand(Message *m, char *cmd)
mkreply(m, "QReplyall", nil, nil, s);
else
mkreply(m, "QReply", nil, nil, s);
// setflags(m, "a");
goto Return;
}
if(strcmp(args[0], "Del") == 0){
@ -667,11 +727,13 @@ mesgcommand(Message *m, char *cmd)
mesgcommand(m, estrdup("Del"));
return 1;
}
// setflags(m, "d");
goto Return;
}
if(strcmp(args[0], "UnDelmesg") == 0){
if(!m->isreply && m->deleted)
mesgmenumarkundel(wbox, &mbox, m);
// setflags(m, "-d");
goto Return;
}
// if(strcmp(args[0], "Headers") == 0){
@ -949,7 +1011,7 @@ ext(char *type)
void
mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
{
char *dest;
char *dest, *maildest;
if(strcmp(m->disposition, "file")==0 || strlen(m->filename)!=0){
if(strlen(m->filename) == 0){
@ -957,6 +1019,11 @@ mimedisplay(Message *m, char *name, char *rootdir, Window *w, int fileonly)
dest[strlen(dest)-1] = '\0';
}else
dest = estrdup(m->filename);
if(maildest = getenv("maildest")){
maildest = eappend(maildest, "/", dest);
Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), maildest);
free(maildest);
}
if(m->filename[0] != '/')
dest = egrow(estrdup(home), "/", dest);
Bprint(w->body, "\tcp %s%sbody%s %q\n", rootdir, name, ext(m->type), dest);
@ -1244,6 +1311,7 @@ mesgopen(Message *mbox, char *dir, char *s, Message *mesg, int plumbed, char *di
winclosebody(m->w);
winclean(m->w);
m->opened = 1;
setflags(m, "s");
if(ndirelem == 1){
free(u);
return 1;

View File

@ -1,4 +1,5 @@
</$objtype/mkfile
<../mkupas
TARG=Mail
OFILES=\
@ -12,7 +13,8 @@ OFILES=\
HFILES=dat.h
LIB=
BIN=/acme/bin/$objtype
# BIN=/acme/bin/$objtype
BIN=$ABIN
UPDATE=\
mkfile\
@ -25,6 +27,6 @@ $O.out: $OFILES
$LD -o $target $LDFLAGS $OFILES
syms:V:
8c -a mail.c >syms
8c -aa mesg.c reply.c util.c win.c >>syms
$CC -a mail.c >syms
$CC -aa mesg.c reply.c util.c win.c >>syms

View File

@ -341,21 +341,20 @@ print2(int fd, int ofd, char *fmt, ...)
return n;
}
void
int
write2(int fd, int ofd, char *buf, int n, int nofrom)
{
char *from, *p;
int m;
int m = 0;
write(fd, buf, n);
if(fd >= 0)
m = write(fd, buf, n);
if(ofd <= 0)
return;
return m;
if(nofrom == 0){
write(ofd, buf, n);
return;
}
if(nofrom == 0)
return write(ofd, buf, n);
/* need to escape leading From lines to avoid corrupting 'outgoing' mailbox */
for(p=buf; *p; p+=m){
@ -367,13 +366,15 @@ write2(int fd, int ofd, char *buf, int n, int nofrom)
if(m > 0)
write(ofd, p, m);
if(from){
/* escape with space if From is at start of line */
if(p==buf || from[-1]=='\n')
write(ofd, " ", 1); /* escape with space if From is at start of line */
write(ofd, " ", 1);
write(ofd, from, 4);
m += 4;
}
n -= m;
}
return p - buf;
}
void

View File

@ -88,7 +88,7 @@ error(char *fmt, ...)
va_end(arg);
fmtprint(&f, "\n");
fmtfdflush(&f);
threadexitsall(buf);
threadexitsall(fmt);
}
void

View File

@ -5,28 +5,30 @@
* local ones.
*/
/* predeclared */
static String *getdbfiles(void);
static int translate(char*, char**, String*, String*);
static int lookup(String**, String*, String*);
static int compare(String*, char*);
static char* mklower(char*);
static String *getdbfiles(void);
static int translate(char*, char**, String*, String*);
static int lookup(String**, String*, String*);
static char *mklower(char*);
static int debug;
static int from;
static char *namefiles = "namefiles";
#define DEBUG if(debug)
static int debug;
static int from;
static char *namefiles = "namefiles";
#define dprint(...) if(debug)fprint(2, __VA_ARGS__); else {}
void
usage(void)
{
fprint(2, "usage: aliasmail [-df] [-n namefile] [names ...]\n");
exits("usage");
}
/* loop through the names to be translated */
void
main(int argc, char *argv[])
{
String *s;
String *alias; /* the alias for the name */
char **names; /* names of this system */
String *files; /* list of files to search */
char *alias, **names, *p; /* names of this system */
int i, rv;
char *p;
String *s, *salias, *files;
ARGBEGIN {
case 'd':
@ -36,50 +38,46 @@ main(int argc, char *argv[])
from = 1;
break;
case 'n':
namefiles = ARGF();
namefiles = EARGF(usage());
break;
default:
usage();
} ARGEND
if (chdir(UPASLIB) < 0) {
perror("translate(chdir):");
exit(1);
}
if (chdir(UPASLIB) < 0)
sysfatal("chdir: %r");
/* get environmental info */
names = sysnames_read();
files = getdbfiles();
alias = s_new();
salias = s_new();
/* loop through the names to be translated (from standard input) */
for(i=0; i<argc; i++) {
s = unescapespecial(s_copy(mklower(argv[i])));
if(strchr(s_to_c(s), '!') == 0)
rv = translate(s_to_c(s), names, files, alias);
rv = translate(s_to_c(s), names, files, salias);
else
rv = -1;
alias = s_to_c(salias);
if(from){
if (rv >= 0 && *s_to_c(alias) != '\0'){
p = strchr(s_to_c(alias), '\n');
if(p)
if (rv >= 0 && *alias != '\0'){
if(p = strchr(alias, '\n'))
*p = 0;
p = strchr(s_to_c(alias), '!');
if(p) {
if(p = strchr(alias, '!')) {
*p = 0;
print("%s", s_to_c(alias));
print("%s", alias);
} else {
p = strchr(s_to_c(alias), '@');
if(p)
if(p = strchr(alias, '@'))
print("%s", p+1);
else
print("%s", s_to_c(alias));
print("%s", alias);
}
}
} else {
if (rv < 0 || *s_to_c(alias) == '\0')
if (rv < 0 || *alias == '\0')
print("local!%s\n", s_to_c(s));
else {
else
/* this must be a write, not a print */
write(1, s_to_c(alias), strlen(s_to_c(alias)));
}
write(1, alias, strlen(alias));
}
s_free(s);
}
@ -90,9 +88,9 @@ main(int argc, char *argv[])
static String *
getdbfiles(void)
{
Sinstack *sp;
String *files = s_new();
char *nf;
Sinstack *sp;
String *files;
if(from)
nf = "fromfiles";
@ -100,37 +98,34 @@ getdbfiles(void)
nf = namefiles;
/* system wide aliases */
files = s_new();
if ((sp = s_allocinstack(nf)) != 0){
while(s_rdinstack(sp, files))
s_append(files, " ");
s_freeinstack(sp);
}
DEBUG print("files are %s\n", s_to_c(files));
dprint("files are %s\n", s_to_c(files));
return files;
}
/* loop through the translation files */
static int
translate(char *name, /* name to translate */
char **namev, /* names of this system */
String *files, /* names of system alias files */
String *alias) /* where to put the alias */
translate(char *name, char **namev, String *files, String *alias)
{
String *file = s_new();
String **fullnamev;
int n, rv;
String *file, **fullnamev;
rv = -1;
file = s_new();
DEBUG print("translate(%s, %s, %s)\n", name,
dprint("translate(%s, %s, %s)\n", name,
s_to_c(files), s_to_c(alias));
/* create the full name to avoid loops (system!name) */
for(n = 0; namev[n]; n++)
;
fullnamev = (String**)malloc(sizeof(String*)*(n+2));
n = 0;
fullnamev[n++] = s_copy(name);
@ -144,12 +139,11 @@ translate(char *name, /* name to translate */
/* look at system-wide names */
s_restart(files);
while (s_parse(files, s_restart(file)) != 0) {
while (s_parse(files, s_restart(file)) != 0)
if (lookup(fullnamev, file, alias)==0) {
rv = 0;
goto out;
}
}
out:
for(n = 0; fullnamev[n]; n++)
@ -183,29 +177,30 @@ attobang(String *token)
/* Loop through the entries in a translation file looking for a match.
* Return 0 if found, -1 otherwise.
*/
#define compare(a, b) cistrcmp(s_to_c(a), b)
static int
lookup(
String **namev,
String *file,
String *alias) /* returned String */
lookup(String **namev, String *file, String *alias)
{
String *line = s_new();
String *token = s_new();
String *bangtoken;
int i, rv = -1;
char *name = s_to_c(namev[0]);
char *name;
int i, rv;
String *line, *token, *bangtoken;
Sinstack *sp;
DEBUG print("lookup(%s, %s, %s, %s)\n", s_to_c(namev[0]), s_to_c(namev[1]),
dprint("lookup(%s, %s, %s, %s)\n", s_to_c(namev[0]), s_to_c(namev[1]),
s_to_c(file), s_to_c(alias));
rv = -1;
name = s_to_c(namev[0]);
line = s_new();
token = s_new();
s_reset(alias);
if ((sp = s_allocinstack(s_to_c(file))) == 0)
return -1;
/* look for a match */
while (s_rdinstack(sp, s_restart(line))!=0) {
DEBUG print("line is %s\n", s_to_c(line));
dprint("line is %s\n", s_to_c(line));
s_restart(token);
if (s_parse(s_restart(line), token)==0)
continue;
@ -247,35 +242,13 @@ lookup(
return rv;
}
#define lower(c) ((c)>='A' && (c)<='Z' ? (c)-('A'-'a'):(c))
/* compare two Strings (case insensitive) */
static int
compare(String *s1,
char *p2)
{
char *p1 = s_to_c(s1);
int rv;
DEBUG print("comparing %s to %s\n", p1, p2);
while((rv = lower(*p1) - lower(*p2)) == 0) {
if (*p1 == '\0')
break;
p1++;
p2++;
}
return rv;
}
static char*
mklower(char *name)
{
char *p;
char c;
char c, *p;
for(p = name; *p; p++){
c = *p;
*p = lower(c);
}
for(p = name; c = *p; p++)
if(c >= 'A' && c <= 'Z')
*p = c + 0x20;
return name;
}

View File

@ -1,4 +1,5 @@
</$objtype/mkfile
<../mkupas
TARG=aliasmail
@ -10,8 +11,6 @@ HFILES=../common/common.h\
../common/sys.h\
BIN=/$objtype/bin/upas
UPDATE=\
mkfile\
$HFILES\

View File

@ -0,0 +1,2 @@
#!/bin/rc
exec /mail/lib/isspam.rc $*

View File

@ -0,0 +1,34 @@
</$objtype/mkfile
<../mkupas
RCFILES=isspam\
msgcat\
spam\
tfmt\
unspam\
all:Q:
;
installall:Q: install
;
install:V: ${RCFILES:%=$BIN/%}
safeinstall:V: install
safeinstallall:V: install
clean:Q:
;
nuke:V:
rm $BIN/^($RCFILES)
UPDATE=$RCFILES
update:V:
update $UPDATEFLAGS $UPDATE
$BIN/%: %.rc
cp $stem.rc $BIN/$stem

View File

@ -0,0 +1,38 @@
RCFILES=mail.rc\
all:Q:
;
installall:Q: install
;
install:V:
cp mail.rc /rc/bin/mail
safeinstall:V:
cp mail.rc /rc/bin/mail
safeinstallall:V:
cp mail.rc /rc/bin/mail
clean:Q:
;
nuke:V:
rm /rc/bin/mail
UPDATE=\
gone.fishing\
gone.msg\
mail.rc\
mail.sh\
makefile\
mkfile\
namefiles\
omail.rc\
qmail\
remotemail\
rewrite\
update:V:
update $UPDATEFLAGS $UPDATE

View File

@ -0,0 +1,11 @@
#!/bin/rc
f=$*
if(~ $#f 0)
f=/mail/fs/mbox/[0-9]*
f=`{echo $f|sed s:/mail/fs/mbox/::g}
{
for(i in $f)
echo $i p
} | upas/nedmail >[2=]

View File

@ -0,0 +1,2 @@
#!/bin/rc
exec /mail/lib/spam.rc $*

View File

@ -0,0 +1,25 @@
#!/bin/rc
# anti-topposting defense
# sed '/^[ ]*>[ ]*>[ ]*>/q'
awk '
{
if(l[i] ~ /^[ ]*>[ ]*>[ ]*>/)
q = 1
if(q == 0)
l[i = NR] = $0;
}
END{
for(; i > 1; i--)
if(l[i] !~ /^([ ]*>)*[ ]*$/)
break;
for(; i > 1; i--)
if(l[i] !~ /^[ ]*>[ ]*>/)
break;
for(; i > 1; i--)
if(l[i] !~ /^([ ]*>)*[ ]*$/)
break;
for(j = 1; j <= i; j++)
print l[j]
}' |dd -conv block >[2=]

View File

@ -0,0 +1,2 @@
#!/bin/rc
exec /mail/lib/unspam.rc $*

View File

@ -1,164 +0,0 @@
#include "common.h"
enum {
Buffersize = 64*1024,
};
typedef struct Inbuf Inbuf;
struct Inbuf
{
char buf[Buffersize];
char *wp;
char *rp;
int eof;
int in;
int out;
int last;
ulong bytes;
};
static Inbuf*
allocinbuf(int in, int out)
{
Inbuf *b;
b = mallocz(sizeof(Inbuf), 1);
if(b == nil)
sysfatal("reading mailbox: %r");
b->rp = b->wp = b->buf;
b->in = in;
b->out = out;
return b;
}
/* should only be called at start of file or when b->rp[-1] == '\n' */
static int
fill(Inbuf *b, int addspace)
{
int i, n;
if(b->eof && b->wp - b->rp == 0)
return 0;
n = b->rp - b->buf;
if(n > 0){
i = write(b->out, b->buf, n);
if(i != n)
return -1;
b->last = b->buf[n-1];
b->bytes += n;
}
if(addspace){
if(write(b->out, " ", 1) != 1)
return -1;
b->last = ' ';
b->bytes++;
}
n = b->wp - b->rp;
memmove(b->buf, b->rp, n);
b->rp = b->buf;
b->wp = b->rp + n;
i = read(b->in, b->buf+n, sizeof(b->buf)-n);
if(i < 0)
return -1;
b->wp += i;
return b->wp - b->rp;
}
enum { Fromlen = sizeof "From " - 1, };
/* code to escape ' '*From' ' at the beginning of a line */
int
appendfiletombox(int in, int out)
{
int addspace, n, sol;
char *p;
Inbuf *b;
seek(out, 0, 2);
b = allocinbuf(in, out);
addspace = 0;
sol = 1;
for(;;){
if(b->wp - b->rp < Fromlen){
/*
* not enough unread bytes in buffer to match "From ",
* so get some more. We must only inject a space at
* the start of a line (one that begins with "From ").
*/
if (b->rp == b->buf || b->rp[-1] == '\n') {
n = fill(b, addspace);
addspace = 0;
} else
n = fill(b, 0);
if(n < 0)
goto error;
if(n == 0)
break;
if(n < Fromlen){ /* still can't match? */
b->rp = b->wp;
continue;
}
}
/* state machine looking for ' '*From' ' */
if(!sol){
p = memchr(b->rp, '\n', b->wp - b->rp);
if(p == nil)
b->rp = b->wp;
else{
b->rp = p+1;
sol = 1;
}
continue;
} else {
if(*b->rp == ' ' || strncmp(b->rp, "From ", Fromlen) != 0){
b->rp++;
continue;
}
addspace = 1;
sol = 0;
}
}
/* mailbox entries always terminate with two newlines */
n = b->last == '\n' ? 1 : 2;
if(write(out, "\n\n", n) != n)
goto error;
n += b->bytes;
free(b);
return n;
error:
free(b);
return -1;
}
int
appendfiletofile(int in, int out)
{
int n;
Inbuf *b;
seek(out, 0, 2);
b = allocinbuf(in, out);
for(;;){
n = fill(b, 0);
if(n < 0)
goto error;
if(n == 0)
break;
b->rp = b->wp;
}
n = b->bytes;
free(b);
return n;
error:
free(b);
return -1;
}

View File

@ -1,42 +1,5 @@
#include "common.h"
/* expand a path relative to some `.' */
extern String *
abspath(char *path, char *dot, String *to)
{
if (*path == '/') {
to = s_append(to, path);
} else {
to = s_append(to, dot);
to = s_append(to, "/");
to = s_append(to, path);
}
return to;
}
/* return a pointer to the base component of a pathname */
extern char *
basename(char *path)
{
char *cp;
cp = strrchr(path, '/');
return cp==0 ? path : cp+1;
}
/* append a sub-expression match onto a String */
extern void
append_match(Resub *subexp, String *sp, int se)
{
char *cp, *ep;
cp = subexp[se].sp;
ep = subexp[se].ep;
for (; cp < ep; cp++)
s_putc(sp, *cp);
s_terminate(sp);
}
/*
* check for shell characters in a String
*/
@ -95,7 +58,7 @@ escapespecial(String *s)
return ns;
}
int
uint
hex2uint(char x)
{
if(x >= '0' && x <= '9')
@ -113,10 +76,9 @@ hex2uint(char x)
extern String*
unescapespecial(String *s)
{
int c;
String *ns;
char *sp;
uint n;
uint c, n;
String *ns;
if(strstr(s_to_c(s), escape) == 0)
return s;
@ -126,7 +88,7 @@ unescapespecial(String *s)
for(sp = s_to_c(s); *sp; sp++){
if(strncmp(sp, escape, n) == 0){
c = (hex2uint(sp[n])<<4) | hex2uint(sp[n+1]);
if(c < 0)
if(c & 0x80)
s_putc(ns, *sp);
else {
s_putc(ns, c);
@ -144,6 +106,5 @@ unescapespecial(String *s)
int
returnable(char *path)
{
return strcmp(path, "/dev/null") != 0;
}

View File

@ -6,11 +6,10 @@
* become powerless user
*/
int
become(char **cmd, char *who)
become(char **, char *who)
{
int fd;
USED(cmd);
if(strcmp(who, "none") == 0) {
fd = open("#c/user", OWRITE);
if(fd < 0 || write(fd, "none", strlen("none")) < 0) {

View File

@ -1,80 +1,79 @@
#include "sys.h"
/* format of REMOTE FROM lines */
extern char *REMFROMRE;
extern int REMSENDERMATCH;
extern int REMDATEMATCH;
extern int REMSYSMATCH;
/* format of mailbox FROM lines */
#define IS_HEADER(p) ((p)[0]=='F'&&(p)[1]=='r'&&(p)[2]=='o'&&(p)[3]=='m'&&(p)[4]==' ')
#define IS_TRAILER(p) ((p)[0]=='m'&&(p)[1]=='o'&&(p)[2]=='r'&&(p)[3]=='F'&&(p)[4]=='\n')
extern char *FROMRE;
extern int SENDERMATCH;
extern int DATEMATCH;
enum
{
Elemlen= 28,
Errlen= ERRMAX,
Pathlen= 256,
Elemlen = 56,
Pathlen = 256,
};
#include "sys.h"
#include <String.h>
enum{
Fields = 18,
/* flags */
Fanswered = 1<<0, /* a */
Fdeleted = 1<<1, /* D */
Fdraft = 1<<2, /* d */
Fflagged = 1<<3, /* f */
Frecent = 1<<4, /* r we are the first fs to see this */
Fseen = 1<<5, /* s */
Fstored = 1<<6, /* S */
Nflags = 7,
};
enum { Atnoteunknown, Atnoterecog };
/*
* routines in mail.c
* flag.c
*/
extern int print_header(Biobuf*, char*, char*);
extern int print_remote_header(Biobuf*, char*, char*, char*);
extern int parse_header(char*, String*, String*);
char *flagbuf(char*, int);
int buftoflags(char*);
char *txflags(char*, uchar*);
/*
* routines in aux.c
*/
extern String *abspath(char*, char*, String*);
extern String *mboxpath(char*, char*, String*, int);
extern char *basename(char*);
extern int delivery_status(String*);
extern void append_match(Resub*, String*, int);
extern int shellchars(char*);
extern String* escapespecial(String*);
extern String* unescapespecial(String*);
extern int returnable(char*);
char *mboxpathbuf(char*, int, char*, char*);
char *basename(char*);
int shellchars(char*);
String *escapespecial(String*);
String *unescapespecial(String*);
int returnable(char*);
/* in copymessage */
extern int appendfiletombox(int, int);
extern int appendfiletofile(int, int);
/* folder.c */
Biobuf *openfolder(char*, long);
int closefolder(Biobuf*);
int appendfolder(Biobuf*, char*, long*, int);
int fappendfolder(char*, long, char *, int);
int fappendfile(char*, char*, int);
char* foldername(char*, char*, char*);
char* ffoldername(char*, char*, char*);
/* mailbox types */
#define MF_NORMAL 0
#define MF_PIPE 1
#define MF_FORWARD 2
#define MF_NOMBOX 3
#define MF_NOTMBOX 4
/* fmt.c */
void mailfmtinstall(void); /* 'U' = 2047fmt */
#pragma varargck type "U" char*
/* totm.c */
int fromtotm(char*, Tm*);
/* a pipe between parent and child*/
typedef struct {
typedef struct{
Biobuf bb;
Biobuf *fp; /* parent process end*/
int fd; /* child process end*/
} stream;
/* a child process*/
typedef struct process{
typedef struct{
stream *std[3]; /* standard fd's*/
int pid; /* process identifier*/
int status; /* exit status*/
Waitmsg *waitmsg;
} process;
extern stream *instream(void);
extern stream *outstream(void);
extern void stream_free(stream*);
extern process *noshell_proc_start(char**, stream*, stream*, stream*, int, char*);
extern process *proc_start(char*, stream*, stream*, stream*, int, char*);
extern int proc_wait(process*);
extern int proc_free(process*);
extern int proc_kill(process*);
/* tell compiler we're using a value so it won't complain */
#define USE(x) if(x)
stream *instream(void);
stream *outstream(void);
void stream_free(stream*);
process *noshell_proc_start(char**, stream*, stream*, stream*, int, char*);
process *proc_start(char*, stream*, stream*, stream*, int, char*);
int proc_wait(process*);
int proc_free(process*);
//int proc_kill(process*);

View File

@ -1,11 +1,9 @@
#include "common.h"
char *MAILROOT = "/mail";
char *UPASLOG = "/sys/log";
char *UPASLIB = "/mail/lib";
char *UPASBIN= "/bin/upas";
char *UPASTMP = "/mail/tmp";
char *SHELL = "/bin/rc";
char *POST = "/sys/lib/post/dispatch";
int MBOXMODE = 0662;
char *MAILROOT = "/mail";
char *SPOOL = "/mail";
char *UPASLOG = "/sys/log";
char *UPASLIB = "/mail/lib";
char *UPASBIN = "/bin/upas";
char *UPASTMP = "/mail/tmp";
char *SHELL = "/bin/rc";

View File

@ -0,0 +1,73 @@
#include "common.h"
static uchar flagtab[] = {
'a', Fanswered,
'D', Fdeleted,
'd', Fdraft,
'f', Fflagged,
'r', Frecent,
's', Fseen,
'S', Fstored,
};
char*
flagbuf(char *buf, int flags)
{
char *p, c;
int i;
p = buf;
for(i = 0; i < nelem(flagtab); i += 2){
c = '-';
if(flags & flagtab[i+1])
c = flagtab[i];
*p++ = c;
}
*p = 0;
return buf;
}
int
buftoflags(char *p)
{
uchar flags;
int i;
flags = 0;
for(i = 0; i < nelem(flagtab); i += 2)
if(p[i>>1] == flagtab[i])
flags |= flagtab[i + 1];
return flags;
}
char*
txflags(char *p, uchar *flags)
{
uchar neg, f, c, i;
for(;;){
neg = 0;
again:
if((c = *p++) == '-'){
neg = 1;
goto again;
}else if(c == '+'){
neg = 0;
goto again;
}
if(c == 0)
return nil;
for(i = 0;; i += 2){
if(i == nelem(flagtab))
return "bad flag";
if(c == flagtab[i]){
f = flagtab[i+1];
break;
}
}
if(neg)
*flags &= ~f;
else
*flags |= f;
}
}

View File

@ -0,0 +1,35 @@
#include "common.h"
int
rfc2047fmt(Fmt *fmt)
{
char *s, *p;
s = va_arg(fmt->args, char*);
if(s == nil)
return fmtstrcpy(fmt, "");
for(p=s; *p; p++)
if((uchar)*p >= 0x80)
goto hard;
return fmtstrcpy(fmt, s);
hard:
fmtprint(fmt, "=?utf-8?q?");
for(p = s; *p; p++){
if(*p == ' ')
fmtrune(fmt, '_');
else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' ||
(uchar)*p >= 0x80)
fmtprint(fmt, "=%.2uX", (uchar)*p);
else
fmtrune(fmt, (uchar)*p);
}
fmtprint(fmt, "?=");
return 0;
}
void
mailfmtinstall(void)
{
fmtinstall('U', rfc2047fmt);
}

View File

@ -0,0 +1,361 @@
#include "common.h"
enum{
Mbox = 1,
Mdir,
};
typedef struct Folder Folder;
struct Folder{
int open;
int ofd;
int type;
Biobuf *out;
Mlock *l;
};
static Folder ftab[5];
static Folder*
getfolder(Biobuf *out)
{
int i;
Folder *f;
for(i = 0; i < nelem(ftab); i++){
f = ftab+i;
if(f->open == 0){
f->open = 1;
f->ofd = -1;
f->type = 0;
return f;
}
if(f->out == out)
return f;
}
sysfatal("folder.c:ftab too small");
return 0;
}
static int
putfolder(Folder *f)
{
int r;
r = 0;
if(f->l)
sysunlock(f->l);
if(f->out)
r |= Bterm(f->out);
if(f->ofd > 0){
close(f->ofd);
free(f->out);
}
memset(f, 0, sizeof *f);
return r;
}
static Biobuf*
mboxopen(char *s)
{
Folder *f;
f = getfolder(0);
f->l = syslock(s); /* traditional botch: ignore failure */
if((f->ofd = open(s, OWRITE)) == -1)
if((f->ofd = create(s, OWRITE|OEXCL, DMAPPEND|0600)) == -1){
putfolder(f);
return nil;
}
seek(f->ofd, 0, 2);
f->out = malloc(sizeof *f->out);
Binit(f->out, f->ofd, OWRITE);
f->type = Mbox;
return f->out;
}
/*
* sync with send/cat_mail.c:/^mdir
*/
static Biobuf*
mdiropen(char *s, long t)
{
char buf[64];
long i;
Folder *f;
f = getfolder(0);
for(i = 0; i < 100; i++){
snprint(buf, sizeof buf, "%s/%lud.%.2ld", s, t, i);
if((f->ofd = create(buf, OWRITE|OEXCL, DMAPPEND|0660)) != -1)
goto found;
}
putfolder(f);
return nil;
found:
werrstr("");
f->out = malloc(sizeof *f->out);
Binit(f->out, f->ofd, OWRITE);
f->type = Mdir;
return f->out;
}
Biobuf*
openfolder(char *s, long t)
{
int isdir;
Dir *d;
if(d = dirstat(s)){
isdir = d->mode&DMDIR;
free(d);
}else{
isdir = create(s, OREAD, DMDIR|0777);
if(isdir == -1)
return nil;
close(isdir);
isdir = 1;
}
if(isdir)
return mdiropen(s, t);
else
return mboxopen(s);
}
int
renamefolder(Biobuf *b, long t)
{
char buf[32];
int i;
Dir d;
Folder *f;
f = getfolder(b);
if(f->type != Mdir)
return 0;
for(i = 0; i < 100; i++){
nulldir(&d);
snprint(buf, sizeof buf, "%lud.%.2d", t, i);
d.name = buf;
if(dirfwstat(Bfildes(b), &d) > 0)
return 0;
}
return -1;
}
int
closefolder(Biobuf *b)
{
return putfolder(getfolder(b));
}
/*
* escape "From " at the beginning of a line;
* translate \r\n to \n for imap
*/
static int
mboxesc(Biobuf *in, Biobuf *out, int type)
{
char *s;
int n;
for(; s = Brdstr(in, '\n', 0); free(s)){
if(!strncmp(s, "From ", 5))
Bputc(out, ' ');
n = strlen(s);
if(n > 1 && s[n-2] == '\r'){
s[n-2] = '\n';
n--;
}
if(Bwrite(out, s, n) == Beof){
free(s);
return -1;
}
if(s[n-1] != '\n')
Bputc(out, '\n');
}
if(type == Mbox)
Bputc(out, '\n');
if(Bflush(out) == Beof)
return -1;
return 0;
}
int
appendfolder(Biobuf *b, char *addr, long *t, int fd)
{
char *s;
int r;
Biobuf bin;
Folder *f;
Tm tm;
f = getfolder(b);
Bseek(f->out, 0, 2);
Binit(&bin, fd, OREAD);
s = Brdstr(&bin, '\n', 0);
if(!s || strncmp(s, "From ", 5))
Bprint(f->out, "From %s %.28s\n", addr, ctime(*t));
else if(fromtotm(s, &tm) >= 0)
*t = tm2sec(&tm);
if(s)
Bwrite(f->out, s, strlen(s));
free(s);
r = mboxesc(&bin, f->out, f->type);
return r | Bterm(&bin);
}
int
fappendfolder(char *addr, long t, char *s, int fd)
{
long t0, r;
Biobuf *b;
b = openfolder(s, t);
if(b == nil)
return -1;
t0 = t;
r = appendfolder(b, addr, &t, fd);
if(t != t0)
renamefolder(b, t);
r |= closefolder(b);
return r;
}
/*
* BOTCH sync with ../imap4d/mbox.c:/^okmbox
*/
static char *specialfile[] =
{
"L.mbox",
"forward",
"headers",
"imap.subscribed",
"names",
"pipefrom",
"pipeto",
};
static int
special(char *s)
{
char *p;
int i;
p = strrchr(s, '/');
if(p == nil)
p = s;
else
p++;
for(i = 0; i < nelem(specialfile); i++)
if(strcmp(p, specialfile[i]) == 0)
return 1;
return 0;
}
static char*
mkmbpath(char *s, int n, char *user, char *mb, char *path)
{
char *p, *e, *r, buf[Pathlen];
if(!mb)
return mboxpathbuf(s, n, user, path);
e = buf+sizeof buf;
p = seprint(buf, e, "%s", mb);
if(r = strrchr(buf, '/'))
p = r;
seprint(p, e, "/%s", path);
return mboxpathbuf(s, n, user, buf);
}
/*
* fancy processing for ned:
* we default to storing in $mail/f then just in $mail.
*/
char*
ffoldername(char *mb, char *user, char *rcvr)
{
char *p;
int c, n;
Dir *d;
static char buf[Pathlen];
d = dirstat(mkmbpath(buf, sizeof buf, user, mb, "f/"));
n = strlen(buf);
if(!d || d->qid.type != QTDIR)
buf[n -= 2] = 0;
free(d);
if(p = strrchr(rcvr, '!'))
rcvr = p+1;
while(n < sizeof buf-1 && (c = *rcvr++)){
if(c== '@')
break;
if(c == '/')
c = '_';
buf[n++] = c;
}
buf[n] = 0;
if(special(buf)){
fprint(2, "!won't overwrite %s\n", buf);
return nil;
}
return buf;
}
char*
foldername(char *mb, char *user, char *path)
{
static char buf[Pathlen];
mkmbpath(buf, sizeof buf, user, mb, path);
if(special(buf)){
fprint(2, "!won't overwrite %s\n", buf);
return nil;
}
return buf;
}
static int
append(Biobuf *in, Biobuf *out)
{
char *buf;
int n, m;
buf = malloc(8192);
for(;;){
m = 0;
n = Bread(in, buf, 8192);
if(n <= 0)
break;
m = Bwrite(out, buf, n);
if(m != n)
break;
}
if(m != n)
n = -1;
else
n = 1;
free(buf);
return n;
}
/* symmetry for nedmail; misnamed */
int
fappendfile(char*, char *target, int in)
{
int fd, r;
Biobuf bin, out;
if((fd = create(target, ORDWR|OEXCL, 0666)) == -1)
return -1;
Binit(&out, fd, OWRITE);
Binit(&bin, in, OREAD);
r = append(&bin, &out);
Bterm(&bin);
Bterm(&out);
close(fd);
return r;
}

View File

@ -2,68 +2,43 @@
#include <auth.h>
#include <ndb.h>
/*
* number of predefined fd's
*/
int nsysfile=3;
static char err[Errlen];
/*
* return the date
*/
extern char *
char*
thedate(void)
{
static char now[64];
char *cp;
strcpy(now, ctime(time(0)));
cp = strchr(now, '\n');
if(cp)
*cp = 0;
now[28] = 0;
return now;
}
/*
* return the user id of the current user
*/
extern char *
char *
getlog(void)
{
static char user[64];
int fd;
int n;
fd = open("/dev/user", 0);
if(fd < 0)
return nil;
if((n=read(fd, user, sizeof(user)-1)) <= 0)
return nil;
close(fd);
user[n] = 0;
return user;
return getuser();
}
/*
* return the lock name (we use one lock per directory)
*/
static String *
lockname(char *path)
static void
lockname(Mlock *l, char *path)
{
String *lp;
char *cp;
char *e, *q;
/*
* get the name of the lock file
*/
lp = s_new();
cp = strrchr(path, '/');
if(cp)
s_nappend(lp, path, cp - path + 1);
s_append(lp, "L.mbox");
return lp;
seprint(l->name, e = l->name+sizeof l->name, "%s", path);
q = strrchr(l->name, '/');
if(q == nil)
q = l->name;
else
q++;
seprint(q, e, "%s", "L.mbox");
}
int
@ -92,58 +67,43 @@ static int
openlockfile(Mlock *l)
{
int fd;
Dir *d;
Dir nd;
Dir *d, nd;
char *p;
fd = open(s_to_c(l->name), OREAD);
if(fd >= 0){
l->fd = fd;
l->fd = open(l->name, OREAD);
if(l->fd >= 0)
return 0;
if(d = dirstat(l->name)){
free(d);
return 1; /* try again later */
}
l->fd = create(l->name, OREAD, DMEXCL|0666);
if(l->fd >= 0){
nulldir(&nd);
nd.mode = DMEXCL|0666;
if(dirfwstat(l->fd, &nd) < 0){
/* if we can't chmod, don't bother */
/* live without the lock but log it */
close(l->fd);
l->fd = -1;
syslog(0, "mail", "lock error: %s: %r", l->name);
remove(l->name);
}
return 0;
}
d = dirstat(s_to_c(l->name));
if(d == nil){
/* file doesn't exist */
/* try creating it */
fd = create(s_to_c(l->name), OREAD, DMEXCL|0666);
if(fd >= 0){
nulldir(&nd);
nd.mode = DMEXCL|0666;
if(dirfwstat(fd, &nd) < 0){
/* if we can't chmod, don't bother */
/* live without the lock but log it */
syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
remove(s_to_c(l->name));
}
l->fd = fd;
return 0;
}
/* couldn't create */
/* do we have write access to the directory? */
p = strrchr(s_to_c(l->name), '/');
if(p != 0){
*p = 0;
fd = access(s_to_c(l->name), 2);
*p = '/';
if(fd < 0){
/* live without the lock but log it */
syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
return 0;
}
} else {
fd = access(".", 2);
if(fd < 0){
/* live without the lock but log it */
syslog(0, "mail", "lock error: %s: %r", s_to_c(l->name));
return 0;
}
}
} else
free(d);
return 1; /* try again later */
/* couldn't create; let's see what we can whine about */
p = strrchr(l->name, '/');
if(p != 0){
*p = 0;
fd = access(l->name, 2);
*p = '/';
}else
fd = access(".", 2);
if(fd < 0)
/* live without the lock but log it */
syslog(0, "mail", "lock error: %s: %r", l->name);
close(fd);
return 0;
}
#define LSECS 5*60
@ -152,7 +112,7 @@ openlockfile(Mlock *l)
* Set a lock for a particular file. The lock is a file in the same directory
* and has L. prepended to the name of the last element of the file name.
*/
extern Mlock *
Mlock*
syslock(char *path)
{
Mlock *l;
@ -162,25 +122,18 @@ syslock(char *path)
if(l == 0)
return nil;
l->name = lockname(path);
lockname(l, path);
/*
* wait LSECS seconds for it to unlock
*/
for(tries = 0; tries < LSECS*2; tries++){
for(tries = 0; tries < LSECS*2; tries++)
switch(openlockfile(l)){
case 0:
return l;
case 1:
sleep(500);
break;
default:
goto noway;
}
}
noway:
s_free(l->name);
free(l);
return nil;
}
@ -188,20 +141,19 @@ noway:
/*
* like lock except don't wait
*/
extern Mlock *
Mlock *
trylock(char *path)
{
Mlock *l;
char buf[1];
int fd;
Mlock *l;
l = malloc(sizeof(Mlock));
l = mallocz(sizeof(Mlock), 1);
if(l == 0)
return 0;
l->name = lockname(path);
lockname(l, path);
if(openlockfile(l) != 0){
s_free(l->name);
free(l);
return 0;
}
@ -217,12 +169,12 @@ trylock(char *path)
if(pread(fd, buf, 1, 0) < 0)
break;
}
_exits(0);
_exits(nil);
}
return l;
}
extern void
void
syslockrefresh(Mlock *l)
{
char buf[1];
@ -230,16 +182,12 @@ syslockrefresh(Mlock *l)
pread(l->fd, buf, 1, 0);
}
extern void
void
sysunlock(Mlock *l)
{
if(l == 0)
return;
if(l->name){
s_free(l->name);
}
if(l->fd >= 0)
close(l->fd);
close(l->fd);
if(l->pid > 0)
postnote(PNPROC, l->pid, "time to die");
free(l);
@ -254,15 +202,10 @@ sysunlock(Mlock *l)
* w - writable
* A - append only (doesn't exist in Bio)
*/
extern Biobuf *
Biobuf*
sysopen(char *path, char *mode, ulong perm)
{
int sysperm;
int sysmode;
int fd;
int docreate;
int append;
int truncate;
int sysperm, sysmode, fd, docreate, append, truncate;
Dir *d, nd;
Biobuf *bp;
@ -274,7 +217,7 @@ sysopen(char *path, char *mode, ulong perm)
docreate = 0;
append = 0;
truncate = 0;
for(; mode && *mode; mode++)
for(; *mode; mode++)
switch(*mode){
case 'A':
sysmode = OWRITE;
@ -371,15 +314,6 @@ sysclose(Biobuf *bp)
return rv;
}
/*
* create a file
*/
int
syscreate(char *file, int mode, ulong perm)
{
return create(file, mode, perm);
}
/*
* make a directory
*/
@ -394,76 +328,45 @@ sysmkdir(char *file, ulong perm)
return 0;
}
/*
* change the group of a file
*/
int
syschgrp(char *file, char *group)
{
Dir nd;
if(group == 0)
return -1;
nulldir(&nd);
nd.gid = group;
return dirwstat(file, &nd);
}
extern int
sysdirreadall(int fd, Dir **d)
{
return dirreadall(fd, d);
}
/*
* read in the system name
*/
extern char *
char *
sysname_read(void)
{
static char name[128];
char *cp;
char *s, *c;
cp = getenv("site");
if(cp == 0 || *cp == 0)
cp = alt_sysname_read();
if(cp == 0 || *cp == 0)
cp = "kremvax";
strecpy(name, name+sizeof name, cp);
c = s = getenv("site");
if(!c)
c = alt_sysname_read();
if(!c)
c = "kremvax";
strecpy(name, name+sizeof name, c);
free(s);
return name;
}
extern char *
char *
alt_sysname_read(void)
{
static char name[128];
int n, fd;
fd = open("/dev/sysname", OREAD);
if(fd < 0)
return 0;
n = read(fd, name, sizeof(name)-1);
close(fd);
if(n <= 0)
return 0;
name[n] = 0;
return name;
return sysname();
}
/*
* get all names
*/
extern char**
char**
sysnames_read(void)
{
static char **namev;
Ndbtuple *t, *nt;
int n;
char *cp;
Ndbtuple *t, *nt;
static char **namev;
if(namev)
return namev;
free(csgetvalue(0, "sys", alt_sysname_read(), "dom", &t));
free(csgetvalue(0, "sys", sysname(), "dom", &t));
n = 0;
for(nt = t; nt; nt = nt->entry)
@ -471,13 +374,10 @@ sysnames_read(void)
n++;
namev = (char**)malloc(sizeof(char *)*(n+3));
if(namev){
n = 0;
namev[n++] = strdup(sysname_read());
cp = alt_sysname_read();
if(cp)
namev[n++] = strdup(cp);
namev[0] = strdup(sysname_read());
namev[1] = strdup(alt_sysname_read());
n = 2;
for(nt = t; nt; nt = nt->entry)
if(strcmp(nt->attr, "dom") == 0)
namev[n++] = strdup(nt->val);
@ -492,69 +392,21 @@ sysnames_read(void)
/*
* read in the domain name
*/
extern char *
char*
domainname_read(void)
{
char **namev;
char **p;
for(namev = sysnames_read(); *namev; namev++)
if(strchr(*namev, '.'))
return *namev;
for(p = sysnames_read(); *p; p++)
if(strchr(*p, '.'))
return *p;
return 0;
}
/*
* return true if the last error message meant file
* did not exist.
*/
extern int
e_nonexistent(void)
{
rerrstr(err, sizeof(err));
return strcmp(err, "file does not exist") == 0;
}
/*
* return true if the last error message meant file
* was locked.
*/
extern int
e_locked(void)
{
rerrstr(err, sizeof(err));
return strcmp(err, "open/create -- file is locked") == 0;
}
/*
* return the length of a file
*/
extern long
sysfilelen(Biobuf *fp)
{
Dir *d;
long rv;
d = dirfstat(Bfildes(fp));
if(d == nil)
return -1;
rv = d->length;
free(d);
return rv;
}
/*
* remove a file
*/
extern int
sysremove(char *path)
{
return remove(path);
}
/*
* rename a file, fails unless both are in the same directory
*/
extern int
int
sysrename(char *old, char *new)
{
Dir d;
@ -579,81 +431,27 @@ sysrename(char *old, char *new)
return dirwstat(old, &d);
}
/*
* see if a file exists
*/
extern int
int
sysexist(char *file)
{
Dir *d;
d = dirstat(file);
if(d == nil)
return 0;
free(d);
return 1;
return access(file, AEXIST) == 0;
}
/*
* return nonzero if file is a directory
*/
extern int
sysisdir(char *file)
{
Dir *d;
int rv;
static char yankeepig[] = "die: yankee pig dog";
d = dirstat(file);
if(d == nil)
return 0;
rv = d->mode & DMDIR;
free(d);
return rv;
}
/*
* kill a process or process group
*/
static int
stomp(int pid, char *file)
{
char name[64];
int fd;
snprint(name, sizeof(name), "/proc/%d/%s", pid, file);
fd = open(name, 1);
if(fd < 0)
return -1;
if(write(fd, "die: yankee pig dog\n", sizeof("die: yankee pig dog\n") - 1) <= 0){
close(fd);
return -1;
}
close(fd);
return 0;
}
/*
* kill a process
*/
extern int
int
syskill(int pid)
{
return stomp(pid, "note");
return postnote(PNPROC, pid, yankeepig);
}
/*
* kill a process group
*/
extern int
int
syskillpg(int pid)
{
return stomp(pid, "notepg");
return postnote(PNGROUP, pid, yankeepig);
}
extern int
int
sysdetach(void)
{
if(rfork(RFENVG|RFNAMEG|RFNOTEG) < 0) {
@ -668,11 +466,10 @@ sysdetach(void)
*/
static int *closedflag;
static int
catchpipe(void *a, char *msg)
catchpipe(void *, char *msg)
{
static char *foo = "sys: write on closed pipe";
USED(a);
if(strncmp(msg, foo, strlen(foo)) == 0){
if(closedflag)
*closedflag = 1;
@ -692,30 +489,21 @@ pipesigoff(void)
atnotify(catchpipe, 0);
}
void
exit(int i)
{
char buf[32];
if(i == 0)
exits(0);
snprint(buf, sizeof(buf), "%d", i);
exits(buf);
}
static int
int
islikeatty(int fd)
{
char buf[64];
int l;
if(fd2path(fd, buf, sizeof buf) != 0)
return 0;
/* might be /mnt/term/dev/cons */
return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
l = strlen(buf);
return l >= 9 && strcmp(buf+l-9, "/dev/cons") == 0;
}
extern int
int
holdon(void)
{
int fd;
@ -729,20 +517,20 @@ holdon(void)
return fd;
}
extern int
int
sysopentty(void)
{
return open("/dev/cons", ORDWR);
}
extern void
void
holdoff(int fd)
{
write(fd, "holdoff", 7);
close(fd);
}
extern int
int
sysfiles(void)
{
return 128;
@ -754,142 +542,66 @@ sysfiles(void)
* if the path starts with / or ./, don't change it
*
*/
extern String *
mboxpath(char *path, char *user, String *to, int dot)
char*
mboxpathbuf(char *to, int n, char *user, char *path)
{
if (dot || *path=='/' || strncmp(path, "./", 2) == 0
|| strncmp(path, "../", 3) == 0) {
to = s_append(to, path);
} else {
to = s_append(to, MAILROOT);
to = s_append(to, "/box/");
to = s_append(to, user);
to = s_append(to, "/");
to = s_append(to, path);
}
if(*path == '/' || !strncmp(path, "./", 2) || !strncmp(path, "../", 3))
snprint(to, n, "%s", path);
else
snprint(to, n, "%s/box/%s/%s", MAILROOT, user, path);
return to;
}
extern String *
mboxname(char *user, String *to)
{
return mboxpath("mbox", user, to, 0);
}
extern String *
deadletter(String *to) /* pass in sender??? */
{
char *cp;
cp = getlog();
if(cp == 0)
return 0;
return mboxpath("dead.letter", cp, to, 0);
}
char *
homedir(char *user)
{
USED(user);
return getenv("home");
}
String *
readlock(String *file)
{
char *cp;
cp = getlog();
if(cp == 0)
return 0;
return mboxpath("reading", cp, file, 0);
}
String *
username(String *from)
/*
* warning: we're not quoting bad characters. we're not encoding
* non-ascii characters. basically this function sucks. don't use.
*/
char*
username0(Biobuf *b, char *from)
{
char *p, *f[6];
int n;
Biobuf *bp;
char *p, *q;
String *s;
static char buf[32];
bp = Bopen("/adm/keys.who", OREAD);
if(bp == 0)
bp = Bopen("/adm/netkeys.who", OREAD);
if(bp == 0)
return 0;
s = 0;
n = strlen(s_to_c(from));
for(;;) {
p = Brdline(bp, '\n');
n = strlen(from);
buf[0] = 0;
for(;; free(p)) {
p = Brdstr(b, '\n', 1);
if(p == 0)
break;
p[Blinelen(bp)-1] = 0;
if(strncmp(p, s_to_c(from), n))
if(strncmp(p, from, n) || p[n] != '|')
continue;
p += n;
if(*p != ' ' && *p != '\t') /* must be full match */
if(getfields(p, f, nelem(f), 0, "|") < 3)
continue;
while(*p && (*p == ' ' || *p == '\t'))
p++;
if(*p == 0)
continue;
for(q = p; *q; q++)
if(('0' <= *q && *q <= '9') || *q == '<')
break;
while(q > p && q[-1] != ' ' && q[-1] != '\t')
q--;
while(q > p && (q[-1] == ' ' || q[-1] == '\t'))
q--;
*q = 0;
s = s_new();
s_append(s, "\"");
s_append(s, p);
s_append(s, "\"");
break;
snprint(buf, sizeof buf, "\"%s\"", f[2]);
/* no break; last match wins */
}
return buf[0]? buf: 0;
}
char*
username(char *from)
{
char *s;
Biobuf *b;
s = 0;
if(b = Bopen("/adm/keys.who", OREAD)){
s = username0(b, from);
Bterm(b);
}
if(s == 0 && (b = Bopen("/adm/netkeys.who", OREAD))){
s = username0(b, from);
Bterm(b);
}
Bterm(bp);
return s;
}
char *
remoteaddr(int fd, char *dir)
{
char buf[128], *p;
int n;
if(dir == 0){
if(fd2path(fd, buf, sizeof(buf)) != 0)
return "";
/* parse something of the form /net/tcp/nnnn/data */
p = strrchr(buf, '/');
if(p == 0)
return "";
strncpy(p+1, "remote", sizeof(buf)-(p-buf)-2);
} else
snprint(buf, sizeof buf, "%s/remote", dir);
buf[sizeof(buf)-1] = 0;
fd = open(buf, OREAD);
if(fd < 0)
return "";
n = read(fd, buf, sizeof(buf)-1);
close(fd);
if(n > 0){
buf[n] = 0;
p = strchr(buf, '!');
if(p)
*p = 0;
return strdup(buf);
}
return "";
}
// create a file and
// 1) ensure the modes we asked for
// 2) make gid == uid
/*
* create a file and
* 1) ensure the modes we asked for
* 2) make gid == uid
*/
static int
docreate(char *file, int perm)
{
@ -897,7 +609,7 @@ docreate(char *file, int perm)
Dir ndir;
Dir *d;
// create the mbox
/* create the mbox */
fd = create(file, OREAD, perm);
if(fd < 0){
fprint(2, "couldn't create %s\n", file);
@ -917,55 +629,77 @@ docreate(char *file, int perm)
return 0;
}
// create a mailbox
int
creatembox(char *user, char *folder)
static int
createfolder0(char *user, char *folder, char *ftype)
{
char *p;
String *mailfile;
char buf[512];
Mlock *ml;
char *p, *s, buf[Pathlen];
int isdir, mode;
Dir *d;
mailfile = s_new();
if(folder == 0)
mboxname(user, mailfile);
else {
snprint(buf, sizeof(buf), "%s/mbox", folder);
mboxpath(buf, user, mailfile, 0);
}
// don't destroy existing mailbox
if(access(s_to_c(mailfile), 0) == 0){
fprint(2, "mailbox already exists\n");
assert(folder != 0);
mboxpathbuf(buf, sizeof buf, user, folder);
if(access(buf, 0) == 0){
fprint(2, "%s already exists\n", ftype);
return -1;
}
fprint(2, "creating new mbox: %s\n", s_to_c(mailfile));
fprint(2, "creating new %s: %s\n", ftype, buf);
// make sure preceding levels exist
for(p = s_to_c(mailfile); p; p++) {
if(*p == '/') /* skip leading or consecutive slashes */
/*
* if we can deliver to this mbox, it needs
* to be read/execable all the way down
*/
mode = 0711;
if(!strncmp(buf, "/mail/box/", 10))
if((s = strrchr(buf, '/')) && !strcmp(s+1, "mbox"))
mode = 0755;
for(p = buf; p; p++) {
if(*p == '/')
continue;
p = strchr(p, '/');
if(p == 0)
break;
*p = 0;
if(access(s_to_c(mailfile), 0) != 0){
if(docreate(s_to_c(mailfile), DMDIR|0711) < 0)
return -1;
}
if(access(buf, 0) != 0)
if(docreate(buf, DMDIR|mode) < 0)
return -1;
*p = '/';
}
// create the mbox
if(docreate(s_to_c(mailfile), 0622|DMAPPEND|DMEXCL) < 0)
return -1;
/* must match folder.c:/^openfolder */
isdir = create(buf, OREAD, DMDIR|0777);
/*
* create the lock file if it doesn't exist
* make sure everyone can write here if it's a mailbox
* rather than a folder
*/
ml = trylock(s_to_c(mailfile));
if(ml != nil)
sysunlock(ml);
if(mode == 0755)
if(isdir >= 0 && (d = dirfstat(isdir))){
d->mode |= 0773;
dirfwstat(isdir, d);
free(d);
}
if(isdir == -1){
fprint(2, "can't create %s: %s\n", ftype, buf);
return -1;
}
close(isdir);
return 0;
}
int
createfolder(char *user, char *folder)
{
return createfolder0(user, folder, "folder");
}
int
creatembox(char *user, char *mbox)
{
char buf[Pathlen];
if(mbox == 0)
snprint(buf, sizeof buf, "mbox");
else
snprint(buf, sizeof buf, "%s/mbox", mbox);
return createfolder0(user, buf, "mbox");
}

View File

@ -1,57 +0,0 @@
#include "common.h"
/* format of REMOTE FROM lines */
char *REMFROMRE =
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)[ \t]+remote[ \t]+from[ \t]+(.*)\n$";
int REMSENDERMATCH = 1;
int REMDATEMATCH = 4;
int REMSYSMATCH = 5;
/* format of LOCAL FROM lines */
char *FROMRE =
"^>?From[ \t]+((\".*\")?[^\" \t]+?(\".*\")?[^\" \t]+?)[ \t]+(.+)\n$";
int SENDERMATCH = 1;
int DATEMATCH = 4;
/* output a unix style local header */
int
print_header(Biobuf *fp, char *sender, char *date)
{
return Bprint(fp, "From %s %s\n", sender, date);
}
/* output a unix style remote header */
int
print_remote_header(Biobuf *fp, char *sender, char *date, char *system)
{
return Bprint(fp, "From %s %s remote from %s\n", sender, date, system);
}
/* parse a mailbox style header */
int
parse_header(char *line, String *sender, String *date)
{
if (!IS_HEADER(line))
return -1;
line += sizeof("From ") - 1;
s_restart(sender);
while(*line==' '||*line=='\t')
line++;
if(*line == '"'){
s_putc(sender, *line++);
while(*line && *line != '"')
s_putc(sender, *line++);
s_putc(sender, *line++);
} else {
while(*line && *line != ' ' && *line != '\t')
s_putc(sender, *line++);
}
s_terminate(sender);
s_restart(date);
while(*line==' '||*line=='\t')
line++;
while(*line)
s_putc(date, *line++);
s_terminate(date);
return 0;
}

View File

@ -1,18 +0,0 @@
CFLAGS=${UNIX} -g -I. -I../libc -I../common -I/usr/include ${SCFLAGS}
OBJS=mail.o aux.o string.o ${SYSOBJ}
AR=ar
.c.o: ; ${CC} -c ${CFLAGS} $*.c
common.a: ${OBJS}
${AR} cr common.a ${OBJS}
-ranlib common.a
aux.o: aux.h string.h mail.h
string.o: string.h mail.h
mail.o: mail.h
syslog.o: sys.h
mail.h: sys.h
clean:
-rm -f *.[oO] core a.out *.a *.sL common.a

View File

@ -1,13 +1,17 @@
</$objtype/mkfile
<../mkupas
LIB=libcommon.a$O
OFILES=aux.$O\
OFILES=\
aux.$O\
become.$O\
mail.$O\
process.$O\
libsys.$O\
config.$O\
appendfiletombox.$O\
folder.$O\
flags.$O\
fmt.$O\
libsys.$O\
process.$O\
totm.$O\
HFILES=common.h\
sys.h\
@ -18,3 +22,7 @@ UPDATE=\
$HFILES\
</sys/src/cmd/mklib
nuke:V:
mk clean
rm -f libcommon.a[$OS]

View File

@ -96,8 +96,7 @@ noshell_proc_start(char **av, stream *inp, stream *outp, stream *errp, int newpg
if(who)
become(av, who);
exec(av[0], av);
perror("proc_start");
exits("proc_start");
sysfatal("proc_start");
default:
for (i=0; i<3; i++)
if (pp->std[i] != 0) {
@ -126,12 +125,12 @@ extern int
proc_wait(process *pp)
{
Waitmsg *status;
char err[Errlen];
char err[ERRMAX];
for(;;){
status = wait();
if(status == nil){
rerrstr(err, sizeof(err));
errstr(err, sizeof(err));
if(strstr(err, "interrupt") == 0)
break;
}
@ -161,13 +160,13 @@ proc_free(process *pp)
if (pp->pid >= 0)
proc_wait(pp);
free(pp->waitmsg);
free((char *)pp);
free(pp);
return 0;
}
/* kill a process */
extern int
proc_kill(process *pp)
{
return syskill(pp->pid);
}
//extern int
//proc_kill(process *pp)
//{
// return syskill(pp->pid);
//}

View File

@ -1,85 +1,63 @@
/*
* System dependent header files for research
*/
#include <u.h>
#include <libc.h>
#include <regexp.h>
#include <bio.h>
#include "String.h"
/*
* for the lock routines in libsys.c
*/
typedef struct Mlock Mlock;
struct Mlock {
int fd;
int pid;
String *name;
int fd;
int pid;
char name[Pathlen];
};
/*
* from config.c
*/
extern char *MAILROOT; /* root of mail system */
extern char *SPOOL; /* spool directory; for spam ctl */
extern char *UPASLOG; /* log directory */
extern char *UPASLIB; /* upas library directory */
extern char *UPASBIN; /* upas binary directory */
extern char *UPASTMP; /* temporary directory */
extern char *SHELL; /* path name of shell */
extern char *POST; /* path name of post server addresses */
extern int MBOXMODE; /* default mailbox protection mode */
enum {
Mboxmode = 0622,
};
/*
* files in libsys.c
*/
extern char *sysname_read(void);
extern char *alt_sysname_read(void);
extern char *domainname_read(void);
extern char **sysnames_read(void);
extern char *getlog(void);
extern char *thedate(void);
extern Biobuf *sysopen(char*, char*, ulong);
extern int sysopentty(void);
extern int sysclose(Biobuf*);
extern int sysmkdir(char*, ulong);
extern int syschgrp(char*, char*);
extern Mlock *syslock(char *);
extern void sysunlock(Mlock *);
extern void syslockrefresh(Mlock *);
extern int e_nonexistent(void);
extern int e_locked(void);
extern long sysfilelen(Biobuf*);
extern int sysremove(char*);
extern int sysrename(char*, char*);
extern int sysexist(char*);
extern int sysisdir(char*);
extern int syskill(int);
extern int syskillpg(int);
extern int syscreate(char*, int, ulong);
extern Mlock *trylock(char *);
extern void exit(int);
extern void pipesig(int*);
extern void pipesigoff(void);
extern int holdon(void);
extern void holdoff(int);
extern int syscreatelocked(char*, int, int);
extern int sysopenlocked(char*, int);
extern int sysunlockfile(int);
extern int sysfiles(void);
extern int become(char**, char*);
extern int sysdetach(void);
extern int sysdirreadall(int, Dir**);
extern String *username(String*);
extern char* remoteaddr(int, char*);
extern int creatembox(char*, char*);
extern String *readlock(String*);
extern char *homedir(char*);
extern String *mboxname(char*, String*);
extern String *deadletter(String*);
/*
* maximum size for a file path
*/
#define MAXPATHLEN 128
char *sysname_read(void);
char *alt_sysname_read(void);
char *domainname_read(void);
char **sysnames_read(void);
char *getlog(void);
char *thedate(void);
Biobuf *sysopen(char*, char*, ulong);
int sysopentty(void);
int sysclose(Biobuf*);
int sysmkdir(char*, ulong);
Mlock *syslock(char *);
void sysunlock(Mlock *);
void syslockrefresh(Mlock *);
int sysrename(char*, char*);
int sysexist(char*);
int syskill(int);
int syskillpg(int);
Mlock *trylock(char *);
void pipesig(int*);
void pipesigoff(void);
int holdon(void);
void holdoff(int);
int syscreatelocked(char*, int, int);
int sysopenlocked(char*, int);
int sysunlockfile(int);
int sysfiles(void);
int become(char**, char*);
int sysdetach(void);
char *username(char*);
int creatembox(char*, char*);
int createfolder(char*, char*);

View File

@ -0,0 +1,36 @@
#include <common.h>
static char mtab[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
int
ctimetotm(char *s, Tm *tm)
{
char buf[32];
if(strlen(s) < 28)
return -1;
snprint(buf, sizeof buf, "%s", s);
memset(tm, 0, sizeof *tm);
buf[7] = 0;
tm->mon = (strstr(mtab, buf+4) - mtab)/3;
tm->mday = atoi(buf+8);
tm->hour = atoi(buf+11);
tm->min = atoi(buf+14);
tm->sec = atoi(buf+17);
tm->zone[0] = buf[20];
tm->zone[1] = buf[21];
tm->zone[2] = buf[22];
tm->year = atoi(buf+24) - 1900;
return 0;
}
int
fromtotm(char *s, Tm *tm)
{
char buf[256], *f[3];
snprint(buf, sizeof buf, "%s", s);
if(getfields(buf, f, nelem(f), 0, " ") != 3)
return -1;
return ctimetotm(f[2], tm);
}

View File

@ -7,64 +7,32 @@
void
usage(void)
{
fprint(2, "usage: %s recipient fromfile mbox\n", argv0);
fprint(2, "usage: deliver recipient fromaddr-file mbox\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int bytes, fd, i;
char now[30];
char *deliveredto;
char *to, *s;
int r;
long l;
Addr *a;
Mlock *l;
ARGBEGIN{
}ARGEND;
if(argc != 3)
usage();
deliveredto = strrchr(argv[0], '!');
if(deliveredto == nil)
deliveredto = argv[0];
if(to = strrchr(argv[0], '!'))
to++;
else
deliveredto++;
to = argv[0];
a = readaddrs(argv[1], nil);
if(a == nil)
sysfatal("missing from address");
l = syslock(argv[2]);
/* append to mbox */
i = 0;
retry:
fd = open(argv[2], OWRITE);
if(fd < 0){
rerrstr(now, sizeof(now));
if(strstr(now, "exclusive lock") && i++ < 20){
sleep(500); /* wait for lock to go away */
goto retry;
}
sysfatal("opening mailbox: %r");
}
seek(fd, 0, 2);
strncpy(now, ctime(time(0)), sizeof(now));
now[28] = 0;
if(fprint(fd, "From %s %s\n", a->val, now) < 0)
sysfatal("writing mailbox: %r");
/* copy message handles escapes and any needed new lines */
bytes = appendfiletombox(0, fd);
if(bytes < 0)
sysfatal("writing mailbox: %r");
close(fd);
sysunlock(l);
/* log it */
syslog(0, "mail", "delivered %s From %s %s (%s) %d", deliveredto,
a->val, now, argv[0], bytes);
exits(0);
s = ctime(l = time(0));
werrstr("");
r = fappendfolder(a->val, l, argv[2], 0);
syslog(0, "mail", "delivered %s From %s %.28s (%s) %d %r", to, a->val, s, argv[0], r);
exits("");
}

View File

@ -2,8 +2,8 @@
#include <libc.h>
#include <regexp.h>
#include <libsec.h>
#include <String.h>
#include <bio.h>
#include <String.h>
#include "dat.h"
int debug;

View File

@ -0,0 +1,63 @@
/*
* deliver to one's own folder with locking & logging
*/
#include "dat.h"
#include "common.h"
void
append(int fd, char *mb, char *from, long t)
{
char *folder, *s;
int r;
s = ctime(t);
folder = foldername(from, getuser(), mb);
r = fappendfolder(0, t, folder, fd);
if(r == 0)
werrstr("");
syslog(0, "mail", "mbappend %s %.28s (%s) %r", mb, s, folder);
if(r)
exits("fail");
}
void
usage(void)
{
fprint(2, "usage: mbappend [-t time] [-f from] mbox [file ...]\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *mb, *from;
int fd;
long t;
from = nil;
t = time(0);
ARGBEGIN{
case 't':
t = strtoul(EARGF(usage()), 0, 0);
break;
case 'f':
from = EARGF(usage());
break;
default:
usage();
}ARGEND;
if(*argv == 0)
usage();
werrstr("");
mb = *argv++;
if(*argv == 0)
append(0, mb, from, t);
else for(; *argv; argv++){
fd = open(*argv, OREAD);
if(fd < 0)
sysfatal("open: %r");
append(fd, mb, from, t);
close(fd);
}
exits("");
}

View File

@ -0,0 +1,32 @@
#include "dat.h"
#include "common.h"
void
usage(void)
{
fprint(2, "usage: mbcreate [-f] ...\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int r;
int (*f)(char*, char*);
f = creatembox;
ARGBEGIN{
case 'f':
f = createfolder;
break;
default:
usage();
}ARGEND
r = 0;
for(; *argv; argv++)
r |= f(getuser(), *argv);
if(r)
exits("errors");
exits("");
}

View File

@ -0,0 +1,243 @@
/*
* why did i write this and not use upas/fs?
*/
#include "dat.h"
#include "common.h"
int qflag;
int pflag;
int rflag;
int tflag;
int vflag;
/* must be [0-9]+(\..*)? */
static int
dirskip(Dir *a, uvlong *uv)
{
char *p;
if(a->length == 0)
return 1;
*uv = strtoul(a->name, &p, 0);
if(*uv < 1000000 || *p != '.')
return 1;
*uv = *uv<<8 | strtoul(p+1, &p, 10);
if(*p)
return 1;
return 0;
}
static int
ismbox(char *path)
{
char buf[512];
int fd, r;
fd = open(path, OREAD);
if(fd == -1)
return 0;
r = 1;
if(read(fd, buf, sizeof buf) < 28+5)
r = 0;
else if(strncmp(buf, "From ", 5))
r = 0;
close(fd);
return r;
}
int
isindex(Dir *d)
{
char *p;
p = strrchr(d->name, '.');
if(!p)
return -1;
if(strcmp(p, ".idx") || strcmp(p, ".imp"))
return 1;
return 0;
}
int
idiotcheck(char *path, Dir *d, int getindex)
{
uvlong v;
if(d->mode & DMDIR)
return 0;
if(!strncmp(d->name, "L.", 2))
return 0;
if(getindex && isindex(d))
return 0;
if(!dirskip(d, &v) || ismbox(path))
return 0;
return -1;
}
int
vremove(char *buf)
{
if(vflag)
fprint(2, "rm %s\n", buf);
if(!pflag)
return remove(buf);
return 0;
}
int
rm(char *dir, int level)
{
char buf[Pathlen];
int i, n, r, fd, isdir;
Dir *d;
d = dirstat(dir);
isdir = d->mode & DMDIR;
free(d);
if(!isdir)
return 0;
fd = open(dir, OREAD);
if(fd == -1)
return -1;
n = dirreadall(fd, &d);
close(fd);
r = 0;
for(i = 0; i < n; i++){
snprint(buf, sizeof buf, "%s/%s", dir, d[i].name);
if(rflag)
r |= rm(buf, level+1);
if(idiotcheck(buf, d+i, level+rflag) == -1)
continue;
if(vremove(buf) != 0)
r = -1;
}
free(d);
return r;
}
void
nukeidx(char *buf)
{
char buf2[Pathlen];
snprint(buf2, sizeof buf2, "%s.idx", buf);
vremove(buf2);
snprint(buf2, sizeof buf2, "%s.imp", buf);
vremove(buf2);
}
void
truncidx(char *buf)
{
char buf2[Pathlen];
snprint(buf2, sizeof buf2, "%s.idx", buf);
vremove(buf2);
// snprint(buf2, sizeof buf2, "%s.imp", buf);
// vremove(buf2);
}
static int
removefolder0(char *user, char *folder, char *ftype)
{
char *msg, buf[Pathlen];
int r, isdir;
Dir *d;
assert(folder != 0);
mboxpathbuf(buf, sizeof buf, user, folder);
if((d = dirstat(buf)) == 0){
fprint(2, "%s: %s doesn't exist\n", buf, ftype);
return 0;
}
isdir = d->mode & DMDIR;
free(d);
msg = "deleting";
if(tflag)
msg = "truncating";
fprint(2, "%s %s: %s\n", msg, ftype, buf);
/* must match folder.c:/^openfolder */
r = rm(buf, 0);
if(!tflag)
r = vremove(buf);
else if(!isdir)
r = open(buf, OWRITE|OTRUNC);
if(tflag)
truncidx(buf);
else
nukeidx(buf);
if(r == -1){
fprint(2, "%s: can't %s %s\n", buf, msg, ftype);
return -1;
}
close(r);
return 0;
}
int
removefolder(char *user, char *folder)
{
return removefolder0(user, folder, "folder");
}
int
removembox(char *user, char *mbox)
{
char buf[Pathlen];
if(mbox == 0)
snprint(buf, sizeof buf, "mbox");
else
snprint(buf, sizeof buf, "%s/mbox", mbox);
return removefolder0(user, buf, "mbox");
}
void
usage(void)
{
fprint(2, "usage: mbremove [-fpqrtv] ...\n");
exits("usage");
}
void
main(int argc, char **argv)
{
int r;
int (*f)(char*, char*);
f = removembox;
ARGBEGIN{
case 'f':
f = removefolder;
break;
case 'p':
pflag++;
break;
case 'q':
qflag++;
close(2);
open("/dev/null", OWRITE);
break;
case 'r':
rflag++;
break;
case 't':
tflag++;
break;
case 'v':
vflag++;
break;
default:
usage();
}ARGEND
r = 0;
for(; *argv; argv++)
r |= f(getuser(), *argv);
if(r)
exits("errors");
exits("");
}

View File

@ -1,13 +1,14 @@
</$objtype/mkfile
<../mkupas
TARG=\
token\
list\
deliver\
list\
mbappend\
token\
LIB=../common/libcommon.a$O\
BIN=/$objtype/bin/upas
OFILES=readaddrs.$O
UPDATE=\
mkfile\

0
sys/src/cmd/upas/filterkit/pipefrom.sample Normal file → Executable file
View File

View File

@ -0,0 +1,4 @@
From: erik quanstrom <quanstro@coraid.com>
Subject: 1 testing
testing

View File

@ -1,60 +1,50 @@
#include <u.h>
#include <libc.h>
#include <libsec.h>
#include <String.h>
#include "dat.h"
void
usage(void)
{
fprint(2, "usage: %s key [token [file]]\n", argv0);
fprint(2, "usage: token key [token]\n");
exits("usage");
}
static String*
mktoken(char *key, long thetime)
static char*
mktoken(char *key, long t)
{
char *now;
char *now, token[64];
uchar digest[SHA1dlen];
char token[64];
String *s;
now = ctime(thetime);
now = ctime(t);
memset(now+11, ':', 8);
hmac_sha1((uchar*)now, strlen(now), (uchar*)key, strlen(key), digest, nil);
enc64(token, sizeof token, digest, sizeof digest);
s = s_new();
s_nappend(s, token, 5);
return s;
return smprint("%.5s", token);
}
static char*
check_token(char *key, char *file)
{
String *s;
char *s, buf[1024];
int i, fd, m;
long now;
int i;
char buf[1024];
int fd;
fd = open(file, OREAD);
if(fd < 0)
return "no match";
i = read(fd, buf, sizeof(buf)-1);
i = read(fd, buf, sizeof buf-1);
close(fd);
if(i < 0)
return "no match";
buf[i] = 0;
now = time(0);
for(i = 0; i < 14; i++){
s = mktoken(key, now-24*60*60*i);
if(strstr(buf, s_to_c(s)) != nil){
s_free(s);
m = s != nil && strstr(buf, s) != nil;
free(s);
if(m)
return nil;
}
s_free(s);
}
return "no match";
}
@ -62,10 +52,7 @@ check_token(char *key, char *file)
static char*
create_token(char *key)
{
String *s;
s = mktoken(key, time(0));
print("%s", s_to_c(s));
print("%s", mktoken(key, time(0)));
return nil;
}
@ -78,10 +65,8 @@ main(int argc, char **argv)
switch(argc){
case 2:
exits(check_token(argv[0], argv[1]));
break;
case 1:
exits(create_token(argv[0]));
break;
default:
usage();
}

40
sys/src/cmd/upas/fs/bos.c Normal file
View File

@ -0,0 +1,40 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
/*
* assume:
* - the stack segment can't be resized
* - stacks may not be segattached (by name Stack, anyway)
* - no thread library
*/
uintptr
absbos(void)
{
char buf[64], *f[10], *s, *r;
int n;
uintptr p, q;
Biobuf *b;
p = 0xd0000000; /* guess pc kernel */
snprint(buf, sizeof buf, "/proc/%ud/segment", getpid());
b = Bopen(buf, OREAD);
if(b == nil)
return p;
for(; s = Brdstr(b, '\n', 1); free(s)){
if((n = tokenize(s, f, nelem(f))) < 3)
continue;
if(strcmp(f[0], "Stack") != 0)
continue;
/*
* addressing from end because segment
* flags could become discontiguous if
* additional flags are added
*/
q = strtoull(f[n - 3], &r, 16);
if(*r == 0 && (char*)q > end)
p = q;
}
Bterm(b);
return p;
}

552
sys/src/cmd/upas/fs/cache.c Normal file
View File

@ -0,0 +1,552 @@
#include "common.h"
#include <libsec.h>
#include "dat.h"
int
findcache(Mcache *c, Message *m)
{
int i;
for(i = 0; i < c->ntab; i++)
if(c->ctab[i] == m)
return i;
return -1;
}
static void
prcache(Mcache *c, char *prefix)
{
int j;
Message *m;
if(!debug)
return;
for(j = 0; j < c->ntab; j++){
m = c->ctab[j];
dprint("%s%d/%s\t%p\t%d\t%ld\n", prefix, j, m->name, m, m->refs, m->csize);
}
}
/* debugging only */
static void
dupchk(Mcache *c)
{
int i, j;
if(!debug)
return;
for(i = 0; i < c->ntab; i++)
for(j = i + 1; j < c->ntab; j++)
if(c->ctab[i] == c->ctab[j])
goto lose;
return;
lose:
for(j = 0; j < c->ntab; j++)
dprint("%d\t%p %d\t%ld\n", j, c->ctab[j], c->ctab[j]->refs, c->ctab[j]->size);
abort();
}
int
addcache(Mcache *c, Message *m)
{
int i;
if((i = findcache(c, m)) < 0){
if(c->ntab + 1 == nelem(c->ctab))
abort();
i = c->ntab++;
}else{
/* rotate */
if(i == c->ntab - 1)
return i; /* silly shortcut to prevent excessive printage. */
dprint("addcache rotate %d %d\n", i, c->ntab);
prcache(c, "");
memmove(c->ctab + i, c->ctab + i + 1, (c->ntab - i - 1)*sizeof c->ctab[0]);
i = c->ntab - 1;
c->ctab[i] = m;
dupchk(c);
}
dprint("addcache %d %d %p\n", i, c->ntab, m);
c->ctab[i] = m;
return i;
}
static void
notecache(Mailbox *mb, Message *m, long sz)
{
assert(Topmsg(mb, m));
assert(sz >= 0 && sz < Maxmsg);
m->csize += sz;
mb->cached += sz;
addcache(mb, m);
}
static long
cachefree0(Mailbox *mb, Message *m, int force)
{
long sz, i;
Message *s;
if(!force && !mb->fetch)
return 0;
for(s = m->part; s; s = s->next)
cachefree(mb, s, force);
dprint("cachefree: %D %p, %p\n", m->fileid, m, m->start);
if(m->mallocd){
free(m->start);
m->mallocd = 0;
}
if(m->ballocd){
free(m->body);
m->ballocd = 0;
}
if(m->hallocd){
free(m->header);
m->hallocd = 0;
}
for(i = 0; i < nelem(m->references); i++){
free(m->references[i]);
m->references[i] = 0;
}
sz = m->csize;
m->csize = 0;
m->start = 0;
m->end = 0;
m->header = 0;
m->hend = 0;
m->hlen = -1;
m->body = 0;
m->bend = 0;
m->mheader = 0;
m->mhend = 0;
if(mb->decache)
mb->decache(mb, m);
m->decoded = 0;
m->converted = 0;
m->badchars = 0;
m->cstate &= ~(Cheader|Cbody);
if(Topmsg(mb, m))
mb->cached -= sz;
return sz;
}
long
cachefree(Mailbox *mb, Message *m, int force)
{
long sz, i;
sz = cachefree0(mb, m, force);
for(i = 0; i < mb->ntab; i++)
if(m == mb->ctab[i]){
mb->ntab--;
memmove(mb->ctab + i, mb->ctab + i + 1, sizeof m*mb->ntab - i);
dupchk(mb);
break;
}
return sz;
}
enum{
Maxntab = nelem(mbl->ctab) - 10,
};
vlong
sumcache(Mcache *c)
{
int i;
vlong sz;
sz = 0;
for(i = 0; i < c->ntab; i++)
sz += c->ctab[i]->csize;
return sz;
}
int
scancache(Mcache *c)
{
int i;
for(i = 0; i < c->ntab; i++)
if(c->ctab[i]->csize > Maxmsg)
return -1;
return 0;
}
/* debugging only */
static void
chkcsize(Mailbox *mb, vlong sz, vlong sz0)
{
int j;
Mcache *c;
Message *m;
if(sumcache(mb) == mb->cached)
if(scancache(mb) == 0)
return;
eprint("sz0 %lld sz %lld sum %lld sumca %lld\n", sz0, sz, sumcache(mb), mb->cached);
eprint("%lld\n", sumcache(mb));
c = mb;
for(j = 0; j < c->ntab; j++){
m = c->ctab[j];
eprint("%d %p %d %ld %ld\n", j, m, m->refs, m->csize, m->size);
}
abort();
}
/*
* strategy: start with i = 0. while cache exceeds limits,
* find j so that all the [i:j] elements have refs == 0.
* uncache all the [i:j], reduce ntab by i-j. the tail
* [j+1:ntab] is shifted to [i:ntab], and finally i = i+1.
* we may safely skip the new i, since the condition
* that stopped our scan there still holds.
*/
void
putcache(Mailbox *mb, Message *m)
{
int i, j, k;
vlong sz, sz0;
Message **p;
p = mb->ctab;
sz0 = mb->cached;
dupchk(mb);
for(i = 0;; i++){
sz = mb->cached;
for(j = i;; j++){
if(j >= mb->ntab ||
sz < cachetarg && mb->ntab - (j - i) < Maxntab){
if(j != i)
break;
chkcsize(mb, sz, sz0);
return;
}
if(p[j]->refs > 0)
break;
sz -= p[j]->csize;
}
if(sz == mb->cached){
if(i >= mb->ntab)
break;
continue;
}
for(k = i; k < j; k++)
cachefree0(mb, p[k], 0);
mb->ntab -= j - i;
memmove(p + i, p + j, (mb->ntab - i)*sizeof *p);
}
chkcsize(mb, sz, sz0);
k = 0;
for(i = 0; i < mb->ntab; i++)
k += p[i]->refs > 0;
if((mb->ntab > 1 || k != mb->ntab) && Topmsg(mb, m))
eprint("cache overflow: %D %llud bytes; %d entries\n",
m? m->fileid: 1ll, mb->cached, mb->ntab);
if(k == mb->ntab)
return;
debug = 1; prcache(mb, "");
abort();
}
static int
squeeze(Message *m, uvlong o, long l, int c)
{
char *p, *q, *e;
int n;
q = memchr(m->start + o, c, l);
if(q == nil)
return 0;
n = 0;
e = m->start + o + l;
for(p = q; q < e; q++){
if(*q == c){
n++;
continue;
}
*p++ = *q;
}
return n;
}
void
msgrealloc(Message *m, ulong l)
{
long l0, h0, m0, me, b0;
l0 = m->end - m->start;
m->mallocd = 1;
h0 = m->hend - m->start;
m0 = m->mheader - m->start;
me = m->mhend - m->start;
b0 = m->body - m->start;
assert(h0 >= 0 && m0 >= 0 && me >= 0 && b0 >= 0);
m->start = erealloc(m->start, l + 1);
m->rbody = m->start + b0;
m->rbend = m->end = m->start + l0;
if(!m->hallocd){
m->header = m->start;
m->hend = m->start + h0;
}
if(!m->ballocd){
m->body = m->start + b0;
m->bend = m->start + l0;
}
m->mheader = m->start + m0;
m->mhend = m->start + me;
}
/*
* the way we squeeze out bad characters is exceptionally sneaky.
*/
static int
fetch(Mailbox *mb, Message *m, uvlong o, ulong l)
{
int expand;
long l0, n, sz0;
top:
l0 = m->end - m->start;
assert(l0 >= 0);
dprint("fetch %lud sz %lud o %llud l %lud badchars %d\n", l0, m->size, o, l, m->badchars);
assert(m->badchars < Maxmsg/10);
if(l0 == m->size || o > m->size)
return 0;
expand = 0;
if(o + l > m->size)
l = m->size - o;
if(o + l == m->size)
l += m->ibadchars - m->badchars;
if(o + l > l0){
expand = 1;
msgrealloc(m, o + m->badchars + l);
}
assert(l0 <= o);
sz0 = m->size;
if(mb->fetch(mb, m, o + m->badchars, l) == -1){
logmsg(m, "can't fetch %D %llud %lud", m->fileid, o, l);
m->deleted = Dead;
return -1;
}
if(m->size - sz0)
l += m->size - sz0; /* awful botch for gmail */
if(expand){
/* grumble. poor planning. */
if(m->badchars > 0)
memmove(m->start + o, m->start + o + m->badchars, l);
n = squeeze(m, o, l, 0);
n += squeeze(m, o, l - n, '\r');
if(n > 0){
if(m->ibadchars == 0)
dprint(" %ld more badchars\n", n);
l -= n;
m->badchars += n;
msgrealloc(m, o + l);
}
notecache(mb, m, l);
m->bend = m->rbend = m->end = m->start + o + l;
if(n)
if(o + l + n == m->size && m->cstate&Cidx){
dprint(" redux %llud %ld\n", o + l, n);
o += l;
l = n;
goto top;
}
}else
eprint("unhandled case in fetch\n");
*m->end = 0;
return 0;
}
void
cachehash(Mailbox *mb, Message *m)
{
// fprint(2, "cachehash %P\n", mpair(mb, m));
if(m->whole == m->whole->whole)
henter(PATH(mb->id, Qmbox), m->name,
(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
else
henter(PATH(m->whole->id, Qdir), m->name,
(Qid){PATH(m->id, Qdir), 0, QTDIR}, m, mb);
henter(PATH(m->id, Qdir), "xxx",
(Qid){PATH(m->id, Qmax), 0, QTFILE}, m, mb); /* sleezy speedup */
}
void
newcachehash(Mailbox *mb, Message *m, int doplumb)
{
if(doplumb)
mailplumb(mb, m, 0);
else
if(insurecache(mb, m) == 0)
msgdecref(mb, m);
/* avoid cachehash on error? */
cachehash(mb, m);
}
static char *itab[] = {
"idx",
"stale",
"header",
"body"
};
char*
cstate(Message *m)
{
char *p, *e;
int i, s;
static char buf[64];
s = m->cstate;
p = e = buf;
e += sizeof buf;
for(i = 0; i < 8; i++)
if(s & 1<<i)
if(i < nelem(itab))
p = seprint(p, e, "%s ", itab[i]);
if(p > buf)
p--;
p[0] = 0;
return buf;
}
static int
middlecache(Mailbox *mb, Message *m)
{
int y;
y = 0;
while(!Topmsg(mb, m)){
m = m->whole;
if((m->cstate & Cbody) == 0)
y = 1;
}
if(y == 0)
return 0;
dprint("middlecache %d [%D] %lud %lud\n", m->id, m->fileid, m->end - m->start, m->size);
return cachebody(mb, m);
}
int
cacheheaders(Mailbox *mb, Message *m)
{
char *p, *e;
int r;
ulong o;
if(!mb->fetch || m->cstate&Cheader)
return 0;
if(!Topmsg(mb, m))
return middlecache(mb, m);
dprint("cacheheaders %d %D\n", m->id, m->fileid);
if(m->size < 10000)
r = fetch(mb, m, 0, m->size);
else for(r = 0; (o = m->end - m->start) < m->size; ){
if((r = fetch(mb, m, o, 4096)) < 0)
break;
p = m->start + o;
if(o)
p--;
for(e = m->end - 2; p < e; p++){
p = memchr(p, '\n', e - p);
if(p == nil)
break;
if(p[1] == '\n' || (p[1] == '\r' && p[2] == '\n'))
goto found;
}
}
if(r < 0)
return -1;
found:
parseheaders(mb, m, mb->addfrom, 0);
return 0;
}
void
digestmessage(Mailbox *mb, Message *m)
{
assert(m->digest == 0);
m->digest = emalloc(SHA1dlen);
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
if(mtreeisdup(mb, m)){
logmsg(m, "dup detected");
m->deleted = Dup; /* no dups allowed */
}else
mtreeadd(mb, m);
dprint("%d %#A\n", m->id, m->digest);
}
int
cachebody(Mailbox *mb, Message *m)
{
ulong o;
while(!Topmsg(mb, m))
m = m->whole;
if(!mb->fetch || m->cstate&Cbody)
return 0;
o = m->end - m->start;
dprint("cachebody %d [%D] %lud %lud %s\n", m->id, m->fileid, o, m->size, cstate(m));
if(o < m->size)
if(fetch(mb, m, o, m->size - o) < 0)
return -1;
if((m->cstate&Cidx) == 0){
assert(m->ibadchars == 0);
if(m->badchars > 0)
dprint("reducing size %ld %ld\n", m->size, m->size - m->badchars);
m->size -= m->badchars; /* sneaky */
m->ibadchars = m->badchars;
}
if(m->digest == 0)
digestmessage(mb, m);
if(m->lines == 0)
m->lines = countlines(m);
parse(mb, m, mb->addfrom, 0);
dprint(" →%s\n", cstate(m));
return 0;
}
int
cacheidx(Mailbox *mb, Message *m)
{
if(m->cstate & Cidx)
return 0;
if(cachebody(mb, m) == -1)
return -1;
m->cstate |= Cidxstale|Cidx;
return 0;
}
static int
countparts(Message *m)
{
Message *p;
if(m->nparts == 0)
for(p = m->part; p; p = p->next){
countparts(p);
m->nparts++;
}
return m->nparts;
}
int
insurecache(Mailbox *mb, Message *m)
{
if(m->deleted || !m->inmbox)
return -1;
msgincref(m);
cacheidx(mb, m);
if((m->cstate & Cidx) == 0){
logmsg(m, "%s: can't cache: %s: %r", mb->path, m->name);
msgdecref(mb, m);
return -1;
}
if(m->digest == 0)
sysfatal("digest?");
countparts(m);
return 0;
}

BIN
sys/src/cmd/upas/fs/chkidx Executable file

Binary file not shown.

View File

@ -0,0 +1,416 @@
#include "common.h"
#include <auth.h>
#include <libsec.h>
#include <bin.h>
#include "dat.h"
#define idprint(...) if(1) fprint(2, __VA_ARGS__); else {}
enum{
Maxver = 10,
};
static char *magictab[Maxver] = {
[4] "idx magic v4\n",
[7] "idx magic v7\n",
};
static int fieldstab[Maxver] = {
[4] 19,
[7] 21,
};
static char *magic;
static int Idxfields;
static int lineno;
static int idxver;
int
newid(void)
{
static int id;
return ++id;
}
void*
emalloc(ulong n)
{
void *p;
p = mallocz(n, 1);
if(!p)
sysfatal("malloc %lud: %r", n);
setmalloctag(p, getcallerpc(&n));
return p;
}
static int
Afmt(Fmt *f)
{
char buf[SHA1dlen*2 + 1];
uchar *u, i;
u = va_arg(f->args, uchar*);
if(u == 0 && f->flags & FmtSharp)
return fmtstrcpy(f, "-");
if(u == 0)
return fmtstrcpy(f, "<nildigest>");
for(i = 0; i < SHA1dlen; i++)
sprint(buf + 2*i, "%2.2ux", u[i]);
return fmtstrcpy(f, buf);
}
static int
Dfmt(Fmt *f)
{
char buf[32];
int seq;
uvlong v;
v = va_arg(f->args, uvlong);
seq = v & 0xff;
if(seq > 99)
seq = 99;
snprint(buf, sizeof buf, "%llud.%.2d", v>>8, seq);
return fmtstrcpy(f, buf);
}
static Mailbox*
shellmailbox(char *path)
{
Mailbox *mb;
mb = malloc(sizeof *mb);
if(mb == 0)
sysfatal("malloc");
memset(mb, 0, sizeof *mb);
snprint(mb->path, sizeof mb->path, "%s", path);
mb->id = newid();
mb->root = newmessage(nil);
mb->mtree = mkavltree(mtreecmp);
return mb;
}
void
shellmailboxfree(Mailbox*)
{
}
Message*
newmessage(Message *parent)
{
static int id;
Message *m;
// msgallocd++;
m = mallocz(sizeof *m, 1);
if(m == 0)
sysfatal("malloc");
m->disposition = Dnone;
// m->type = newrefs("text/plain");
// m->charset = newrefs("iso-8859-1");
m->cstate = Cidxstale;
m->flags = Frecent;
m->id = newid();
if(parent)
snprint(m->name, sizeof m->name, "%d", ++(parent->subname));
if(parent == nil)
parent = m;
m->whole = parent;
m->hlen = -1;
return m;
}
void
unnewmessage(Mailbox *mb, Message *parent, Message *m)
{
assert(parent->subname > 0);
// delmessage(mb, m);
USED(mb, m);
parent->subname -= 1;
}
static int
validmessage(Mailbox *mb, Message *m, int level)
{
if(level){
if(m->digest != 0)
goto lose;
if(m->fileid <= 1000000ull<<8)
if(m->fileid != 0)
goto lose;
}else{
if(m->digest == 0)
goto lose;
if(m->size == 0)
goto lose;
if(m->fileid <= 1000000ull<<8)
goto lose;
if(mtreefind(mb, m->digest))
goto lose;
}
return 1;
lose:
fprint(2, "invalid cache[%d] %#A size %ld %D\n", level, m->digest, m->size, m->fileid);
return 0;
}
static char*
(char *x)
{
if(x && *x)
return x;
return nil;
}
static char*
brdstr(Biobuf *b, int c, int eat)
{
char *s;
s = Brdstr(b, c, eat);
if(s)
lineno++;
return s;
}
static int
nibble(int c)
{
if(c >= '0' && c <= '9')
return c - '0';
if(c < 0x20)
c += 0x20;
if(c >= 'a' && c <= 'f')
return c - 'a'+10;
return 0xff;
}
static uchar*
hackdigest(char *s)
{
uchar t[SHA1dlen];
int i;
if(strcmp(s, "-") == 0)
return 0;
if(strlen(s) != 2*SHA1dlen){
fprint(2, "bad digest %s\n", s);
return 0;
}
for(i = 0; i < SHA1dlen; i++)
t[i] = nibble(s[2*i])<<4 | nibble(s[2*i + 1]);
memmove(s, t, SHA1dlen);
return (uchar*)s;
}
static Message*
findmessage(Mailbox *, Message *parent, int n)
{
Message *m;
for(m = parent->part; m; m = m->next)
if(!m->digest && n-- == 0)
return m;
return 0;
}
static uvlong
rdfileid(char *s, int level)
{
char *p;
uvlong uv;
uv = strtoul(s, &p, 0);
if((level == 0 && uv < 1000000) || *p != '.')
return 0;
return uv<<8 | strtoul(p + 1, 0, 10);
}
static int
rdidx(Biobuf *b, Mailbox *mb, Message *parent, int npart, int level)
{
char *f[50 + 1], *s;
uchar *digest;
int n, nparts, good, bad, redux;
Message *m, **ll, *l;
bad = good = redux = 0;
ll = &parent->part;
nparts = npart;
for(; npart != 0 && (s = brdstr(b, '\n', 1)); npart--){
//if(lineno>18&&lineno<25)idprint("%d: %d [%s]\n", lineno, level, s);
n = tokenize(s, f, nelem(f));
if(n != Idxfields){
print("%d: bad line\n", lineno);
bad++;
free(s);
continue;
}
digest = hackdigest(f[0]);
if(level == 0){
if(digest == 0)
idprint("%d: no digest\n", lineno);
m = mtreefind(mb, digest);
}else{
m = findmessage(mb, parent, nparts - npart);
if(m == 0){
// idprint("can't find message\n");
}
}
if(m){
/*
* read in mutable information.
* currently this is only flags
*/
idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
redux++;
m->flags |= strtoul(f[1], 0, 16);
m->cstate &= ~Cidxstale;
m->cstate |= Cidx;
idprint("→%.2ux\n", m->cstate);
free(s);
if(m->nparts)
rdidx(b, mb, m, m->nparts, level + 1);
ll = &m->next;
continue;
}
m = newmessage(parent);
//if(lineno>18&&lineno<25)idprint("%d: %d %d %A\n", lineno, level, m->id, digest);
// idprint("%d new %d %#A \n", level, m->id, digest);
m->digest = digest;
m->flags = strtoul(f[1], 0, 16);
m->fileid = rdfileid(f[2], level);
m->lines = atoi(f[3]);
m->ffrom = (f[4]);
m->from = (f[5]);
m->to = (f[6]);
m->cc = (f[7]);
m->bcc = (f[8]);
m->replyto = (f[9]);
m->messageid = (f[10]);
m->subject = (f[11]);
m->sender = (f[12]);
m->inreplyto = (f[13]);
// m->type = newrefs(f[14]);
m->disposition = atoi(f[15]);
m->size = strtoul(f[16], 0, 0);
m->rawbsize = strtoul(f[17], 0, 0);
switch(idxver){
case 4:
m->nparts = strtoul(f[18], 0, 0);
case 7:
m->ibadchars = strtoul(f[18], 0, 0);
m->idxaux = (f[19]);
m->nparts = strtoul(f[20], 0, 0);
}
m->cstate &= ~Cidxstale;
m->cstate |= Cidx;
m->str = s;
// free(s);
if(!validmessage(mb, m, level)){
/*
* if this was an okay message, and somebody
* wrote garbage to the index file, we lose the msg.
*/
print("%d: !validmessage\n", lineno);
bad++;
unnewmessage(mb, parent, m);
continue;
}
if(level == 0)
m->inmbox = 1;
// cachehash(mb, m); /* hokey */
l = *ll;
*ll = m;
ll = &m->next;
*ll = l;
good++;
if(m->nparts){
// fprint(2, "%d: %d parts [%s]\n", lineno, m->nparts, f[18]);
rdidx(b, mb, m, m->nparts, level + 1);
}
}
if(level == 0)
print("idx: %d %d %d\n", good, bad, redux);
return 0;
}
static int
verscmp(Biobuf *b)
{
char *s;
int i;
if((s = brdstr(b, '\n', 0)) == 0)
return -1;
for(i = 0; i < Maxver; i++)
if(magictab[i])
if(strcmp(s, magictab[i]) == 0)
break;
free(s);
if(i == Maxver)
return -1;
idxver = i;
magic = magictab[i];
Idxfields = fieldstab[i];
fprint(2, "version %d\n", i);
return 0;
}
int
mbvers(Biobuf *b)
{
char *s;
if(s = brdstr(b, '\n', 1)){
free(s);
return 0;
}
return -1;
}
int
ckidxfile(Mailbox *mb)
{
char buf[Pathlen + 4];
int r;
Biobuf *b;
snprint(buf, sizeof buf, "%s", mb->path);
b = Bopen(buf, OREAD);
if(b == nil)
return -1;
if(verscmp(b) == -1)
return -1;
if(idxver >= 7)
mbvers(b);
r = rdidx(b, mb, mb->root, -1, 0);
Bterm(b);
return r;
}
static char *bargv[] = {"/fd/0", 0};
void
main(int argc, char **argv)
{
Mailbox *mb;
fmtinstall('A', Afmt);
fmtinstall('D', Dfmt);
ARGBEGIN{
}ARGEND
if(*argv == 0)
argv = bargv;
for(; *argv; argv++){
mb = shellmailbox(*argv);
lineno = 0;
if(ckidxfile(mb) == -1)
fprint(2, "%s: %r\n", *argv);
shellmailboxfree(mb);
}
exits("");
}

View File

@ -1,191 +1,313 @@
typedef struct Message Message;
struct Message
{
int id;
int refs;
int subname;
char name[Elemlen];
#include <avl.h>
// pointers into message
char *start; // start of message
char *end; // end of message
char *header; // start of header
char *hend; // end of header
int hlen; // length of header minus ignored fields
char *mheader; // start of mime header
char *mhend; // end of mime header
char *body; // start of body
char *bend; // end of body
char *rbody; // raw (unprocessed) body
char *rbend; // end of raw (unprocessed) body
char *lim;
char deleted;
char inmbox;
char mallocd; // message is malloc'd
char ballocd; // body is malloc'd
char hallocd; // header is malloce'd
enum {
/* cache states */
Cidx = 1<<0,
Cidxstale = 1<<1,
Cheader = 1<<2,
Cbody = 1<<3,
// mail info
String *unixheader;
String *unixfrom;
String *unixdate;
String *from822;
String *sender822;
String *to822;
String *bcc822;
String *cc822;
String *replyto822;
String *date822;
String *inreplyto822;
String *subject822;
String *messageid822;
String *addrs;
String *mimeversion;
String *sdigest;
// mime info
String *boundary;
String *type;
int encoding;
int disposition;
String *charset;
String *filename;
int converted;
int decoded;
char lines[10]; // number of lines in rawbody
Message *next; // same level
Message *part; // down a level
Message *whole; // up a level
uchar digest[SHA1dlen];
vlong imapuid; // used by imap4
char uidl[80]; // used by pop3
int mesgno;
};
enum
{
// encodings
/* encodings */
Enone= 0,
Ebase64,
Equoted,
// disposition possibilities
/* dispositions */
Dnone= 0,
Dinline,
Dfile,
Dignore,
PAD64= '=',
/* mb create flags */
DMcreate = 0x02000000,
/* rm flags */
Rrecur = 1<<0,
Rtrunc = 1<<1,
/* m->deleted flags */
Deleted = 1<<0,
Dup = 1<<1,
Dead = 1<<2,
Disappear = 1<<3,
Dmark = 1<<4, /* temporary mark for idx scan */
/* mime flags */
Mtrunc = 1<<0, /* message had no boundary */
Maxmsg = 75*1024*1024, /* maxmessage size; debugging */
Maxcache = 512*1024, /* target cache size; set low for debugging */
Nctab = 15, /* max # of cached messages >10 */
Nref = 10,
};
typedef struct Idx Idx;
struct Idx {
char *str; /* as read from idx file */
uchar *digest;
uchar flags;
uvlong fileid;
ulong lines;
ulong size;
ulong rawbsize; /* nasty imap4d */
ulong ibadchars;
char *ffrom;
char *from;
char *to;
char *cc;
char *bcc;
char *replyto;
char *messageid;
char *subject;
char *sender;
char *inreplyto;
char *idxaux; /* mailbox specific */
int type; /* very few types: refstring */
int disposition; /* very few types: refstring */
int nparts;
};
typedef struct Message Message;
struct Message {
int id;
int refs;
int subname;
char name[12];
/* top-level indexed information */
Idx;
/* caching help */
uchar cstate;
ulong infolen;
ulong csize;
/*
* a plethoria of pointers into message
* and some status. not valid unless cached
*/
char *start; /* start of message */
char *end; /* end of message */
char *header; /* start of header */
char *hend; /* end of header */
int hlen; /* length of header minus ignored fields */
char *mheader; /* start of mime header */
char *mhend; /* end of mime header */
char *body; /* start of body */
char *bend; /* end of body */
char *rbody; /* raw (unprocessed) body */
char *rbend; /* end of raw (unprocessed) body */
char mallocd; /* message is malloc'd */
char ballocd; /* body is malloc'd */
char hallocd; /* header is malloc'd */
int badchars; /* running count of bad chars. */
char deleted;
char inmbox;
/* mail info */
char *unixheader;
char *unixfrom;
char *date822;
char *references[Nref];
/* mime info */
char *boundary;
int charset;
char *filename;
char encoding;
char converted;
char decoded;
char mimeflag;
Message *next;
Message *part;
Message *whole;
union{
char *lim; /* used by plan9; not compatable with cache */
vlong imapuid; /* used by imap4 */
void *aux;
};
};
typedef struct {
Avl;
Message *m;
} Mtree;
typedef struct Mcache Mcache;
struct Mcache {
uvlong cached;
int ntab;
Message *ctab[Nctab];
};
typedef struct Mailbox Mailbox;
struct Mailbox
{
struct Mailbox {
QLock;
long idxsem; /* abort on concurrent index access */
long syncsem; /* abort on concurrent syncs */
int refs;
Mailbox *next;
int id;
int dolock; // lock when syncing?
int std;
int flags;
char rmflags;
char dolock; /* lock when syncing? */
char addfrom;
char name[Elemlen];
char path[Pathlen];
Dir *d;
Message *root;
int vers; // goes up each time mailbox is read
Avltree *mtree;
ulong vers; /* goes up each time mailbox is changed */
ulong waketime;
char *(*sync)(Mailbox*, int);
/* cache tracking */
Mcache;
/* index tracking */
Qid qid;
ulong waketime;
void (*close)(Mailbox*);
char *(*fetch)(Mailbox*, Message*);
void (*decache)(Mailbox*, Message*);
int (*fetch)(Mailbox*, Message*, uvlong, ulong);
void (*delete)(Mailbox*, Message*);
char *(*ctl)(Mailbox*, int, char**);
void *aux; // private to Mailbox implementation
char *(*remove)(Mailbox *, int);
char *(*rename)(Mailbox*, char*, int);
char *(*sync)(Mailbox*, int, int*);
void (*modflags)(Mailbox*, Message*, int);
void (*idxwrite)(Biobuf*, Mailbox*);
int (*idxread)(char*, Mailbox*);
void (*idxinvalid)(Mailbox*);
void *aux; /* private to Mailbox implementation */
};
/* print argument tango; can't varargck 2 types. should fix compiler */
typedef struct Mpair Mpair;
struct Mpair {
Mailbox *mb;
Message *m;
};
Mpair mpair(Mailbox*, Message*);
typedef char *Mailboxinit(Mailbox*, char*);
extern Message *root;
extern Mailboxinit plan9mbox;
extern Mailboxinit pop3mbox;
extern Mailboxinit imap4mbox;
extern Mailboxinit planbmbox;
extern Mailboxinit planbvmbox;
Mailboxinit plan9mbox;
Mailboxinit planbmbox;
Mailboxinit pop3mbox;
Mailboxinit imap4mbox;
Mailboxinit mdirmbox;
void genericidxwrite(Biobuf*, Mailbox*);
int genericidxread(char*, Mailbox*);
void genericidxinvalid(Mailbox*);
void cachehash(Mailbox*, Message*);
void newcachehash(Mailbox*, Message*, int);
int cacheheaders(Mailbox*, Message*); /* "getcache" */
int cachebody(Mailbox*, Message*);
int cacheidx(Mailbox*, Message*);
int insurecache(Mailbox*, Message*);
/**/
void putcache(Mailbox*, Message*); /* asymmetricial */
long cachefree(Mailbox*, Message*, int);
Message* gettopmsg(Mailbox*, Message*);
char* syncmbox(Mailbox*, int);
char* geterrstr(void);
void* emalloc(ulong);
void* erealloc(void*, ulong);
Message* newmessage(Message*);
void unnewmessage(Mailbox*, Message*, Message*);
void delmessage(Mailbox*, Message*);
void delmessages(int, char**);
char* delmessages(int, char**);
char *flagmessages(int, char**);
void digestmessage(Mailbox*, Message*);
uintptr absbos(void);
void eprint(char*, ...);
void iprint(char *, ...);
int newid(void);
void mailplumb(Mailbox*, Message*, int);
char* newmbox(char*, char*, int);
char* newmbox(char*, char*, int, Mailbox**);
void freembox(char*);
void logmsg(char*, Message*);
char* removembox(char*, int);
void syncallmboxes(void);
void logmsg(Message*, char*, ...);
void msgincref(Message*);
void msgdecref(Mailbox*, Message*);
void mboxincref(Mailbox*);
void mboxdecref(Mailbox*);
char *mboxrename(char*, char*, int);
void convert(Message*);
void decode(Message*);
int cistrncmp(char*, char*, int);
int cistrcmp(char*, char*);
int decquoted(char*, char*, char*, int);
int xtoutf(char*, char**, char*, char*);
void countlines(Message*);
int headerlen(Message*);
void parse(Message*, int, Mailbox*, int);
void parseheaders(Message*, int, Mailbox*, int);
ulong countlines(Message*);
void parse(Mailbox*, Message*, int, int);
void parseheaders(Mailbox*, Message*, int, int);
void parsebody(Message*, Mailbox*);
void parseunix(Message*);
String* date822tounix(char*);
char* date822tounix(Message*, char*);
int fidmboxrefs(Mailbox*);
int hashmboxrefs(Mailbox*);
void checkmboxrefs(void);
int strtotm(char*, Tm*);
char* lowercase(char*);
extern int debug;
extern int fflag;
extern int logging;
extern char user[Elemlen];
extern char stdmbox[Pathlen];
extern QLock mbllock;
extern Mailbox *mbl;
extern char *mntpt;
extern int biffing;
extern int plumbing;
extern char* Enotme;
char* sputc(char*, char*, int);
char* seappend(char*, char*, char*, int);
enum
{
/* mail subobjects */
Qbody,
int hdrlen(char*, char*);
char* rfc2047(char*, char*, char*, int, int);
char* localremove(Mailbox*, int);
char* localrename(Mailbox*, char*, int);
void rmidx(char*, int);
int vremove(char*);
int rename(char *, char*, int);
int mtreecmp(Avl*, Avl*);
int mtreeisdup(Mailbox *, Message *);
Message* mtreefind(Mailbox*, uchar*);
void mtreeadd(Mailbox*, Message*);
void mtreedelete(Mailbox*, Message*);
enum {
/* mail sub-objects; must be sorted */
Qbcc,
Qbody,
Qcc,
Qdate,
Qdigest,
Qdisposition,
Qffrom,
Qfileid,
Qfilename,
Qflags,
Qfrom,
Qheader,
Qinfo,
Qinreplyto,
Qlines,
Qmimeheader,
Qmessageid,
Qmimeheader,
Qraw,
Qrawbody,
Qrawheader,
Qrawunix,
Qreferences,
Qreplyto,
Qsender,
Qsize,
Qsubject,
Qto,
Qtype,
Qunixheader,
Qinfo,
Qunixdate,
Qunixheader,
Qmax,
/* other files */
@ -196,12 +318,10 @@ enum
Qmboxctl,
};
#define PATH(id, f) ((((id)&0xfffff)<<10) | (f))
#define PATH(id, f) ((((id) & 0xfffff)<<10) | (f))
#define FILE(p) ((p) & 0x3ff)
char *dirtab[];
// hash table to aid in name lookup, all files have an entry
/* hash table to aid in name lookup, all files have an entry */
typedef struct Hash Hash;
struct Hash {
Hash *next;
@ -216,5 +336,50 @@ Hash *hlook(ulong, char*);
void henter(ulong, char*, Qid, Message*, Mailbox*);
void hfree(ulong, char*);
ulong msgallocd, msgfreed;
typedef struct {
char *s;
int l;
ulong ref;
} Refs;
int newrefs(char*);
void delrefs(int);
void refsinit(void);
int prrefs(Biobuf*);
int rdrefs(Biobuf*);
void idxfree(Idx*);
int rdidxfile(Mailbox*, int);
int wridxfile(Mailbox*);
char *modflags(Mailbox*, Message*, char*);
int getmtokens(char *, char**, int, int);
extern char Enotme[];
extern char *mntpt;
extern char user[Elemlen];
extern char *dirtab[];
extern int Sflag;
extern int iflag;
extern int biffing;
extern ulong cachetarg;
extern int debug;
extern int lflag;
extern int plumbing;
extern ulong msgallocd;
extern ulong msgfreed;
extern Mailbox *mbl;
extern Message *root;
extern QLock mbllock;
extern Refs *rtab;
#define dprint(...) if(debug) fprint(2, __VA_ARGS__); else {}
#define Topmsg(mb, m) (m->whole == mb->root)
#pragma varargck type "A" uchar*
#pragma varargck type "D" uvlong
#pragma varargck type "P" Mpair
#pragma varargck type "Δ" uvlong
#pragma varargck argpos eprint 1
#pragma varargck argpos iprint 1
#pragma varargck argpos logmsg 2

BIN
sys/src/cmd/upas/fs/extra/fd2path Executable file

Binary file not shown.

View File

@ -0,0 +1,32 @@
#include <u.h>
#include <libc.h>
void
usage(void)
{
fprint(2, "usage: fd2path path ...\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char buf[1024];
int fd;
ARGBEGIN{
default:
usage();
}ARGEND
if(argc == 0){
if(fd2path(0, buf, sizeof buf) != -1)
fprint(2, "%s\n", buf);
}else for(; *argv; argv++){
fd = open(*argv, OREAD);
if(fd != -1 && fd2path(fd, buf, sizeof buf) != -1)
fprint(2, "%s\n", buf);
close(fd);
}
exits("");
}

View File

@ -0,0 +1,58 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
static char *etab[] = {
"not found",
"does not exist",
"file is locked",
"exclusive lock",
};
static int
bad(int idx)
{
char buf[ERRMAX];
int i;
rerrstr(buf, sizeof buf);
for(i = idx; i < nelem(etab); i++)
if(strstr(buf, etab[i]))
return 0;
return 1;
}
static int
exopen(char *s)
{
int i, fd;
for(i = 0; i < 30; i++){
if((fd = open(s, OWRITE|OTRUNC)) >= 0 || bad(0))
return fd;
if((fd = create(s, OWRITE|OEXCL, DMEXCL|0600)) >= 0 || bad(2))
return fd;
sleep(1000);
}
werrstr("lock timeout");
return -1;
}
void
main(void)
{
int fd;
Biobuf *b;
fd = exopen("testingex");
if(fd == -1)
sysfatal("exopen: %r");
b = Bopen("testingex", OREAD);
if(b){
free(b);
fprint(2, "print both opened at once\n");
}else
fprint(2, "bopen: %r\n");
close(fd);
exits("");
}

View File

@ -0,0 +1,89 @@
/*
* simulate the read patterns of external programs for testing
* info file. "infotest 511 512" simulates what ned does today.
*
* here's how the new info scheme was verified:
*
ramfs
s=/sys/src/cmd/upas
unmount /mail/fs
$s/fs/8.out -p
for(f in /mail/fs/mbox/*/info){
for(i in `{seq 1 1026})
$s/fs/infotst $i `{echo $i + 1 | hoc} > /tmp/$i < $f
for(i in /tmp/*)
cmp $i /tmp/1
rm /tmp/*
}
# now test for differences with old scheme under
# ideal reading conditions
for(f in /mail/fs/mbox/*/info){
i = `{echo $f | sed 's:/mail/fs/mbox/([^/]+)/info:\1:g'}
$s/fs/infotst 2048 > /tmp/$i < $f
}
unmount /mail/fs
upas/fs -p
for(f in /mail/fs/mbox/*/info){
i = `{echo $f | sed 's:/mail/fs/mbox/([^/]+)/info:\1:g'}
$s/fs/infotst 2048 > /tmp/$i.o < $f
}
for(i in /tmp/*.o)
cmp $i `{echo $i | sed 's:\.o$::g'}
rm /tmp/*
*/
#include <u.h>
#include <libc.h>
enum{
Ntab = 100,
};
int tab[Ntab];
int ntab;
int largest;
void
usage(void)
{
fprint(2, "usage: infotest n1 n2 ... nm\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *buf;
int i, n;
ARGBEGIN{
default:
usage();
}ARGEND
if(argc == 0)
usage();
for(; *argv; argv++){
if(ntab == nelem(tab))
break;
i = atoi(*argv);
if(i > largest)
largest = i;
tab[ntab++] = i;
}
buf = malloc(largest);
if(!buf)
sysfatal("malloc: %r");
for(i = 0;; ){
switch(n = read(0, buf, tab[i])){
case -1:
sysfatal("read: %r");
case 0:
exits("");
default:
write(1, buf, n);
break;
}
if(i < ntab-1)
i++;
}
}

BIN
sys/src/cmd/upas/fs/extra/paw Executable file

Binary file not shown.

View File

@ -0,0 +1,23 @@
#include<u.h>
#include<libc.h>
#include<bio.h>
void
main(void)
{
char *f[10], *s;
vlong sum;
Biobuf b;
sum = 0;
Binit(&b, 0, OREAD);
while(s = Brdstr(&b, '\n', 1)){
if(getfields(s, f, nelem(f), 1, " ") > 2)
sum += strtoul(f[2], 0, 0);
free(s);
}
Bterm(&b);
print("%lld\n", sum);
exits("");
}

View File

@ -0,0 +1,37 @@
#include "common.h"
void
usage(void)
{
fprint(2, "usage: prflags\n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *f[Fields+1], buf[20], *s;
int n;
Biobuf b, o;
ARGBEGIN{
default:
usage();
}ARGEND
if(argc)
usage();
Binit(&b, 0, OREAD);
Binit(&o, 1, OWRITE);
for(; s = Brdstr(&b, '\n', 1); free(s)){
n = gettokens(s, f, nelem(f), " ");
if(n != Fields)
continue;
if(!strcmp(f[0], "-"))
continue;
Bprint(&o, "%s\n", flagbuf(buf, strtoul(f[1], 0, 16)));
}
Bterm(&b);
Bterm(&o);
exits("");
}

View File

@ -0,0 +1,17 @@
#include "strtotm.c"
void
main(int argc, char **argv)
{
Tm tm;
ARGBEGIN{
}ARGEND
for(; *argv; argv++)
if(strtotm(*argv, &tm) >= 0)
print("%s", asctime(&tm));
else
print("bad\n");
exits("");
}

View File

@ -0,0 +1,62 @@
#include <u.h>
#include <libc.h>
/* unfortunately, tokenize insists on collapsing multiple seperators */
static char qsep[] = " \t\r\n";
static char*
qtoken(char *s, char *sep)
{
int quoting;
char *t;
quoting = 0;
t = s; /* s is output string, t is input string */
while(*t!='\0' && (quoting || utfrune(sep, *t)==nil)){
if(*t != '\''){
*s++ = *t++;
continue;
}
/* *t is a quote */
if(!quoting){
quoting = 1;
t++;
continue;
}
/* quoting and we're on a quote */
if(t[1] != '\''){
/* end of quoted section; absorb closing quote */
t++;
quoting = 0;
continue;
}
/* doubled quote; fold one quote into two */
t++;
*s++ = *t++;
}
if(*s != '\0'){
*s = '\0';
if(t == s)
t++;
}
return t;
}
int
getmtokens(char *s, char **args, int maxargs, int multiflag)
{
int i;
for(i = 0; i < maxargs; i++){
if(multiflag)
while(*s && utfrune(qsep, *s))
s++;
else if(*s && utfrune(qsep, *s))
s++;
if(*s == 0)
break;
args[i] = s;
s = qtoken(s, qsep);
}
return i;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
#include "common.h"
#include <ctype.h>
#include <libsec.h>
#include "dat.h"
int
hdrlen(char *p, char *e)
{
char *ep;
ep = p;
do {
ep = strchr(ep, '\n');
if(ep == nil){
ep = e;
break;
}
if(ep == p)
break;
if(ep - p == 1 && ep[-1] == '\r')
break;
ep++;
if(ep >= e){
ep = e;
break;
}
} while(*ep == ' ' || *ep == '\t');
return ep - p;
}
/* rfc2047 non-ascii: =?charset?q?encoded-text?= */
static int
tok(char **sp, char *se, char *token, int len)
{
char charset[100], *s, *e, *x;
int l;
if(len == 0)
return -1;
s = *sp;
e = token + len - 2;
token += 2;
x = memchr(token, '?', e - token);
if(x == nil || (l = x - token) >= sizeof charset)
return -1;
memmove(charset, token, l);
charset[l] = 0;
/* bail if it doesn't fit */
token = x + 1;
if(e - token > se - s - 1)
return -1;
if(cistrncmp(token, "b?", 2) == 0){
token += 2;
len = dec64((uchar*)s, se - s - 1, token, e - token);
if(len == -1)
return -1;
s[len] = 0;
}else if(cistrncmp(token, "q?", 2) == 0){
token += 2;
len = decquoted(s, token, e, 1);
if(len > 0 && s[len - 1] == '\n')
len--;
s[len] = 0;
}else
return -1;
if(xtoutf(charset, &x, s, s + len) <= 0)
s += len;
else {
s = seprint(s, se, "%s", x);
free(x);
}
*sp = s;
return 0;
}
char*
tokbegin(char *start, char *end)
{
int quests;
if(*--end != '=')
return nil;
if(*--end != '?')
return nil;
quests = 0;
for(end--; end >= start; end--){
switch(*end){
case '=':
if(quests == 3 && *(end + 1) == '?')
return end;
break;
case '?':
++quests;
break;
case ' ':
case '\t':
case '\n':
case '\r':
/* can't have white space in a token */
return nil;
}
}
return nil;
}
static char*
seappend822f(char *s, char *e, char *a, int n)
{
int skip, c;
skip = 0;
for(; n--; a++){
c = *a;
if(skip && isspace(c))
continue;
if(c == '\n'){
c = ' ';
skip = 1;
}else{
if(c < 0x20)
continue;
skip = 0;
}
s = sputc(s, e, c);
}
return s;
}
static char*
seappend822(char *s, char *e, char *a, int n)
{
int c;
for(; n--; a++){
c = *a;
if(c < 0x20 && c != '\n' && c != '\t')
continue;
s = sputc(s, e, c);
}
return s;
}
/* convert a header line */
char*
rfc2047(char *s, char *se, char *uneaten, int len, int fold)
{
char *sp, *token, *p, *e;
char *(*f)(char*, char*, char*, int);
f = seappend822;
if(fold)
f = seappend822f;
sp = s;
p = uneaten;
for(e = p + len; p < e; ){
while(*p++ == '=' && (token = tokbegin(uneaten, p))){
sp = f(sp, se, uneaten, token - uneaten);
if(tok(&sp, se, token, p - token) < 0)
sp = f(sp, se, token, p - token);
uneaten = p;
for(; p < e && isspace(*p);)
p++;
if(p + 2 < e && p[0] == '=' && p[1] == '?')
uneaten = p; /* paste */
}
}
if(p > uneaten)
sp = f(sp, se, uneaten, e - uneaten);
*sp = 0;
return sp;
}

535
sys/src/cmd/upas/fs/idx.c Normal file
View File

@ -0,0 +1,535 @@
#include "common.h"
#include <libsec.h>
#include "dat.h"
#define idprint(...) if(iflag > 1) fprint(2, __VA_ARGS__); else {}
#define iprint(...) if(iflag) fprint(2, __VA_ARGS__); else {}
static char *magic = "idx magic v7\n";
static char *mbmagic = "genericv1";
enum {
Idxfields = 21,
Idxto = 30000, /* index timeout in ms */
Idxstep = 300, /* sleep between tries */
};
void
idxfree(Idx *i)
{
if(i->str)
free(i->str);
else{
free(i->digest);
free(i->ffrom);
free(i->from);
free(i->to);
free(i->cc);
free(i->bcc);
free(i->replyto);
free(i->messageid);
free(i->subject);
free(i->sender);
free(i->inreplyto);
free(i->idxaux);
}
memset(i, 0, sizeof *i);
}
static char*
(char *x)
{
if(x)
return x;
return "";
}
static int
pridxmsg(Biobuf *b, Idx *x)
{
Bprint(b, "%#A %ux %D %lud ", x->digest, x->flags&~Frecent, x->fileid, x->lines);
Bprint(b, "%q %q %q %q %q ", (x->ffrom), (x->from), (x->to), (x->cc), (x->bcc));
Bprint(b, "%q %q %q %q %q ", (x->replyto), (x->messageid), (x->subject), (x->sender), (x->inreplyto));
Bprint(b, "%s %d %lud %lud ", rtab[x->type].s, x->disposition, x->size, x->rawbsize);
Bprint(b, "%lud %q %d\n", x->ibadchars, (x->idxaux), x->nparts);
return 0;
}
static int
pridx0(Biobuf *b, Mailbox *mb, Message *m, int l)
{
for(; m; m = m->next){
if(l == 0)
if(insurecache(mb, m) == -1)
continue;
if(pridxmsg(b, m))
return -1;
if(m->part)
pridx0(b, mb, m->part, l + 1);
m->cstate &= ~Cidxstale;
m->cstate |= Cidx;
if(l == 0)
msgdecref(mb, m);
}
return 0;
}
void
genericidxwrite(Biobuf *b, Mailbox*)
{
Bprint(b, "%s\n", mbmagic);
}
static int
pridx(Biobuf *b, Mailbox *mb)
{
int i;
Bprint(b, magic);
mb->idxwrite(b, mb);
// prrefs(b);
i = pridx0(b, mb, mb->root->part, 0);
return i;
}
static char *eopen[] = {
"not found",
"does not exist",
"file is locked",
"file locked",
"exclusive lock",
0,
};
static char *ecreate[] = {
"already exists",
"file is locked",
"file locked",
"exclusive lock",
0,
};
static int
bad(char **t)
{
char buf[ERRMAX];
int i;
rerrstr(buf, sizeof buf);
for(i = 0; t[i]; i++)
if(strstr(buf, t[i]))
return 0;
return 1;
}
static int
forceexcl(int fd)
{
int r;
Dir *d;
d = dirfstat(fd);
if(d == nil)
return 0; /* ignore: assume file removed */
if(d->mode & DMEXCL){
free(d);
return 0;
}
d->mode |= DMEXCL;
d->qid.type |= QTEXCL;
r = dirfwstat(fd, d);
free(d);
if(r == -1)
return 0; /* ignore unwritable (e.g dump) */
close(fd);
return -1;
}
static int
exopen(char *s)
{
int i, fd;
for(i = 0; i < Idxto/Idxstep; i++){
if((fd = open(s, OWRITE|OTRUNC)) >= 0 || bad(eopen)){
if(fd != -1 && forceexcl(fd) == -1)
continue;
return fd;
}
if((fd = create(s, OWRITE|OEXCL, DMTMP|DMEXCL|0600)) >= 0 || bad(ecreate))
return fd;
sleep(Idxstep);
}
werrstr("lock timeout");
return -1;
}
static Message*
findmessage(Mailbox *, Message *parent, int n)
{
Message *m;
for(m = parent->part; m; m = m->next)
if(!m->digest && n-- == 0)
return m;
return 0;
}
static int
validmessage(Mailbox *mb, Message *m, int level)
{
if(level){
if(m->digest != 0)
goto lose;
if(m->fileid <= 1000000ull<<8)
if(m->fileid != 0)
goto lose;
}else{
if(m->digest == 0)
goto lose;
if(m->size == 0)
goto lose;
if(m->fileid <= 1000000ull<<8)
goto lose;
if(mtreefind(mb, m->digest))
goto lose;
}
return 1;
lose:
eprint("invalid cache[%d] %#A size %ld %D\n", level, m->digest, m->size, m->fileid);
return 0;
}
/*
* n.b.: we don't insure this is the index version we last read.
*
* we may overwrite changes. dualing deletes should sync eventually.
* mboxsync should complain about missing messages but
* mutable information (which is not in the email itself)
* may be lost.
*/
int
wridxfile(Mailbox *mb)
{
char buf[Pathlen + 4];
int r, fd;
Biobuf b;
Dir *d;
assert(semacquire(&mb->idxsem, 0) != -1);
snprint(buf, sizeof buf, "%s.idx", mb->path);
iprint("wridxfile %s\n", buf);
if((fd = exopen(buf)) == -1){
rerrstr(buf, sizeof buf);
if(strcmp(buf, "no creates") != 0)
if(strstr(buf, "file system read only") == 0)
eprint("wridxfile: %r\n");
semrelease(&mb->idxsem, 1);
return -1;
}
seek(fd, 0, 0);
Binit(&b, fd, OWRITE);
r = pridx(&b, mb);
Bterm(&b);
d = dirfstat(fd);
if(d == 0)
sysfatal("dirfstat: %r");
mb->qid = d->qid;
free(d);
close(fd);
semrelease(&mb->idxsem, 1);
return r;
}
static int
nibble(int c)
{
if(c >= '0' && c <= '9')
return c - '0';
if(c < 0x20)
c += 0x20;
if(c >= 'a' && c <= 'f')
return c - 'a'+10;
return 0xff;
}
static uchar*
hackdigest(char *s)
{
uchar t[SHA1dlen];
int i;
if(strcmp(s, "-") == 0)
return 0;
if(strlen(s) != 2*SHA1dlen){
eprint("bad digest %s\n", s);
return 0;
}
for(i = 0; i < SHA1dlen; i++)
t[i] = nibble(s[2*i])<<4 | nibble(s[2*i + 1]);
memmove(s, t, SHA1dlen);
return (uchar*)s;
}
static uvlong
rdfileid(char *s, int level)
{
char *p;
uvlong uv;
uv = strtoul(s, &p, 0);
if((level == 0 && uv < 1000000) || *p != '.')
return 0;
return uv<<8 | strtoul(p + 1, 0, 10);
}
static char*
(char *x)
{
if(x && *x)
return x;
return nil;
}
/*
* strategy: use top-level avl tree to merge index with
* our ideas about the mailbox. new or old messages
* with corrupt index entries are marked Dead. they
* will be cleared out of the mailbox and are kept out
* of the index. when messages are marked Dead, a
* reread of the mailbox is forced.
*
* side note. if we get a new message while we are
* running it is added to the list in order but m->id
* looks out-of-order. this is because m->id must
* increase monotonicly. a new instance of the fs
* will result in a different ordering.
*/
static int
rdidx(Biobuf *b, Mailbox *mb, Message *parent, int npart, int level, int doplumb)
{
char *f[Idxfields + 1], *s;
uchar *digest;
int n, flags, nparts, good, bad, redux;
Message *m, **ll, *l;
bad = good = redux = 0;
ll = &parent->part;
nparts = npart;
for(; npart != 0 && (s = Brdstr(b, '\n', 1)); npart--){
m = 0;
digest = 0;
n = tokenize(s, f, nelem(f));
if(n != Idxfields){
dead:
eprint("bad index %#A %d %d n=%d\n", digest, level, npart, n);
bad++;
free(s);
if(level)
return -1;
if(m)
m->deleted = Dead;
continue;
}
digest = hackdigest(f[0]);
if(digest == 0 ^ level != 0)
goto dead;
if(level == 0)
m = mtreefind(mb, digest);
else
m = findmessage(mb, parent, nparts - npart);
if(m){
/*
* read in mutable information.
* currently this is only flags
*/
redux++;
if(level == 0)
m->deleted &= ~Dmark;
if(m->nparts)
if(rdidx(b, mb, m, m->nparts, level + 1, 0) == -1)
goto dead;
ll = &m->next;
idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
flags = m->flags;
m->flags |= strtoul(f[1], 0, 16);
if(flags != m->flags)
m->cstate |= Cidxstale;
m->cstate |= Cidx;
idprint("→%.2ux\n", m->cstate);
free(s);
// s = 0;
continue;
}
m = newmessage(parent);
idprint("%d new %d %#A\n", level, m->id, digest);
m->digest = digest;
m->flags = strtoul(f[1], 0, 16);
m->fileid = rdfileid(f[2], level);
m->lines = atoi(f[3]);
m->ffrom = (f[4]);
m->from = (f[5]);
m->to = (f[6]);
m->cc = (f[7]);
m->bcc = (f[8]);
m->replyto = (f[9]);
m->messageid = (f[10]);
m->subject = (f[11]);
m->sender = (f[12]);
m->inreplyto = (f[13]);
m->type = newrefs(f[14]);
m->disposition = atoi(f[15]);
m->size = strtoul(f[16], 0, 0);
m->rawbsize = strtoul(f[17], 0, 0);
m->ibadchars = strtoul(f[18], 0, 0);
m->idxaux = (f[19]);
m->nparts = strtoul(f[20], 0, 0);
m->cstate &= ~Cidxstale;
m->cstate |= Cidx;
m->str = s;
s = 0;
if(!validmessage(mb, m, level))
goto dead;
if(level == 0){
mtreeadd(mb, m);
m->inmbox = 1;
}
cachehash(mb, m); /* hokey */
l = *ll;
*ll = m;
ll = &m->next;
*ll = l;
good++;
if(m->nparts)
if(rdidx(b, mb, m, m->nparts, level + 1, 0) == -1)
goto dead;
if(doplumb && level == 0)
mailplumb(mb, m, 0);
}
if(level == 0 && bad + redux > 0)
iprint("idx: %d %d %d\n", good, bad, redux);
if(bad)
return -1;
return 0;
}
/* bug: should check time. */
static int
qidcmp(int fd, Qid *q)
{
int r;
Dir *d;
Qid q0;
d = dirfstat(fd);
if(!d)
sysfatal("dirfstat: %r");
r = 1;
if(d->qid.path == q->path)
if(d->qid.vers == q->vers)
r = 0;
q0 = *q;
*q = d->qid;
free(d);
if(q0.path != 0 && r)
iprint("qidcmp ... index changed [%ld .. %ld]\n", q0.vers, q->vers);
return r;
}
static int
verscmp(Biobuf *b, Mailbox *mb)
{
char *s;
int n;
n = -1;
if(s = Brdstr(b, '\n', 0))
n = strcmp(s, magic);
free(s);
if(n)
return -1;
n = -1;
if(s = Brdstr(b, '\n', 0))
n = mb->idxread(s, mb);
free(s);
return n;
}
int
genericidxread(char *s, Mailbox*)
{
return strcmp(s, mbmagic);
}
void
genericidxinvalid(Mailbox *mb)
{
if(mb->d)
memset(&mb->d->qid, 0, sizeof mb->d->qid);
mb->waketime = time(0);
}
void
mark(Mailbox *mb)
{
Message *m;
for(m = mb->root->part; m != nil; m = m->next)
m->deleted |= Dmark;
}
int
unmark(Mailbox *mb)
{
int i;
Message *m;
i = 0;
for(m = mb->root->part; m != nil; m = m->next)
if(m->deleted & Dmark){
i++;
m->deleted &= ~Dmark; /* let mailbox scan figure this out. BOTCH?? */
}
return i;
}
int
rdidxfile0(Mailbox *mb, int doplumb)
{
char buf[Pathlen + 4];
int r, v;
Biobuf *b;
snprint(buf, sizeof buf, "%s.idx", mb->path);
b = Bopen(buf, OREAD);
if(b == nil)
return -2;
if(qidcmp(Bfildes(b), &mb->qid) == 0)
r = 0;
else if(verscmp(b, mb) == -1)
r = -1;
else{
mark(mb);
r = rdidx(b, mb, mb->root, -1, 0, doplumb);
v = unmark(mb);
if(r == 0 && v > 0)
r = -1;
}
Bterm(b);
return r;
}
int
rdidxfile(Mailbox *mb, int doplumb)
{
int r;
assert(semacquire(&mb->idxsem, 0) > 0);
r = rdidxfile0(mb, doplumb);
if(r == -1 && mb->idxinvalid)
mb->idxinvalid(mb);
semrelease(&mb->idxsem, 1);
return r;
}

1271
sys/src/cmd/upas/fs/imap.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,878 +0,0 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include <auth.h>
#include "dat.h"
#pragma varargck argpos imap4cmd 2
#pragma varargck type "Z" char*
int doublequote(Fmt*);
// if pipeline == 1 and upas/fs is used with dovecot,
// 9Xn OK responses sometimes come much later after FETCH responses, i.e.
// <- * 1 FETCH ...
// <- * 2 FETCH ...
// <- * 3 FETCH ...
// <- 9X5 OK Fetch completed.
// <- 9X6 OK Fetch completed.
// download 40: did not get message body
// <- 9X7 OK Fetch completed.
// causing multiple messages to turn into one in imap4.c:/^imap4resp.
int pipeline = 0;
static char Eio[] = "i/o error";
typedef struct Imap Imap;
struct Imap {
char *freep; // free this to free the strings below
char *host;
char *user;
char *mbox;
int mustssl;
int refreshtime;
int debug;
ulong tag;
ulong validity;
int nmsg;
int size;
char *base;
char *data;
vlong *uid;
int nuid;
int muid;
Thumbprint *thumb;
// open network connection
Biobuf bin;
Biobuf bout;
int fd;
};
static char*
removecr(char *s)
{
char *r, *w;
for(r=w=s; *r; r++)
if(*r != '\r')
*w++ = *r;
*w = '\0';
return s;
}
//
// send imap4 command
//
static void
imap4cmd(Imap *imap, char *fmt, ...)
{
char buf[128], *p;
va_list va;
va_start(va, fmt);
p = buf+sprint(buf, "9X%lud ", imap->tag);
vseprint(p, buf+sizeof(buf), fmt, va);
va_end(va);
p = buf+strlen(buf);
if(p > (buf+sizeof(buf)-3))
sysfatal("imap4 command too long");
if(imap->debug)
fprint(2, "-> %s\n", buf);
strcpy(p, "\r\n");
Bwrite(&imap->bout, buf, strlen(buf));
Bflush(&imap->bout);
}
enum {
OK,
NO,
BAD,
BYE,
EXISTS,
STATUS,
FETCH,
UNKNOWN,
};
static char *verblist[] = {
[OK] "OK",
[NO] "NO",
[BAD] "BAD",
[BYE] "BYE",
[EXISTS] "EXISTS",
[STATUS] "STATUS",
[FETCH] "FETCH",
};
static int
verbcode(char *verb)
{
int i;
char *q;
if(q = strchr(verb, ' '))
*q = '\0';
for(i=0; i<nelem(verblist); i++)
if(verblist[i] && strcmp(verblist[i], verb)==0){
if(q)
*q = ' ';
return i;
}
if(q)
*q = ' ';
return UNKNOWN;
}
static void
strupr(char *s)
{
for(; *s; s++)
if('a' <= *s && *s <= 'z')
*s += 'A'-'a';
}
static void
imapgrow(Imap *imap, int n)
{
int i;
if(imap->data == nil){
imap->base = emalloc(n+1);
imap->data = imap->base;
imap->size = n+1;
}
if(n >= imap->size){
// friggin microsoft - reallocate
i = imap->data - imap->base;
imap->base = erealloc(imap->base, i+n+1);
imap->data = imap->base + i;
imap->size = n+1;
}
}
//
// get imap4 response line. there might be various
// data or other informational lines mixed in.
//
static char*
imap4resp(Imap *imap)
{
char *line, *p, *ep, *op, *q, *r, *en, *verb;
int i, n;
static char error[256];
while(p = Brdline(&imap->bin, '\n')){
ep = p+Blinelen(&imap->bin);
while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r'))
*--ep = '\0';
if(imap->debug)
fprint(2, "<- %s\n", p);
strupr(p);
switch(p[0]){
case '+':
if(imap->tag == 0)
fprint(2, "unexpected: %s\n", p);
break;
// ``unsolicited'' information; everything happens here.
case '*':
if(p[1]!=' ')
continue;
p += 2;
line = p;
n = strtol(p, &p, 10);
if(*p==' ')
p++;
verb = p;
if(p = strchr(verb, ' '))
p++;
else
p = verb+strlen(verb);
switch(verbcode(verb)){
case OK:
case NO:
case BAD:
// human readable text at p;
break;
case BYE:
// early disconnect
// human readable text at p;
break;
// * 32 EXISTS
case EXISTS:
imap->nmsg = n;
break;
// * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964)
case STATUS:
if(q = strstr(p, "MESSAGES"))
imap->nmsg = atoi(q+8);
if(q = strstr(p, "UIDVALIDITY"))
imap->validity = strtoul(q+11, 0, 10);
break;
case FETCH:
// * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031}
// <3031 bytes of data>
// )
if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){
if((q = strchr(p, '{'))
&& (n=strtol(q+1, &en, 0), *en=='}')){
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
if((i = Bread(&imap->bin, imap->data, n)) != n){
snprint(error, sizeof error,
"short read %d != %d: %r\n",
i, n);
return error;
}
if(imap->debug)
fprint(2, "<- read %d bytes\n", n);
imap->data[n] = '\0';
if(imap->debug)
fprint(2, "<- %s\n", imap->data);
imap->data += n;
imap->size -= n;
p = Brdline(&imap->bin, '\n');
if(imap->debug)
fprint(2, "<- ignoring %.*s\n",
Blinelen(&imap->bin), p);
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
*r = '\0';
q++;
n = r-q;
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
memmove(imap->data, q, n);
imap->data[n] = '\0';
imap->data += n;
imap->size -= n;
}else
return "confused about FETCH response";
break;
}
// * 1 FETCH (UID 1 RFC822.SIZE 511)
if(q=strstr(p, "RFC822.SIZE")){
imap->size = atoi(q+11);
break;
}
// * 1 FETCH (UID 1 RFC822.HEADER {496}
// <496 bytes of data>
// )
// * 1 FETCH (UID 1 RFC822.HEADER "data")
if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){
if((q = strchr(p, '{'))
&& (n=strtol(q+1, &en, 0), *en=='}')){
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
if((i = Bread(&imap->bin, imap->data, n)) != n){
snprint(error, sizeof error,
"short read %d != %d: %r\n",
i, n);
return error;
}
if(imap->debug)
fprint(2, "<- read %d bytes\n", n);
imap->data[n] = '\0';
if(imap->debug)
fprint(2, "<- %s\n", imap->data);
imap->data += n;
imap->size -= n;
p = Brdline(&imap->bin, '\n');
if(imap->debug)
fprint(2, "<- ignoring %.*s\n",
Blinelen(&imap->bin), p);
}else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){
*r = '\0';
q++;
n = r-q;
if(imap->data == nil || n >= imap->size)
imapgrow(imap, n);
memmove(imap->data, q, n);
imap->data[n] = '\0';
imap->data += n;
imap->size -= n;
}else
return "confused about FETCH response";
break;
}
// * 1 FETCH (UID 1)
// * 2 FETCH (UID 6)
if(q = strstr(p, "UID")){
if(imap->nuid < imap->muid)
imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10);
break;
}
}
if(imap->tag == 0)
return line;
break;
case '9': // response to our message
op = p;
if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){
while(*p==' ')
p++;
imap->tag++;
return p;
}
fprint(2, "expected %lud; got %s\n", imap->tag, op);
break;
default:
if(imap->debug || *p)
fprint(2, "unexpected line: %s\n", p);
}
}
snprint(error, sizeof error, "i/o error: %r\n");
return error;
}
static int
isokay(char *resp)
{
return strncmp(resp, "OK", 2)==0;
}
//
// log in to IMAP4 server, select mailbox, no SSL at the moment
//
static char*
imap4login(Imap *imap)
{
char *s;
UserPasswd *up;
imap->tag = 0;
s = imap4resp(imap);
if(!isokay(s))
return "error in initial IMAP handshake";
if(imap->user != nil)
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
else
up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
if(up == nil)
return "cannot find IMAP password";
imap->tag = 1;
imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
free(up);
if(!isokay(s = imap4resp(imap)))
return s;
imap4cmd(imap, "SELECT %Z", imap->mbox);
if(!isokay(s = imap4resp(imap)))
return s;
return nil;
}
static char*
imaperrstr(char *host, char *port)
{
/*
* make mess big enough to hold a TLS certificate fingerprint
* plus quite a bit of slop.
*/
static char mess[3 * Errlen];
char err[Errlen];
err[0] = '\0';
errstr(err, sizeof(err));
snprint(mess, sizeof(mess), "%s/%s:%s", host, port, err);
return mess;
}
static int
starttls(Imap *imap)
{
int sfd;
uchar digest[SHA1dlen];
TLSconn conn;
memset(&conn, 0, sizeof(conn));
sfd = tlsClient(imap->fd, &conn);
if(sfd < 0) {
werrstr("tlsClient: %r");
return -1;
}
imap->fd = sfd;
free(conn.sessionID);
if(conn.cert==nil || conn.certlen <= 0) {
werrstr("server did not provide TLS certificate");
return -1;
}
sha1(conn.cert, conn.certlen, digest, nil);
free(conn.cert);
if(!imap->thumb || !okThumbprint(digest, imap->thumb)){
fmtinstall('H', encodefmt);
werrstr("server certificate %.*H not recognized",
SHA1dlen, digest);
return -1;
}
return sfd;
}
//
// dial and handshake with the imap server
//
static char*
imap4dial(Imap *imap)
{
char *err, *port;
if(imap->fd >= 0){
imap4cmd(imap, "noop");
if(isokay(imap4resp(imap)))
return nil;
close(imap->fd);
imap->fd = -1;
}
if(imap->mustssl)
port = "imaps";
else
port = "imap";
if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
return imaperrstr(imap->host, port);
if(imap->mustssl){
if(starttls(imap) < 0){
err = imaperrstr(imap->host, port);
goto Out;
}
}
Binit(&imap->bin, imap->fd, OREAD);
Binit(&imap->bout, imap->fd, OWRITE);
err = imap4login(imap);
Out:
if(err != nil){
if(imap->fd >= 0){
close(imap->fd);
imap->fd = -1;
}
}
return err;
}
//
// close connection
//
static void
imap4hangup(Imap *imap)
{
if(imap->fd < 0)
return;
imap4cmd(imap, "LOGOUT");
imap4resp(imap);
close(imap->fd);
imap->fd = -1;
}
//
// download a single message
//
static char*
imap4fetch(Mailbox *mb, Message *m)
{
int i;
char *p, *s, sdigest[2*SHA1dlen+1];
Imap *imap;
imap = mb->aux;
imap->size = 0;
if(!isokay(s = imap4resp(imap)))
return s;
p = imap->base;
if(p == nil)
return "did not get message body";
removecr(p);
free(m->start);
m->start = p;
m->end = p+strlen(p);
m->bend = m->rbend = m->end;
m->header = m->start;
imap->base = nil;
imap->data = nil;
parse(m, 0, mb, 1);
// digest headers
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
return nil;
}
//
// check for new messages on imap4 server
// download new messages, mark deleted messages
//
static char*
imap4read(Imap *imap, Mailbox *mb, int doplumb)
{
char *s;
int i, ignore, nnew, t;
Message *m, *next, **l;
imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
if(!isokay(s = imap4resp(imap)))
return s;
imap->nuid = 0;
imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
imap->muid = imap->nmsg;
if(imap->nmsg > 0){
imap4cmd(imap, "UID FETCH 1:* UID");
if(!isokay(s = imap4resp(imap)))
return s;
}
l = &mb->root->part;
for(i=0; i<imap->nuid; i++){
ignore = 0;
while(*l != nil){
if((*l)->imapuid == imap->uid[i]){
ignore = 1;
l = &(*l)->next;
break;
}else{
// old mail, we don't have it anymore
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
}
if(ignore)
continue;
// new message
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
m->imapuid = imap->uid[i];
// add to chain, will download soon
*l = m;
l = &m->next;
}
// whatever is left at the end of the chain is gone
while(*l != nil){
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
l = &(*l)->next;
}
// download new messages
t = imap->tag;
if(pipeline)
switch(rfork(RFPROC|RFMEM)){
case -1:
sysfatal("rfork: %r");
default:
break;
case 0:
for(m = mb->root->part; m != nil; m = m->next){
if(m->start != nil)
continue;
if(imap->debug)
fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
t, (ulong)m->imapuid);
Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
t++, (ulong)m->imapuid);
}
Bflush(&imap->bout);
_exits(nil);
}
nnew = 0;
for(m=mb->root->part; m!=nil; m=next){
next = m->next;
if(m->start != nil)
continue;
if(!pipeline){
Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
(ulong)imap->tag, (ulong)m->imapuid);
Bflush(&imap->bout);
}
if(s = imap4fetch(mb, m)){
// message disappeared? unchain
fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
delmessage(mb, m);
mb->root->subname--;
continue;
}
nnew++;
if(doplumb)
mailplumb(mb, m, 0);
}
if(pipeline)
waitpid();
if(nnew || mb->vers == 0){
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
}
return nil;
}
//
// sync mailbox
//
static void
imap4purge(Imap *imap, Mailbox *mb)
{
int ndel;
Message *m, *next;
ndel = 0;
for(m=mb->root->part; m!=nil; m=next){
next = m->next;
if(m->deleted && m->refs==0){
if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity){
imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
if(isokay(imap4resp(imap))){
ndel++;
delmessage(mb, m);
}
}else
delmessage(mb, m);
}
}
if(ndel){
imap4cmd(imap, "EXPUNGE");
imap4resp(imap);
}
}
//
// connect to imap4 server, sync mailbox
//
static char*
imap4sync(Mailbox *mb, int doplumb)
{
char *err;
Imap *imap;
imap = mb->aux;
if(err = imap4dial(imap)){
mb->waketime = time(0) + imap->refreshtime;
return err;
}
if((err = imap4read(imap, mb, doplumb)) == nil){
imap4purge(imap, mb);
mb->d->atime = mb->d->mtime = time(0);
}
/*
* don't hang up; leave connection open for next time.
*/
// imap4hangup(imap);
mb->waketime = time(0) + imap->refreshtime;
return err;
}
static char Eimap4ctl[] = "bad imap4 control message";
static char*
imap4ctl(Mailbox *mb, int argc, char **argv)
{
int n;
Imap *imap;
imap = mb->aux;
if(argc < 1)
return Eimap4ctl;
if(argc==1 && strcmp(argv[0], "debug")==0){
imap->debug = 1;
return nil;
}
if(argc==1 && strcmp(argv[0], "nodebug")==0){
imap->debug = 0;
return nil;
}
if(argc==1 && strcmp(argv[0], "thumbprint")==0){
if(imap->thumb)
freeThumbprints(imap->thumb);
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
}
if(strcmp(argv[0], "refresh")==0){
if(argc==1){
imap->refreshtime = 60;
return nil;
}
if(argc==2){
n = atoi(argv[1]);
if(n < 15)
return Eimap4ctl;
imap->refreshtime = n;
return nil;
}
}
return Eimap4ctl;
}
//
// free extra memory associated with mb
//
static void
imap4close(Mailbox *mb)
{
Imap *imap;
imap = mb->aux;
free(imap->freep);
free(imap->base);
free(imap->uid);
if(imap->fd >= 0)
close(imap->fd);
free(imap);
}
//
// open mailboxes of the form /imap/host/user
//
char*
imap4mbox(Mailbox *mb, char *path)
{
char *f[10];
int mustssl, nf;
Imap *imap;
quotefmtinstall();
fmtinstall('Z', doublequote);
if(strncmp(path, "/imap/", 6) != 0 && strncmp(path, "/imaps/", 7) != 0)
return Enotme;
mustssl = (strncmp(path, "/imaps/", 7) == 0);
path = strdup(path);
if(path == nil)
return "out of memory";
nf = getfields(path, f, 5, 0, "/");
if(nf < 3){
free(path);
return "bad imap path syntax /imap[s]/system[/user[/mailbox]]";
}
imap = emalloc(sizeof(*imap));
imap->fd = -1;
imap->debug = debug;
imap->freep = path;
imap->mustssl = mustssl;
imap->host = f[2];
if(nf < 4)
imap->user = nil;
else
imap->user = f[3];
if(nf < 5)
imap->mbox = "Inbox";
else
imap->mbox = f[4];
imap->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
mb->aux = imap;
mb->sync = imap4sync;
mb->close = imap4close;
mb->ctl = imap4ctl;
mb->d = emalloc(sizeof(*mb->d));
//mb->fetch = imap4fetch;
return nil;
}
//
// Formatter for %"
// Use double quotes to protect white space, frogs, \ and "
//
enum
{
Qok = 0,
Qquote,
Qbackslash,
};
static int
needtoquote(Rune r)
{
if(r >= Runeself)
return Qquote;
if(r <= ' ')
return Qquote;
if(r=='\\' || r=='"')
return Qbackslash;
return Qok;
}
int
doublequote(Fmt *f)
{
char *s, *t;
int w, quotes;
Rune r;
s = va_arg(f->args, char*);
if(s == nil || *s == '\0')
return fmtstrcpy(f, "\"\"");
quotes = 0;
for(t=s; *t; t+=w){
w = chartorune(&r, t);
quotes |= needtoquote(r);
}
if(quotes == 0)
return fmtstrcpy(f, s);
fmtrune(f, '"');
for(t=s; *t; t+=w){
w = chartorune(&r, t);
if(needtoquote(r) == Qbackslash)
fmtrune(f, '\\');
fmtrune(f, r);
}
return fmtrune(f, '"');
}

File diff suppressed because it is too large Load Diff

354
sys/src/cmd/upas/fs/mdir.c Normal file
View File

@ -0,0 +1,354 @@
#include "common.h"
#include "dat.h"
typedef struct {
int debug;
} Mdir;
#define mdprint(mdir, ...) if(mdir->debug) fprint(2, __VA_ARGS__)
static int
slurp(char *f, char *b, uvlong o, long l)
{
int fd, r;
if((fd = open(f, OREAD)) == -1)
return -1;
seek(fd, o, 0);
r = readn(fd, b, l) != l;
close(fd);
return r? -1: 0;
}
static void
parseunix(Message *m)
{
char *s, *p;
int l;
l = m->header - m->start;
m->unixheader = smprint("%.*s", l, m->start);
s = m->start + 5;
if((p = strchr(s, ' ')) == nil)
abort();
*p = 0;
m->unixfrom = strdup(s);
*p = ' ';
}
static int
mdirfetch(Mailbox *mb, Message *m, uvlong o, ulong l)
{
char buf[Pathlen], *x;
Mdir *mdir;
mdir = mb->aux;
mdprint(mdir, "mdirfetch(%D) ...", m->fileid);
snprint(buf, sizeof buf, "%s/%D", mb->path, m->fileid);
if(slurp(buf, m->start + o, o, l) == -1){
logmsg(m, "fetch failed: %r");
mdprint(mdir, "%r\n");
return -1;
}
if(m->header == nil)
m->header = m->start;
if(m->header == m->start)
if(o + l >= 36)
if(strncmp(m->start, "From ", 5) == 0)
if(x = strchr(m->start, '\n')){
m->header = x + 1;
if(m->unixfrom == nil)
parseunix(m);
}
m->mheader = m->mhend = m->header;
mdprint(mdir, "fetched [%llud, %llud]\n", o, o + l);
return 0;
}
static int
setsize(Mailbox *mb, Message *m)
{
char buf[Pathlen];
Dir *d;
snprint(buf, sizeof buf, "%s/%D", mb->path, m->fileid);
if((d = dirstat(buf)) == nil)
return -1;
m->size = d->length;
free(d);
return 0;
}
/* must be [0-9]+(\..*)? */
int
dirskip(Dir *a, uvlong *uv)
{
char *p;
if(a->length == 0)
return 1;
*uv = strtoul(a->name, &p, 0);
if(*uv < 1000000 || *p != '.')
return 1;
*uv = *uv<<8 | strtoul(p + 1, &p, 10);
if(*p)
return 1;
return 0;
}
static int
vcmp(vlong a, vlong b)
{
a -= b;
if(a > 0)
return 1;
if(a < 0)
return -1;
return 0;
}
static int
dircmp(Dir *a, Dir *b)
{
uvlong x, y;
dirskip(a, &x);
dirskip(b, &y);
return vcmp(x, y);
}
static char*
mdirread(Mdir* mdir, Mailbox* mb, int doplumb, int *new)
{
int i, nnew, ndel, fd, n, c;
uvlong uv;
Dir *d;
Message *m, **ll;
static char err[ERRMAX];
mdprint(mdir, "mdirread()\n");
if((fd = open(mb->path, OREAD)) == -1){
errstr(err, sizeof err);
return err;
}
if((d = dirfstat(fd)) == nil){
errstr(err, sizeof err);
close(fd);
return err;
}
*new = nnew = 0;
if(mb->d){
if(d->qid.path == mb->d->qid.path)
if(d->qid.vers == mb->d->qid.vers){
mdprint(mdir, "\tqids match\n");
close(fd);
free(d);
goto finished;
}
free(mb->d);
}
logmsg(nil, "reading %s (mdir)", mb->path);
mb->d = d;
n = dirreadall(fd, &d);
close(fd);
if(n == -1){
errstr(err, sizeof err);
return err;
}
qsort(d, n, sizeof *d, (int(*)(void*, void*))dircmp);
ndel = 0;
ll = &mb->root->part;
for(i = 0; *ll || i < n; ){
if(i < n && dirskip(d + i, &uv)){
i++;
continue;
}
c = -1;
if(i >= n)
c = 1;
else if(*ll)
c = vcmp(uv, (*ll)->fileid);
mdprint(mdir, "consider %s and %D -> %d\n", i<n? d[i].name: 0, *ll? (*ll)->fileid: 1ull, c);
if(c < 0){
/* new message */
mdprint(mdir, "new: %s (%D)\n", d[i].name, *ll? (*ll)->fileid: 0);
m = newmessage(mb->root);
m->fileid = uv;
if(setsize(mb, m) < 0 || m->size >= Maxmsg){
/* message disappeared? unchain */
mdprint(mdir, "deleted → %r (%D)\n", m->fileid);
logmsg(m, "disappeared");
if(doplumb)
mailplumb(mb, m, 1); /* redundant */
unnewmessage(mb, mb->root, m);
/* we're out of sync; note this by dropping cached qid */
mb->d->qid.path = 0;
break;
}
m->inmbox = 1;
nnew++;
m->next = *ll;
*ll = m;
ll = &m->next;
logmsg(m, "new %s", d[i].name);
i++;
newcachehash(mb, m, doplumb);
putcache(mb, m);
}else if(c > 0){
/* deleted message; */
mdprint(mdir, "deleted: %s (%D)\n", i<n? d[i].name: 0, *ll? (*ll)->fileid: 0);
ndel++;
logmsg(*ll, "deleted (refs=%d)", *ll? (*ll)->refs: -42);
if(doplumb)
mailplumb(mb, *ll, 1);
(*ll)->inmbox = 0;
(*ll)->deleted = Disappear;
ll = &(*ll)->next;
}else{
//logmsg(*ll, "duplicate %s", d[i].name);
i++;
ll = &(*ll)->next;
}
}
free(d);
logmsg(nil, "mbox read: %d new %d deleted", nnew, ndel);
finished:
*new = nnew;
return nil;
}
static void
mdirdelete(Mailbox *mb, Message *m)
{
char mpath[Pathlen];
Mdir* mdir;
mdir = mb->aux;
snprint(mpath, sizeof mpath, "%s/%D", mb->path, m->fileid);
mdprint(mdir, "remove: %s\n", mpath);
/* may have been removed by other fs. just log the error. */
if(remove(mpath) == -1)
logmsg(m, "remove: %s: %r", mpath);
m->inmbox = 0;
}
static char*
mdirsync(Mailbox* mb, int doplumb, int *new)
{
Mdir *mdir;
mdir = mb->aux;
mdprint(mdir, "mdirsync()\n");
return mdirread(mdir, mb, doplumb, new);
}
static char*
mdirctl(Mailbox *mb, int c, char **v)
{
Mdir *mdir;
mdir = mb->aux;
if(c == 1 && strcmp(*v, "debug") == 0)
mdir->debug = 1;
else if(c == 1 && strcmp(*v, "nodebug") == 0)
mdir->debug = 0;
else
return "bad mdir control message";
return nil;
}
static void
mdirclose(Mailbox *mb)
{
free(mb->aux);
}
static int
qidcmp(Qid *a, Qid *b)
{
if(a->path != b->path)
return 1;
return a->vers - b->vers;
}
/*
* .idx strategy. we save the qid.path and .vers
* of the mdir directory and the date to the index.
* we accept the work done by the other upas/fs if
* the index is based on the same (or a newer)
* qid. in any event, we recheck the directory after
* the directory is four hours old.
*/
static int
idxr(char *s, Mailbox *mb)
{
char *f[5];
long t, δt, n;
Dir d;
n = tokenize(s, f, nelem(f));
if(n != 4 || strcmp(f[0], "mdirv1") != 0)
return -1;
t = strtoul(f[1], 0, 0);
δt = time(0) - t;
if(δt < 0 || δt > 4*3600)
return 0;
memset(&d, 0, sizeof d);
d.qid.path = strtoull(f[2], 0, 0);
d.qid.vers = strtoull(f[3], 0, 0);
if(mb->d && qidcmp(&mb->d->qid, &d.qid) >= 0)
return 0;
if(mb->d == 0)
mb->d = emalloc(sizeof d);
mb->d->qid = d.qid;
mb->d->mtime = t;
return 0;
}
static void
idxw(Biobuf *b, Mailbox *mb)
{
Qid q;
memset(&q, 0, sizeof q);
if(mb->d)
q = mb->d->qid;
Bprint(b, "mdirv1 %lud %llud %lud\n", time(0), q.path, q.vers);
}
char*
mdirmbox(Mailbox *mb, char *path)
{
int m;
Dir *d;
Mdir *mdir;
d = dirstat(path);
if(!d && mb->flags & DMcreate){
createfolder(getuser(), path);
d = dirstat(path);
}
m = d && (d->mode & DMDIR);
free(d);
if(!m)
return Enotme;
snprint(mb->path, sizeof mb->path, "%s", path);
mdir = emalloc(sizeof *mdir);
mdir->debug = 0;
mb->aux = mdir;
mb->sync = mdirsync;
mb->close = mdirclose;
mb->fetch = mdirfetch;
mb->delete = mdirdelete;
mb->remove = localremove;
mb->rename = localrename;
mb->idxread = idxr;
mb->idxwrite = idxw;
mb->ctl = mdirctl;
return nil;
}

View File

@ -1,14 +1,24 @@
</$objtype/mkfile
<../mkupas
TARG= fs\
OFILES=\
bos.$O\
cache.$O\
fs.$O\
imap4.$O\
header.$O\
idx.$O\
imap.$O\
mbox.$O\
mdir.$O\
mtree.$O\
plan9.$O\
planb.$O\
pop3.$O\
ref.$O\
remove.$O\
rename.$O\
strtotm.$O\
LIB=../common/libcommon.a$O\
@ -16,8 +26,6 @@ LIB=../common/libcommon.a$O\
HFILES= ../common/common.h\
dat.h
BIN=/$objtype/bin/upas
UPDATE=\
mkfile\
$HFILES\
@ -25,4 +33,10 @@ UPDATE=\
${OFILES:%.$O=%.c}\
</sys/src/cmd/mkone
CFLAGS=$CFLAGS -I/sys/include -I../common
CFLAGS=$CFLAGS -I../common
acid:V:
$CC -a $CFLAGS fs.c>a$O
chkidx: mtree.$O chkidx.$O
$LD $LDFLAGS -o $target $prereq

View File

@ -0,0 +1,80 @@
#include "common.h"
#include <libsec.h>
#include "dat.h"
int
mtreecmp(Avl *va, Avl *vb)
{
Mtree *a, *b;
a = (Mtree*)va;
b = (Mtree*)vb;
return memcmp(a->m->digest, b->m->digest, SHA1dlen);
}
int
mtreeisdup(Mailbox *mb, Message *m)
{
Mtree t;
assert(Topmsg(mb, m) && m->digest);
if(!m->digest)
return 0;
memset(&t, 0, sizeof t);
t.m = m;
if(avllookup(mb->mtree, &t))
return 1;
return 0;
}
Message*
mtreefind(Mailbox *mb, uchar *digest)
{
Message m0;
Mtree t, *p;
m0.digest = digest;
memset(&t, 0, sizeof t);
t.m = &m0;
if(p = (Mtree*)avllookup(mb->mtree, &t))
return p->m;
return nil;
}
void
mtreeadd(Mailbox *mb, Message *m)
{
Avl *old;
Mtree *p;
assert(Topmsg(mb, m) && m->digest);
p = emalloc(sizeof *p);
p->m = m;
old = avlinsert(mb->mtree, p);
assert(old == 0);
}
void
mtreedelete(Mailbox *mb, Message *m)
{
Mtree t, *p;
assert(Topmsg(mb, m));
memset(&t, 0, sizeof t);
t.m = m;
if(m->deleted & ~Deleted){
if(m->digest == nil)
return;
p = (Mtree*)avllookup(mb->mtree, &t);
if(p == nil || p->m != m)
return;
p = (Mtree*)avldelete(mb->mtree, &t);
free(p);
return;
}
assert(m->digest);
p = (Mtree*)avldelete(mb->mtree, &t);
if(p == nil)
_assert("mtree delete fails");
free(p);
}

View File

@ -1,151 +1,188 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include "dat.h"
enum {
Buffersize = 64*1024,
};
typedef struct {
Biobuf *in;
char *shift;
} Inbuf;
typedef struct Inbuf Inbuf;
struct Inbuf
/*
* parse a Unix style header
*/
static int
memtotm(char *p, int n, Tm *t)
{
int fd;
uchar *lim;
uchar *rptr;
uchar *wptr;
uchar data[Buffersize+7];
};
char buf[128];
if(n > sizeof buf - 1)
n = sizeof buf -1;
memcpy(buf, p, n);
buf[n] = 0;
return strtotm(buf, t);
}
static int
chkunix0(char *s, int n)
{
char *p;
Tm tm;
if(n > 256)
return -1;
if((p = memchr(s, ' ', n)) == nil)
return -1;
if(memtotm(p, n - (p - s), &tm) < 0)
return -1;
if(tm2sec(&tm) < 1000000)
return -1;
return 0;
}
static int
chkunix(char *s, int n)
{
int r;
r = chkunix0(s, n);
if(r == -1)
eprint("plan9: warning naked from [%.*s]\n", n, s);
return r;
}
static char*
parseunix(Message *m)
{
char *s, *p, *q;
int l;
Tm tm;
l = m->header - m->start;
m->unixheader = smprint("%.*s", l, m->start);
s = m->start + 5;
if((p = strchr(s, ' ')) == nil)
return s;
*p = 0;
m->unixfrom = strdup(s);
*p++ = ' ';
if(q = strchr(p, '\n'))
*q = 0;
if(strtotm(p, &tm) < 0)
return p;
if(q)
*q = '\n';
m->fileid = (uvlong)tm2sec(&tm) << 8;
return 0;
}
static void
addtomessage(Message *m, uchar *p, int n, int done)
addtomessage(Message *m, char *p, int n)
{
int i, len;
// add to message (+1 in malloc is for a trailing NUL)
if(n == 0)
return;
/* add to message (+1 in malloc is for a trailing NUL) */
if(m->lim - m->end < n){
if(m->start != nil){
i = m->end-m->start;
if(done)
len = i + n;
else
len = (4*(i+n))/3;
i = m->end - m->start;
len = (4*(i + n))/3;
m->start = erealloc(m->start, len + 1);
m->end = m->start + i;
} else {
if(done)
len = n;
else
len = 2*n;
len = 2*n;
m->start = emalloc(len + 1);
m->end = m->start;
}
m->lim = m->start + len;
*m->lim = '\0';
*m->lim = 0;
}
memmove(m->end, p, n);
m->end += n;
*m->end = '\0';
*m->end = 0;
}
//
// read in a single message
//
/*
* read in a single message
*/
static int
readmessage(Message *m, Inbuf *inb)
okmsg(Mailbox *mb, Message *m, Inbuf *b)
{
int i, n, done;
uchar *p, *np;
char sdigest[SHA1dlen*2+1];
char tmp[64];
char e[ERRMAX], buf[128];
for(done = 0; !done;){
n = inb->wptr - inb->rptr;
if(n < 6){
if(n)
memmove(inb->data, inb->rptr, n);
inb->rptr = inb->data;
inb->wptr = inb->rptr + n;
i = read(inb->fd, inb->wptr, Buffersize);
if(i < 0){
if(fd2path(inb->fd, tmp, sizeof tmp) < 0)
strcpy(tmp, "unknown mailbox");
fprint(2, "error reading '%s': %r\n", tmp);
return -1;
}
if(i == 0){
if(n != 0)
addtomessage(m, inb->rptr, n, 1);
if(m->end == m->start)
return -1;
break;
}
inb->wptr += i;
}
// look for end of message
for(p = inb->rptr; p < inb->wptr; p = np+1){
// first part of search for '\nFrom '
np = memchr(p, '\n', inb->wptr - p);
if(np == nil){
p = inb->wptr;
break;
}
/*
* if we've found a \n but there's
* not enough room for '\nFrom ', don't do
* the comparison till we've read in more.
*/
if(inb->wptr - np < 6){
p = np;
break;
}
if(strncmp((char*)np, "\nFrom ", 6) == 0){
done = 1;
p = np+1;
break;
}
}
// add to message (+ 1 in malloc is for a trailing null)
n = p - inb->rptr;
addtomessage(m, inb->rptr, n, done);
inb->rptr += n;
}
// if it doesn't start with a 'From ', this ain't a mailbox
if(strncmp(m->start, "From ", 5) != 0)
rerrstr(e, sizeof e);
if(strlen(e)){
if(fd2path(Bfildes(b->in), buf, sizeof buf) < 0)
strcpy(buf, "unknown mailbox");
eprint("plan9: error reading %s: %r\n", buf);
return -1;
// dump trailing newline, make sure there's a trailing null
// (helps in body searches)
if(*(m->end-1) == '\n')
}
if(m->end == m->start)
return -1;
if(m->end[-1] == '\n')
m->end--;
*m->end = 0;
m->size = m->end - m->start;
if(m->size >= Maxmsg)
return -1;
m->bend = m->rbend = m->end;
// digest message
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
if(m->digest == 0)
digestmessage(mb, m);
return 0;
}
static char*
inbread(Inbuf *b)
{
if(b->shift)
return b->shift;
return b->shift = Brdline(b->in, '\n');
}
// throw out deleted messages. return number of freshly deleted messages
void
inbconsume(Inbuf *b)
{
b->shift = 0;
}
/*
* bug: very long line with From at the buffer break.
*/
static int
readmessage(Mailbox *mb, Message *m, Inbuf *b)
{
char *s, *n;
long l, state;
werrstr("");
state = 0;
for(;;){
s = inbread(b);
if(s == 0)
break;
n = s + (l = Blinelen(b->in)) - 1;
if(l >= 28 + 7 && n[0] == '\n')
if(strncmp(s, "From ", 5) == 0)
if(!chkunix(s + 5, l - 5))
if(++state == 2)
break;
if(state == 0)
return -1;
addtomessage(m, s, l);
inbconsume(b);
}
return okmsg(mb, m, b);
}
/* throw out deleted messages. return number of freshly deleted messages */
int
purgedeleted(Mailbox *mb)
{
Message *m, *next;
int newdels;
// forget about what's no longer in the mailbox
/* forget about what's no longer in the mailbox */
newdels = 0;
for(m = mb->root->part; m != nil; m = next){
next = m->next;
@ -158,44 +195,51 @@ purgedeleted(Mailbox *mb)
return newdels;
}
//
// read in the mailbox and parse into messages.
//
static char*
_readmbox(Mailbox *mb, int doplumb, Mlock *lk)
static void
mergemsg(Message *m, Message *x)
{
int fd, n;
String *tmp;
assert(m->start == 0);
m->mallocd = 1;
m->inmbox = 1;
m->lim = x->lim;
m->start = x->start;
m->end = x->end;
m->bend = x->bend;
m->rbend = x->rbend;
x->lim = 0;
x->start = 0;
x->end = 0;
x->bend = 0;
x->rbend = 0;
}
/*
* read in the mailbox and parse into messages.
*/
static char*
readmbox(Mailbox *mb, int doplumb, int *new, Mlock *lk)
{
char *p, *x, buf[Pathlen];
int nnew;
Biobuf *in;
Dir *d;
static char err[Errlen];
Inbuf b;
Message *m, **l;
Inbuf *inb;
char *x;
static char err[ERRMAX];
l = &mb->root->part;
/*
* open the mailbox. If it doesn't exist, try the temporary one.
*/
n = 0;
retry:
fd = open(mb->path, OREAD);
if(fd < 0){
rerrstr(err, sizeof(err));
if(strstr(err, "locked") != nil
|| strstr(err, "exclusive lock") != nil)
if(n++ < 20){
sleep(500); /* wait for lock to go away */
in = Bopen(mb->path, OREAD);
if(in == nil){
errstr(err, sizeof(err));
if(strstr(err, "exist") != 0){
snprint(buf, sizeof buf, "%s.tmp", mb->path);
if(sysrename(buf, mb->path) == 0)
goto retry;
}
if(strstr(err, "exist") != nil){
tmp = s_copy(mb->path);
s_append(tmp, ".tmp");
if(sysrename(s_to_c(tmp), mb->path) == 0){
s_free(tmp);
goto retry;
}
s_free(tmp);
}
return err;
}
@ -204,173 +248,170 @@ retry:
* a new qid.path means reread the mailbox, while
* a new qid.vers means read any new messages
*/
d = dirfstat(fd);
d = dirfstat(Bfildes(in));
if(d == nil){
close(fd);
errstr(err, sizeof(err));
Bterm(in);
errstr(err, sizeof err);
return err;
}
if(mb->d != nil){
if(d->qid.path == mb->d->qid.path && d->qid.vers == mb->d->qid.vers){
close(fd);
*new = 0;
Bterm(in);
free(d);
return nil;
}
if(d->qid.path == mb->d->qid.path){
while(*l != nil)
l = &(*l)->next;
seek(fd, mb->d->length, 0);
Bseek(in, mb->d->length, 0);
}
free(mb->d);
}
mb->d = d;
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
inb = emalloc(sizeof(Inbuf));
inb->rptr = inb->wptr = inb->data;
inb->fd = fd;
memset(&b, 0, sizeof b);
b.in = in;
b.shift = 0;
// read new messages
snprint(err, sizeof err, "reading '%s'", mb->path);
logmsg(err, nil);
/* read new messages */
logmsg(nil, "reading %s", mb->path);
nnew = 0;
for(;;){
if(lk != nil)
syslockrefresh(lk);
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
if(readmessage(m, inb) < 0){
delmessage(mb, m);
mb->root->subname--;
if(readmessage(mb, m, &b) < 0){
unnewmessage(mb, mb->root, m);
break;
}
// merge mailbox versions
/* merge mailbox versions */
while(*l != nil){
if(memcmp((*l)->digest, m->digest, SHA1dlen) == 0){
// matches mail we already read, discard
logmsg("duplicate", *l);
delmessage(mb, m);
mb->root->subname--;
m = nil;
l = &(*l)->next;
if((*l)->start == nil){
logmsg(*l, "read indexed");
mergemsg(*l, m);
unnewmessage(mb, mb->root, m);
m = *l;
}else{
logmsg(*l, "duplicate");
m->inmbox = 1; /* remove it */
unnewmessage(mb, mb->root, m);
m = nil;
l = &(*l)->next;
}
break;
} else {
// old mail no longer in box, mark deleted
logmsg("disappeared", *l);
/* old mail no longer in box, mark deleted */
logmsg(*l, "disappeared");
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
(*l)->deleted = Disappear;
l = &(*l)->next;
}
}
if(m == nil)
continue;
x = strchr(m->start, '\n');
if(x == nil)
m->header = m->end;
else
m->header = m->end;
if(x = strchr(m->start, '\n'))
m->header = x + 1;
if(p = parseunix(m))
sysfatal("%s:%lld naked From in body? [%s]", mb->path, seek(Bfildes(in), 0, 1), p);
m->mheader = m->mhend = m->header;
parseunix(m);
parse(m, 0, mb, 0);
logmsg("new", m);
parse(mb, m, 0, 0);
if(m != *l && m->deleted != Dup){
logmsg(m, "new");
newcachehash(mb, m, doplumb);
putcache(mb, m);
nnew++;
}
/* chain in */
*l = m;
l = &m->next;
if(doplumb)
mailplumb(mb, m, 0);
}
logmsg("mbox read", nil);
logmsg(nil, "mbox read");
// whatever is left has been removed from the mbox, mark deleted
/* whatever is left has been removed from the mbox, mark deleted */
while(*l != nil){
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
(*l)->deleted = Deleted;
l = &(*l)->next;
}
close(fd);
free(inb);
Bterm(in);
*new = nnew;
return nil;
}
static void
_writembox(Mailbox *mb, Mlock *lk)
writembox(Mailbox *mb, Mlock *lk)
{
Dir *d;
Message *m;
String *tmp;
char buf[Pathlen];
int mode, errs;
Biobuf *b;
Dir *d;
Message *m;
tmp = s_copy(mb->path);
s_append(tmp, ".tmp");
snprint(buf, sizeof buf, "%s.tmp", mb->path);
/*
* preserve old files permissions, if possible
*/
d = dirstat(mb->path);
if(d != nil){
mode = d->mode&0777;
mode = Mboxmode;
if(d = dirstat(mb->path)){
mode = d->mode & 0777;
free(d);
} else
mode = MBOXMODE;
}
sysremove(s_to_c(tmp));
b = sysopen(s_to_c(tmp), "alc", mode);
remove(buf);
b = sysopen(buf, "alc", mode);
if(b == 0){
fprint(2, "can't write temporary mailbox %s: %r\n", s_to_c(tmp));
eprint("plan9: can't write temporary mailbox %s: %r\n", buf);
return;
}
logmsg("writing new mbox", nil);
logmsg(nil, "writing new mbox");
errs = 0;
for(m = mb->root->part; m != nil; m = m->next){
if(lk != nil)
syslockrefresh(lk);
if(m->deleted)
continue;
logmsg("writing", m);
logmsg(m, "writing");
if(Bwrite(b, m->start, m->end - m->start) < 0)
errs = 1;
if(Bwrite(b, "\n", 1) < 0)
errs = 1;
}
logmsg("wrote new mbox", nil);
logmsg(nil, "wrote new mbox");
if(sysclose(b) < 0)
errs = 1;
if(errs){
fprint(2, "error writing temporary mail file\n");
s_free(tmp);
eprint("plan9: error writing temporary mail file\n");
return;
}
sysremove(mb->path);
if(sysrename(s_to_c(tmp), mb->path) < 0)
fprint(2, "%s: can't rename %s to %s: %r\n", argv0,
s_to_c(tmp), mb->path);
s_free(tmp);
remove(mb->path);
if(sysrename(buf, mb->path) < 0)
eprint("plan9: can't rename %s to %s: %r\n",
buf, mb->path);
if(mb->d != nil)
free(mb->d);
mb->d = dirstat(mb->path);
}
char*
plan9syncmbox(Mailbox *mb, int doplumb)
plan9syncmbox(Mailbox *mb, int doplumb, int *new)
{
Mlock *lk;
char *rv;
Mlock *lk;
lk = nil;
if(mb->dolock){
@ -379,9 +420,9 @@ plan9syncmbox(Mailbox *mb, int doplumb)
return "can't lock mailbox";
}
rv = _readmbox(mb, doplumb, lk); /* interpolate */
rv = readmbox(mb, doplumb, new, lk); /* interpolate */
if(purgedeleted(mb) > 0)
_writembox(mb, lk);
writembox(mb, lk);
if(lk != nil)
sysunlock(lk);
@ -389,26 +430,30 @@ plan9syncmbox(Mailbox *mb, int doplumb)
return rv;
}
//
// look to see if we can open this mail box
//
void
plan9decache(Mailbox*, Message *m)
{
m->lim = 0;
}
/*
* look to see if we can open this mail box
*/
char*
plan9mbox(Mailbox *mb, char *path)
{
static char err[Errlen];
String *tmp;
char buf[Pathlen];
static char err[Pathlen];
if(access(path, AEXIST) < 0){
errstr(err, sizeof(err));
tmp = s_copy(path);
s_append(tmp, ".tmp");
if(access(s_to_c(tmp), AEXIST) < 0){
s_free(tmp);
errstr(err, sizeof err);
snprint(buf, sizeof buf, "%s.tmp", path);
if(access(buf, AEXIST) < 0)
return err;
}
s_free(tmp);
}
mb->sync = plan9syncmbox;
mb->remove = localremove;
mb->rename = localrename;
mb->decache = plan9decache;
return nil;
}

View File

@ -15,12 +15,37 @@
#include <libsec.h>
#include "dat.h"
static char*
parseunix(Message *m)
{
char *s, *p, *q;
int l;
Tm tm;
l = m->header - m->start;
m->unixheader = smprint("%.*s", l, m->start);
s = m->start + 5;
if((p = strchr(s, ' ')) == nil)
return s;
*p = 0;
m->unixfrom = strdup(s);
*p++ = ' ';
if(q = strchr(p, '\n'))
*q = 0;
if(strtotm(p, &tm) < 0)
return p;
if(q)
*q = '\n';
m->fileid = (uvlong)tm2sec(&tm) << 8;
return 0;
}
static int
readmessage(Message *m, char *msg)
{
int fd, i, n;
int fd, n;
char *buf, *name, *p;
char hdr[128], sdigest[SHA1dlen*2+1];
char hdr[128];
Dir *d;
buf = nil;
@ -29,8 +54,10 @@ readmessage(Message *m, char *msg)
if(name == nil)
return -1;
if(m->filename != nil)
s_free(m->filename);
m->filename = s_copy(name);
free(m->filename);
m->filename = strdup(name);
if(m->filename == nil)
sysfatal("malloc: %r");
fd = open(name, OREAD);
if(fd < 0)
goto Fail;
@ -75,10 +102,7 @@ readmessage(Message *m, char *msg)
m->end--;
*m->end = 0;
m->bend = m->rbend = m->end;
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
return 0;
Fail:
if(fd >= 0)
@ -98,7 +122,7 @@ archive(Message *m)
char *dir, *p, *nname;
Dir d;
dir = strdup(s_to_c(m->filename));
dir = strdup(m->filename);
nname = nil;
if(dir == nil)
return;
@ -162,21 +186,20 @@ mustshow(char* name)
}
static int
readpbmessage(Mailbox *mb, char *msg, int doplumb)
readpbmessage(Mailbox *mb, char *msg, int doplumb, int *nnew)
{
Message *m, **l;
char *x;
char *x, *p;
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
if(readmessage(m, msg) < 0){
delmessage(mb, m);
mb->root->subname--;
unnewmessage(mb, mb->root, m);
return -1;
}
for(l = &mb->root->part; *l != nil; l = &(*l)->next)
if(strcmp(s_to_c((*l)->filename), s_to_c(m->filename)) == 0 &&
if(strcmp((*l)->filename, m->filename) == 0 &&
*l != m){
if((*l)->deleted < 0)
(*l)->deleted = 0;
@ -184,15 +207,19 @@ readpbmessage(Mailbox *mb, char *msg, int doplumb)
mb->root->subname--;
return -1;
}
x = strchr(m->start, '\n');
if(x == nil)
m->header = m->end;
else
m->header = m->end;
if(x = strchr(m->start, '\n'))
m->header = x + 1;
if(p = parseunix(m))
sysfatal("%s:%s naked From in body? [%s]", mb->path, (*l)->filename, p);
m->mheader = m->mhend = m->header;
parseunix(m);
parse(m, 0, mb, 0);
logmsg("new", m);
parse(mb, m, 0, 0);
if(m != *l && m->deleted != Dup){
logmsg(m, "new");
newcachehash(mb, m, doplumb);
putcache(mb, m);
nnew[0]++;
}
/* chain in */
*l = m;
@ -215,17 +242,18 @@ dcmp(Dir *a, Dir *b)
return strcmp(an, bn);
}
static void
readpbmbox(Mailbox *mb, int doplumb)
static char*
readpbmbox(Mailbox *mb, int doplumb, int *new)
{
int fd, i, j, nd, nmd;
char *month, *msg;
int fd, i, j, nd, nmd;
Dir *d, *md;
static char err[ERRMAX];
fd = open(mb->path, OREAD);
if(fd < 0){
fprint(2, "%s: %s: %r\n", argv0, mb->path);
return;
errstr(err, sizeof err);
return err;
}
nd = dirreadall(fd, &d);
close(fd);
@ -249,7 +277,7 @@ readpbmbox(Mailbox *mb, int doplumb)
for(j = 0; j < nmd; j++)
if(mustshow(md[j].name)){
msg = smprint("%s/%s", month, md[j].name);
readpbmessage(mb, msg, doplumb);
readpbmessage(mb, msg, doplumb, new);
free(msg);
}
}
@ -259,45 +287,45 @@ readpbmbox(Mailbox *mb, int doplumb)
md = nil;
}
free(d);
return nil;
}
static void
readpbvmbox(Mailbox *mb, int doplumb)
static char*
readpbvmbox(Mailbox *mb, int doplumb, int *new)
{
char *data, *ln, *p, *nln, *msg;
int fd, nr;
long sz;
char *data, *ln, *p, *nln, *msg;
Dir *d;
static char err[ERRMAX];
fd = open(mb->path, OREAD);
if(fd < 0){
fprint(2, "%s: %s: %r\n", argv0, mb->path);
return;
errstr(err, sizeof err);
return err;
}
d = dirfstat(fd);
if(d == nil){
fprint(2, "%s: %s: %r\n", argv0, mb->path);
close(fd);
return;
errstr(err, sizeof err);
return err;
}
sz = d->length;
free(d);
if(sz > 2 * 1024 * 1024){
sz = 2 * 1024 * 1024;
fprint(2, "%s: %s: bug: folder too big\n", argv0, mb->path);
fprint(2, "upas/fs: %s: bug: folder too big\n", mb->path);
}
data = malloc(sz+1);
if(data == nil){
close(fd);
fprint(2, "%s: no memory\n", argv0);
return;
errstr(err, sizeof err);
return err;
}
nr = readn(fd, data, sz);
close(fd);
if(nr < 0){
fprint(2, "%s: %s: %r\n", argv0, mb->path);
errstr(err, sizeof err);
free(data);
return;
return err;
}
data[nr] = 0;
@ -318,22 +346,24 @@ readpbvmbox(Mailbox *mb, int doplumb)
*p = 0;
msg = smprint("/mail/box/%s/msgs/%s", user, ln);
if(msg == nil){
fprint(2, "%s: no memory\n", argv0);
fprint(2, "upas/fs: malloc: %r\n");
continue;
}
readpbmessage(mb, msg, doplumb);
readpbmessage(mb, msg, doplumb, new);
free(msg);
}
free(data);
return nil;
}
static char*
readmbox(Mailbox *mb, int doplumb, int virt)
readmbox(Mailbox *mb, int doplumb, int virt, int *new)
{
char *mberr;
int fd;
Dir *d;
Message *m;
static char err[Errlen];
static char err[128];
if(debug)
fprint(2, "read mbox %s\n", mb->path);
@ -364,45 +394,45 @@ readmbox(Mailbox *mb, int doplumb, int virt)
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
snprint(err, sizeof err, "reading '%s'", mb->path);
logmsg(err, nil);
logmsg(nil, err, nil);
for(m = mb->root->part; m != nil; m = m->next)
if(m->deleted == 0)
m->deleted = -1;
if(virt == 0)
readpbmbox(mb, doplumb);
mberr = readpbmbox(mb, doplumb, new);
else
readpbvmbox(mb, doplumb);
mberr = readpbvmbox(mb, doplumb, new);
/*
* messages removed from the mbox; flag them to go.
*/
for(m = mb->root->part; m != nil; m = m->next)
if(m->deleted < 0 && doplumb){
m->inmbox = 0;
m->deleted = 1;
mailplumb(mb, m, 1);
delmessage(mb, m);
if(doplumb)
mailplumb(mb, m, 1);
}
logmsg("mbox read", nil);
return nil;
logmsg(nil, "mbox read");
return mberr;
}
static char*
mbsync(Mailbox *mb, int doplumb)
mbsync(Mailbox *mb, int doplumb, int *new)
{
char *rv;
rv = readmbox(mb, doplumb, 0);
rv = readmbox(mb, doplumb, 0, new);
purgembox(mb, 0);
return rv;
}
static char*
mbvsync(Mailbox *mb, int doplumb)
mbvsync(Mailbox *mb, int doplumb, int *new)
{
char *rv;
rv = readmbox(mb, doplumb, 1);
rv = readmbox(mb, doplumb, 1, new);
purgembox(mb, 1);
return rv;
}

View File

@ -1,54 +1,64 @@
#include "common.h"
#include <ctype.h>
#include <plumb.h>
#include <libsec.h>
#include <auth.h>
#include "dat.h"
#pragma varargck type "M" uchar*
#pragma varargck argpos pop3cmd 2
#define pdprint(p, ...) if((p)->debug) fprint(2, __VA_ARGS__); else{}
typedef struct Popm Popm;
struct Popm{
int mesgno;
};
typedef struct Pop Pop;
struct Pop {
char *freep; // free this to free the strings below
char *freep; /* free this to free the strings below */
char *host;
char *user;
char *port;
char *host;
char *user;
char *port;
int ppop;
int refreshtime;
int debug;
int pipeline;
int encrypted;
int needtls;
int notls;
int needssl;
// open network connection
Biobuf bin;
Biobuf bout;
int fd;
char *lastline; // from Brdstr
int ppop;
int refreshtime;
int debug;
int pipeline;
int encrypted;
int needtls;
int notls;
int needssl;
Biobuf bin; /* open network connection */
Biobuf bout;
int fd;
char *lastline; /* from Brdstr */
Thumbprint *thumb;
};
char*
static int
mesgno(Message *m)
{
Popm *a;
a = m->aux;
return a->mesgno;
}
static char*
geterrstr(void)
{
static char err[Errlen];
static char err[64];
err[0] = '\0';
errstr(err, sizeof(err));
return err;
}
//
// get pop3 response line , without worrying
// about multiline responses; the clients
// will deal with that.
//
/*
* get pop3 response line , without worrying
* about multiline responses; the clients
* will deal with that.
*/
static int
isokay(char *s)
{
@ -62,15 +72,13 @@ pop3cmd(Pop *pop, char *fmt, ...)
va_list va;
va_start(va, fmt);
vseprint(buf, buf+sizeof(buf), fmt, va);
vseprint(buf, buf + sizeof buf, fmt, va);
va_end(va);
p = buf+strlen(buf);
if(p > (buf+sizeof(buf)-3))
p = buf + strlen(buf);
if(p > buf + sizeof buf - 3)
sysfatal("pop3 command too long");
if(pop->debug)
fprint(2, "<- %s\n", buf);
pdprint(pop, "<- %s\n", buf);
strcpy(p, "\r\n");
Bwrite(&pop->bout, buf, strlen(buf));
Bflush(&pop->bout);
@ -83,20 +91,19 @@ pop3resp(Pop *pop)
char *p;
alarm(60*1000);
s = Brdstr(&pop->bin, '\n', 0);
alarm(0);
if(s == nil){
if((s = Brdstr(&pop->bin, '\n', 0)) == nil){
close(pop->fd);
pop->fd = -1;
alarm(0);
return "unexpected eof";
}
alarm(0);
p = s+strlen(s)-1;
p = s + strlen(s) - 1;
while(p >= s && (*p == '\r' || *p == '\n'))
*p-- = '\0';
if(pop->debug)
fprint(2, "-> %s\n", s);
pdprint(pop, "-> %s\n", s);
free(pop->lastline);
pop->lastline = s;
return s;
@ -119,40 +126,35 @@ pop3pushtls(Pop *pop)
int fd;
uchar digest[SHA1dlen];
TLSconn conn;
char *err;
err = nil;
memset(&conn, 0, sizeof conn);
// conn.trace = pop3log;
fd = tlsClient(pop->fd, &conn);
if(fd < 0){
err = "tls error";
goto out;
}
pop->fd = fd;
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
if(fd < 0)
return "tls error";
if(conn.cert==nil || conn.certlen <= 0){
err = "server did not provide TLS certificate";
goto out;
close(fd);
return "server did not provide TLS certificate";
}
sha1(conn.cert, conn.certlen, digest, nil);
if(!pop->thumb || !okThumbprint(digest, pop->thumb)){
fmtinstall('H', encodefmt);
fprint(2, "upas/fs pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
err = "bad server certificate";
goto out;
close(fd);
free(conn.cert);
eprint("pop3: server certificate %.*H not recognized\n", SHA1dlen, digest);
return "bad server certificate";
}
pop->encrypted = 1;
out:
free(conn.sessionID);
free(conn.cert);
return err;
close(pop->fd);
pop->fd = fd;
pop->encrypted = 1;
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
return nil;
}
//
// get capability list, possibly start tls
//
/*
* get capability list, possibly start tls
*/
static char*
pop3capa(Pop *pop)
{
@ -186,9 +188,9 @@ pop3capa(Pop *pop)
return nil;
}
//
// log in using APOP if possible, password if allowed by user
//
/*
* log in using APOP if possible, password if allowed by user
*/
static char*
pop3login(Pop *pop)
{
@ -207,10 +209,10 @@ pop3login(Pop *pop)
else
ubuf[0] = '\0';
// look for apop banner
if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
/* look for apop banner */
if(pop->ppop == 0 && (p = strchr(s, '<')) && (q = strchr(p + 1, '>'))) {
*++q = '\0';
if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
if((n=auth_respond(p, q - p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
pop->host, ubuf)) < 0)
return "factotum failed";
if(user[0]=='\0')
@ -253,83 +255,75 @@ pop3login(Pop *pop)
}
}
//
// dial and handshake with pop server
//
/*
* dial and handshake with pop server
*/
static char*
pop3dial(Pop *pop)
{
char *err;
if(pop->fd >= 0){
close(pop->fd);
pop->fd = -1;
}
if((pop->fd = dial(netmkaddr(pop->host, "net", pop->needssl ? "pop3s" : "pop3"), 0, 0, 0)) < 0)
return geterrstr();
if(pop->needssl){
if((err = pop3pushtls(pop)) != nil)
goto Out;
return err;
}else{
Binit(&pop->bin, pop->fd, OREAD);
Binit(&pop->bout, pop->fd, OWRITE);
}
err = pop3login(pop);
Out:
if(err != nil){
if(pop->fd >= 0){
close(pop->fd);
pop->fd = -1;
}
if(err = pop3login(pop)) {
close(pop->fd);
return err;
}
return err;
return nil;
}
//
// close connection
//
/*
* close connection
*/
static void
pop3hangup(Pop *pop)
{
if(pop->fd < 0)
return;
pop3cmd(pop, "QUIT");
pop3resp(pop);
close(pop->fd);
pop->fd = -1;
}
//
// download a single message
//
/*
* download a single message
*/
static char*
pop3download(Pop *pop, Message *m)
pop3download(Mailbox *mb, Pop *pop, Message *m)
{
char *s, *f[3], *wp, *ep;
char sdigest[SHA1dlen*2+1];
int i, l, sz;
int l, sz, pos, n;
Popm *a;
a = m->aux;
if(!pop->pipeline)
pop3cmd(pop, "LIST %d", m->mesgno);
pop3cmd(pop, "LIST %d", a->mesgno);
if(!isokay(s = pop3resp(pop)))
return s;
if(tokenize(s, f, 3) != 3)
return "syntax error in LIST response";
if(atoi(f[1]) != m->mesgno)
if(atoi(f[1]) != a->mesgno)
return "out of sync with pop3 server";
sz = atoi(f[2])+200; /* 200 because the plan9 pop3 server lies */
sz = atoi(f[2]) + 200; /* 200 because the plan9 pop3 server lies */
if(sz == 0)
return "invalid size in LIST response";
m->start = wp = emalloc(sz+1);
ep = wp+sz;
m->start = wp = emalloc(sz + 1);
ep = wp + sz;
if(!pop->pipeline)
pop3cmd(pop, "RETR %d", m->mesgno);
pop3cmd(pop, "RETR %d", a->mesgno);
if(!isokay(s = pop3resp(pop))) {
m->start = nil;
free(wp);
@ -347,7 +341,7 @@ pop3download(Pop *pop, Message *m)
if(strcmp(s, ".") == 0)
break;
l = strlen(s)+1;
l = strlen(s) + 1;
if(s[0] == '.') {
s++;
l--;
@ -356,14 +350,17 @@ pop3download(Pop *pop, Message *m)
* grow by 10%/200bytes - some servers
* lie about message sizes
*/
if(wp+l > ep) {
int pos = wp - m->start;
sz += ((sz / 10) < 200)? 200: sz/10;
m->start = erealloc(m->start, sz+1);
wp = m->start+pos;
ep = m->start+sz;
if(wp + l > ep) {
pos = wp - m->start;
n = sz/10;
if(n < 200)
n = 200;
sz += n;
m->start = erealloc(m->start, sz + 1);
wp = m->start + pos;
ep = m->start + sz;
}
memmove(wp, s, l-1);
memmove(wp, s, l - 1);
wp[l-1] = '\n';
wp += l;
}
@ -373,40 +370,41 @@ pop3download(Pop *pop, Message *m)
m->end = wp;
// make sure there's a trailing null
// (helps in body searches)
/*
* make sure there's a trailing null
* (helps in body searches)
*/
*m->end = 0;
m->bend = m->rbend = m->end;
m->header = m->start;
// digest message
sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
for(i = 0; i < SHA1dlen; i++)
sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
m->sdigest = s_copy(sdigest);
m->size = m->end - m->start;
if(m->digest == nil)
digestmessage(mb, m);
return nil;
}
//
// check for new messages on pop server
// UIDL is not required by RFC 1939, but
// netscape requires it, so almost every server supports it.
// we'll use it to make our lives easier.
//
/*
* check for new messages on pop server
* UIDL is not required by RFC 1939, but
* netscape requires it, so almost every server supports it.
* we'll use it to make our lives easier.
*/
static char*
pop3read(Pop *pop, Mailbox *mb, int doplumb)
pop3read(Pop *pop, Mailbox *mb, int doplumb, int *new)
{
char *s, *p, *uidl, *f[2];
int mesgno, ignore, nnew;
int mno, ignore, nnew;
Message *m, *next, **l;
Popm *a;
// Some POP servers disallow UIDL if the maildrop is empty.
*new = 0;
/* Some POP servers disallow UIDL if the maildrop is empty. */
pop3cmd(pop, "STAT");
if(!isokay(s = pop3resp(pop)))
return s;
// fetch message listing; note messages to grab
/* fetch message listing; note messages to grab */
l = &mb->root->part;
if(strncmp(s, "+OK 0 ", 6) != 0) {
pop3cmd(pop, "UIDL");
@ -421,25 +419,32 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
if(tokenize(p, f, 2) != 2)
continue;
mesgno = atoi(f[0]);
mno = atoi(f[0]);
uidl = f[1];
if(strlen(uidl) > 75) // RFC 1939 says 70 characters max
if(strlen(uidl) > 75) /* RFC 1939 says 70 characters max */
continue;
ignore = 0;
while(*l != nil) {
if(strcmp((*l)->uidl, uidl) == 0) {
// matches mail we already have, note mesgno for deletion
(*l)->mesgno = mesgno;
a = (*l)->aux;
if(strcmp((*l)->idxaux, uidl) == 0){
if(a == 0){
m = *l;
m->mallocd = 1;
m->inmbox = 1;
m->aux = a = emalloc(sizeof *a);
}
/* matches mail we already have, note mesgno for deletion */
a->mesgno = mno;
ignore = 1;
l = &(*l)->next;
break;
} else {
// old mail no longer in box mark deleted
}else{
/* old mail no longer in box mark deleted */
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
(*l)->deleted = Deleted;
l = &(*l)->next;
}
}
@ -449,30 +454,31 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
m = newmessage(mb->root);
m->mallocd = 1;
m->inmbox = 1;
m->mesgno = mesgno;
strcpy(m->uidl, uidl);
m->idxaux = strdup(uidl);
m->aux = a = emalloc(sizeof *a);
a->mesgno = mno;
// chain in; will fill in message later
/* chain in; will fill in message later */
*l = m;
l = &m->next;
}
}
// whatever is left has been removed from the mbox, mark as deleted
/* whatever is left has been removed from the mbox, mark as deleted */
while(*l != nil) {
if(doplumb)
mailplumb(mb, *l, 1);
(*l)->inmbox = 0;
(*l)->deleted = 1;
(*l)->deleted = Disappear;
l = &(*l)->next;
}
// download new messages
/* download new messages */
nnew = 0;
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
fprint(2, "rfork: %r\n");
eprint("pop3: rfork: %r\n");
pop->pipeline = 0;
default:
@ -480,49 +486,41 @@ pop3read(Pop *pop, Mailbox *mb, int doplumb)
case 0:
for(m = mb->root->part; m != nil; m = m->next){
if(m->start != nil)
if(m->start != nil || m->deleted)
continue;
Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", mesgno(m), mesgno(m));
}
Bflush(&pop->bout);
_exits(nil);
_exits("");
}
}
for(m = mb->root->part; m != nil; m = next) {
next = m->next;
if(m->start != nil)
if(m->start != nil || m->deleted)
continue;
if(s = pop3download(pop, m)) {
// message disappeared? unchain
fprint(2, "download %d: %s\n", m->mesgno, s);
if(s = pop3download(mb, pop, m)) {
/* message disappeared? unchain */
eprint("pop3: download %d: %s\n", mesgno(m), s);
delmessage(mb, m);
mb->root->subname--;
continue;
}
nnew++;
parse(m, 0, mb, 1);
if(doplumb)
mailplumb(mb, m, 0);
parse(mb, m, 1, 0);
newcachehash(mb, m, doplumb);
putcache(mb, m);
}
if(pop->pipeline)
waitpid();
if(nnew || mb->vers == 0) {
mb->vers++;
henter(PATH(0, Qtop), mb->name,
(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
}
*new = nnew;
return nil;
}
//
// delete marked messages
//
/*
* delete marked messages
*/
static void
pop3purge(Pop *pop, Mailbox *mb)
{
@ -531,7 +529,7 @@ pop3purge(Pop *pop, Mailbox *mb)
if(pop->pipeline){
switch(rfork(RFPROC|RFMEM)){
case -1:
fprint(2, "rfork: %r\n");
eprint("pop3: rfork: %r\n");
pop->pipeline = 0;
default:
@ -542,11 +540,11 @@ pop3purge(Pop *pop, Mailbox *mb)
next = m->next;
if(m->deleted && m->refs == 0){
if(m->inmbox)
Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
Bprint(&pop->bout, "DELE %d\r\n", mesgno(m));
}
}
Bflush(&pop->bout);
_exits(nil);
_exits("");
}
}
for(m = mb->root->part; m != nil; m = next) {
@ -554,7 +552,7 @@ pop3purge(Pop *pop, Mailbox *mb)
if(m->deleted && m->refs == 0) {
if(m->inmbox) {
if(!pop->pipeline)
pop3cmd(pop, "DELE %d", m->mesgno);
pop3cmd(pop, "DELE %d", mesgno(m));
if(isokay(pop3resp(pop)))
delmessage(mb, m);
} else
@ -564,19 +562,21 @@ pop3purge(Pop *pop, Mailbox *mb)
}
// connect to pop3 server, sync mailbox
/* connect to pop3 server, sync mailbox */
static char*
pop3sync(Mailbox *mb, int doplumb)
pop3sync(Mailbox *mb, int doplumb, int *new)
{
char *err;
Pop *pop;
pop = mb->aux;
if(err = pop3dial(pop)) {
mb->waketime = time(0) + pop->refreshtime;
return err;
}
if((err = pop3read(pop, mb, doplumb)) == nil){
if((err = pop3read(pop, mb, doplumb, new)) == nil){
pop3purge(pop, mb);
mb->d->atime = mb->d->mtime = time(0);
}
@ -629,7 +629,7 @@ pop3ctl(Mailbox *mb, int argc, char **argv)
return Epop3ctl;
}
// free extra memory associated with mb
/* free extra memory associated with mb */
static void
pop3close(Mailbox *mb)
{
@ -640,9 +640,18 @@ pop3close(Mailbox *mb)
free(pop);
}
//
// open mailboxes of the form /pop/host/user or /apop/host/user
//
static char*
mkmbox(Pop *pop, char *p, char *e)
{
p = seprint(p, e, "%s/box/%s/pop.%s", MAILROOT, getlog(), pop->host);
if(pop->user && strcmp(pop->user, getlog()))
p = seprint(p, e, ".%s", pop->user);
return p;
}
/*
* open mailboxes of the form /pop/host/user or /apop/host/user
*/
char*
pop3mbox(Mailbox *mb, char *path)
{
@ -650,7 +659,6 @@ pop3mbox(Mailbox *mb, char *path)
int nf, apop, ppop, popssl, apopssl, apoptls, popnotls, apopnotls, poptls;
Pop *pop;
quotefmtinstall();
popssl = strncmp(path, "/pops/", 6) == 0;
apopssl = strncmp(path, "/apops/", 7) == 0;
poptls = strncmp(path, "/poptls/", 8) == 0;
@ -673,8 +681,7 @@ pop3mbox(Mailbox *mb, char *path)
return "bad pop3 path syntax /[a]pop[tls|ssl]/system[/user]";
}
pop = emalloc(sizeof(*pop));
pop->fd = -1;
pop = emalloc(sizeof *pop);
pop->freep = path;
pop->host = f[2];
if(nf < 4)
@ -688,12 +695,13 @@ pop3mbox(Mailbox *mb, char *path)
pop->notls = popnotls || apopnotls;
pop->thumb = initThumbprints("/sys/lib/tls/mail", "/sys/lib/tls/mail.exclude");
mkmbox(pop, mb->path, mb->path + sizeof mb->path);
mb->aux = pop;
mb->sync = pop3sync;
mb->close = pop3close;
mb->ctl = pop3ctl;
mb->d = emalloc(sizeof(*mb->d));
mb->d = emalloc(sizeof *mb->d);
mb->addfrom = 1;
return nil;
}

View File

@ -1,15 +0,0 @@
#include <u.h>
#include <libc.h>
void
main(void)
{
Dir d;
int fd, n;
fd = open("/mail/fs", OREAD);
while((n = dirread(fd, &d, sizeof(d))) > 0){
print("%s\n", d.name);
}
print("n = %d\n", n);
}

100
sys/src/cmd/upas/fs/ref.c Normal file
View File

@ -0,0 +1,100 @@
#include "common.h"
#include <libsec.h>
#include "dat.h"
/* all the data that's fit to cache */
typedef struct{
char *s;
int l;
ulong ref;
}Refs;
Refs *rtab;
int nrtab;
int nralloc;
int
newrefs(char *s)
{
int l, i;
Refs *r;
l = strlen(s);
for(i = 0; i < nrtab; i++){
r = rtab + i;
if(r->ref == 0)
goto enter;
if(l == r->l && strcmp(r->s, s) == 0){
r->ref++;
return i;
}
}
if(nrtab == nralloc)
rtab = erealloc(rtab, sizeof *rtab*(nralloc += 50));
nrtab = i + 1;
enter:
r = rtab + i;
r->s = strdup(s);
r->l = l;
r->ref = 1;
return i;
}
void
delrefs(int i)
{
Refs *r;
r = rtab + i;
if(--r->ref > 0)
return;
free(r->s);
memset(r, 0, sizeof *r);
}
void
refsinit(void)
{
newrefs("");
}
static char *sep = "--------\n";
int
prrefs(Biobuf *b)
{
int i, n;
n = 0;
for(i = 1; i < nrtab; i++){
if(rtab[i].ref == 0)
continue;
Bprint(b, "%s ", rtab[i].s);
if(n++%8 == 7)
Bprint(b, "\n");
}
if(n%8 != 7)
Bprint(b, "\n");
Bprint(b, sep);
return 0;
}
int
rdrefs(Biobuf *b)
{
char *f[10], *s;
int i, n;
while(s = Brdstr(b, '\n', 1)){
if(strcmp(s, sep) == 0){
free(s);
return 0;
}
n = tokenize(s, f, nelem(f));
for(i = 0; i < n; i++)
newrefs(f[i]);
free(s);
}
return -1;
}

View File

@ -0,0 +1,141 @@
#include "common.h"
#include "dat.h"
#define deprint(...) /* eprint(__VA_ARGS__) */
extern int dirskip(Dir*, uvlong*);
static int
ismbox(char *path)
{
char buf[512];
int fd, r;
fd = open(path, OREAD);
if(fd == -1)
return 0;
r = 1;
if(read(fd, buf, sizeof buf) < 28 + 5)
r = 0;
else if(strncmp(buf, "From ", 5))
r = 0;
close(fd);
return r;
}
static int
isindex(Dir *d)
{
char *p;
p = strrchr(d->name, '.');
if(!p)
return -1;
if(strcmp(p, ".idx") || strcmp(p, ".imp"))
return 1;
return 0;
}
static int
idiotcheck(char *path, Dir *d, int getindex)
{
uvlong v;
if(d->mode & DMDIR)
return 0;
if(strncmp(d->name, "L.", 2) == 0)
return 0;
if(getindex && isindex(d))
return 0;
if(!dirskip(d, &v) || ismbox(path))
return 0;
return -1;
}
int
vremove(char *buf)
{
deprint("rm %s\n", buf);
return remove(buf);
}
static int
rm(char *dir, int flags, int level)
{
char buf[Pathlen];
int i, n, r, fd, isdir, rflag;
Dir *d;
d = dirstat(dir);
isdir = d->mode & DMDIR;
free(d);
if(!isdir)
return 0;
fd = open(dir, OREAD);
if(fd == -1)
return -1;
n = dirreadall(fd, &d);
close(fd);
r = 0;
rflag = flags & Rrecur;
for(i = 0; i < n; i++){
snprint(buf, sizeof buf, "%s/%s", dir, d[i].name);
if(rflag)
r |= rm(buf, flags, level + 1);
if(idiotcheck(buf, d + i, level + rflag) == -1)
continue;
if(vremove(buf) != 0)
r = -1;
}
free(d);
return r;
}
void
rmidx(char *buf, int flags)
{
char buf2[Pathlen];
snprint(buf2, sizeof buf2, "%s.idx", buf);
vremove(buf2);
if((flags & Rtrunc) == 0){
snprint(buf2, sizeof buf2, "%s.imp", buf);
vremove(buf2);
}
}
char*
localremove(Mailbox *mb, int flags)
{
char *msg, *path;
int r, isdir;
Dir *d;
static char err[2*Pathlen];
path = mb->path;
if((d = dirstat(path)) == 0){
snprint(err, sizeof err, "%s: doesn't exist\n", path);
return 0;
}
isdir = d->mode & DMDIR;
free(d);
msg = "deleting";
if(flags & Rtrunc)
msg = "truncating";
deprint("%s: %s\n", msg, path);
/* must match folder.c:/^openfolder */
r = rm(path, flags, 0);
if((flags & Rtrunc) == 0)
r = vremove(path);
else if(!isdir)
close(r = open(path, OWRITE|OTRUNC));
rmidx(path, flags);
if(r == -1){
snprint(err, sizeof err, "%s: can't %s\n", path, msg);
return err;
}
return 0;
}

View File

@ -0,0 +1,234 @@
#include "common.h"
#include "dat.h"
#define deprint(...) /* eprint(__VA_ARGS__) */
static int
delivery(char *s)
{
if(strncmp(s, "/mail/fs/", 9) == 0)
if((s = strrchr(s, '/')) && strcmp(s + 1, "mbox") == 0)
return 1;
return 0;
}
static int
isdir(char *s)
{
int isdir;
Dir *d;
d = dirstat(s);
isdir = d && d->mode & DMDIR;
free(d);
return isdir;
}
static int
docreate(char *file, int perm)
{
int fd;
Dir ndir;
Dir *d;
fd = create(file, OREAD, perm);
if(fd < 0)
return -1;
d = dirfstat(fd);
if(d == nil)
return -1;
nulldir(&ndir);
ndir.mode = perm;
ndir.gid = d->uid;
dirfwstat(fd, &ndir);
close(fd);
return 0;
}
static int
rollup(char *s)
{
char *p;
int mode;
if(access(s, 0) == 0)
return -1;
/*
* if we can deliver to this mbox, it needs
* to be read/execable all the way down
*/
mode = 0711;
if(delivery(s))
mode = 0755;
for(p = s; p; p++) {
if(*p == '/')
continue;
p = strchr(p, '/');
if(p == 0)
break;
*p = 0;
if(access(s, 0) != 0)
if(docreate(s, DMDIR|mode) < 0)
return -1;
*p = '/';
}
return 0;
}
static int
copyfile(char *a, char *b, int flags)
{
char *s;
int fd, fd1, mode, i, m, n, r;
Dir *d;
mode = 0600;
if(delivery(b))
mode = 0622;
fd = open(a, OREAD);
fd1 = create(b, OWRITE|OEXCL, DMEXCL|mode);
if(fd == -1 || fd1 == -1){
close(fd);
close(fd1);
return -1;
}
s = malloc(64*1024);
i = m = 0;
while((n = read(fd, s, sizeof s)) > 0)
for(i = 0; i != n; i += m)
if((m = write(fd1, s + i, n - i)) == -1)
goto lose;
lose:
free(s);
close(fd);
close(fd1);
if(i != m || n != 0)
return -1;
if((flags & Rtrunc) == 0)
return vremove(a);
fd = open(a, ORDWR);
if(fd == -1)
return -1;
r = -1;
if(d = dirfstat(fd)){
d->length = 0;
r = dirfwstat(fd, d);
free(d);
}
return r;
}
static int
copydir(char *a, char *b, int flags)
{
char *p, buf[Pathlen], ns[Pathlen], owd[Pathlen];
int fd, fd1, len, i, n, r;
Dir *d;
fd = open(a, OREAD);
fd1 = create(b, OWRITE|OEXCL, DMEXCL|0777);
close(fd1);
if(fd == -1 || fd1 == -1){
close(fd);
return -1;
}
/* fixup mode */
if(delivery(b))
if(d = dirfstat(fd)){
d->mode |= 0777;
dirfwstat(fd, d);
free(d);
}
getwd(owd, sizeof owd);
if(chdir(a) == -1)
return -1;
p = seprint(buf, buf + sizeof buf, "%s/", b);
len = buf + sizeof buf - p;
n = dirreadall(fd, &d);
r = 0;
for(i = 0; i < n; i++){
snprint(p, len, "%s", d[i].name);
if(d->mode & DMDIR){
snprint(ns, sizeof ns, "%s/%s", a, d[i].name);
r |= copydir(ns, buf, 0);
chdir(a);
}else
r |= copyfile(d[i].name, buf, 0);
if(r)
break;
}
free(d);
if((flags & Rtrunc) == 0)
r |= vremove(a);
chdir(owd);
return r;
}
int
rename(char *a, char *b, int flags)
{
char *e0, *e1;
int fd, r;
Dir *d;
e0 = strrchr(a, '/');
e1 = strrchr(b, '/');
if(!e0 || !e1 || !e1[1])
return -1;
if(e0 - a == e1 - b)
if(strncmp(a, b, e0 - a) == 0)
if(!delivery(a) || isdir(a)){
fd = open(a, OREAD);
if(!(d = dirfstat(fd))){
close(fd);
return -1;
}
d->name = e1 + 1;
r = dirfwstat(fd, d);
deprint("rename %s %s -> %d\n", a, b, r);
if(r != -1 && flags & Rtrunc)
close(create(a, OWRITE, d->mode));
free(d);
close(fd);
return r;
}
if(rollup(b) == -1)
return -1;
if(isdir(a))
return copydir(a, b, flags);
return copyfile(a, b, flags);
}
char*
localrename(Mailbox *mb, char *p2, int flags)
{
char *path, *msg;
int r;
static char err[2*Pathlen];
path = mb->path;
msg = "rename";
if(flags & Rtrunc)
msg = "move";
deprint("localrename %s: %s %s\n", msg, path, p2);
r = rename(path, p2, flags);
if(r == -1){
snprint(err, sizeof err, "%s: can't %s\n", path, msg);
deprint("localrename %s\n", err);
return err;
}
close(r);
return 0;
}

View File

@ -1,28 +0,0 @@
From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
Subject: Time for ISO 10646?
From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
To: Dave Crocker <dcrocker@mordor.stanford.edu>
Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
Subject: Re: RFC-HDR care and feeding
From moore@cs.utk.edu Tue Mar 28 21:58:10 CST 2006
From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
(=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
<ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
Subject: Test of new header generator
MIME-Version: 1.0
Content-type: text/plain; charset=ISO-8859-1

164
sys/src/cmd/upas/fs/seg.c Normal file
View File

@ -0,0 +1,164 @@
#include "common.h"
#include <libsec.h>
#include "dat.h"
/*
* unnatural acts with virtual memory
*/
typedef struct{
int ref;
char *va;
long sz;
}S;
static S s[15]; /* 386 only gives 4 */
static int nstab = nelem(s);
static long ssem = 1;
//static ulong thresh = 10*1024*1024;
static ulong thresh = 1024;
void*
segmalloc(ulong sz)
{
int i, j;
void *va;
if(sz < thresh)
return emalloc(sz);
semacquire(&ssem, 1);
for(i = 0; i < nstab; i++)
if(s[i].ref == 0)
goto found;
notfound:
/* errstr not informative; assume we hit seg limit */
for(j = nstab - 1; j >= i; j--)
if(s[j].ref)
break;
nstab = j;
semrelease(&ssem, 1);
return emalloc(sz);
found:
/*
* the system doesn't leave any room for expansion
*/
va = segattach(SG_CEXEC, "memory", 0, sz + sz/10 + 4096);
if(va == 0)
goto notfound;
s[i].ref++;
s[i].va = va;
s[i].sz = sz;
semrelease(&ssem, 1);
memset(va, 0, sz);
return va;
}
void
segmfree(void *va)
{
char *a;
int i;
a = va;
for(i = 0; i < nstab; i++)
if(s[i].va == a)
goto found;
free(va);
return;
found:
semacquire(&ssem, 1);
s[i].ref--;
s[i].va = 0;
s[i].sz = 0;
semrelease(&ssem, 1);
}
void*
segreallocfixup(int i, ulong sz)
{
char buf[ERRMAX];
void *va, *ova;
rerrstr(buf, sizeof buf);
if(strstr(buf, "segments overlap") == 0)
sysfatal("segibrk: %r");
va = segattach(SG_CEXEC, "memory", 0, sz);
if(va == 0)
sysfatal("segattach: %r");
ova = s[i].va;
fprint(2, "fix memcpy(%p, %p, %lud)\n", va, ova, s[i].sz);
memcpy(va, ova, s[i].sz);
s[i].va = va;
s[i].sz = sz;
segdetach(ova);
return va;
}
void*
segrealloc(void *va, ulong sz)
{
char *a;
int i;
ulong sz0;
fprint(2, "segrealloc %p %lud\n", va, sz);
if(va == 0)
return segmalloc(sz);
a = va;
for(i = 0; i < nstab; i++)
if(s[i].va == a)
goto found;
if(sz >= thresh)
if(a = segmalloc(sz)){
sz0 = msize(va);
memcpy(a, va, sz0);
fprint(2, "memset(%p, 0, %lud)\n", a + sz0, sz - sz0);
memset(a + sz0, 0, sz - sz0);
return a;
}
return realloc(va, sz);
found:
sz0 = s[i].sz;
fprint(2, "segbrk(%p, %p)\n", s[i].va, s[i].va + sz);
va = segbrk(s[i].va, s[i].va + sz);
if(va == (void*)-1 || va < end)
return segreallocfixup(i, sz);
a = va;
if(sz > sz0)
{
fprint(2, "memset(%p, 0, %lud)\n", a + sz0, sz - sz0);
memset(a + sz0, 0, sz - sz0);
}
s[i].va = va;
s[i].sz = sz;
return va;
}
void*
emalloc(ulong n)
{
void *p;
fprint(2, "emalloc %lud\n", n);
p = mallocz(n, 1);
if(!p)
sysfatal("malloc %lud: %r", n);
setmalloctag(p, getcallerpc(&n));
return p;
}
void
main(void)
{
char *p;
int i;
ulong sz;
p = 0;
for(i = 0; i < 6; i++){
sz = i*512;
p = segrealloc(p, sz);
memset(p, 0, sz);
}
segmfree(p);
exits("");
}

View File

@ -1,11 +1,10 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
static char*
skiptext(char *q)
{
while(*q!='\0' && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
while(*q != '\0' && *q != ' ' && *q != '\t' && *q != '\r' && *q != '\n')
q++;
return q;
}
@ -13,36 +12,19 @@ skiptext(char *q)
static char*
skipwhite(char *q)
{
while(*q==' ' || *q=='\t' || *q=='\r' || *q=='\n')
while(*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n')
q++;
return q;
}
static char* months[] = {
"jan", "feb", "mar", "apr",
"may", "jun", "jul", "aug",
"may", "jun", "jul", "aug",
"sep", "oct", "nov", "dec"
};
static int
strcmplwr(char *a, char *b, int n)
{
char *eb;
eb = b+n;
while(*a && *b && b<eb){
if(tolower(*a) != tolower(*b))
return 1;
a++;
b++;
}
if(b==eb)
return 0;
return *a != *b;
}
int
strtotm(char *p, Tm *tmp)
strtotm(char *p, Tm *t)
{
char *q, *r;
int j;
@ -56,58 +38,60 @@ strtotm(char *p, Tm *tmp)
tm.min = -1;
tm.year = -1;
tm.mday = -1;
for(p=skipwhite(p); *p; p=skipwhite(q)){
for(p = skipwhite(p); *p; p = skipwhite(q)){
q = skiptext(p);
/* look for time in hh:mm[:ss] */
if(r = memchr(p, ':', q-p)){
if(r = memchr(p, ':', q - p)){
tm.hour = strtol(p, 0, 10);
tm.min = strtol(r+1, 0, 10);
if(r = memchr(r+1, ':', q-(r+1)))
tm.sec = strtol(r+1, 0, 10);
tm.min = strtol(r + 1, 0, 10);
if(r = memchr(r + 1, ':', q - (r + 1)))
tm.sec = strtol(r + 1, 0, 10);
else
tm.sec = 0;
continue;
}
/* look for month */
for(j=0; j<12; j++)
if(strcmplwr(p, months[j], 3)==0){
for(j = 0; j < 12; j++)
if(cistrncmp(p, months[j], 3) == 0){
tm.mon = j;
break;
}
if(j!=12)
if(j != 12)
continue;
/* look for time zone [A-Z][A-Z]T */
if(q-p==3 && 'A' <= p[0] && p[0] <= 'Z'
&& 'A' <= p[1] && p[1] <= 'Z' && p[2] == 'T'){
strecpy(tm.zone, tm.zone+4, p);
if(q - p == 3)
if(p[0] >= 'A' && p[0] <= 'Z')
if(p[1] >= 'A' && p[1] <= 'Z')
if(p[2] == 'T'){
strecpy(tm.zone, tm.zone + 4, p);
continue;
}
if(p[0]=='+'||p[0]=='-')
if(q-p==5 && strspn(p+1, "0123456789") == 4){
delta = (((p[1]-'0')*10+p[2]-'0')*60+(p[3]-'0')*10+p[4]-'0')*60;
if(p[0] == '+'||p[0] == '-')
if(q - p == 5 && strspn(p + 1, "0123456789") == 4){
delta = (((p[1] - '0')*10 + p[2] - '0')*60 + (p[3] - '0')*10 + p[4] - '0')*60;
if(p[0] == '-')
delta = -delta;
continue;
}
if(strspn(p, "0123456789") == q-p){
if(strspn(p, "0123456789") == q - p){
j = strtol(p, nil, 10);
if(1 <= j && j <= 31)
if(j >= 1 && j <= 31)
tm.mday = j;
if(j >= 1900)
tm.year = j-1900;
tm.year = j - 1900;
continue;
}
//eprint("strtotm: garbage %.*s\n", q - p, p);
}
if(tm.mon<0 || tm.year<0
|| tm.hour<0 || tm.min<0
|| tm.mday<0)
if(tm.mon < 0 || tm.year < 0
|| tm.hour < 0 || tm.min < 0
|| tm.mday < 0)
return -1;
*tmp = *localtime(tm2sec(&tm)-delta);
*t = *localtime(tm2sec(&tm) - delta);
return 0;
}

View File

@ -1,81 +0,0 @@
#include <u.h>
#include <libc.h>
#include <ctype.h>
#include <String.h>
#include "message.h"
Message *root;
void
prindent(int i)
{
for(; i > 0; i--)
print(" ");
}
void
prstring(int indent, char *tag, String *s)
{
if(s == nil)
return;
prindent(indent+1);
print("%s %s\n", tag, s_to_c(s));
}
void
info(int indent, int mno, Message *m)
{
int i;
Message *nm;
prindent(indent);
print("%d%c %d ", mno, m->allocated?'*':' ', m->end - m->start);
if(m->unixfrom != nil)
print("uf %s ", s_to_c(m->unixfrom));
if(m->unixdate != nil)
print("ud %s ", s_to_c(m->unixdate));
print("\n");
prstring(indent, "from:", m->from822);
prstring(indent, "sender:", m->sender822);
prstring(indent, "to:", m->to822);
prstring(indent, "cc:", m->cc822);
prstring(indent, "reply-to:", m->replyto822);
prstring(indent, "subject:", m->subject822);
prstring(indent, "date:", m->date822);
prstring(indent, "filename:", m->filename);
prstring(indent, "type:", m->type);
prstring(indent, "charset:", m->charset);
i = 1;
for(nm = m->part; nm != nil; nm = nm->next){
info(indent+1, i++, nm);
}
}
void
main(int argc, char **argv)
{
char *err;
char *mboxfile;
ARGBEGIN{
}ARGEND;
if(argc > 0)
mboxfile = argv[0];
else
mboxfile = "./mbox";
root = newmessage(nil);
err = readmbox(mboxfile, &root->part);
if(err != nil){
fprint(2, "boom: %s\n", err);
exits(0);
}
info(0, 1, root);
exits(0);
}

View File

@ -0,0 +1,280 @@
#include "imap4d.h"
#include <libsec.h>
static char Ebadch[] = "can't get challenge";
static char Ecantstart[] = "can't initialize mail system: %r";
static char Ecancel[] = "client cancelled authentication";
static char Ebadau[] = "login failed";
/*
* hack to allow smtp forwarding.
* hide the peer IP address under a rock in the ratifier FS.
*/
void
enableforwarding(void)
{
char buf[64], peer[64], *p;
int fd;
ulong now;
static ulong last;
if(remote == nil)
return;
now = time(0);
if(now < last + 5*60)
return;
last = now;
fd = open("/srv/ratify", ORDWR);
if(fd < 0)
return;
if(!mount(fd, -1, "/mail/ratify", MBEFORE, "")){
close(fd);
return;
}
close(fd);
strncpy(peer, remote, sizeof peer);
peer[sizeof peer - 1] = 0;
p = strchr(peer, '!');
if(p != nil)
*p = 0;
snprint(buf, sizeof buf, "/mail/ratify/trusted/%s#32", peer);
/*
* if the address is already there and the user owns it,
* remove it and recreate it to give him a new time quanta.
*/
if(access(buf, 0) >= 0 && remove(buf) < 0)
return;
fd = create(buf, OREAD, 0666);
if(fd >= 0)
close(fd);
}
void
setupuser(AuthInfo *ai)
{
int pid;
Waitmsg *w;
if(ai){
strecpy(username, username + sizeof username, ai->cuid);
if(auth_chuid(ai, nil) == -1)
bye("user auth failed: %r");
auth_freeAI(ai);
}else
strecpy(username, username + sizeof username, getuser());
if(strcmp(username, "none") == 0 || newns(username, 0) == -1)
bye("user login failed: %r");
if(binupas){
if(bind(binupas, "/bin/upas", MREPL) > 0)
ilog("bound %s on /bin/upas", binupas);
else
bye("bind %s failed: %r", binupas);
}
/*
* hack to allow access to outgoing smtp forwarding
*/
enableforwarding();
snprint(mboxdir, Pathlen, "/mail/box/%s", username);
if(mychdir(mboxdir) < 0)
bye("can't open user's mailbox");
switch(pid = fork()){
case -1:
bye(Ecantstart);
break;
case 0:
if(!strstr(argv0, "8.out"))
execl("/bin/upas/fs", "upas/fs", "-np", nil);
else{
ilog("using /sys/src/cmd/upas/fs/8.out");
execl("/sys/src/cmd/upas/fs/8.out", "upas/fs", "-np", nil);
}
_exits(0);
break;
default:
break;
}
if((w = wait()) == nil || w->pid != pid || w->msg[0] != 0)
bye(Ecantstart);
free(w);
}
static char*
authread(int *len)
{
char *t;
int n;
t = Brdline(&bin, '\n');
n = Blinelen(&bin);
if(n < 2)
return nil;
n--;
if(t[n-1] == '\r')
n--;
t[n] = 0;
if(n == 0 || strcmp(t, "*") == 0)
return nil;
*len = n;
return t;
}
static char*
authresp(void)
{
char *s, *t;
int n;
t = authread(&n);
if(t == nil)
return nil;
s = binalloc(&parsebin, n + 1, 1);
n = dec64((uchar*)s, n, t, n);
s[n] = 0;
return s;
}
/*
* rfc 2195 cram-md5 authentication
*/
char*
cramauth(void)
{
char *s, *t;
int n;
AuthInfo *ai;
Chalstate *cs;
if((cs = auth_challenge("proto=cram role=server")) == nil)
return Ebadch;
n = cs->nchal;
s = binalloc(&parsebin, n * 2, 0);
n = enc64(s, n * 2, (uchar*)cs->chal, n);
Bprint(&bout, "+ ");
Bwrite(&bout, s, n);
Bprint(&bout, "\r\n");
if(Bflush(&bout) < 0)
writeerr();
s = authresp();
if(s == nil)
return Ecancel;
t = strchr(s, ' ');
if(t == nil)
return Ebadch;
*t++ = 0;
strncpy(username, s, Userlen);
username[Userlen - 1] = 0;
cs->user = username;
cs->resp = t;
cs->nresp = strlen(t);
if((ai = auth_response(cs)) == nil)
return Ebadau;
auth_freechal(cs);
setupuser(ai);
return nil;
}
char*
crauth(char *u, char *p)
{
char response[64];
AuthInfo *ai;
static char nchall[64];
static Chalstate *ch;
again:
if(ch == nil){
if(!(ch = auth_challenge("proto=p9cr role=server user=%q", u)))
return Ebadch;
snprint(nchall, 64, " encrypt challenge: %s", ch->chal);
return nchall;
} else {
strncpy(response, p, 64);
ch->resp = response;
ch->nresp = strlen(response);
ai = auth_response(ch);
auth_freechal(ch);
ch = nil;
if(ai == nil)
goto again;
setupuser(ai);
return nil;
}
}
char*
passauth(char *u, char *secret)
{
char response[2*MD5dlen + 1];
uchar digest[MD5dlen];
int i;
AuthInfo *ai;
Chalstate *cs;
if((cs = auth_challenge("proto=cram role=server")) == nil)
return Ebadch;
hmac_md5((uchar*)cs->chal, strlen(cs->chal),
(uchar*)secret, strlen(secret), digest, nil);
for(i = 0; i < MD5dlen; i++)
snprint(response + 2*i, sizeof response - 2*i, "%2.2ux", digest[i]);
cs->user = u;
cs->resp = response;
cs->nresp = strlen(response);
ai = auth_response(cs);
if(ai == nil)
return Ebadau;
auth_freechal(cs);
setupuser(ai);
return nil;
}
static int
niltokenize(char *buf, int n, char **f, int nelemf)
{
int i, nf;
f[0] = buf;
nf = 1;
for(i = 0; i < n - 1; i++)
if(buf[i] == 0){
f[nf++] = buf + i + 1;
if(nf == nelemf)
break;
}
return nf;
}
char*
plainauth(char *ch)
{
char buf[256*3 + 2], *f[4];
int n, nf;
if(ch == nil){
Bprint(&bout, "+ \r\n");
if(Bflush(&bout) < 0)
writeerr();
ch = authread(&n);
}
if(ch == nil || strlen(ch) == 0)
return Ecancel;
n = dec64((uchar*)buf, sizeof buf, ch, strlen(ch));
nf = niltokenize(buf, n, f, nelem(f));
if(nf != 3)
return Ebadau;
return passauth(f[1], f[2]);
}

View File

@ -0,0 +1,37 @@
status
u acl [rfc2086][rfc4314]
u annotate-experiment-1 [rfc5257]
u binary [rfc3516]
catenate [rfc4469]
children [rfc3348]
u compress=deflate [rfc4978]
condstore [rfc4551]
context=search [rfc5267]
context=sort [rfc5267]
u convert [rfc-ietf-lemonade-convert-20.txt]
enable [rfc5161]
* esearch [rfc4731]
esort [rfc5267]
u i18nlevel=1 [rfc5255]
u i18nlevel=2 [rfc5255]
u id [rfc2971]
y idle [rfc2177]
u language [rfc5255]
literal+ [rfc2088]
login-referrals [rfc2221]
y logindisabled [rfc2595][rfc3501]
mailbox-referrals [rfc2193]
multiappend [rfc3502]
y namespace [rfc2342]
qresync [rfc5162]
y quota [rfc2087]
u rights= [rfc4314]
sasl-ir [rfc4959]
* searchres [rfc5182]
* sort [rfc5256]
starttls [rfc2595][rfc3501]
n thread [rfc5256]
y uidplus [rfc4315]
n unselect [rfc3691]
u urlauth [rfc4467]
within [rfc5032]

Some files were not shown because too many files have changed in this diff Show More