merging erik quanstros nupas
parent
8177d20fb2
commit
963cfc9a6f
|
@ -1,4 +0,0 @@
|
|||
Mail stored
|
||||
plumb /mail/box/$user/names
|
||||
mail -'x' someaddress
|
||||
mkbox /mail/box/$user/new_box
|
|
@ -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
|
||||
}
|
|
@ -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.
|
|
@ -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}
|
||||
|
|
|
@ -40,6 +40,7 @@ ALL=\
|
|||
spin\
|
||||
port\
|
||||
colophon\
|
||||
nupas/nupas\
|
||||
|
||||
ALLPS=${ALL:%=%.ps}
|
||||
HTML=${ALL:%=%.html} release3.html release4.html
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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),
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
|
@ -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
|
@ -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"
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -88,7 +88,7 @@ error(char *fmt, ...)
|
|||
va_end(arg);
|
||||
fmtprint(&f, "\n");
|
||||
fmtfdflush(&f);
|
||||
threadexitsall(buf);
|
||||
threadexitsall(fmt);
|
||||
}
|
||||
|
||||
void
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/rc
|
||||
exec /mail/lib/isspam.rc $*
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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=]
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/rc
|
||||
exec /mail/lib/spam.rc $*
|
|
@ -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=]
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/rc
|
||||
exec /mail/lib/unspam.rc $*
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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]
|
||||
|
|
|
@ -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);
|
||||
//}
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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("");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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("");
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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,0 +1,4 @@
|
|||
From: erik quanstrom <quanstro@coraid.com>
|
||||
Subject: 1 testing
|
||||
|
||||
testing
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Binary file not shown.
|
@ -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("");
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
Binary file not shown.
|
@ -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("");
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -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("");
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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("");
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
@ -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("");
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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]);
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue