Browse Source

get rid of imap4d

We may someday have a MTA but it's not likely to be this old piece of cruft.

Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Ronald G. Minnich 8 years ago
parent
commit
52ed560476

+ 0 - 0
sys/log/imap4d


+ 0 - 168
sys/man/8/pop3

@@ -1,168 +0,0 @@
-.TH POP3 8
-.SH NAME
-pop3, imap4d \- Internet mail servers
-.SH SYNOPSIS
-.PP
-.B upas/pop3
-[
-.B -d
-.I debugfile
-][
-.B -a
-.I mailbox
-][
-.B -r
-.I peeraddr
-][
-.B -t
-.I tlscertfile
-][
-.B -p
-]
-.PP
-.B ip/imap4d
-.RB [ -acpv ]
-.RB [ -d
-.IR smtpdomain ]
-.RB [ -s
-.IR servername ]
-.SH DESCRIPTION
-These programs support remote access to mail across the Internet.
-All expect the network connection to be standard input, output, and error.
-They are normally started from scripts in
-.B /rc/bin/service
-(see
-.IR listen (8)).
-.PP
-.I Pop3
-provides access to a user's mailboxes via the POP3 protocol.
-The options are:
-.TP 4
-.B -d
-create
-.I debugfile
-and write debugging output to it
-.TP
-.B -a
-causes pop3 to assume that it it already authenticated
-and to read
-.I mailbox
-immediately
-.TP
-.B -r
-causes
-.I pop3
-to create the file
-.B /mail/ratify/trusted/\fIpeeraddr\fP#32
-to allow subsequent SMTP sessions from that
-address.  See
-.IR ratfs (4)
-for details.
-.TP
-.B -t
-get the local TLS certificate from the file
-.IR tlscertfile .
-.TP
-.B -p
-allow passwords in the clear for authenticating the connection
-.PP
-.I Imap4d
-provides access to a user's mailboxes via the IMAP4rev1 protocol.
-Only files rooted in
-.BI /mail/box/ username /
-are accessible.
-The list of subscribed mailboxes is contained in
-.BI /mail/box/ username /imap.subscribed ,
-and initially contains only
-.BR INBOX ,
-IMAP's name for the user's mailbox.
-A shadow file,
-.IB mailbox .imp ,
-is created for each mailbox examined.
-.PP
-.IR Imap4d 's
-options are:
-.TP 4
-.B a
-Assume the user is already authenticated.
-By default, the user must authenticate using
-CRAM-MD5 or
-.IR securenet (8)
-challenge/response authentication.
-.TP
-.B c
-Allow plan 9 challenge response authentication.
-.TP
-.B p
-Allow login authentication.  This option
-should only be enabled for servers using
-an encrypted connection, such as SSL,
-and when enabled, all non-encrypted connections should be disallowed.
-.I Imap4d
-does not enforce this policy.
-.TP
-.B v
-Turn on verbose output to the debug file.
-.TP
-.B s
-The server's name.
-If none is provided,
-.B cs
-(see
-.IR ndb (8))
-is queried or
-.B /env/sysname
-is used.
-.TP
-.B d
-The local mail domain.
-Defaults to the server
-.B /env/site
-in the mail server's domain.
-.PP
-For both
-.I imap4d
-and
-.IR pop3 ,
-the password used to authenticate the connection is the APOP
-secret held by
-.IR keyfs (4)
-running on the authentication server.
-.SH FILES
-.TF /mail/box/username/imap.subscrib
-.TP
-.B /sys/log/imap4d
-debugging output
-.TP
-.BI /mail/box/ username / mailbox
-.TP
-.BI /mail/box/ username / mailbox .imp
-.TP
-.BI /mail/box/ username /imap.subscribed
-.SH SOURCE
-.B /sys/src/cmd/upas/pop3
-.br
-.B /sys/src/cmd/ip/imap4d
-.SH "SEE ALSO"
-.IR aliasmail (8),
-.IR faces (1),
-.IR filter (1),
-.IR mail (1),
-.IR marshal (1),
-.IR mlmgr (1),
-.IR nedmail (1),
-.IR qer (8),
-.IR rewrite (6),
-.IR send (8),
-.IR upasfs (4)
-.SH BUGS
-Usually messages flagged for deletion with
-.B DELE
-are not actually deleted until the client sends a
-.B QUIT
-command to end the conversation.
-.I Pop3
-implements a non-standard command
-.B SYNC
-that deletes messages flagged for deletion
-without ending the conversation.

+ 0 - 285
sys/man/8/ppp

@@ -1,285 +0,0 @@
-.TH PPP 8
-.SH NAME
-ppp, pppoe, pptp, pptpd \- point-to-point protocol
-.SH SYNOPSIS
-.B ip/ppp
-[
-.B -CPSacdfu
-] [
-.B -b
-.I baud
-] [
-.B -k
-.I keyspec
-] [
-.B -m
-.I mtu
-] [
-.B -M
-.I chatfile
-] [
-.B -p
-.I dev
-] [
-.B -x
-.I netmntpt
-] [
-.B -t
-.I modemcmd
-] [
-.I local
-[
-.I remote
-] ]
-.PP
-.B ip/pppoe
-[
-.B -Pd
-]
-[
-.B -A
-.I acname
-]
-[
-.B -S
-.I srvname
-]
-[
-.B -k
-.I keyspec
-]
-[
-.B -m
-.I mtu
-]
-[
-.B -x
-.I pppnetmntpt
-]
-[
-.I ether
-]
-.PP
-.B ip/pptp
-[
-.B -dP
-]
-[
-.B -k
-.I keyspec
-]
-[
-.B -w
-.I window
-]
-[
-.B -x
-.I pppnetmntpt
-]
-.I server
-.PP
-.B ip/pptpd
-[
-.B -d
-] [
-.B -p
-.I pppnetmtpt
-] [
-.B -w
-.I window
-] [
-.B -D
-.I fraction
-]
-.I tcp-dir
-.SH DESCRIPTION
-The Point-to-Point Protocol is used to encapsulate Internet Protocol packets
-in IPv4 packets
-for transfer over serial lines or other protocol connections.
-.I Ppp
-can run either as a client or, with the
-.I \-S
-option, as a server.  The only differences between a client and a server is
-that the server will not believe any local address the client tries to
-supply it and that the server always initiates the authentication of the
-client.
-.PP
-With no option,
-.I ppp
-communicates with the remote system via standard input and output.
-This is useful if a program wants to use
-.I ppp
-in a communications stream.  However, the normal mode is to
-specify a communications device, usually a serial line with a modem.
-.PP
-.I Ppp
-supports the following options:
-.TP 3
-.B a
-as server, don't request authentication from the client
-.TP
-.B b
-set the baud rate on the communications device
-.TP
-.B c
-disallow packet compression
-.TP
-.B C
-disallow IP header compression
-.TP
-.B f
-make PPP add HDLC framing.  This is necessary when using
-PPP over a serial line or a TCP connection
-.TP
-.B k
-add
-.I keyspec
-to the
-.IR factotum (4)
-key pattern when looking for a user name and password
-for authentication; the default key pattern is
-.B "proto=pass" "service=ppp"
-.TP
-.B m
-set the maximum transfer unit (default 1450)
-.TP
-.B M
-chat with the modem as specified in the chat file.  Each line in
-the chat file contains a string that is transmitted to the modem
-and the response expected (e.g. 'AT' 'OK')
-.TP
-.B P
-use this as the primary IP interface; set the default
-route through this interface and write its configuration
-to
-.B /net/ndb
-.TP
-.B p
-communicate over
-.I dev
-instead of standard I/O
-.TP
-.B S
-run as a server
-.TP
-.B t
-before starting the PPP protocol, write
-.I modemcmd
-to the device
-.TP
-.B u
-before starting the PPP protocol with the remote end, shuttle
-bytes between the device and standard I/O until an EOF on standard
-input.  This allows a user to start
-.I ppp
-and then type commands at a modem before
-.I ppp
-takes over
-.TP
-.B x
-use the IP stack mounted at
-.I netmntpt
-.PD
-.PP
-If both the
-.I local
-and
-.I remote
-addresses are specified, don't ask the other end for either
-or believe it if it supplies one.  If either is missing, get
-it from the remote end.
-.PP
-.I Pppoe
-is a PPP over ethernet (PPPoE) client.
-It invokes
-.I ppp
-to start a PPP conversation which is
-tunneled in PPPoE packets on 
-the ethernet device mounted at 
-.I etherdir
-(default
-.BR /net/ether0 ).
-The 
-.IR pppoe -specific
-options are:
-.TP 3
-.B A
-insist on an access concentrator named
-.I acname
-during PPPoE discovery
-.TP
-.B d
-write debugging output to standard error,
-and pass
-.B -d
-to 
-.I ppp
-.TP
-.B S
-insist on a service named
-.I srvname
-during PPPoE discovery
-.PD
-.PP
-The other options are relayed to 
-.IR ppp .
-.PP
-.I Pptp
-is a client for a PPTP encrypted tunnel.
-.I Server
-is the name of the server to dial.
-.I Pptp
-takes the same options as
-.IR pppoe ,
-except for the lack of a
-.B -m
-option and the addition of a
-.B -w
-option.
-The
-.B -w
-option specifies the local send window size
-(default 16) in packets.
-.PP
-.I Pptpd
-is the server side of a PPTP encrypted tunnel.
-.I Tcpdir
-is the directory of a TCP connection to the client.
-The TCP connection is used to control the tunnel while
-packets are sent back and forth using PPP inside of
-GRE packets.
-The options are:
-.TP 3
-.B d
-write debugging output to standard error.
-.TP
-.B D
-drop
-.I fraction
-of the received packets.  This is used for testing.
-.TP
-.B p
-use the IP stack mounted at
-.I pppnetmtpt
-to terminate the PPP connection.
-.TP
-.B w
-set the receive window to
-.IR window .
-.PD
-.SH SOURCE
-.B /sys/src/cmd/ip/ppp
-.br
-.B /sys/src/cmd/ip/pptpd.c
-.br
-.B /sys/src/cmd/ip/pppoe.c
-.SH SEE ALSO
-.I gre
-in
-.IR ip (3)
-.SH BUGS
-.I Ppp
-should use factotum to execute
-the client side of the challenge-reponse
-protocol, but instead it reads a password
-from factotum and runs the protocol itself.

+ 0 - 37
sys/man/8/tlssrv

@@ -95,43 +95,6 @@ use these tools and
 to provide TLS network tunnels, allowing legacy
 application to take advantage of TLS encryption.
 .SH EXAMPLES
-Listen for TLS-encrypted IMAP by creating a server certificate
-.B /sys/lib/tls/imap.pem
-and a listener script
-.B /bin/service.auth/tcp993
-containing:
-.IP
-.EX
-#!/bin/rc
-exec tlssrv -c/sys/lib/tls/imap.pem -limap4d -r`{cat $3/remote} \e
-    /bin/ip/imap4d -p -dyourdomain -r`{cat $3/remote} \e
-    >[2]/sys/log/imap4d
-.EE
-.PP
-Interact with the server, putting the appropriate hash into
-.B /sys/lib/tls/mail
-and running:
-.IP
-.EX
-tlsclient -t /sys/lib/tls/mail tcp!server!imaps
-.EE
-.PP
-Create a TLS-encrypted VNC connection from a client on
-.B kremvax
-to a server on
-.BR moscvax :
-.IP
-.EX
-mosc% vncs -d :3
-mosc% tlssrvtunnel tcp!moscvax!5903 tcp!*!12345 \e
-        /usr/you/lib/cert.pem
-krem% tlsclienttunnel tcp!moscvax!12345 tcp!*!5905 \e
-        /usr/you/lib/cert.thumb
-krem% vncv kremvax:5
-.EE
-.LP
-(The port numbers passed to the VNC tools are offset by 5900 from the
-actual TCP port numbers.)
 .SH FILES
 .TP
 .B /sys/lib/tls

+ 0 - 201
sys/src/cmd/ip/imap4d/auth.c

@@ -1,201 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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 uint32_t last;
-	uint32_t 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, "", 'M')){
-		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((uint8_t*)s, n, t, n);
-	s[n] = '\0';
-	return s;
-}
-
-/*
- * rfc 2195 cram-md5 authentication
- */
-char*
-cramauth(void)
-{
-	AuthInfo *ai;
-	Chalstate *cs;
-	char *s, *t;
-	int n;
-
-	if((cs = auth_challenge("proto=cram role=server")) == nil)
-		return "couldn't get cram challenge";
-
-	n = cs->nchal;
-	s = binalloc(&parseBin, n * 2, 0);
-	n = enc64(s, n * 2, (uint8_t*)cs->chal, n);
-	Bprint(&bout, "+ ");
-	Bwrite(&bout, s, n);
-	Bprint(&bout, "\r\n");
-	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;
-	uint8_t digest[MD5dlen];
-	char response[2*MD5dlen+1];
-	int i;
-
-	if((cs = auth_challenge("proto=cram role=server")) == nil)
-		return nil;
-
-	hmac_md5((uint8_t*)cs->chal, strlen(cs->chal),
-		(uint8_t*)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 = user;
-	cs->resp = response;
-	cs->nresp = strlen(response);
-	ai = auth_response(cs);
-	auth_freechal(cs);
-	return ai;
-}

+ 0 - 273
sys/src/cmd/ip/imap4d/copy.c

@@ -1,273 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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,
-			  int32_t n);
-static int	saveb(int fd, DigestState *dstate, char *buf, int nr,
-			int nw);
-static int32_t	appSpool(Biobuf *bout, Biobuf *bin, int32_t 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;
-	int64_t 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, int32_t 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 int32_t
-appSpool(Biobuf *bout, Biobuf *bin, int32_t 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,
-	int32_t n)
-{
-	DigestState *dstate;
-	MbLock *ml;
-	uint8_t 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((uint8_t*)buf, nr, nil, dstate);
-	if(write(fd, buf, nw) != nw)
-		return 0;
-	return 1;
-}

+ 0 - 53
sys/src/cmd/ip/imap4d/csquery.c

@@ -1,53 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include "imap4d.h"
-
-/*
- *  query the connection server
- */
-char*
-csquery(char *attr, char *val, char *rattr)
-{
-	char token[64+4];
-	char buf[256], *p, *sp;
-	int fd, n;
-
-	if(val == nil || val[0] == 0)
-		return nil;
-	fd = open("/net/cs", ORDWR);
-	if(fd < 0)
-		return nil;
-	fprint(fd, "!%s=%s", attr, val);
-	seek(fd, 0, 0);
-	snprint(token, sizeof(token), "%s=", rattr);
-	for(;;){
-		n = read(fd, buf, sizeof(buf)-1);
-		if(n <= 0)
-			break;
-		buf[n] = 0;
-		p = strstr(buf, token);
-		if(p != nil && (p == buf || *(p-1) == 0)){
-			close(fd);
-			sp = strchr(p, ' ');
-			if(sp)
-				*sp = 0;
-			p = strchr(p, '=');
-			if(p == nil)
-				return nil;
-			return strdup(p+1);
-		}
-	}
-	close(fd);
-	return nil;
-}

+ 0 - 317
sys/src/cmd/ip/imap4d/date.c

@@ -1,317 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include "imap4d.h"
-
-char *
-wdayname[7] =
-{
-	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-};
-
-char *
-monname[12] =
-{
-	"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-static void	time2tm(Tm *tm, char *s);
-static void	zone2tm(Tm *tm, char *s);
-static int	dateindex(char *d, char **tab, int n);
-
-int
-rfc822date(char *s, int n, Tm *tm)
-{
-	char *plus;
-	int m;
-
-	plus = "+";
-	if(tm->tzoff < 0)
-		plus = "";
-	m = 0;
-	if(0 <= tm->wday && tm->wday < 7){
-		m = snprint(s, n, "%s, ", wdayname[tm->wday]);
-		if(m < 0)
-			return m;
-	}
-	return snprint(s+m, n-m, "%.2d %s %.4d %.2d:%.2d:%.2d %s%.4d",
-		tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec,
-		plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
-}
-
-int
-imap4date(char *s, int n, Tm *tm)
-{
-	char *plus;
-
-	plus = "+";
-	if(tm->tzoff < 0)
-		plus = "";
-	return snprint(s, n, "%2d-%s-%.4d %2.2d:%2.2d:%2.2d %s%4.4d",
-		tm->mday, monname[tm->mon], tm->year+1900, tm->hour, tm->min, tm->sec, plus, (tm->tzoff/3600)*100 + (tm->tzoff/60)%60);
-}
-
-int
-imap4Date(Tm *tm, char *date)
-{
-	char *flds[4];
-
-	if(getfields(date, flds, 3, 0, "-") != 3)
-		return 0;
-
-	tm->mday = strtol(flds[0], nil, 10);
-	tm->mon = dateindex(flds[1], monname, 12);
-	tm->year = strtol(flds[2], nil, 10) - 1900;
-	return 1;
-}
-
-/*
- * parse imap4 dates
- */
-uint32_t
-imap4DateTime(char *date)
-{
-	Tm tm;
-	char *flds[4], *sflds[4];
-	uint32_t t;
-
-	if(getfields(date, flds, 4, 0, " ") != 3)
-		return ~0;
-
-	if(!imap4Date(&tm, flds[0]))
-		return ~0;
-
-	if(getfields(flds[1], sflds, 3, 0, ":") != 3)
-		return ~0;
-
-	tm.hour = strtol(sflds[0], nil, 10);
-	tm.min = strtol(sflds[1], nil, 10);
-	tm.sec = strtol(sflds[2], nil, 10);
-
-	strcpy(tm.zone, "GMT");
-	tm.yday = 0;
-	t = tm2sec(&tm);
-	zone2tm(&tm, flds[2]);
-	t -= tm.tzoff;
-	return t;
-}
-
-/*
- * parse dates of formats
- * [Wkd[,]] DD Mon YYYY HH:MM:SS zone
- * [Wkd] Mon ( D|DD) HH:MM:SS zone YYYY
- * plus anything similar
- * return nil for a failure
- */
-Tm*
-date2tm(Tm *tm, char *date)
-{
-	Tm gmt, *atm;
-	char *flds[7], *s, dstr[64];
-	int n;
-
-	/*
-	 * default date is Thu Jan  1 00:00:00 GMT 1970
-	 */
-	tm->wday = 4;
-	tm->mday = 1;
-	tm->mon = 1;
-	tm->hour = 0;
-	tm->min = 0;
-	tm->sec = 0;
-	tm->year = 70;
-	strcpy(tm->zone, "GMT");
-	tm->tzoff = 0;
-
-	strncpy(dstr, date, sizeof(dstr));
-	dstr[sizeof(dstr)-1] = '\0';
-	n = tokenize(dstr, flds, 7);
-	if(n != 6 && n != 5)
-		return nil;
-
-	if(n == 5){
-		for(n = 5; n >= 1; n--)
-			flds[n] = flds[n - 1];
-		n = 5;
-	}else{
-		/*
-		 * Wday[,]
-		 */
-		s = strchr(flds[0], ',');
-		if(s != nil)
-			*s = '\0';
-		tm->wday = dateindex(flds[0], wdayname, 7);
-		if(tm->wday < 0)
-			return nil;
-	}
-
-	/*
-	 * check for the two major formats:
-	 * Month first or day first
-	 */
-	tm->mon = dateindex(flds[1], monname, 12);
-	if(tm->mon >= 0){
-		tm->mday = strtoul(flds[2], nil, 10);
-		time2tm(tm, flds[3]);
-		zone2tm(tm, flds[4]);
-		tm->year = strtoul(flds[5], nil, 10);
-		if(strlen(flds[5]) > 2)
-			tm->year -= 1900;
-	}else{
-		tm->mday = strtoul(flds[1], nil, 10);
-		tm->mon = dateindex(flds[2], monname, 12);
-		tm->year = strtoul(flds[3], nil, 10);
-		if(strlen(flds[3]) > 2)
-			tm->year -= 1900;
-		time2tm(tm, flds[4]);
-		zone2tm(tm, flds[5]);
-	}
-
-	if(n == 5){
-		gmt = *tm;
-		strncpy(gmt.zone, "", 4);
-		gmt.tzoff = 0;
-		atm = gmtime(tm2sec(&gmt));
-		tm->wday = atm->wday;
-	}else{
-		/*
-		 * Wday[,]
-		 */
-		s = strchr(flds[0], ',');
-		if(s != nil)
-			*s = '\0';
-		tm->wday = dateindex(flds[0], wdayname, 7);
-		if(tm->wday < 0)
-			return nil;
-	}
-	return tm;
-}
-
-/*
- * zone	: [A-Za-z][A-Za-z][A-Za-z]	some time zone names
- *	| [A-IK-Z]			military time; rfc1123 says the rfc822 spec is wrong.
- *	| "UT"				universal time
- *	| [+-][0-9][0-9][0-9][0-9]
- * zones is the rfc-822 list of time zone names
- */
-static NamedInt zones[] =
-{
-	{"A",	-1 * 3600},
-	{"B",	-2 * 3600},
-	{"C",	-3 * 3600},
-	{"CDT", -5 * 3600},
-	{"CST", -6 * 3600},
-	{"D",	-4 * 3600},
-	{"E",	-5 * 3600},
-	{"EDT", -4 * 3600},
-	{"EST", -5 * 3600},
-	{"F",	-6 * 3600},
-	{"G",	-7 * 3600},
-	{"GMT", 0},
-	{"H",	-8 * 3600},
-	{"I",	-9 * 3600},
-	{"K",	-10 * 3600},
-	{"L",	-11 * 3600},
-	{"M",	-12 * 3600},
-	{"MDT", -6 * 3600},
-	{"MST", -7 * 3600},
-	{"N",	+1 * 3600},
-	{"O",	+2 * 3600},
-	{"P",	+3 * 3600},
-	{"PDT", -7 * 3600},
-	{"PST", -8 * 3600},
-	{"Q",	+4 * 3600},
-	{"R",	+5 * 3600},
-	{"S",	+6 * 3600},
-	{"T",	+7 * 3600},
-	{"U",	+8 * 3600},
-	{"UT",	0},
-	{"V",	+9 * 3600},
-	{"W",	+10 * 3600},
-	{"X",	+11 * 3600},
-	{"Y",	+12 * 3600},
-	{"Z",	0},
-	{nil,	0}
-};
-
-static void
-zone2tm(Tm *tm, char *s)
-{
-	Tm aux, *atm;
-	int i;
-
-	if(*s == '+' || *s == '-'){
-		i = strtol(s, &s, 10);
-		tm->tzoff = (i / 100) * 3600 + i % 100;
-		strncpy(tm->zone, "", 4);
-		return;
-	}
-
-	/*
-	 * look it up in the standard rfc822 table
-	 */
-	strncpy(tm->zone, s, 3);
-	tm->zone[3] = '\0';
-	tm->tzoff = 0;
-	for(i = 0; zones[i].name != nil; i++){
-		if(cistrcmp(zones[i].name, s) == 0){
-			tm->tzoff = zones[i].v;
-			return;
-		}
-	}
-
-	/*
-	 * one last try: look it up in the current local timezone
-	 * probe a couple of times to get daylight/standard time change.
-	 */
-	aux = *tm;
-	memset(aux.zone, 0, 4);
-	aux.hour--;
-	for(i = 0; i < 2; i++){
-		atm = localtime(tm2sec(&aux));
-		if(cistrcmp(tm->zone, atm->zone) == 0){
-			tm->tzoff = atm->tzoff;
-			return;
-		}
-		aux.hour++;
-	}
-
-	strncpy(tm->zone, "GMT", 4);
-	tm->tzoff = 0;
-}
-
-/*
- * hh[:mm[:ss]]
- */
-static void
-time2tm(Tm *tm, char *s)
-{
-	tm->hour = strtoul(s, &s, 10);
-	if(*s++ != ':')
-		return;
-	tm->min = strtoul(s, &s, 10);
-	if(*s++ != ':')
-		return;
-	tm->sec = strtoul(s, &s, 10);
-}
-
-static int
-dateindex(char *d, char **tab, int n)
-{
-	int i;
-
-	for(i = 0; i < n; i++)
-		if(cistrcmp(d, tab[i]) == 0)
-			return i;
-	return -1;
-}

+ 0 - 117
sys/src/cmd/ip/imap4d/debug.c

@@ -1,117 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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;
-	uint32_t 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);
-}

+ 0 - 634
sys/src/cmd/ip/imap4d/fetch.c

@@ -1,634 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include "imap4d.h"
-
-char *fetchPartNames[FPMax] =
-{
-	"",
-	"HEADER",
-	"HEADER.FIELDS",
-	"HEADER.FIELDS.NOT",
-	"MIME",
-	"TEXT",
-};
-
-/*
- * implicitly set the \seen flag.  done in a separate pass
- * so the .imp file doesn't need to be open while the
- * messages are sent to the client.
- */
-int
-fetchSeen(Box *box, Msg *m, int uids, void *vf)
-{
-	Fetch *f;
-
-	USED(uids);
-
-	if(m->expunged)
-		return uids;
-	for(f = vf; f != nil; f = f->next){
-		switch(f->op){
-		case FRfc822:
-		case FRfc822Text:
-		case FBodySect:
-			msgSeen(box, m);
-			goto breakout;
-		}
-	}
-breakout:
-
-	return 1;
-}
-
-/*
- * fetch messages
- *
- * imap4 body[] requestes get translated to upas/fs files as follows
- *	body[id.header] == id/rawheader file + extra \r\n
- *	body[id.text] == id/rawbody
- *	body[id.mime] == id/mimeheader + extra \r\n
- *	body[id] === body[id.header] + body[id.text]
-*/
-int
-fetchMsg(Box *b, Msg *m, int uids, void *vf)
-{
-	Tm tm;
-	Fetch *f;
-	char *sep;
-	int todo;
-
-	if(m->expunged)
-		return uids;
-
-	todo = 0;
-	for(f = vf; f != nil; f = f->next){
-		switch(f->op){
-		case FFlags:
-			todo = 1;
-			break;
-		case FUid:
-			todo = 1;
-			break;
-		case FInternalDate:
-		case FEnvelope:
-		case FRfc822:
-		case FRfc822Head:
-		case FRfc822Size:
-		case FRfc822Text:
-		case FBodySect:
-		case FBodyPeek:
-		case FBody:
-		case FBodyStruct:
-			todo = 1;
-			if(!msgStruct(m, 1)){
-				msgDead(m);
-				return uids;
-			}
-			break;
-		default:
-			bye("bad implementation of fetch");
-			return 0;
-		}
-	}
-
-	if(m->expunged)
-		return uids;
-	if(!todo)
-		return 1;
-
-	/*
-	 * note: it is allowed to send back the responses one at a time
-	 * rather than all together.  this is exploited to send flags elsewhere.
-	 */
-	Bprint(&bout, "* %lud FETCH (", m->seq);
-	sep = "";
-	if(uids){
-		Bprint(&bout, "UID %lud", m->uid);
-		sep = " ";
-	}
-	for(f = vf; f != nil; f = f->next){
-		switch(f->op){
-		default:
-			bye("bad implementation of fetch");
-			break;
-		case FFlags:
-			Bprint(&bout, "%sFLAGS (", sep);
-			writeFlags(&bout, m, 1);
-			Bprint(&bout, ")");
-			break;
-		case FUid:
-			if(uids)
-				continue;
-			Bprint(&bout, "%sUID %lud", sep, m->uid);
-			break;
-		case FEnvelope:
-			Bprint(&bout, "%sENVELOPE ", sep);
-			fetchEnvelope(m);
-			break;
-		case FInternalDate:
-			Bprint(&bout, "%sINTERNALDATE ", sep);
-			Bimapdate(&bout, date2tm(&tm, m->unixDate));
-			break;
-		case FBody:
-			Bprint(&bout, "%sBODY ", sep);
-			fetchBodyStruct(m, &m->head, 0);
-			break;
-		case FBodyStruct:
-			Bprint(&bout, "%sBODYSTRUCTURE ", sep);
-			fetchBodyStruct(m, &m->head, 1);
-			break;
-		case FRfc822Size:
-			Bprint(&bout, "%sRFC822.SIZE %lud", sep, msgSize(m));
-			break;
-		case FRfc822:
-			f->part = FPAll;
-			Bprint(&bout, "%sRFC822", sep);
-			fetchBody(m, f);
-			break;
-		case FRfc822Head:
-			f->part = FPHead;
-			Bprint(&bout, "%sRFC822.HEADER", sep);
-			fetchBody(m, f);
-			break;
-		case FRfc822Text:
-			f->part = FPText;
-			Bprint(&bout, "%sRFC822.TEXT", sep);
-			fetchBody(m, f);
-			break;
-		case FBodySect:
-		case FBodyPeek:
-			Bprint(&bout, "%sBODY", sep);
-			fetchBody(fetchSect(m, f), f);
-			break;
-		}
-		sep = " ";
-	}
-	Bprint(&bout, ")\r\n");
-
-	return 1;
-}
-
-/*
- * print out section, part, headers;
- * find and return message section
- */
-Msg *
-fetchSect(Msg *m, Fetch *f)
-{
-	Bputc(&bout, '[');
-	BNList(&bout, f->sect, ".");
-	if(f->part != FPAll){
-		if(f->sect != nil)
-			Bputc(&bout, '.');
-		Bprint(&bout, "%s", fetchPartNames[f->part]);
-		if(f->hdrs != nil){
-			Bprint(&bout, " (");
-			BSList(&bout, f->hdrs, " ");
-			Bputc(&bout, ')');
-		}
-	}
-	Bprint(&bout, "]");
-	return findMsgSect(m, f->sect);
-}
-
-/*
- * actually return the body pieces
- */
-void
-fetchBody(Msg *m, Fetch *f)
-{
-	Pair p;
-	char *s, *t, *e, buf[BufSize + 2];
-	uint32_t n, start, stop, pos;
-	int fd, nn;
-
-	if(m == nil){
-		fetchBodyStr(f, "", 0);
-		return;
-	}
-	switch(f->part){
-	case FPHeadFields:
-	case FPHeadFieldsNot:
-		n = m->head.size + 3;
-		s = emalloc(n);
-		n = selectFields(s, n, m->head.buf, f->hdrs, f->part == FPHeadFields);
-		fetchBodyStr(f, s, n);
-		free(s);
-		return;
-	case FPHead:
-		fetchBodyStr(f, m->head.buf, m->head.size);
-		return;
-	case FPMime:
-		fetchBodyStr(f, m->mime.buf, m->mime.size);
-		return;
-	case FPAll:
-		fd = msgFile(m, "rawbody");
-		if(fd < 0){
-			msgDead(m);
-			fetchBodyStr(f, "", 0);
-			return;
-		}
-		p = fetchBodyPart(f, msgSize(m));
-		start = p.start;
-		if(start < m->head.size){
-			stop = p.stop;
-			if(stop > m->head.size)
-				stop = m->head.size;
-			Bwrite(&bout, &m->head.buf[start], stop - start);
-			start = 0;
-			stop = p.stop;
-			if(stop <= m->head.size){
-				close(fd);
-				return;
-			}
-		}else
-			start -= m->head.size;
-		stop = p.stop - m->head.size;
-		break;
-	case FPText:
-		fd = msgFile(m, "rawbody");
-		if(fd < 0){
-			msgDead(m);
-			fetchBodyStr(f, "", 0);
-			return;
-		}
-		p = fetchBodyPart(f, m->size);
-		start = p.start;
-		stop = p.stop;
-		break;
-	default:
-		fetchBodyStr(f, "", 0);
-		return;
-	}
-
-	/*
-	 * read in each block, convert \n without \r to \r\n.
-	 * this means partial fetch requires fetching everything
-	 * through stop, since we don't know how many \r's will be added
-	 */
-	buf[0] = ' ';
-	for(pos = 0; pos < stop; ){
-		n = BufSize;
-		if(n > stop - pos)
-			n = stop - pos;
-		n = read(fd, &buf[1], n);
-		if(n <= 0){
-			fetchBodyFill(stop - pos);
-			break;
-		}
-		e = &buf[n + 1];
-		*e = '\0';
-		for(s = &buf[1]; s < e && pos < stop; s = t + 1){
-			t = memchr(s, '\n', e - s);
-			if(t == nil)
-				t = e;
-			n = t - s;
-			if(pos < start){
-				if(pos + n <= start){
-					s = t;
-					pos += n;
-				}else{
-					s += start - pos;
-					pos = start;
-				}
-				n = t - s;
-			}
-			nn = n;
-			if(pos + nn > stop)
-				nn = stop - pos;
-			if(Bwrite(&bout, s, nn) != nn)
-				writeErr();
-			pos += n;
-			if(*t == '\n'){
-				if(t[-1] != '\r'){
-					if(pos >= start && pos < stop)
-						Bputc(&bout, '\r');
-					pos++;
-				}
-				if(pos >= start && pos < stop)
-					Bputc(&bout, '\n');
-				pos++;
-			}
-		}
-		buf[0] = e[-1];
-	}
-	close(fd);
-}
-
-/*
- * resolve the actual bounds of any partial fetch,
- * and print out the bounds & size of string returned
- */
-Pair
-fetchBodyPart(Fetch *f, uint32_t size)
-{
-	Pair p;
-	uint32_t start, stop;
-
-	start = 0;
-	stop = size;
-	if(f->partial){
-		start = f->start;
-		if(start > size)
-			start = size;
-		stop = start + f->size;
-		if(stop > size)
-			stop = size;
-		Bprint(&bout, "<%lud>", start);
-	}
-	Bprint(&bout, " {%lud}\r\n", stop - start);
-	p.start = start;
-	p.stop = stop;
-	return p;
-}
-
-/*
- * something went wrong fetching data
- * produce fill bytes for what we've committed to produce
- */
-void
-fetchBodyFill(uint32_t n)
-{
-	while(n-- > 0)
-		if(Bputc(&bout, ' ') < 0)
-			writeErr();
-}
-
-/*
- * return a simple string
- */
-void
-fetchBodyStr(Fetch *f, char *buf, uint32_t size)
-{
-	Pair p;
-
-	p = fetchBodyPart(f, size);
-	Bwrite(&bout, &buf[p.start], p.stop-p.start);
-}
-
-char*
-printnlist(NList *sect)
-{
-	static char buf[100];
-	char *p;
-
-	for(p= buf; sect; sect=sect->next){
-		p += sprint(p, "%ld", sect->n);
-		if(sect->next)
-			*p++ = '.';
-	}
-	*p = '\0';
-	return buf;
-}
-
-/*
- * find the numbered sub-part of the message
- */
-Msg*
-findMsgSect(Msg *m, NList *sect)
-{
-	uint32_t id;
-
-	for(; sect != nil; sect = sect->next){
-		id = sect->n;
-#ifdef HACK
-		/* HACK to solve extra level of structure not visible from upas/fs  */
-		if(m->kids == 0 && id == 1 && sect->next == nil){
-			if(m->mime.type->s && strcmp(m->mime.type->s, "message")==0)
-			if(m->mime.type->t && strcmp(m->mime.type->t, "rfc822")==0)
-			if(m->head.type->s && strcmp(m->head.type->s, "text")==0)
-			if(m->head.type->t && strcmp(m->head.type->t, "plain")==0)
-				break;
-		}
-		/* end of HACK */
-#endif
-		for(m = m->kids; m != nil; m = m->next)
-			if(m->id == id)
-				break;
-		if(m == nil)
-			return nil;
-	}
-	return m;
-}
-
-void
-fetchEnvelope(Msg *m)
-{
-	Tm tm;
-
-	Bputc(&bout, '(');
-	Brfc822date(&bout, date2tm(&tm, m->info[IDate]));
-	Bputc(&bout, ' ');
-	Bimapstr(&bout, m->info[ISubject]);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->from);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->sender);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->replyTo);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->to);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->cc);
-	Bputc(&bout, ' ');
-	Bimapaddr(&bout, m->bcc);
-	Bputc(&bout, ' ');
-	Bimapstr(&bout, m->info[IInReplyTo]);
-	Bputc(&bout, ' ');
-	Bimapstr(&bout, m->info[IMessageId]);
-	Bputc(&bout, ')');
-}
-
-void
-fetchBodyStruct(Msg *m, Header *h, int extensions)
-{
-	Msg *k;
-	uint32_t len;
-
-	if(msgIsMulti(h)){
-		Bputc(&bout, '(');
-		for(k = m->kids; k != nil; k = k->next)
-			fetchBodyStruct(k, &k->mime, extensions);
-
-		Bputc(&bout, ' ');
-		Bimapstr(&bout, h->type->t);
-
-		if(extensions){
-			Bputc(&bout, ' ');
-			BimapMimeParams(&bout, h->type->next);
-			fetchStructExt(h);
-		}
-
-		Bputc(&bout, ')');
-		return;
-	}
-
-	Bputc(&bout, '(');
-	if(h->type != nil){
-		Bimapstr(&bout, h->type->s);
-		Bputc(&bout, ' ');
-		Bimapstr(&bout, h->type->t);
-		Bputc(&bout, ' ');
-		BimapMimeParams(&bout, h->type->next);
-	}else
-		Bprint(&bout, "\"text\" \"plain\" NIL");
-
-	Bputc(&bout, ' ');
-	if(h->id != nil)
-		Bimapstr(&bout, h->id->s);
-	else
-		Bprint(&bout, "NIL");
-
-	Bputc(&bout, ' ');
-	if(h->description != nil)
-		Bimapstr(&bout, h->description->s);
-	else
-		Bprint(&bout, "NIL");
-
-	Bputc(&bout, ' ');
-	if(h->encoding != nil)
-		Bimapstr(&bout, h->encoding->s);
-	else
-		Bprint(&bout, "NIL");
-
-	/*
-	 * this is so strange: return lengths for a body[text] response,
-	 * except in the case of a multipart message, when return lengths for a body[] response
-	 */
-	len = m->size;
-	if(h == &m->mime)
-		len += m->head.size;
-	Bprint(&bout, " %lud", len);
-
-	len = m->lines;
-	if(h == &m->mime)
-		len += m->head.lines;
-
-	if(h->type == nil || cistrcmp(h->type->s, "text") == 0){
-		Bprint(&bout, " %lud", len);
-	}else if(msgIsRfc822(h)){
-		Bputc(&bout, ' ');
-		k = m;
-		if(h != &m->mime)
-			k = m->kids;
-		if(k == nil)
-			Bprint(&bout, "(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"text\" \"plain\" NIL NIL NIL NIL 0 0) 0");
-		else{
-			fetchEnvelope(k);
-			Bputc(&bout, ' ');
-			fetchBodyStruct(k, &k->head, extensions);
-			Bprint(&bout, " %lud", len);
-		}
-	}
-
-	if(extensions){
-		Bputc(&bout, ' ');
-
-		/*
-		 * don't have the md5 laying around,
-		 * since the header & body have added newlines.
-		 */
-		Bprint(&bout, "NIL");
-
-		fetchStructExt(h);
-	}
-	Bputc(&bout, ')');
-}
-
-/*
- * common part of bodystructure extensions
- */
-void
-fetchStructExt(Header *h)
-{
-	Bputc(&bout, ' ');
-	if(h->disposition != nil){
-		Bputc(&bout, '(');
-		Bimapstr(&bout, h->disposition->s);
-		Bputc(&bout, ' ');
-		BimapMimeParams(&bout, h->disposition->next);
-		Bputc(&bout, ')');
-	}else
-		Bprint(&bout, "NIL");
-	Bputc(&bout, ' ');
-	if(h->language != nil){
-		if(h->language->next != nil)
-			BimapMimeParams(&bout, h->language->next);
-		else
-			Bimapstr(&bout, h->language->s);
-	}else
-		Bprint(&bout, "NIL");
-}
-
-int
-BimapMimeParams(Biobuf *b, MimeHdr *mh)
-{
-	char *sep;
-	int n;
-
-	if(mh == nil)
-		return Bprint(b, "NIL");
-
-	n = Bputc(b, '(');
-
-	sep = "";
-	for(; mh != nil; mh = mh->next){
-		n += Bprint(b, sep);
-		n += Bimapstr(b, mh->s);
-		n += Bputc(b, ' ');
-		n += Bimapstr(b, mh->t);
-		sep = " ";
-	}
-
-	n += Bputc(b, ')');
-	return n;
-}
-
-/*
- * print a list of addresses;
- * each address is printed as '(' personalName AtDomainList mboxName hostName ')'
- * the AtDomainList is always NIL
- */
-int
-Bimapaddr(Biobuf *b, MAddr *a)
-{
-	char *host, *sep;
-	int n;
-
-	if(a == nil)
-		return Bprint(b, "NIL");
-
-	n = Bputc(b, '(');
-	sep = "";
-	for(; a != nil; a = a->next){
-		n += Bprint(b, "%s(", sep);
-		n += Bimapstr(b, a->personal);
-		n += Bprint(b," NIL ");
-		n += Bimapstr(b, a->box);
-		n += Bputc(b, ' ');
-
-		/*
-		 * can't send NIL as hostName, since that is code for a group
-		 */
-		host = a->host;
-		if(host == nil)
-			host = "";
-		n += Bimapstr(b, host);
-
-		n += Bputc(b, ')');
-		sep = " ";
-	}
-	n += Bputc(b, ')');
-	return n;
-}

+ 0 - 135
sys/src/cmd/ip/imap4d/fns.h

@@ -1,135 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-/*
- * 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, int32_t n);
-void	bye(char *fmt, ...);
-int	cdCreate(char *dir, char *file, int mode, uint32_t 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(uint32_t);
-int	emptyImp(char *mbox);
-void	enableForwarding(void);
-int	encmutf7(char *out, int nout, char *in);
-void	*erealloc(void*, uint32_t);
-char	*estrdup(char*);
-int	expungeMsgs(Box *box, int send);
-void	*ezmalloc(uint32_t);
-void	fetchBodyFill(uint32_t n);
-void	fetchBody(Msg *m, Fetch *f);
-Pair	fetchBodyPart(Fetch *f, uint32_t size);
-void	fetchBodyStr(Fetch *f, char *buf, uint32_t 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, uint32_t max, int uids,
-		   int (*f)(Box*, Msg*, int, void*), void *rock);
-void	freeMsg(Msg *m);
-uint32_t	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);
-uint32_t	mapFlag(char *name);
-uint32_t	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(uint32_t 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);
-uint32_t	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);
-int32_t	selectFields(char *dst, int32_t 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)

+ 0 - 407
sys/src/cmd/ip/imap4d/folder.c

@@ -1,407 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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, uint32_t 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;
-}

+ 0 - 2110
sys/src/cmd/ip/imap4d/imap4d.c

@@ -1,2110 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <auth.h>
-#include <bio.h>
-#include "imap4d.h"
-
-/*
- * these should be in libraries
- */
-char	*csquery(char *attr, char *val, char *rattr);
-
-/*
- * /lib/rfc/rfc2060 imap4rev1
- * /lib/rfc/rfc2683 is implementation advice
- * /lib/rfc/rfc2342 is namespace capability
- * /lib/rfc/rfc2222 is security protocols
- * /lib/rfc/rfc1731 is security protocols
- * /lib/rfc/rfc2221 is LOGIN-REFERRALS
- * /lib/rfc/rfc2193 is MAILBOX-REFERRALS
- * /lib/rfc/rfc2177 is IDLE capability
- * /lib/rfc/rfc2195 is CRAM-MD5 authentication
- * /lib/rfc/rfc2088 is LITERAL+ capability
- * /lib/rfc/rfc1760 is S/Key authentication
- *
- * outlook uses "Secure Password Authentication" aka ntlm authentication
- *
- * capabilities from nslocum
- * CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT
- */
-
-typedef struct	ParseCmd	ParseCmd;
-
-enum
-{
-	UlongMax	= 4294967295,
-};
-
-struct ParseCmd
-{
-	char	*name;
-	void	(*f)(char *tg, char *cmd);
-};
-
-static	void	appendCmd(char *tg, char *cmd);
-static	void	authenticateCmd(char *tg, char *cmd);
-static	void	capabilityCmd(char *tg, char *cmd);
-static	void	closeCmd(char *tg, char *cmd);
-static	void	copyCmd(char *tg, char *cmd);
-static	void	createCmd(char *tg, char *cmd);
-static	void	deleteCmd(char *tg, char *cmd);
-static	void	expungeCmd(char *tg, char *cmd);
-static	void	fetchCmd(char *tg, char *cmd);
-static	void	idleCmd(char *tg, char *cmd);
-static	void	listCmd(char *tg, char *cmd);
-static	void	loginCmd(char *tg, char *cmd);
-static	void	logoutCmd(char *tg, char *cmd);
-static	void	namespaceCmd(char *tg, char *cmd);
-static	void	noopCmd(char *tg, char *cmd);
-static	void	renameCmd(char *tg, char *cmd);
-static	void	searchCmd(char *tg, char *cmd);
-static	void	selectCmd(char *tg, char *cmd);
-static	void	statusCmd(char *tg, char *cmd);
-static	void	storeCmd(char *tg, char *cmd);
-static	void	subscribeCmd(char *tg, char *cmd);
-static	void	uidCmd(char *tg, char *cmd);
-static	void	unsubscribeCmd(char *tg, char *cmd);
-
-static	void	copyUCmd(char *tg, char *cmd, int uids);
-static	void	fetchUCmd(char *tg, char *cmd, int uids);
-static	void	searchUCmd(char *tg, char *cmd, int uids);
-static	void	storeUCmd(char *tg, char *cmd, int uids);
-
-static	void	imap4(int);
-static	void	status(int expungeable, int uids);
-static	void	cleaner(void);
-static	void	check(void);
-static	int	catcher(void*, char*);
-
-static	Search	*searchKey(int first);
-static	Search	*searchKeys(int first, Search *tail);
-static	char	*astring(void);
-static	char	*atomString(char *disallowed, char *initial);
-static	char	*atom(void);
-static	void	badsyn(void);
-static	void	clearcmd(void);
-static	char	*command(void);
-static	void	crnl(void);
-static	Fetch	*fetchAtt(char *s, Fetch *f);
-static	Fetch	*fetchWhat(void);
-static	int	flagList(void);
-static	int	flags(void);
-static	int	getc(void);
-static	char	*listmbox(void);
-static	char	*literal(void);
-static	uint32_t	litlen(void);
-static	MsgSet	*msgSet(int);
-static	void	mustBe(int c);
-static	uint32_t	number(int nonzero);
-static	int	peekc(void);
-static	char	*quoted(void);
-static	void	sectText(Fetch *f, int mimeOk);
-static	uint32_t	seqNo(void);
-static	Store	*storeWhat(void);
-static	char	*tag(void);
-static	uint32_t	uidNo(void);
-static	void	ungetc(void);
-
-static	ParseCmd	SNonAuthed[] =
-{
-	{"capability",		capabilityCmd},
-	{"logout",		logoutCmd},
-	{"x-exit",		logoutCmd},
-	{"noop",		noopCmd},
-
-	{"login",		loginCmd},
-	{"authenticate",	authenticateCmd},
-
-	nil
-};
-
-static	ParseCmd	SAuthed[] =
-{
-	{"capability",		capabilityCmd},
-	{"logout",		logoutCmd},
-	{"x-exit",		logoutCmd},
-	{"noop",		noopCmd},
-
-	{"append",		appendCmd},
-	{"create",		createCmd},
-	{"delete",		deleteCmd},
-	{"examine",		selectCmd},
-	{"select",		selectCmd},
-	{"idle",		idleCmd},
-	{"list",		listCmd},
-	{"lsub",		listCmd},
-	{"namespace",		namespaceCmd},
-	{"rename",		renameCmd},
-	{"status",		statusCmd},
-	{"subscribe",		subscribeCmd},
-	{"unsubscribe",		unsubscribeCmd},
-
-	nil
-};
-
-static	ParseCmd	SSelected[] =
-{
-	{"capability",		capabilityCmd},
-	{"logout",		logoutCmd},
-	{"x-exit",		logoutCmd},
-	{"noop",		noopCmd},
-
-	{"append",		appendCmd},
-	{"create",		createCmd},
-	{"delete",		deleteCmd},
-	{"examine",		selectCmd},
-	{"select",		selectCmd},
-	{"idle",		idleCmd},
-	{"list",		listCmd},
-	{"lsub",		listCmd},
-	{"namespace",		namespaceCmd},
-	{"rename",		renameCmd},
-	{"status",		statusCmd},
-	{"subscribe",		subscribeCmd},
-	{"unsubscribe",		unsubscribeCmd},
-
-	{"check",		noopCmd},
-	{"close",		closeCmd},
-	{"copy",		copyCmd},
-	{"expunge",		expungeCmd},
-	{"fetch",		fetchCmd},
-	{"search",		searchCmd},
-	{"store",		storeCmd},
-	{"uid",			uidCmd},
-
-	nil
-};
-
-static	char		*atomStop = "(){%*\"\\";
-static	Chalstate	*chal;
-static	int		chaled;
-static	ParseCmd	*imapState;
-static	jmp_buf		parseJmp;
-static	char		*parseMsg;
-static	int		allowPass;
-static	int		allowCR;
-static	int		exiting;
-static	QLock		imaplock;
-static	int		idlepid = -1;
-
-Biobuf	bout;
-Biobuf	bin;
-char	username[UserNameLen];
-char	mboxDir[MboxNameLen];
-char	*servername;
-char	*site;
-char	*remote;
-Box	*selected;
-Bin	*parseBin;
-int	debug;
-
-void
-main(int argc, char *argv[])
-{
-	char *s, *t;
-	int preauth, n;
-
-	Binit(&bin, 0, OREAD);
-	Binit(&bout, 1, OWRITE);
-
-	preauth = 0;
-	allowPass = 0;
-	allowCR = 0;
-	ARGBEGIN{
-	case 'a':
-		preauth = 1;
-		break;
-	case 'd':
-		site = ARGF();
-		break;
-	case 'c':
-		allowCR = 1;
-		break;
-	case 'p':
-		allowPass = 1;
-		break;
-	case 'r':
-		remote = ARGF();
-		break;
-	case 's':
-		servername = ARGF();
-		break;
-	case 'v':
-		debug = 1;
-		debuglog("imap4d debugging enabled\n");
-		break;
-	default:
-		fprint(2, "usage: ip/imap4d [-acpv] [-d site] [-r remotehost] [-s servername]\n");
-		bye("usage");
-		break;
-	}ARGEND
-
-	if(allowPass && allowCR){
-		fprint(2, "%s: -c and -p are mutually exclusive\n", argv0);
-		bye("usage");
-	}
-
-	if(preauth)
-		setupuser(nil);
-
-	if(servername == nil){
-		servername = csquery("sys", sysname(), "dom");
-		if(servername == nil)
-			servername = sysname();
-		if(servername == nil){
-			fprint(2, "ip/imap4d can't find server name: %r\n");
-			bye("can't find system name");
-		}
-	}
-	if(site == nil){
-		t = getenv("site");
-		if(t == nil)
-			site = servername;
-		else{
-			n = strlen(t);
-			s = strchr(servername, '.');
-			if(s == nil)
-				s = servername;
-			else
-				s++;
-			n += strlen(s) + 2;
-			site = emalloc(n);
-			snprint(site, n, "%s.%s", t, s);
-		}
-	}
-
-	rfork(RFNOTEG|RFREND);
-
-	atnotify(catcher, 1);
-	qlock(&imaplock);
-	atexit(cleaner);
-	imap4(preauth);
-}
-
-static void
-imap4(int preauth)
-{
-	char *volatile tg;
-	char *volatile cmd;
-	ParseCmd *st;
-
-	if(preauth){
-		Bprint(&bout, "* preauth %s IMAP4rev1 server ready user %s authenticated\r\n", servername, username);
-		imapState = SAuthed;
-	}else{
-		Bprint(&bout, "* OK %s IMAP4rev1 server ready\r\n", servername);
-		imapState = SNonAuthed;
-	}
-	if(Bflush(&bout) < 0)
-		writeErr();
-
-	chaled = 0;
-
-	tg = nil;
-	cmd = nil;
-	if(setjmp(parseJmp)){
-		if(tg == nil)
-			Bprint(&bout, "* bad empty command line: %s\r\n", parseMsg);
-		else if(cmd == nil)
-			Bprint(&bout, "%s BAD no command: %s\r\n", tg, parseMsg);
-		else
-			Bprint(&bout, "%s BAD %s %s\r\n", tg, cmd, parseMsg);
-		clearcmd();
-		if(Bflush(&bout) < 0)
-			writeErr();
-		binfree(&parseBin);
-	}
-	for(;;){
-		if(mbLocked())
-			bye("internal error: mailbox lock held");
-		tg = nil;
-		cmd = nil;
-		tg = tag();
-		mustBe(' ');
-		cmd = atom();
-
-		/*
-		 * note: outlook express is broken: it requires echoing the
-		 * command as part of matching response
-		 */
-		for(st = imapState; st->name != nil; st++){
-			if(cistrcmp(cmd, st->name) == 0){
-				(*st->f)(tg, cmd);
-				break;
-			}
-		}
-		if(st->name == nil){
-			clearcmd();
-			Bprint(&bout, "%s BAD %s illegal command\r\n", tg, cmd);
-		}
-
-		if(Bflush(&bout) < 0)
-			writeErr();
-		binfree(&parseBin);
-	}
-}
-
-void
-bye(char *fmt, ...)
-{
-	va_list arg;
-
-	va_start(arg, fmt);
-	Bprint(&bout, "* bye ");
-	Bvprint(&bout, fmt, arg);
-	Bprint(&bout, "\r\n");
-	Bflush(&bout);
-exits("rob2");
-	exits(0);
-}
-
-void
-parseErr(char *msg)
-{
-	parseMsg = msg;
-	longjmp(parseJmp, 1);
-}
-
-/*
- * an error occured while writing to the client
- */
-void
-writeErr(void)
-{
-	cleaner();
-	_exits("connection closed");
-}
-
-static int
-catcher(void *v, char *msg)
-{
-	USED(v);
-	if(strstr(msg, "closed pipe") != nil)
-		return 1;
-	return 0;
-}
-
-/*
- * wipes out the idleCmd backgroung process if it is around.
- * this can only be called if the current proc has qlocked imaplock.
- * it must be the last piece of imap4d code executed.
- */
-static void
-cleaner(void)
-{
-	int i;
-
-	if(idlepid < 0)
-		return;
-	exiting = 1;
-	close(0);
-	close(1);
-	close(2);
-
-	/*
-	 * the other proc is either stuck in a read, a sleep,
-	 * or is trying to lock imap4lock.
-	 * get him out of it so he can exit cleanly
-	 */
-	qunlock(&imaplock);
-	for(i = 0; i < 4; i++)
-		postnote(PNGROUP, getpid(), "die");
-}
-
-/*
- * send any pending status updates to the client
- * careful: shouldn't exit, because called by idle polling proc
- *
- * can't always send pending info
- * in particular, can't send expunge info
- * in response to a fetch, store, or search command.
- * 
- * rfc2060 5.2:	server must send mailbox size updates
- * rfc2060 5.2:	server may send flag updates
- * rfc2060 5.5:	servers prohibited from sending expunge while fetch, store, search in progress
- * rfc2060 7:	in selected state, server checks mailbox for new messages as part of every command
- * 		sends untagged EXISTS and RECENT respsonses reflecting new size of the mailbox
- * 		should also send appropriate untagged FETCH and EXPUNGE messages if another agent
- * 		changes the state of any message flags or expunges any messages
- * rfc2060 7.4.1	expunge server response must not be sent when no command is in progress,
- * 		nor while responding to a fetch, stort, or search command (uid versions are ok)
- * 		command only "in progress" after entirely parsed.
- *
- * strategy for third party deletion of messages or of a mailbox
- *
- * deletion of a selected mailbox => act like all message are expunged
- *	not strictly allowed by rfc2180, but close to method 3.2.
- *
- * renaming same as deletion
- *
- * copy
- *	reject iff a deleted message is in the request
- *
- * search, store, fetch operations on expunged messages
- *	ignore the expunged messages
- *	return tagged no if referenced
- */
-static void
-status(int expungeable, int uids)
-{
-	int tell;
-
-	if(!selected)
-		return;
-	tell = 0;
-	if(expungeable)
-		tell = expungeMsgs(selected, 1);
-	if(selected->sendFlags)
-		sendFlags(selected, uids);
-	if(tell || selected->toldMax != selected->max){
-		Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
-		selected->toldMax = selected->max;
-	}
-	if(tell || selected->toldRecent != selected->recent){
-		Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
-		selected->toldRecent = selected->recent;
-	}
-	if(tell)
-		closeImp(selected, checkBox(selected, 1));
-}
-
-/*
- * careful: can't exit, because called by idle polling proc
- */
-static void
-check(void)
-{
-	if(!selected)
-		return;
-	checkBox(selected, 0);
-	status(1, 0);
-}
-
-static void
-appendCmd(char *tg, char *cmd)
-{
-	char *mbox, head[128];
-	uint32_t t, n, now;
-	int flags, ok;
-
-	mustBe(' ');
-	mbox = astring();
-	mustBe(' ');
-	flags = 0;
-	if(peekc() == '('){
-		flags = flagList();
-		mustBe(' ');
-	}
-	now = time(nil);
-	if(peekc() == '"'){
-		t = imap4DateTime(quoted());
-		if(t == ~0)
-			parseErr("illegal date format");
-		mustBe(' ');
-		if(t > now)
-			t = now;
-	}else
-		t = now;
-	n = litlen();
-
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		check();
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-		return;
-	}
-	if(!cdExists(mboxDir, mbox)){
-		check();
-		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
-		return;
-	}
-
-	snprint(head, sizeof(head), "From %s %s", username, ctime(t));
-	ok = appendSave(mbox, flags, head, &bin, n);
-	crnl();
-	check();
-	if(ok)
-		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-	else
-		Bprint(&bout, "%s NO %s message save failed\r\n", tg, cmd);
-}
-
-static void
-authenticateCmd(char *tg, char *cmd)
-{
-	char *s, *t;
-
-	mustBe(' ');
-	s = atom();
-	crnl();
-	auth_freechal(chal);
-	chal = nil;
-	if(cistrcmp(s, "cram-md5") == 0){
-		t = cramauth();
-		if(t == nil){
-			Bprint(&bout, "%s OK %s\r\n", tg, cmd);
-			imapState = SAuthed;
-		}else
-			Bprint(&bout, "%s NO %s failed %s\r\n", tg, cmd, t);
-	}else
-		Bprint(&bout, "%s NO %s unsupported authentication protocol\r\n", tg, cmd);
-}
-
-static void
-capabilityCmd(char *tg, char *cmd)
-{
-	crnl();
-	check();
-// nslocum's capabilities
-//	Bprint(&bout, "* CAPABILITY IMAP4 IMAP4REV1 NAMESPACE IDLE SCAN SORT MAILBOX-REFERRALS LOGIN-REFERRALS AUTH=LOGIN THREAD=ORDEREDSUBJECT\r\n");
-	Bprint(&bout, "* CAPABILITY IMAP4REV1 IDLE NAMESPACE AUTH=CRAM-MD5\r\n");
-	Bprint(&bout, "%s OK %s\r\n", tg, cmd);
-}
-
-static void
-closeCmd(char *tg, char *cmd)
-{
-	crnl();
-	imapState = SAuthed;
-	closeBox(selected, 1);
-	selected = nil;
-	Bprint(&bout, "%s OK %s mailbox closed, now in authenticated state\r\n", tg, cmd);
-}
-
-/*
- * note: message id's are before any pending expunges
- */
-static void
-copyCmd(char *tg, char *cmd)
-{
-	copyUCmd(tg, cmd, 0);
-}
-
-static void
-copyUCmd(char *tg, char *cmd, int uids)
-{
-	MsgSet *ms;
-	char *uid, *mbox;
-	uint32_t max;
-	int ok;
-
-	mustBe(' ');
-	ms = msgSet(uids);
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-
-	uid = "";
-	if(uids)
-		uid = "uid ";
-
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		status(1, uids);
-		Bprint(&bout, "%s NO %s%s bad mailbox\r\n", tg, uid, cmd);
-		return;
-	}
-	if(cistrcmp(mbox, "inbox") == 0)
-		mbox = "mbox";
-	if(!cdExists(mboxDir, mbox)){
-		check();
-		Bprint(&bout, "%s NO [TRYCREATE] %s mailbox does not exist\r\n", tg, cmd);
-		return;
-	}
-
-	max = selected->max;
-	checkBox(selected, 0);
-	ok = forMsgs(selected, ms, max, uids, copyCheck, nil);
-	if(ok)
-		ok = forMsgs(selected, ms, max, uids, copySave, mbox);
-
-	status(1, uids);
-	if(ok)
-		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
-	else
-		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
-}
-
-static void
-createCmd(char *tg, char *cmd)
-{
-	char *mbox, *m;
-	int fd, slash;
-
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-	check();
-
-	m = strchr(mbox, '\0');
-	slash = m != mbox && m[-1] == '/';
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-		return;
-	}
-	if(cistrcmp(mbox, "inbox") == 0){
-		Bprint(&bout, "%s NO %s cannot remotely create INBOX\r\n", tg, cmd);
-		return;
-	}
-	if(access(mbox, AEXIST) >= 0){
-		Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
-		return;
-	}
-
-	fd = createBox(mbox, slash);
-	close(fd);
-	if(fd < 0)
-		Bprint(&bout, "%s NO %s cannot create mailbox %s\r\n", tg, cmd, mbox);
-	else
-		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
-}
-
-static void
-deleteCmd(char *tg, char *cmd)
-{
-	char *mbox, *imp;
-
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-	check();
-
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-		return;
-	}
-
-	imp = impName(mbox);
-	if(cistrcmp(mbox, "inbox") == 0
-	|| imp != nil && cdRemove(mboxDir, imp) < 0 && cdExists(mboxDir, imp)
-	|| cdRemove(mboxDir, mbox) < 0)
-		Bprint(&bout, "%s NO %s cannot delete mailbox %s\r\n", tg, cmd, mbox);
-	else
-		Bprint(&bout, "%s OK %s %s completed\r\n", tg, mbox, cmd);
-}
-
-static void
-expungeCmd(char *tg, char *cmd)
-{
-	int ok;
-
-	crnl();
-	ok = deleteMsgs(selected);
-	check();
-	if(ok)
-		Bprint(&bout, "%s OK %s messages erased\r\n", tg, cmd);
-	else
-		Bprint(&bout, "%s NO %s some messages not expunged\r\n", tg, cmd);
-}
-
-static void
-fetchCmd(char *tg, char *cmd)
-{
-	fetchUCmd(tg, cmd, 0);
-}
-
-static void
-fetchUCmd(char *tg, char *cmd, int uids)
-{
-	Fetch *f;
-	MsgSet *ms;
-	MbLock *ml;
-	char *uid;
-	uint32_t max;
-	int ok;
-
-	mustBe(' ');
-	ms = msgSet(uids);
-	mustBe(' ');
-	f = fetchWhat();
-	crnl();
-	uid = "";
-	if(uids)
-		uid = "uid ";
-	max = selected->max;
-	ml = checkBox(selected, 1);
-	if(ml != nil)
-		forMsgs(selected, ms, max, uids, fetchSeen, f);
-	closeImp(selected, ml);
-	ok = ml != nil && forMsgs(selected, ms, max, uids, fetchMsg, f);
-	status(uids, uids);
-	if(ok)
-		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
-	else
-		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
-}
-
-static void
-idleCmd(char *tg, char *cmd)
-{
-	int c, pid;
-
-	crnl();
-	Bprint(&bout, "+ idling, waiting for done\r\n");
-	if(Bflush(&bout) < 0)
-		writeErr();
-
-	if(idlepid < 0){
-		pid = rfork(RFPROC|RFMEM|RFNOWAIT);
-		if(pid == 0){
-			for(;;){
-				qlock(&imaplock);
-				if(exiting)
-					break;
-
-				/*
-				 * parent may have changed curDir, but it doesn't change our .
-				 */
-				resetCurDir();
-
-				check();
-				if(Bflush(&bout) < 0)
-					writeErr();
-				qunlock(&imaplock);
-				sleep(15*1000);
-				enableForwarding();
-			}
-_exits("rob3");
-			_exits(0);
-		}
-		idlepid = pid;
-	}
-
-	qunlock(&imaplock);
-
-	/*
-	 * clear out the next line, which is supposed to contain (case-insensitive)
-	 * done\n
-	 * this is special code since it has to dance with the idle polling proc
-	 * and handle exiting correctly.
-	 */
-	for(;;){
-		c = getc();
-		if(c < 0){
-			qlock(&imaplock);
-			if(!exiting)
-				cleaner();
-_exits("rob4");
-			_exits(0);
-		}
-		if(c == '\n')
-			break;
-	}
-
-	qlock(&imaplock);
-	if(exiting)
-{_exits("rob5");
-		_exits(0);
-}
-
-	/*
-	 * child may have changed curDir, but it doesn't change our .
-	 */
-	resetCurDir();
-
-	check();
-	Bprint(&bout, "%s OK %s terminated\r\n", tg, cmd);
-}
-
-static void
-listCmd(char *tg, char *cmd)
-{
-	char *s, *t, *ss, *ref, *mbox;
-	int n;
-
-	mustBe(' ');
-	s = astring();
-	mustBe(' ');
-	t = listmbox();
-	crnl();
-	check();
-	ref = mutf7str(s);
-	mbox = mutf7str(t);
-	if(ref == nil || mbox == nil){
-		Bprint(&bout, "%s BAD %s mailbox name not in modified utf-7\r\n", tg, cmd);
-		return;
-	}
-
-	/*
-	 * special request for hierarchy delimiter and root name
-	 * root name appears to be name up to and including any delimiter,
-	 * or the empty string, if there is no delimiter.
-	 *
-	 * this must change if the # namespace convention is supported.
-	 */
-	if(*mbox == '\0'){
-		s = strchr(ref, '/');
-		if(s == nil)
-			ref = "";
-		else
-			s[1] = '\0';
-		Bprint(&bout, "* %s (\\Noselect) \"/\" \"%s\"\r\n", cmd, ref);
-		Bprint(&bout, "%s OK %s\r\n", tg, cmd);
-		return;
-	}
-
-
-	/*
-	 * massage the listing name:
-	 * clean up the components individually,
-	 * then rip off componenets from the ref to
-	 * take care of leading ..'s in the mbox.
-	 *
-	 * the cleanup can wipe out * followed by a ..
-	 * tough luck if such a stupid pattern is given.
-	 */
-	cleanname(mbox);
-	if(strcmp(mbox, ".") == 0)
-		*mbox = '\0';
-	if(mbox[0] == '/')
-		*ref = '\0';
-	else if(*ref != '\0'){
-		cleanname(ref);
-		if(strcmp(ref, ".") == 0)
-			*ref = '\0';
-	}else
-		*ref = '\0';
-	while(*ref && isdotdot(mbox)){
-		s = strrchr(ref, '/');
-		if(s == nil)
-			s = ref;
-		if(isdotdot(s))
-			break;
-		*s = '\0';
-		mbox += 2;
-		if(*mbox == '/')
-			mbox++;
-	}
-	if(*ref == '\0'){
-		s = mbox;
-		ss = s;
-	}else{
-		n = strlen(ref) + strlen(mbox) + 2;
-		t = binalloc(&parseBin, n, 0);
-		if(t == nil)
-			parseErr("out of memory");
-		snprint(t, n, "%s/%s", ref, mbox);
-		s = t;
-		ss = s + strlen(ref);
-	}
-
-	/*
-	 * only allow activity in /mail/box
-	 */
-	if(s[0] == '/' || isdotdot(s)){
-		Bprint(&bout, "%s NO illegal mailbox pattern\r\n", tg);
-		return;
-	}
-
-	if(cistrcmp(cmd, "lsub") == 0)
-		lsubBoxes(cmd, s, ss);
-	else
-		listBoxes(cmd, s, ss);
-	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-}
-
-static char*
-passCR(char*u, char*p)
-{
-	static char Ebadch[] = "can't get challenge";
-	static char nchall[64];
-	static char response[64];
-	static Chalstate *ch = nil;
-	AuthInfo *ai;
-
-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;
-	}
-		
-}
-
-static void
-loginCmd(char *tg, char *cmd)
-{
-	char *s, *t;
-	AuthInfo *ai;
-	char*r;
-	mustBe(' ');
-	s = astring();	/* uid */
-	mustBe(' ');
-	t = astring();	/* password */
-	crnl();
-	if(allowCR){
-		if ((r = passCR(s, t)) == nil){
-			Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
-			imapState = SAuthed;
-		} else {
-			Bprint(&bout, "* NO [ALERT] %s\r\n", r);
-			Bprint(&bout, "%s NO %s succeeded\r\n", tg, cmd);
-		}
-		return;
-	}
-	else if(allowPass){
-		if(ai = passLogin(s, t)){
-			setupuser(ai);
-			Bprint(&bout, "%s OK %s succeeded\r\n", tg, cmd);
-			imapState = SAuthed;
-		}else
-			Bprint(&bout, "%s NO %s failed check\r\n", tg, cmd);
-		return;
-	}
-	Bprint(&bout, "%s NO %s plaintext passwords disallowed\r\n", tg, cmd);
-}
-
-/*
- * logout or x-exit, which doesn't expunge the mailbox
- */
-static void
-logoutCmd(char *tg, char *cmd)
-{
-	crnl();
-
-	if(cmd[0] != 'x' && selected){
-		closeBox(selected, 1);
-		selected = nil;
-	}
-	Bprint(&bout, "* bye\r\n");
-	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-exits("rob6");
-	exits(0);
-}
-
-static void
-namespaceCmd(char *tg, char *cmd)
-{
-	crnl();
-	check();
-
-	/*
-	 * personal, other users, shared namespaces
-	 * send back nil or descriptions of (prefix heirarchy-delim) for each case
-	 */
-	Bprint(&bout, "* NAMESPACE ((\"\" \"/\")) nil nil\r\n");
-	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-}
-
-static void
-noopCmd(char *tg, char *cmd)
-{
-	crnl();
-	check();
-	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-	enableForwarding();
-}
-
-/*
- * this is only a partial implementation
- * should copy files to other directories,
- * and copy & truncate inbox
- */
-static void
-renameCmd(char *tg, char *cmd)
-{
-	char *from, *to;
-	int ok;
-
-	mustBe(' ');
-	from = astring();
-	mustBe(' ');
-	to = astring();
-	crnl();
-	check();
-
-	to = mboxName(to);
-	if(to == nil || !okMbox(to) || cistrcmp(to, "inbox") == 0){
-		Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
-		return;
-	}
-	if(access(to, AEXIST) >= 0){
-		Bprint(&bout, "%s NO %s mailbox already exists\r\n", tg, cmd);
-		return;
-	}
-	from = mboxName(from);
-	if(from == nil || !okMbox(from)){
-		Bprint(&bout, "%s NO %s bad mailbox destination name\r\n", tg, cmd);
-		return;
-	}
-	if(cistrcmp(from, "inbox") == 0)
-		ok = copyBox(from, to, 0);
-	else
-		ok = moveBox(from, to);
-
-	if(ok)
-		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-	else
-		Bprint(&bout, "%s NO %s failed\r\n", tg, cmd);
-}
-
-static void
-searchCmd(char *tg, char *cmd)
-{
-	searchUCmd(tg, cmd, 0);
-}
-
-static void
-searchUCmd(char *tg, char *cmd, int uids)
-{
-	Search rock;
-	Msg *m;
-	char *uid;
-	uint32_t id;
-
-	mustBe(' ');
-	rock.next = nil;
-	searchKeys(1, &rock);
-	crnl();
-	uid = "";
-	if(uids)
-		uid = "uid ";
-	if(rock.next != nil && rock.next->key == SKCharset){
-		if(cistrcmp(rock.next->s, "utf-8") != 0
-		&& cistrcmp(rock.next->s, "us-ascii") != 0){
-			Bprint(&bout, "%s NO [BADCHARSET] (\"US-ASCII\" \"UTF-8\") %s%s failed\r\n",
-				tg, uid, cmd);
-			checkBox(selected, 0);
-			status(uids, uids);
-			return;
-		}
-		rock.next = rock.next->next;
-	}
-	Bprint(&bout, "* search");
-	for(m = selected->msgs; m != nil; m = m->next)
-		m->matched = searchMsg(m, rock.next);
-	for(m = selected->msgs; m != nil; m = m->next){
-		if(m->matched){
-			if(uids)
-				id = m->uid;
-			else
-				id = m->seq;
-			Bprint(&bout, " %lud", id);
-		}
-	}
-	Bprint(&bout, "\r\n");
-	checkBox(selected, 0);
-	status(uids, uids);
-	Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
-}
-
-static void
-selectCmd(char *tg, char *cmd)
-{
-	Msg *m;
-	char *s, *mbox;
-
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-
-	if(selected){
-		imapState = SAuthed;
-		closeBox(selected, 1);
-		selected = nil;
-	}
-
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-		return;
-	}
-
-	selected = openBox(mbox, "imap", cistrcmp(cmd, "select") == 0);
-	if(selected == nil){
-		Bprint(&bout, "%s NO %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
-		return;
-	}
-
-	imapState = SSelected;
-
-	Bprint(&bout, "* FLAGS (\\Seen \\Answered \\Flagged \\Deleted \\Draft)\r\n");
-	Bprint(&bout, "* %lud EXISTS\r\n", selected->max);
-	selected->toldMax = selected->max;
-	Bprint(&bout, "* %lud RECENT\r\n", selected->recent);
-	selected->toldRecent = selected->recent;
-	for(m = selected->msgs; m != nil; m = m->next){
-		if(!m->expunged && (m->flags & MSeen) != MSeen){
-			Bprint(&bout, "* OK [UNSEEN %ld]\r\n", m->seq);
-			break;
-		}
-	}
-	Bprint(&bout, "* OK [PERMANENTFLAGS (\\Seen \\Answered \\Flagged \\Draft \\Deleted)]\r\n");
-	Bprint(&bout, "* OK [UIDNEXT %ld]\r\n", selected->uidnext);
-	Bprint(&bout, "* OK [UIDVALIDITY %ld]\r\n", selected->uidvalidity);
-	s = "READ-ONLY";
-	if(selected->writable)
-		s = "READ-WRITE";
-	Bprint(&bout, "%s OK [%s] %s %s completed\r\n", tg, s, cmd, mbox);
-}
-
-static NamedInt	statusItems[] =
-{
-	{"MESSAGES",	SMessages},
-	{"RECENT",	SRecent},
-	{"UIDNEXT",	SUidNext},
-	{"UIDVALIDITY",	SUidValidity},
-	{"UNSEEN",	SUnseen},
-	{nil,		0}
-};
-
-static void
-statusCmd(char *tg, char *cmd)
-{
-	Box *box;
-	Msg *m;
-	char *s, *mbox;
-	uint32_t v;
-	int si, i;
-
-	mustBe(' ');
-	mbox = astring();
-	mustBe(' ');
-	mustBe('(');
-	si = 0;
-	for(;;){
-		s = atom();
-		i = mapInt(statusItems, s);
-		if(i == 0)
-			parseErr("illegal status item");
-		si |= i;
-		if(peekc() == ')')
-			break;
-		mustBe(' ');
-	}
-	mustBe(')');
-	crnl();
-
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox)){
-		check();
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-		return;
-	}
-
-	box = openBox(mbox, "status", 1);
-	if(box == nil){
-		check();
-		Bprint(&bout, "%s NO [TRYCREATE] %s can't open mailbox %s: %r\r\n", tg, cmd, mbox);
-		return;
-	}
-
-	Bprint(&bout, "* STATUS %s (", mbox);
-	s = "";
-	for(i = 0; statusItems[i].name != nil; i++){
-		if(si & statusItems[i].v){
-			v = 0;
-			switch(statusItems[i].v){
-			case SMessages:
-				v = box->max;
-				break;
-			case SRecent:
-				v = box->recent;
-				break;
-			case SUidNext:
-				v = box->uidnext;
-				break;
-			case SUidValidity:
-				v = box->uidvalidity;
-				break;
-			case SUnseen:
-				v = 0;
-				for(m = box->msgs; m != nil; m = m->next)
-					if((m->flags & MSeen) != MSeen)
-						v++;
-				break;
-			default:
-				Bprint(&bout, ")");
-				bye("internal error: status item not implemented");
-				break;
-			}
-			Bprint(&bout, "%s%s %lud", s, statusItems[i].name, v);
-			s = " ";
-		}
-	}
-	Bprint(&bout, ")\r\n");
-	closeBox(box, 1);
-
-	check();
-	Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-}
-
-static void
-storeCmd(char *tg, char *cmd)
-{
-	storeUCmd(tg, cmd, 0);
-}
-
-static void
-storeUCmd(char *tg, char *cmd, int uids)
-{
-	Store *st;
-	MsgSet *ms;
-	MbLock *ml;
-	char *uid;
-	uint32_t max;
-	int ok;
-
-	mustBe(' ');
-	ms = msgSet(uids);
-	mustBe(' ');
-	st = storeWhat();
-	crnl();
-	uid = "";
-	if(uids)
-		uid = "uid ";
-	max = selected->max;
-	ml = checkBox(selected, 1);
-	ok = ml != nil && forMsgs(selected, ms, max, uids, storeMsg, st);
-	closeImp(selected, ml);
-	status(uids, uids);
-	if(ok)
-		Bprint(&bout, "%s OK %s%s completed\r\n", tg, uid, cmd);
-	else
-		Bprint(&bout, "%s NO %s%s failed\r\n", tg, uid, cmd);
-}
-
-/*
- * minimal implementation of subscribe
- * all folders are automatically subscribed,
- * and can't be unsubscribed
- */
-static void
-subscribeCmd(char *tg, char *cmd)
-{
-	Box *box;
-	char *mbox;
-	int ok;
-
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-	check();
-	mbox = mboxName(mbox);
-	ok = 0;
-	if(mbox != nil && okMbox(mbox)){
-		box = openBox(mbox, "subscribe", 0);
-		if(box != nil){
-			ok = subscribe(mbox, 's');
-			closeBox(box, 1);
-		}
-	}
-	if(!ok)
-		Bprint(&bout, "%s NO %s bad mailbox\r\n", tg, cmd);
-	else
-		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-}
-
-static void
-uidCmd(char *tg, char *cmd)
-{
-	char *sub;
-
-	mustBe(' ');
-	sub = atom();
-	if(cistrcmp(sub, "copy") == 0)
-		copyUCmd(tg, sub, 1);
-	else if(cistrcmp(sub, "fetch") == 0)
-		fetchUCmd(tg, sub, 1);
-	else if(cistrcmp(sub, "search") == 0)
-		searchUCmd(tg, sub, 1);
-	else if(cistrcmp(sub, "store") == 0)
-		storeUCmd(tg, sub, 1);
-	else{
-		clearcmd();
-		Bprint(&bout, "%s BAD %s illegal uid command %s\r\n", tg, cmd, sub);
-	}
-}
-
-static void
-unsubscribeCmd(char *tg, char *cmd)
-{
-	char *mbox;
-
-	mustBe(' ');
-	mbox = astring();
-	crnl();
-	check();
-	mbox = mboxName(mbox);
-	if(mbox == nil || !okMbox(mbox) || !subscribe(mbox, 'u'))
-		Bprint(&bout, "%s NO %s can't unsubscribe\r\n", tg, cmd);
-	else
-		Bprint(&bout, "%s OK %s completed\r\n", tg, cmd);
-}
-
-static void
-badsyn(void)
-{
-	parseErr("bad syntax");
-}
-
-static void
-clearcmd(void)
-{
-	int c;
-
-	for(;;){
-		c = getc();
-		if(c < 0)
-			bye("end of input");
-		if(c == '\n')
-			return;
-	}
-}
-
-static void
-crnl(void)
-{
-	int c;
-
-	c = getc();
-	if(c == '\n')
-		return;
-	if(c != '\r' || getc() != '\n')
-		badsyn();
-}
-
-static void
-mustBe(int c)
-{
-	if(getc() != c){
-		ungetc();
-		badsyn();
-	}
-}
-
-/*
- * flaglist	: '(' ')' | '(' flags ')'
- */
-static int
-flagList(void)
-{
-	int f;
-
-	mustBe('(');
-	f = 0;
-	if(peekc() != ')')
-		f = flags();
-
-	mustBe(')');
-	return f;
-}
-
-/*
- * flags	: flag | flags ' ' flag
- * flag		: '\' atom | atom
- */
-static int
-flags(void)
-{
-	int ff, flags;
-	char *s;
-	int c;
-
-	flags = 0;
-	for(;;){
-		c = peekc();
-		if(c == '\\'){
-			mustBe('\\');
-			s = atomString(atomStop, "\\");
-		}else if(strchr(atomStop, c) != nil)
-			s = atom();
-		else
-			break;
-		ff = mapFlag(s);
-		if(ff == 0)
-			parseErr("flag not supported");
-		flags |= ff;
-		if(peekc() != ' ')
-			break;
-		mustBe(' ');
-	}
-	if(flags == 0)
-		parseErr("no flags given");
-	return flags;
-}
-
-/*
- * storeWhat	: osign 'FLAGS' ' ' storeflags
- *		| osign 'FLAGS.SILENT' ' ' storeflags
- * osign	:
- *		| '+' | '-'
- * storeflags	: flagList | flags
- */
-static Store*
-storeWhat(void)
-{
-	int f;
-	char *s;
-	int c, w;
-
-	c = peekc();
-	if(c == '+' || c == '-')
-		mustBe(c);
-	else
-		c = 0;
-	s = atom();
-	w = 0;
-	if(cistrcmp(s, "flags") == 0)
-		w = STFlags;
-	else if(cistrcmp(s, "flags.silent") == 0)
-		w = STFlagsSilent;
-	else
-		parseErr("illegal store attribute");
-	mustBe(' ');
-	if(peekc() == '(')
-		f = flagList();
-	else
-		f = flags();
-	return mkStore(c, w, f);
-}
-
-/*
- * fetchWhat	: "ALL" | "FULL" | "FAST" | fetchAtt | '(' fetchAtts ')'
- * fetchAtts	: fetchAtt | fetchAtts ' ' fetchAtt
- */
-static char *fetchAtom	= "(){}%*\"\\[]";
-static Fetch*
-fetchWhat(void)
-{
-	Fetch *f;
-	char *s;
-
-	if(peekc() == '('){
-		getc();
-		f = nil;
-		for(;;){
-			s = atomString(fetchAtom, "");
-			f = fetchAtt(s, f);
-			if(peekc() == ')')
-				break;
-			mustBe(' ');
-		}
-		getc();
-		return revFetch(f);
-	}
-
-	s = atomString(fetchAtom, "");
-	if(cistrcmp(s, "all") == 0)
-		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, nil))));
-	else if(cistrcmp(s, "fast") == 0)
-		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, nil)));
-	else if(cistrcmp(s, "full") == 0)
-		f = mkFetch(FFlags, mkFetch(FInternalDate, mkFetch(FRfc822Size, mkFetch(FEnvelope, mkFetch(FBody, nil)))));
-	else
-		f = fetchAtt(s, nil);
-	return f;
-}
-
-/*
- * fetchAtt	: "ENVELOPE" | "FLAGS" | "INTERNALDATE"
- *		| "RFC822" | "RFC822.HEADER" | "RFC822.SIZE" | "RFC822.TEXT"
- *		| "BODYSTRUCTURE"
- *		| "UID"
- *		| "BODY"
- *		| "BODY" bodysubs
- *		| "BODY.PEEK" bodysubs
- * bodysubs	: sect
- *		| sect '<' number '.' nz-number '>'
- * sect		: '[' sectSpec ']'
- * sectSpec	: sectMsgText
- *		| sectPart
- *		| sectPart '.' sectText
- * sectPart	: nz-number
- *		| sectPart '.' nz-number
- */
-static Fetch*
-fetchAtt(char *s, Fetch *f)
-{
-	NList *sect;
-	int c;
-
-	if(cistrcmp(s, "envelope") == 0)
-		return mkFetch(FEnvelope, f);
-	if(cistrcmp(s, "flags") == 0)
-		return mkFetch(FFlags, f);
-	if(cistrcmp(s, "internaldate") == 0)
-		return mkFetch(FInternalDate, f);
-	if(cistrcmp(s, "RFC822") == 0)
-		return mkFetch(FRfc822, f);
-	if(cistrcmp(s, "RFC822.header") == 0)
-		return mkFetch(FRfc822Head, f);
-	if(cistrcmp(s, "RFC822.size") == 0)
-		return mkFetch(FRfc822Size, f);
-	if(cistrcmp(s, "RFC822.text") == 0)
-		return mkFetch(FRfc822Text, f);
-	if(cistrcmp(s, "bodystructure") == 0)
-		return mkFetch(FBodyStruct, f);
-	if(cistrcmp(s, "uid") == 0)
-		return mkFetch(FUid, f);
-
-	if(cistrcmp(s, "body") == 0){
-		if(peekc() != '[')
-			return mkFetch(FBody, f);
-		f = mkFetch(FBodySect, f);
-	}else if(cistrcmp(s, "body.peek") == 0)
-		f = mkFetch(FBodyPeek, f);
-	else
-		parseErr("illegal fetch attribute");
-
-	mustBe('[');
-	c = peekc();
-	if(c >= '1' && c <= '9'){
-		sect = mkNList(number(1), nil);
-		while(peekc() == '.'){
-			getc();
-			c = peekc();
-			if(c >= '1' && c <= '9'){
-				sect = mkNList(number(1), sect);
-			}else{
-				break;
-			}
-		}
-		f->sect = revNList(sect);
-	}
-	if(peekc() != ']')
-		sectText(f, f->sect != nil);
-	mustBe(']');
-
-	if(peekc() != '<')
-		return f;
-
-	f->partial = 1;
-	mustBe('<');
-	f->start = number(0);
-	mustBe('.');
-	f->size = number(1);
-	mustBe('>');
-	return f;
-}
-
-/*
- * sectText	: sectMsgText | "MIME"
- * sectMsgText	: "HEADER"
- *		| "TEXT"
- *		| "HEADER.FIELDS" ' ' hdrList
- *		| "HEADER.FIELDS.NOT" ' ' hdrList
- * hdrList	: '(' hdrs ')'
- * hdrs:	: astring
- *		| hdrs ' ' astring
- */
-static void
-sectText(Fetch *f, int mimeOk)
-{
-	SList *h;
-	char *s;
-
-	s = atomString(fetchAtom, "");
-	if(cistrcmp(s, "header") == 0){
-		f->part = FPHead;
-		return;
-	}
-	if(cistrcmp(s, "text") == 0){
-		f->part = FPText;
-		return;
-	}
-	if(mimeOk && cistrcmp(s, "mime") == 0){
-		f->part = FPMime;
-		return;
-	}
-	if(cistrcmp(s, "header.fields") == 0)
-		f->part = FPHeadFields;
-	else if(cistrcmp(s, "header.fields.not") == 0)
-		f->part = FPHeadFieldsNot;
-	else
-		parseErr("illegal fetch section text");
-	mustBe(' ');
-	mustBe('(');
-	h = nil;
-	for(;;){
-		h = mkSList(astring(), h);
-		if(peekc() == ')')
-			break;
-		mustBe(' ');
-	}
-	mustBe(')');
-	f->hdrs = revSList(h);
-}
-
-/*
- * searchWhat	: "CHARSET" ' ' astring searchkeys | searchkeys
- * searchkeys	: searchkey | searchkeys ' ' searchkey
- * searchkey	: "ALL" | "ANSWERED" | "DELETED" | "FLAGGED" | "NEW" | "OLD" | "RECENT"
- *		| "SEEN" | "UNANSWERED" | "UNDELETED" | "UNFLAGGED" | "DRAFT" | "UNDRAFT"
- *		| astrkey ' ' astring
- *		| datekey ' ' date
- *		| "KEYWORD" ' ' flag | "UNKEYWORD" flag
- *		| "LARGER" ' ' number | "SMALLER" ' ' number
- * 		| "HEADER" astring ' ' astring
- *		| set | "UID" ' ' set
- *		| "NOT" ' ' searchkey
- *		| "OR" ' ' searchkey ' ' searchkey
- *		| '(' searchkeys ')'
- * astrkey	: "BCC" | "BODY" | "CC" | "FROM" | "SUBJECT" | "TEXT" | "TO"
- * datekey	: "BEFORE" | "ON" | "SINCE" | "SENTBEFORE" | "SENTON" | "SENTSINCE"
- */
-static NamedInt searchMap[] =
-{
-	{"ALL",		SKAll},
-	{"ANSWERED",	SKAnswered},
-	{"DELETED",	SKDeleted},
-	{"FLAGGED",	SKFlagged},
-	{"NEW",		SKNew},
-	{"OLD",		SKOld},
-	{"RECENT",	SKRecent},
-	{"SEEN",	SKSeen},
-	{"UNANSWERED",	SKUnanswered},
-	{"UNDELETED",	SKUndeleted},
-	{"UNFLAGGED",	SKUnflagged},
-	{"DRAFT",	SKDraft},
-	{"UNDRAFT",	SKUndraft},
-	{"UNSEEN",	SKUnseen},
-	{nil,		0}
-};
-
-static NamedInt searchMapStr[] =
-{
-	{"CHARSET",	SKCharset},
-	{"BCC",		SKBcc},
-	{"BODY",	SKBody},
-	{"CC",		SKCc},
-	{"FROM",	SKFrom},
-	{"SUBJECT",	SKSubject},
-	{"TEXT",	SKText},
-	{"TO",		SKTo},
-	{nil,		0}
-};
-
-static NamedInt searchMapDate[] =
-{
-	{"BEFORE",	SKBefore},
-	{"ON",		SKOn},
-	{"SINCE",	SKSince},
-	{"SENTBEFORE",	SKSentBefore},
-	{"SENTON",	SKSentOn},
-	{"SENTSINCE",	SKSentSince},
-	{nil,		0}
-};
-
-static NamedInt searchMapFlag[] =
-{
-	{"KEYWORD",	SKKeyword},
-	{"UNKEYWORD",	SKUnkeyword},
-	{nil,		0}
-};
-
-static NamedInt searchMapNum[] =
-{
-	{"SMALLER",	SKSmaller},
-	{"LARGER",	SKLarger},
-	{nil,		0}
-};
-
-static Search*
-searchKeys(int first, Search *tail)
-{
-	Search *s;
-
-	for(;;){
-		if(peekc() == '('){
-			getc();
-			tail = searchKeys(0, tail);
-			mustBe(')');
-		}else{
-			s = searchKey(first);
-			tail->next = s;
-			tail = s;
-		}
-		first = 0;
-		if(peekc() != ' ')
-			break;
-		getc();
-	}
-	return tail;
-}
-
-static Search*
-searchKey(int first)
-{
-	Search *sr, rock;
-	Tm tm;
-	char *a;
-	int i, c;
-
-	sr = binalloc(&parseBin, sizeof(Search), 1);
-	if(sr == nil)
-		parseErr("out of memory");
-
-	c = peekc();
-	if(c >= '0' && c <= '9'){
-		sr->key = SKSet;
-		sr->set = msgSet(0);
-		return sr;
-	}
-
-	a = atom();
-	if(i = mapInt(searchMap, a))
-		sr->key = i;
-	else if(i = mapInt(searchMapStr, a)){
-		if(!first && i == SKCharset)
-			parseErr("illegal search key");
-		sr->key = i;
-		mustBe(' ');
-		sr->s = astring();
-	}else if(i = mapInt(searchMapDate, a)){
-		sr->key = i;
-		mustBe(' ');
-		c = peekc();
-		if(c == '"')
-			getc();
-		a = atom();
-		if(!imap4Date(&tm, a))
-			parseErr("bad date format");
-		sr->year = tm.year;
-		sr->mon = tm.mon;
-		sr->mday = tm.mday;
-		if(c == '"')
-			mustBe('"');
-	}else if(i = mapInt(searchMapFlag, a)){
-		sr->key = i;
-		mustBe(' ');
-		c = peekc();
-		if(c == '\\'){
-			mustBe('\\');
-			a = atomString(atomStop, "\\");
-		}else
-			a = atom();
-		i = mapFlag(a);
-		if(i == 0)
-			parseErr("flag not supported");
-		sr->num = i;
-	}else if(i = mapInt(searchMapNum, a)){
-		sr->key = i;
-		mustBe(' ');
-		sr->num = number(0);
-	}else if(cistrcmp(a, "HEADER") == 0){
-		sr->key = SKHeader;
-		mustBe(' ');
-		sr->hdr = astring();
-		mustBe(' ');
-		sr->s = astring();
-	}else if(cistrcmp(a, "UID") == 0){
-		sr->key = SKUid;
-		mustBe(' ');
-		sr->set = msgSet(0);
-	}else if(cistrcmp(a, "NOT") == 0){
-		sr->key = SKNot;
-		mustBe(' ');
-		rock.next = nil;
-		searchKeys(0, &rock);
-		sr->left = rock.next;
-	}else if(cistrcmp(a, "OR") == 0){
-		sr->key = SKOr;
-		mustBe(' ');
-		rock.next = nil;
-		searchKeys(0, &rock);
-		sr->left = rock.next;
-		mustBe(' ');
-		rock.next = nil;
-		searchKeys(0, &rock);
-		sr->right = rock.next;
-	}else
-		parseErr("illegal search key");
-	return sr;
-}
-
-/*
- * set	: seqno
- *	| seqno ':' seqno
- *	| set ',' set
- * seqno: nz-number
- *	| '*'
- *
- */
-static MsgSet*
-msgSet(int uids)
-{
-	MsgSet head, *last, *ms;
-	uint32_t from, to;
-
-	last = &head;
-	head.next = nil;
-	for(;;){
-		from = uids ? uidNo() : seqNo();
-		to = from;
-		if(peekc() == ':'){
-			getc();
-			to = uids ? uidNo() : seqNo();
-		}
-		ms = binalloc(&parseBin, sizeof(MsgSet), 0);
-		if(ms == nil)
-			parseErr("out of memory");
-		ms->from = from;
-		ms->to = to;
-		ms->next = nil;
-		last->next = ms;
-		last = ms;
-		if(peekc() != ',')
-			break;
-		getc();
-	}
-	return head.next;
-}
-
-static uint32_t
-seqNo(void)
-{
-	if(peekc() == '*'){
-		getc();
-		return ~0UL&0xFF;
-	}
-	return number(1);
-}
-
-static uint32_t
-uidNo(void)
-{
-	if(peekc() == '*'){
-		getc();
-		return ~0UL&0xFF;
-	}
-	return number(0);
-}
-
-/*
- * 7 bit, non-ctl chars, no (){%*"\
- * NIL is special case for nstring or parenlist
- */
-static char *
-atom(void)
-{
-	return atomString(atomStop, "");
-}
-
-/*
- * like an atom, but no +
- */
-static char *
-tag(void)
-{
-	return atomString("+(){%*\"\\", "");
-}
-
-/*
- * string or atom allowing %*
- */
-static char *
-listmbox(void)
-{
-	int c;
-
-	c = peekc();
-	if(c == '{')
-		return literal();
-	if(c == '"')
-		return quoted();
-	return atomString("(){\"\\", "");
-}
-
-/*
- * string or atom
- */
-static char *
-astring(void)
-{
-	int c;
-
-	c = peekc();
-	if(c == '{')
-		return literal();
-	if(c == '"')
-		return quoted();
-	return atom();
-}
-
-/*
- * 7 bit, non-ctl chars, none from exception list
- */
-static char *
-atomString(char *disallowed, char *initial)
-{
-	char *s;
-	int c, ns, as;
-
-	ns = strlen(initial);
-	s = binalloc(&parseBin, ns + StrAlloc, 0);
-	if(s == nil)
-		parseErr("out of memory");
-	strcpy(s, initial);
-	as = ns + StrAlloc;
-	for(;;){
-		c = getc();
-		if(c <= ' ' || c >= 0x7f || strchr(disallowed, c) != nil){
-			ungetc();
-			break;
-		}
-		s[ns++] = c;
-		if(ns >= as){
-			s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
-			if(s == nil)
-				parseErr("out of memory");
-			as += StrAlloc;
-		}
-	}
-	if(ns == 0)
-		badsyn();
-	s[ns] = '\0';
-	return s;
-}
-
-/*
- * quoted: '"' chars* '"'
- * chars:	1-128 except \r and \n
- */
-static char *
-quoted(void)
-{
-	char *s;
-	int c, ns, as;
-
-	mustBe('"');
-	s = binalloc(&parseBin, StrAlloc, 0);
-	if(s == nil)
-		parseErr("out of memory");
-	as = StrAlloc;
-	ns = 0;
-	for(;;){
-		c = getc();
-		if(c == '"')
-			break;
-		if(c < 1 || c > 0x7f || c == '\r' || c == '\n')
-			badsyn();
-		if(c == '\\'){
-			c = getc();
-			if(c != '\\' && c != '"')
-				badsyn();
-		}
-		s[ns++] = c;
-		if(ns >= as){
-			s = bingrow(&parseBin, s, as, as + StrAlloc, 0);
-			if(s == nil)
-				parseErr("out of memory");
-			as += StrAlloc;
-		}
-	}
-	s[ns] = '\0';
-	return s;
-}
-
-/*
- * litlen: {number}\r\n
- */
-static uint32_t
-litlen(void)
-{
-	uint32_t v;
-
-	mustBe('{');
-	v = number(0);
-	mustBe('}');
-	crnl();
-	return v;
-}
-
-/*
- * literal: litlen data<0:litlen>
- */
-static char *
-literal(void)
-{
-	char *s;
-	uint32_t v;
-
-	v = litlen();
-	s = binalloc(&parseBin, v+1, 0);
-	if(s == nil)
-		parseErr("out of memory");
-	Bprint(&bout, "+ Ready for literal data\r\n");
-	if(Bflush(&bout) < 0)
-		writeErr();
-	if(v != 0 && Bread(&bin, s, v) != v)
-		badsyn();
-	s[v] = '\0';
-	return s;
-}
-
-/*
- * digits; number is 32 bits
- */
-static uint32_t
-number(int nonzero)
-{
-	uint32_t v;
-	int c, first;
-
-	v = 0;
-	first = 1;
-	for(;;){
-		c = getc();
-		if(c < '0' || c > '9'){
-			ungetc();
-			if(first)
-				badsyn();
-			break;
-		}
-		if(nonzero && first && c == '0')
-			badsyn();
-		c -= '0';
-		first = 0;
-		if(v > UlongMax/10 || v == UlongMax/10 && c > UlongMax%10)
-			parseErr("number out of range\r\n");
-		v = v * 10 + c;
-	}
-	return v;
-}
-
-static int
-getc(void)
-{
-	return Bgetc(&bin);
-}
-
-static void
-ungetc(void)
-{
-	Bungetc(&bin);
-}
-
-static int
-peekc(void)
-{
-	int c;
-
-	c = Bgetc(&bin);
-	Bungetc(&bin);
-	return c;
-}
-

+ 0 - 387
sys/src/cmd/ip/imap4d/imap4d.h

@@ -1,387 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-/*
- * 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 */
-	uint8_t	writable;	/* can write back messages? */
-	uint8_t	dirtyImp;	/* .imp file needs to be written? */
-	uint8_t	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 */
-	uint32_t	max;		/* maximum msgs->seq, same as number of messages */
-	uint32_t	toldMax;	/* last value sent to client */
-	uint32_t	recent;		/* number of recently received messaged */
-	uint32_t	toldRecent;	/* last value sent to client */
-	uint32_t	uidnext;	/* next uid value assigned to a message */
-	uint32_t	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 */
-	uint32_t	size;		/* strlen(buf) */
-	uint32_t	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;
-	uint8_t	sendFlags;	/* flags value needs to be sent to client */
-	uint8_t	expunged;	/* message actually expunged, but not yet reported to client */
-	uint8_t	matched;	/* search succeeded? */
-	uint8_t	bogus;		/* implies the message is invalid, ie contains nulls; see flags above */
-	uint32_t	uid;		/* imap unique identifier */
-	uint32_t	seq;		/* position in box; 1 is oldest */
-	uint32_t	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 */
-
-	uint32_t	size;		/* size of fs/rawbody, in bytes, with \r added before \n */
-	uint32_t	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
-{
-	uint8_t	op;		/* F.* operator */
-	uint8_t	part;		/* FP.* subpart for body[] & body.peek[]*/
-	uint8_t	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
-{
-	uint8_t	sign;
-	uint8_t	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;
-	uint32_t	num;
-	int	year;
-	int	mon;
-	int	mday;
-	MsgSet	*set;
-	Search	*left;
-	Search	*right;
-	Search	*next;
-};
-
-struct NList
-{
-	uint32_t	n;
-	NList	*next;
-};
-
-struct SList
-{
-	char	*s;
-	SList	*next;
-};
-
-struct MsgSet
-{
-	uint32_t	from;
-	uint32_t	to;
-	MsgSet	*next;
-};
-
-struct Pair
-{
-	uint32_t	start;
-	uint32_t	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"

+ 0 - 27
sys/src/cmd/ip/imap4d/imap4d.json

@@ -1,27 +0,0 @@
-{
-	"imap4d": {
-		"Include": [
-			"/sys/src/cmd/cmd.json"
-		],
-		"Install": "/$ARCH/bin/ip",
-		"Program": "imap4d",
-		"SourceFiles": [
-			"auth.c",
-			"copy.c",
-			"csquery.c",
-			"date.c",
-			"fetch.c",
-			"imap4d.c",
-			"list.c",
-			"mbox.c",
-			"msg.c",
-			"mutf7.c",
-			"nodes.c",
-			"folder.c",
-			"search.c",
-			"store.c",
-			"utils.c",
-			"debug.c"
-		]
-	}
-}

+ 0 - 428
sys/src/cmd/ip/imap4d/list.c

@@ -1,428 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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,
-			     int32_t mtime, int isdir);
-static int	listAll(char *cmd, char *ref, char *pat,
-			  char *mbox,
-			  int32_t mtime);
-static int	listMatch(char *cmd, char *ref, char *pat,
-			    char *mbox, char *mm);
-static int	mkSubscribed(void);
-
-static int32_t
-listMtime(char *file)
-{
-	Dir *d;
-	int32_t 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;
-	int32_t 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;
-	int32_t 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, int32_t 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 = *(uint8_t*)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,
-	   int32_t 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 = *(uint8_t*)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;
-}

+ 0 - 872
sys/src/cmd/ip/imap4d/mbox.c

@@ -1,872 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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	uint32_t	uidRenumber(Msg *m, uint32_t 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;
-	int32_t 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&0xFF;
-			m->lines = ~0UL&0xFF;
-			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;
-	int32_t 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;
-	int32_t 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];
-	uint32_t 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 uint32_t
-uidRenumber(Msg *m, uint32_t 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;
-	uint32_t 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;
-}

+ 0 - 1791
sys/src/cmd/ip/imap4d/msg.c

@@ -1,1791 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <libsec.h>
-#include <auth.h>
-#include <fcall.h>
-#include "imap4d.h"
-
-static void	body64(int in, int out);
-static void	bodystrip(int in, int out);
-static void	cleanupHeader(Header *h);
-static char	*domBang(char *s);
-static void	freeMAddr(MAddr *a);
-static void	freeMimeHdr(MimeHdr *mh);
-static char	*headAddrSpec(char *e, char *w);
-static MAddr	*headAddresses(void);
-static MAddr	*headAddress(void);
-static char	*headAtom(char *disallowed);
-static int	headChar(int eat);
-static char	*headDomain(char *e);
-static MAddr	*headMAddr(MAddr *old);
-static char	*headPhrase(char *e, char *w);
-static char	*headQuoted(int start, int stop);
-static char	*headSkipWhite(int);
-static void	headSkip(void);
-static char	*headSubDomain(void);
-static char	*headText(void);
-static void	headToEnd(void);
-static char	*headWord(void);
-static void	mimeDescription(Header *h);
-static void	mimeDisposition(Header *h);
-static void	mimeEncoding(Header *h);
-static void	mimeId(Header *h);
-static void	mimeLanguage(Header *h);
-static void	mimeMd5(Header *h);
-static MimeHdr	*mimeParams(void);
-static void	mimeType(Header *h);
-static MimeHdr	*mkMimeHdr(char *s, char *t, MimeHdr *next);
-static void	msgAddDate(Msg *m);
-static void	msgAddHead(Msg *m, char *head, char *body);
-static int	msgBodySize(Msg *m);
-static int	msgHeader(Msg *m, Header *h, char *file);
-static int32_t	msgReadFile(Msg *m, char *file, char **ss);
-static int	msgUnix(Msg *m, int top);
-static void	stripQuotes(char *q);
-static MAddr	*unixFrom(char *s);
-
-
-static char bogusBody[] = 
-	"This message contains null characters, so it cannot be displayed correctly.\r\n"
-	"Most likely you were sent a bogus message or a binary file.\r\n"
-	"\r\n"
-	"Each of the following attachments has a different version of the message.\r\n"
-	"The first is inlined with all non-printable characters stripped.\r\n"
-	"The second contains the message as it was stored in your mailbox.\r\n"
-	"The third has the initial header stripped.\r\n";
-
-static char bogusMimeText[] =
-	"Content-Disposition: inline\r\n"
-	"Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
-	"Content-Transfer-Encoding: 7bit\r\n";
-
-static char bogusMimeBinary[] =
-	"Content-Disposition: attachment\r\n"
-	"Content-Type: application/octet-stream\r\n"
-	"Content-Transfer-Encoding: base64\r\n";
-
-/*
- * stop list for header fields
- */
-static char	*headFieldStop = ":";
-static char	*mimeTokenStop = "()<>@,;:\\\"/[]?=";
-static char	*headAtomStop = "()<>@,;:\\\".[]";
-static uint8_t	*headStr;
-static uint8_t	*lastWhite;
-
-int32_t
-selectFields(char *dst, int32_t n, char *hdr, SList *fields,
-	     int matches)
-{
-	SList *f;
-	uint8_t *start;
-	char *s;
-	int32_t m, nf;
-
-	headStr = (uint8_t*)hdr;
-	m = 0;
-	for(;;){
-		start = headStr;
-		s = headAtom(headFieldStop);
-		if(s == nil)
-			break;
-		headSkip();
-		for(f = fields; f != nil; f = f->next){
-			if(cistrcmp(s, f->s) == !matches){
-				nf = headStr - start;
-				if(m + nf > n)
-					return 0;
-				memmove(&dst[m], start, nf);
-				m += nf;
-			}
-		}
-		free(s);
-	}
-	if(m + 3 > n)
-		return 0;
-	dst[m++] = '\r';
-	dst[m++] = '\n';
-	dst[m] = '\0';
-	return m;
-}
-
-void
-freeMsg(Msg *m)
-{
-	Msg *k, *last;
-
-	free(m->iBuf);
-	freeMAddr(m->to);
-	if(m->replyTo != m->from)
-		freeMAddr(m->replyTo);
-	if(m->sender != m->from)
-		freeMAddr(m->sender);
-	if(m->from != m->unixFrom)
-		freeMAddr(m->from);
-	freeMAddr(m->unixFrom);
-	freeMAddr(m->cc);
-	freeMAddr(m->bcc);
-	free(m->unixDate);
-	cleanupHeader(&m->head);
-	cleanupHeader(&m->mime);
-	for(k = m->kids; k != nil; ){
-		last = k;
-		k = k->next;
-		freeMsg(last);
-	}
-	free(m->fs);
-	free(m);
-}
-
-uint32_t
-msgSize(Msg *m)
-{
-	return m->head.size + m->size;
-}
-
-int
-infoIsNil(char *s)
-{
-	return s == nil || s[0] == '\0';
-}
-
-char*
-maddrStr(MAddr *a)
-{
-	char *host, *addr;
-	int n;
-
-	host = a->host;
-	if(host == nil)
-		host = "";
-	n = strlen(a->box) + strlen(host) + 2;
-	if(a->personal != nil)
-		n += strlen(a->personal) + 3;
-	addr = emalloc(n);
-	if(a->personal != nil)
-		snprint(addr, n, "%s <%s@%s>", a->personal, a->box, host);
-	else
-		snprint(addr, n, "%s@%s", a->box, host);
-	return addr;
-}
-
-/*
- * return actual name of f in m's fs directory
- * this is special cased when opening m/rawbody, m/mimeheader, or m/rawheader,
- * if the message was corrupted.  in that case,
- * a temporary file is made to hold the base64 encoding of m/raw.
- */
-int
-msgFile(Msg *m, char *f)
-{
-	Msg *parent, *p;
-	Dir d;
-	Tm tm;
-	char buf[64], nbuf[2];
-	uint8_t dbuf[64];
-	int i, n, fd, fd1, fd2;
-
-	if(!m->bogus
-	|| strcmp(f, "") != 0 && strcmp(f, "rawbody") != 0
-	&& strcmp(f, "rawheader") != 0 && strcmp(f, "mimeheader") != 0
-	&& strcmp(f, "info") != 0 && strcmp(f, "unixheader") != 0){
-		if(strlen(f) > MsgNameLen)
-			bye("internal error: msgFile name too long");
-		strcpy(m->efs, f);
-		return cdOpen(m->fsDir, m->fs, OREAD);
-	}
-
-	/*
-	 * walk up the stupid runt message parts for non-multipart messages
-	 */
-	parent = m->parent;
-	if(parent != nil && parent->parent != nil){
-		m = parent;
-		parent = m->parent;
-	}
-	p = m;
-	if(parent != nil)
-		p = parent;
-
-	if(strcmp(f, "info") == 0 || strcmp(f, "unixheader") == 0){
-		strcpy(p->efs, f);
-		return cdOpen(p->fsDir, p->fs, OREAD);
-	}
-
-	fd = imapTmp();
-	if(fd < 0)
-		return -1;
-
-	/*
-	 * craft the message parts for bogus messages
-	 */
-	if(strcmp(f, "") == 0){
-		/*
-		 * make a fake directory for each kid
-		 * all we care about is the name
-		 */
-		if(parent == nil){
-			nulldir(&d);
-			d.mode = DMDIR|0600;
-			d.qid.type = QTDIR;
-			d.name = nbuf;
-			nbuf[1] = '\0';
-			for(i = '1'; i <= '4'; i++){
-				nbuf[0] = i;
-				n = convD2M(&d, dbuf, sizeof(dbuf));
-				if(n <= BIT16SZ)
-					fprint(2, "bad convD2M %d\n", n);
-				write(fd, dbuf, n);
-			}
-		}
-	}else if(strcmp(f, "mimeheader") == 0){
-		if(parent != nil){
-			switch(m->id){
-			case 1:
-			case 2:
-				fprint(fd, "%s", bogusMimeText);
-				break;
-			case 3:
-			case 4:
-				fprint(fd, "%s", bogusMimeBinary);
-				break;
-			}
-		}
-	}else if(strcmp(f, "rawheader") == 0){
-		if(parent == nil){
-			date2tm(&tm, m->unixDate);
-			rfc822date(buf, sizeof(buf), &tm);
-			fprint(fd,
-				"Date: %s\r\n"
-				"From: imap4 daemon <%s@%s>\r\n"
-				"To: <%s@%s>\r\n"
-				"Subject: This message was illegal or corrupted\r\n"
-				"MIME-Version: 1.0\r\n"
-				"Content-Type: multipart/mixed;\r\n\tboundary=\"upas-%s\"\r\n",
-					buf, username, site, username, site, m->info[IDigest]);
-		}
-	}else if(strcmp(f, "rawbody") == 0){
-		fd1 = msgFile(p, "raw");
-		strcpy(p->efs, "rawbody");
-		fd2 = cdOpen(p->fsDir, p->fs, OREAD);
-		if(fd1 < 0 || fd2 < 0){
-			close(fd);
-			close(fd1);
-			close(fd2);
-			return -1;
-		}
-		if(parent == nil){
-			fprint(fd,
-				"This is a multi-part message in MIME format.\r\n"
-				"--upas-%s\r\n"
-				"%s"
-				"\r\n"
-				"%s"
-				"\r\n",
-					m->info[IDigest], bogusMimeText, bogusBody);
-
-			fprint(fd,
-				"--upas-%s\r\n"
-				"%s"
-				"\r\n",
-					m->info[IDigest], bogusMimeText);
-			bodystrip(fd1, fd);
-
-			fprint(fd,
-				"--upas-%s\r\n"
-				"%s"
-				"\r\n",
-					m->info[IDigest], bogusMimeBinary);
-			seek(fd1, 0, 0);
-			body64(fd1, fd);
-
-			fprint(fd,
-				"--upas-%s\r\n"
-				"%s"
-				"\r\n",
-					m->info[IDigest], bogusMimeBinary);
-			body64(fd2, fd);
-
-			fprint(fd, "--upas-%s--\r\n", m->info[IDigest]);
-		}else{
-			switch(m->id){
-			case 1:
-				fprint(fd, "%s", bogusBody);
-				break;
-			case 2:
-				bodystrip(fd1, fd);
-				break;
-			case 3:
-				body64(fd1, fd);
-				break;
-			case 4:
-				body64(fd2, fd);
-				break;
-			}
-		}
-		close(fd1);
-		close(fd2);
-	}
-	seek(fd, 0, 0);
-	return fd;
-}
-
-int
-msgIsMulti(Header *h)
-{
-	return h->type != nil && cistrcmp("multipart", h->type->s) == 0;
-}
-
-int
-msgIsRfc822(Header *h)
-{
-	return h->type != nil && cistrcmp("message", h->type->s) == 0 && cistrcmp("rfc822", h->type->t) == 0;
-}
-
-/*
- * check if a message has been deleted by someone else
- */
-void
-msgDead(Msg *m)
-{
-	if(m->expunged)
-		return;
-	*m->efs = '\0';
-	if(!cdExists(m->fsDir, m->fs))
-		m->expunged = 1;
-}
-
-/*
- * make sure the message has valid associated info
- * used for ISubject, IDigest, IInReplyTo, IMessageId.
- */
-int
-msgInfo(Msg *m)
-{
-	char *s;
-	int i;
-
-	if(m->info[0] != nil)
-		return 1;
-
-	i = msgReadFile(m, "info", &m->iBuf);
-	if(i < 0)
-		return 0;
-
-	s = m->iBuf;
-	for(i = 0; i < IMax; i++){
-		m->info[i] = s;
-		s = strchr(s, '\n');
-		if(s == nil)
-			break;
-		*s++ = '\0';
-	}
-	for(; i < IMax; i++)
-		m->info[i] = nil;
-
-	for(i = 0; i < IMax; i++)
-		if(infoIsNil(m->info[i]))
-			m->info[i] = nil;
-
-	return 1;
-}
-
-/*
- * make sure the message has valid mime structure
- * and sub-messages
- */
-int
-msgStruct(Msg *m, int top)
-{
-	Msg *k, head, *last;
-	Dir *d;
-	char *s;
-	uint32_t max, id;
-	int i, nd, fd, ns;
-
-	if(m->kids != nil)
-		return 1;
-
-	if(m->expunged
-	|| !msgInfo(m)
-	|| !msgUnix(m, top)
-	|| !msgBodySize(m)
-	|| !msgHeader(m, &m->mime, "mimeheader")
-	|| (top || msgIsRfc822(&m->mime) || msgIsMulti(&m->mime)) && !msgHeader(m, &m->head, "rawheader")){
-		if(top && m->bogus && !(m->bogus & BogusTried)){
-			m->bogus |= BogusTried;
-			return msgStruct(m, top);
-		}
-		msgDead(m);
-		return 0;
-	}
-
-	/*
-	 * if a message has no kids, it has a kid which is just the body of the real message
-	 */
-	if(!msgIsMulti(&m->head) && !msgIsMulti(&m->mime) && !msgIsRfc822(&m->head) && !msgIsRfc822(&m->mime)){
-		k = MKZ(Msg);
-		k->id = 1;
-		k->fsDir = m->fsDir;
-		k->bogus = m->bogus;
-		k->parent = m->parent;
-		ns = m->efs - m->fs;
-		k->fs = emalloc(ns + (MsgNameLen + 1));
-		memmove(k->fs, m->fs, ns);
-		k->efs = k->fs + ns;
-		*k->efs = '\0';
-		k->size = m->size;
-		m->kids = k;
-		return 1;
-	}
-
-	/*
-	 * read in all child messages messages
-	 */
-	fd = msgFile(m, "");
-	if(fd < 0){
-		msgDead(m);
-		return 0;
-	}
-
-	max = 0;
-	head.next = nil;
-	last = &head;
-	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;
-
-			k = MKZ(Msg);
-			k->id = id;
-			k->fsDir = m->fsDir;
-			k->bogus = m->bogus;
-			k->parent = m;
-			ns = strlen(m->fs);
-			k->fs = emalloc(ns + 2 * (MsgNameLen + 1));
-			k->efs = seprint(k->fs, k->fs + ns + (MsgNameLen + 1), "%s%lud/", m->fs, id);
-			k->prev = last;
-			k->size = ~0UL&0xFF;
-			k->lines = ~0UL&0xFF;
-			last->next = k;
-			last = k;
-		}
-	}
-	close(fd);
-	m->kids = head.next;
-
-	/*
-	 * if kids fail, just whack them
-	 */
-	top = top && (msgIsRfc822(&m->head) || msgIsMulti(&m->head));
-	for(k = m->kids; k != nil; k = k->next){
-		if(!msgStruct(k, top)){
-			for(k = m->kids; k != nil; ){
-				last = k;
-				k = k->next;
-				freeMsg(last);
-			}
-			m->kids = nil;
-			break;
-		}
-	}
-	return 1;
-}
-
-static int32_t
-msgReadFile(Msg *m, char *file, char **ss)
-{
-	Dir *d;
-	char *s, buf[BufSize];
-	int64_t length;
-	int32_t n, nn;
-	int fd;
-
-	fd = msgFile(m, file);
-	if(fd < 0){
-		msgDead(m);
-		return -1;
-	}
-
-	n = read(fd, buf, BufSize);
-	if(n < BufSize){
-		close(fd);
-		if(n < 0){
-			*ss = nil;
-			return -1;
-		}
-		s = emalloc(n + 1);
-		memmove(s, buf, n);
-		s[n] = '\0';
-		*ss = s;
-		return n;
-	}
-
-	d = dirfstat(fd);
-	if(d == nil){
-		close(fd);
-		return -1;
-	}
-	length = d->length;
-	free(d);
-	nn = length;
-	s = emalloc(nn + 1);
-	memmove(s, buf, n);
-	if(nn > n)
-		nn = readn(fd, s+n, nn-n) + n;
-	close(fd);
-	if(nn != length){
-		free(s);
-		return -1;
-	}
-	s[nn] = '\0';
-	*ss = s;
-	return nn;
-}
-
-static void
-freeMAddr(MAddr *a)
-{
-	MAddr *p;
-
-	while(a != nil){
-		p = a;
-		a = a->next;
-		free(p->personal);
-		free(p->box);
-		free(p->host);
-		free(p);
-	}
-}
-
-/*
- * the message is corrupted or illegal.
- * reset message fields.  msgStruct will reparse the message,
- * relying on msgFile to make up corrected body parts.
- */
-static int
-msgBogus(Msg *m, int flags)
-{
-	if(!(m->bogus & flags))
-		m->bogus |= flags;
-	m->lines = ~0;
-	free(m->head.buf);
-	free(m->mime.buf);
-	memset(&m->head, 0, sizeof(Header));
-	memset(&m->mime, 0, sizeof(Header));
-	return 0;
-}
-
-/*
- *  stolen from upas/marshal; base64 encodes from one fd to another.
- *
- *  the size of buf is very important to enc64.  Anything other than
- *  a multiple of 3 will cause enc64 to output a termination sequence.
- *  To ensure that a full buf corresponds to a multiple of complete lines,
- *  we make buf a multiple of 3*18 since that's how many enc64 sticks on
- *  a single line.  This avoids short lines in the output which is pleasing
- *  but not necessary.
- */
-static int
-enc64x18(char *out, int lim, uint8_t *in, int n)
-{
-	int m, mm, nn;
-
-	nn = 0;
-	for(; n > 0; n -= m){
-		m = 18 * 3;
-		if(m > n)
-			m = n;
-		mm = enc64(out, lim - nn, in, m);
-		in += m;
-		out += mm;
-		*out++ = '\r';
-		*out++ = '\n';
-		nn += mm + 2;
-	}
-	return nn;
-}
-
-static void
-body64(int in, int out)
-{
-	uint8_t buf[3*18*54];
-	char obuf[3*18*54*2];
-	int m, n;
-
-	for(;;){
-		n = read(in, buf, sizeof(buf));
-		if(n < 0)
-			return;
-		if(n == 0)
-			break;
-		m = enc64x18(obuf, sizeof(obuf), buf, n);
-		if(write(out, obuf, m) < 0)
-			return;
-	}
-}
-
-/*
- * strip all non-printable characters from a file
- */
-static void
-bodystrip(int in, int out)
-{
-	uint8_t buf[3*18*54];
-	int m, n, i, c;
-
-	for(;;){
-		n = read(in, buf, sizeof(buf));
-		if(n < 0)
-			return;
-		if(n == 0)
-			break;
-		m = 0;
-		for(i = 0; i < n; i++){
-			c = buf[i];
-			if(c > 0x1f && c < 0x7f		/* normal characters */
-			|| c >= 0x9 && c <= 0xd)	/* \t, \n, vertical tab, form feed, \r */
-				buf[m++] = c;
-		}
-
-		if(m && write(out, buf, m) < 0)
-			return;
-	}
-}
-
-/*
- * read in the message body to count \n without a preceding \r
- */
-static int
-msgBodySize(Msg *m)
-{
-	Dir *d;
-	char buf[BufSize + 2], *s, *se;
-	int64_t length;
-	uint32_t size, lines, bad;
-	int n, fd, c;
-
-	if(m->lines != ~0UL)
-		return 1;
-	fd = msgFile(m, "rawbody");
-	if(fd < 0)
-		return 0;
-	d = dirfstat(fd);
-	if(d == nil){
-		close(fd);
-		return 0;
-	}
-	length = d->length;
-	free(d);
-
-	size = 0;
-	lines = 0;
-	bad = 0;
-	buf[0] = ' ';
-	for(;;){
-		n = read(fd, &buf[1], BufSize);
-		if(n <= 0)
-			break;
-		size += n;
-		se = &buf[n + 1];
-		for(s = &buf[1]; s < se; s++){
-			c = *s;
-			if(c == '\0'){
-				close(fd);
-				return msgBogus(m, BogusBody);
-			}
-			if(c != '\n')
-				continue;
-			if(s[-1] != '\r')
-				bad++;
-			lines++;
-		}
-		buf[0] = buf[n];
-	}
-	if(size != length)
-		bye("bad length reading rawbody");
-	size += bad;
-	m->size = size;
-	m->lines = lines;
-	close(fd);
-	return 1;
-}
-
-/*
- * retrieve information from the unixheader file
- */
-static int
-msgUnix(Msg *m, int top)
-{
-	Tm tm;
-	char *s, *ss;
-
-	if(m->unixDate != nil)
-		return 1;
-
-	if(!top){
-bogus:
-		m->unixDate = estrdup("");
-		m->unixFrom = unixFrom(nil);
-		return 1;
-	}
-
-	if(msgReadFile(m, "unixheader", &ss) < 0)
-		return 0;
-	s = ss;
-	s = strchr(s, ' ');
-	if(s == nil){
-		free(ss);
-		goto bogus;
-	}
-	s++;
-	m->unixFrom = unixFrom(s);
-	s = (char*)headStr;
-	if(date2tm(&tm, s) == nil)
-		s = m->info[IUnixDate];
-	if(s == nil){
-		free(ss);
-		goto bogus;
-	}
-	m->unixDate = estrdup(s);
-	free(ss);
-	return 1;
-}
-
-/*
- * parse the address in the unix header
- * last line of defence, so must return something
- */
-static MAddr *
-unixFrom(char *s)
-{
-	MAddr *a;
-	char *e, *t;
-
-	if(s == nil)
-		return nil;
-	headStr = (uint8_t*)s;
-	t = emalloc(strlen(s) + 2);
-	e = headAddrSpec(t, nil);
-	if(e == nil)
-		a = nil;
-	else{
-		if(*e != '\0')
-			*e++ = '\0';
-		else
-			e = site;
-		a = MKZ(MAddr);
-
-		a->box = estrdup(t);
-		a->host = estrdup(e);
-	}
-	free(t);
-	return a;
-}
-
-/*
- * read in the entire header,
- * and parse out any existing mime headers
- */
-static int
-msgHeader(Msg *m, Header *h, char *file)
-{
-	char *s, *ss, *t, *te;
-	uint32_t lines, n, nn;
-	int32_t ns;
-	int dated, c;
-
-	if(h->buf != nil)
-		return 1;
-
-	ns = msgReadFile(m, file, &ss);
-	if(ns < 0)
-		return 0;
-	s = ss;
-	n = ns;
-
-	/*
-	 * count lines ending with \n and \r\n
-	 * add an extra line at the end, since upas/fs headers
-	 * don't have a terminating \r\n
-	 */
-	lines = 1;
-	te = s + ns;
-	for(t = s; t < te; t++){
-		c = *t;
-		if(c == '\0')
-			return msgBogus(m, BogusHeader);
-		if(c != '\n')
-			continue;
-		if(t == s || t[-1] != '\r')
-			n++;
-		lines++;
-	}
-	if(t > s && t[-1] != '\n'){
-		if(t[-1] != '\r')
-			n++;
-		n++;
-	}
-	n += 2;
-	h->buf = emalloc(n + 1);
-	h->size = n;
-	h->lines = lines;
-
-	/*
-	 * make sure all headers end in \r\n
-	 */
-	nn = 0;
-	for(t = s; t < te; t++){
-		c = *t;
-		if(c == '\n'){
-			if(!nn || h->buf[nn - 1] != '\r')
-				h->buf[nn++] = '\r';
-			lines++;
-		}
-		h->buf[nn++] = c;
-	}
-	if(nn && h->buf[nn-1] != '\n'){
-		if(h->buf[nn-1] != '\r')
-			h->buf[nn++] = '\r';
-		h->buf[nn++] = '\n';
-	}
-	h->buf[nn++] = '\r';
-	h->buf[nn++] = '\n';
-	h->buf[nn] = '\0';
-	if(nn != n)
-		bye("misconverted header %ld %ld", nn, n);
-	free(s);
-
-	/*
-	 * and parse some mime headers
-	 */
-	headStr = (uint8_t*)h->buf;
-	dated = 0;
-	while(s = headAtom(headFieldStop)){
-		if(cistrcmp(s, "content-type") == 0)
-			mimeType(h);
-		else if(cistrcmp(s, "content-transfer-encoding") == 0)
-			mimeEncoding(h);
-		else if(cistrcmp(s, "content-id") == 0)
-			mimeId(h);
-		else if(cistrcmp(s, "content-description") == 0)
-			mimeDescription(h);
-		else if(cistrcmp(s, "content-disposition") == 0)
-			mimeDisposition(h);
-		else if(cistrcmp(s, "content-md5") == 0)
-			mimeMd5(h);
-		else if(cistrcmp(s, "content-language") == 0)
-			mimeLanguage(h);
-		else if(h == &m->head && cistrcmp(s, "from") == 0)
-			m->from = headMAddr(m->from);
-		else if(h == &m->head && cistrcmp(s, "to") == 0)
-			m->to = headMAddr(m->to);
-		else if(h == &m->head && cistrcmp(s, "reply-to") == 0)
-			m->replyTo = headMAddr(m->replyTo);
-		else if(h == &m->head && cistrcmp(s, "sender") == 0)
-			m->sender = headMAddr(m->sender);
-		else if(h == &m->head && cistrcmp(s, "cc") == 0)
-			m->cc = headMAddr(m->cc);
-		else if(h == &m->head && cistrcmp(s, "bcc") == 0)
-			m->bcc = headMAddr(m->bcc);
-		else if(h == &m->head && cistrcmp(s, "date") == 0)
-			dated = 1;
-		headSkip();
-		free(s);
-	}
-
-	if(h == &m->head){
-		if(m->from == nil){
-			m->from = m->unixFrom;
-			if(m->from != nil){
-				s = maddrStr(m->from);
-				msgAddHead(m, "From", s);
-				free(s);
-			}
-		}
-		if(m->sender == nil)
-			m->sender = m->from;
-		if(m->replyTo == nil)
-			m->replyTo = m->from;
-
-		if(infoIsNil(m->info[IDate]))
-			m->info[IDate] = m->unixDate;
-		if(!dated && m->from != nil)
-			msgAddDate(m);
-	}
-	return 1;
-}
-
-/*
- * prepend head: body to the cached header
- */
-static void
-msgAddHead(Msg *m, char *head, char *body)
-{
-	char *s;
-	int32_t size, n;
-
-	n = strlen(head) + strlen(body) + 4;
-	size = m->head.size + n;
-	s = emalloc(size + 1);
-	snprint(s, size + 1, "%s: %s\r\n%s", head, body, m->head.buf);
-	free(m->head.buf);
-	m->head.buf = s;
-	m->head.size = size;
-	m->head.lines++;
-}
-
-static void
-msgAddDate(Msg *m)
-{
-	Tm tm;
-	char buf[64];
-
-	/* don't bother if we don't have a date */
-	if(infoIsNil(m->info[IDate]))
-		return;
-
-	date2tm(&tm, m->info[IDate]);
-	rfc822date(buf, sizeof(buf), &tm);
-	msgAddHead(m, "Date", buf);
-}
-
-static MimeHdr*
-mkMimeHdr(char *s, char *t, MimeHdr *next)
-{
-	MimeHdr *mh;
-
-	mh = MK(MimeHdr);
-	mh->s = s;
-	mh->t = t;
-	mh->next = next;
-	return mh;
-}
-
-static void
-freeMimeHdr(MimeHdr *mh)
-{
-	MimeHdr *last;
-
-	while(mh != nil){
-		last = mh;
-		mh = mh->next;
-		free(last->s);
-		free(last->t);
-		free(last);
-	}
-}
-
-static void
-cleanupHeader(Header *h)
-{
-	freeMimeHdr(h->type);
-	freeMimeHdr(h->id);
-	freeMimeHdr(h->description);
-	freeMimeHdr(h->encoding);
-	freeMimeHdr(h->md5);
-	freeMimeHdr(h->disposition);
-	freeMimeHdr(h->language);
-}
-
-/*
- * parser for rfc822 & mime header fields
- */
-
-/*
- * type		: 'content-type' ':' token '/' token params
- */
-static void
-mimeType(Header *h)
-{
-	char *s, *t;
-
-	if(headChar(1) != ':')
-		return;
-	s = headAtom(mimeTokenStop);
-	if(s == nil || headChar(1) != '/'){
-		free(s);
-		return;
-	}
-	t = headAtom(mimeTokenStop);
-	if(t == nil){
-		free(s);
-		return;
-	}
-	h->type = mkMimeHdr(s, t, mimeParams());
-}
-
-/*
- * params	:
- *		| params ';' token '=' token
- * 		| params ';' token '=' quoted-str
- */
-static MimeHdr*
-mimeParams(void)
-{
-	MimeHdr head, *last;
-	char *s, *t;
-
-	head.next = nil;
-	last = &head;
-	for(;;){
-		if(headChar(1) != ';')
-			break;
-		s = headAtom(mimeTokenStop);
-		if(s == nil || headChar(1) != '='){
-			free(s);
-			break;
-		}
-		if(headChar(0) == '"'){
-			t = headQuoted('"', '"');
-			stripQuotes(t);
-		}else
-			t = headAtom(mimeTokenStop);
-		if(t == nil){
-			free(s);
-			break;
-		}
-		last->next = mkMimeHdr(s, t, nil);
-		last = last->next;
-	}
-	return head.next;
-}
-
-/*
- * encoding	: 'content-transfer-encoding' ':' token
- */
-static void
-mimeEncoding(Header *h)
-{
-	char *s;
-
-	if(headChar(1) != ':')
-		return;
-	s = headAtom(mimeTokenStop);
-	if(s == nil)
-		return;
-	h->encoding = mkMimeHdr(s, nil, nil);
-}
-
-/*
- * mailaddr	: ':' addresses
- */
-static MAddr*
-headMAddr(MAddr *old)
-{
-	MAddr *a;
-
-	if(headChar(1) != ':')
-		return old;
-
-	if(headChar(0) == '\n')
-		return old;
-
-	a = headAddresses();
-	if(a == nil)
-		return old;
-
-	freeMAddr(old);
-	return a;
-}
-
-/*
- * addresses	: address | addresses ',' address
- */
-static MAddr*
-headAddresses(void)
-{
-	MAddr *addr, *tail, *a;
-
-	addr = headAddress();
-	if(addr == nil)
-		return nil;
-	tail = addr;
-	while(headChar(0) == ','){
-		headChar(1);
-		a = headAddress();
-		if(a == nil){
-			freeMAddr(addr);
-			return nil;
-		}
-		tail->next = a;
-		tail = a;
-	}
-	return addr;
-}
-
-/*
- * address	: mailbox | group
- * group	: phrase ':' mboxes ';' | phrase ':' ';'
- * mailbox	: addr-spec
- *		| optphrase '<' addr-spec '>'
- *		| optphrase '<' route ':' addr-spec '>'
- * optphrase	: | phrase
- * route	: '@' domain
- *		| route ',' '@' domain
- * personal names are the phrase before '<',
- * or a comment before or after a simple addr-spec
- */
-static MAddr*
-headAddress(void)
-{
-	MAddr *addr;
-	uint8_t *hs;
-	char *s, *e, *w, *personal;
-	int c;
-
-	s = emalloc(strlen((char*)headStr) + 2);
-	e = s;
-	personal = headSkipWhite(1);
-	c = headChar(0);
-	if(c == '<')
-		w = nil;
-	else{
-		w = headWord();
-		c = headChar(0);
-	}
-	if(c == '.' || c == '@' || c == ',' || c == '\n' || c == '\0'){
-		lastWhite = headStr;
-		e = headAddrSpec(s, w);
-		if(personal == nil){
-			hs = headStr;
-			headStr = lastWhite;
-			personal = headSkipWhite(1);
-			headStr = hs;
-		}
-	}else{
-		if(c != '<' || w != nil){
-			free(personal);
-			if(!headPhrase(e, w)){
-				free(s);
-				return nil;
-			}
-
-			/*
-			 * ignore addresses with groups,
-			 * so the only thing left if <
-			 */
-			c = headChar(1);
-			if(c != '<'){
-				free(s);
-				return nil;
-			}
-			personal = estrdup(s);
-		}else
-			headChar(1);
-
-		/*
-		 * after this point, we need to free personal before returning.
-		 * set e to nil to everything afterwards fails.
-		 *
-		 * ignore routes, they are useless, and heavily discouraged in rfc1123.
-		 * imap4 reports them up to, but not including, the terminating :
-		 */
-		e = s;
-		c = headChar(0);
-		if(c == '@'){
-			for(;;){
-				c = headChar(1);
-				if(c != '@'){
-					e = nil;
-					break;
-				}
-				headDomain(e);
-				c = headChar(1);
-				if(c != ','){
-					e = s;
-					break;
-				}
-			}
-			if(c != ':')
-				e = nil;
-		}
-
-		if(e != nil)
-			e = headAddrSpec(s, nil);
-		if(headChar(1) != '>')
-			e = nil;
-	}
-
-	/*
-	 * e points to @host, or nil if an error occured
-	 */
-	if(e == nil){
-		free(personal);
-		addr = nil;
-	}else{
-		if(*e != '\0')
-			*e++ = '\0';
-		else
-			e = site;
-		addr = MKZ(MAddr);
-
-		addr->personal = personal;
-		addr->box = estrdup(s);
-		addr->host = estrdup(e);
-	}
-	free(s);
-	return addr;
-}
-
-/*
- * phrase	: word
- *		| phrase word
- * w is the optional initial word of the phrase
- * returns the end of the phrase, or nil if a failure occured
- */
-static char*
-headPhrase(char *e, char *w)
-{
-	int c;
-
-	for(;;){
-		if(w == nil){
-			w = headWord();
-			if(w == nil)
-				return nil;
-		}
-		if(w[0] == '"')
-			stripQuotes(w);
-		strcpy(e, w);
-		free(w);
-		w = nil;
-		e = strchr(e, '\0');
-		c = headChar(0);
-		if(c <= ' ' || strchr(headAtomStop, c) != nil && c != '"')
-			break;
-		*e++ = ' ';
-		*e = '\0';
-	}
-	return e;
-}
-
-/*
- * addr-spec	: local-part '@' domain
- *		| local-part			extension to allow ! and local names
- * local-part	: word
- *		| local-part '.' word
- *
- * if no '@' is present, rewrite d!e!f!u as @d,@e:u@f,
- * where d, e, f are valid domain components.
- * the @d,@e: is ignored, since routes are ignored.
- * perhaps they should be rewritten as e!f!u@d, but that is inconsistent with upas.
- *
- * returns a pointer to '@', the end if none, or nil if there was an error
- */
-static char*
-headAddrSpec(char *e, char *w)
-{
-	char *s, *at, *b, *bang, *dom;
-	int c;
-
-	s = e;
-	for(;;){
-		if(w == nil){
-			w = headWord();
-			if(w == nil)
-				return nil;
-		}
-		strcpy(e, w);
-		free(w);
-		w = nil;
-		e = strchr(e, '\0');
-		lastWhite = headStr;
-		c = headChar(0);
-		if(c != '.')
-			break;
-		headChar(1);
-		*e++ = '.';
-		*e = '\0';
-	}
-
-	if(c != '@'){
-		/*
-		 * extenstion: allow name without domain
-		 * check for domain!xxx
-		 */
-		bang = domBang(s);
-		if(bang == nil)
-			return e;
-
-		/*
-		 * if dom1!dom2!xxx, ignore dom1!
-		 */
-		dom = s;
-		for(; b = domBang(bang + 1); bang = b)
-			dom = bang + 1;
-
-		/*
-		 * convert dom!mbox into mbox@dom
-		 */
-		*bang = '@';
-		strrev(dom, bang);
-		strrev(bang+1, e);
-		strrev(dom, e);
-		bang = &dom[e - bang - 1];
-		if(dom > s){
-			bang -= dom - s;
-			for(e = s; *e = *dom; e++)
-				dom++;
-		}
-
-		/*
-		 * eliminate a trailing '.'
-		 */
-		if(e[-1] == '.')
-			e[-1] = '\0';
-		return bang;
-	}
-	headChar(1);
-
-	at = e;
-	*e++ = '@';
-	*e = '\0';
-	if(!headDomain(e))
-		return nil;
-	return at;
-}
-
-/*
- * find the ! in domain!rest, where domain must have at least
- * one internal '.'
- */
-static char*
-domBang(char *s)
-{
-	int dot, c;
-
-	dot = 0;
-	for(; c = *s; s++){
-		if(c == '!'){
-			if(!dot || dot == 1 && s[-1] == '.' || s[1] == '\0')
-				return nil;
-			return s;
-		}
-		if(c == '"')
-			break;
-		if(c == '.')
-			dot++;
-	}
-	return nil;
-}
-
-/*
- * domain	: sub-domain
- *		| domain '.' sub-domain
- * returns the end of the domain, or nil if a failure occured
- */
-static char*
-headDomain(char *e)
-{
-	char *w;
-
-	for(;;){
-		w = headSubDomain();
-		if(w == nil)
-			return nil;
-		strcpy(e, w);
-		free(w);
-		e = strchr(e, '\0');
-		lastWhite = headStr;
-		if(headChar(0) != '.')
-			break;
-		headChar(1);
-		*e++ = '.';
-		*e = '\0';
-	}
-	return e;
-}
-
-/*
- * id		: 'content-id' ':' msg-id
- * msg-id	: '<' addr-spec '>'
- */
-static void
-mimeId(Header *h)
-{
-	char *s, *e, *w;
-
-	if(headChar(1) != ':')
-		return;
-	if(headChar(1) != '<')
-		return;
-
-	s = emalloc(strlen((char*)headStr) + 3);
-	e = s;
-	*e++ = '<';
-	e = headAddrSpec(e, nil);
-	if(e == nil || headChar(1) != '>'){
-		free(s);
-		return;
-	}
-	e = strchr(e, '\0');
-	*e++ = '>';
-	e[0] = '\0';
-	w = strdup(s);
-	free(s);
-	h->id = mkMimeHdr(w, nil, nil);
-}
-
-/*
- * description	: 'content-description' ':' *text
- */
-static void
-mimeDescription(Header *h)
-{
-	if(headChar(1) != ':')
-		return;
-	headSkipWhite(0);
-	h->description = mkMimeHdr(headText(), nil, nil);
-}
-
-/*
- * disposition	: 'content-disposition' ':' token params
- */
-static void
-mimeDisposition(Header *h)
-{
-	char *s;
-
-	if(headChar(1) != ':')
-		return;
-	s = headAtom(mimeTokenStop);
-	if(s == nil)
-		return;
-	h->disposition = mkMimeHdr(s, nil, mimeParams());
-}
-
-/*
- * md5		: 'content-md5' ':' token
- */
-static void
-mimeMd5(Header *h)
-{
-	char *s;
-
-	if(headChar(1) != ':')
-		return;
-	s = headAtom(mimeTokenStop);
-	if(s == nil)
-		return;
-	h->md5 = mkMimeHdr(s, nil, nil);
-}
-
-/*
- * language	: 'content-language' ':' langs
- * langs	: token
- *		| langs commas token
- * commas	: ','
- *		| commas ','
- */
-static void
-mimeLanguage(Header *h)
-{
-	MimeHdr head, *last;
-	char *s;
-
-	head.next = nil;
-	last = &head;
-	for(;;){
-		s = headAtom(mimeTokenStop);
-		if(s == nil)
-			break;
-		last->next = mkMimeHdr(s, nil, nil);
-		last = last->next;
-		while(headChar(0) != ',')
-			headChar(1);
-	}
-	h->language = head.next;
-}
-
-/*
- * token	: 1*<char 33-255, except "()<>@,;:\\\"/[]?=" aka mimeTokenStop>
- * atom		: 1*<chars 33-255, except "()<>@,;:\\\".[]" aka headAtomStop>
- * note this allows 8 bit characters, which occur in utf.
- */
-static char*
-headAtom(char *disallowed)
-{
-	char *s;
-	int c, ns, as;
-
-	headSkipWhite(0);
-
-	s = emalloc(StrAlloc);
-	as = StrAlloc;
-	ns = 0;
-	for(;;){
-		c = *headStr++;
-		if(c <= ' ' || strchr(disallowed, c) != nil){
-			headStr--;
-			break;
-		}
-		s[ns++] = c;
-		if(ns >= as){
-			as += StrAlloc;
-			s = erealloc(s, as);
-		}
-	}
-	if(ns == 0){
-		free(s);
-		return 0;
-	}
-	s[ns] = '\0';
-	return s;
-}
-
-/*
- * sub-domain	: atom | domain-lit
- */
-static char *
-headSubDomain(void)
-{
-	if(headChar(0) == '[')
-		return headQuoted('[', ']');
-	return headAtom(headAtomStop);
-}
-
-/*
- * word	: atom | quoted-str
- */
-static char *
-headWord(void)
-{
-	if(headChar(0) == '"')
-		return headQuoted('"', '"');
-	return headAtom(headAtomStop);
-}
-
-/*
- * q is a quoted string.  remove enclosing " and and \ escapes
- */
-static void
-stripQuotes(char *q)
-{
-	char *s;
-	int c;
-
-	if(q == nil)
-		return;
-	s = q++;
-	while(c = *q++){
-		if(c == '\\'){
-			c = *q++;
-			if(!c)
-				return;
-		}
-		*s++ = c;
-	}
-	s[-1] = '\0';
-}
-
-/*
- * quoted-str	: '"' *(any char but '"\\\r', or '\' any char, or linear-white-space) '"'
- * domain-lit	: '[' *(any char but '[]\\\r', or '\' any char, or linear-white-space) ']'
- */
-static char *
-headQuoted(int start, int stop)
-{
-	char *s;
-	int c, ns, as;
-
-	if(headChar(1) != start)
-		return nil;
-	s = emalloc(StrAlloc);
-	as = StrAlloc;
-	ns = 0;
-	s[ns++] = start;
-	for(;;){
-		c = *headStr;
-		if(c == stop){
-			headStr++;
-			break;
-		}
-		if(c == '\0'){
-			free(s);
-			return nil;
-		}
-		if(c == '\r'){
-			headStr++;
-			continue;
-		}
-		if(c == '\n'){
-			headStr++;
-			while(*headStr == ' ' || *headStr == '\t' || *headStr == '\r' || *headStr == '\n')
-				headStr++;
-			c = ' ';
-		}else if(c == '\\'){
-			headStr++;
-			s[ns++] = c;
-			c = *headStr;
-			if(c == '\0'){
-				free(s);
-				return nil;
-			}
-			headStr++;
-		}else
-			headStr++;
-		s[ns++] = c;
-		if(ns + 1 >= as){	/* leave room for \c or "0 */
-			as += StrAlloc;
-			s = erealloc(s, as);
-		}
-	}
-	s[ns++] = stop;
-	s[ns] = '\0';
-	return s;
-}
-
-/*
- * headText	: contents of rest of header line
- */
-static char *
-headText(void)
-{
-	uint8_t *v;
-	char *s;
-
-	v = headStr;
-	headToEnd();
-	s = emalloc(headStr - v + 1);
-	memmove(s, v, headStr - v);
-	s[headStr - v] = '\0';
-	return s;
-}
-
-/*
- * white space is ' ' '\t' or nested comments.
- * skip white space.
- * if com and a comment is seen,
- * return it's contents and stop processing white space.
- */
-static char*
-headSkipWhite(int com)
-{
-	char *s;
-	int c, incom, as, ns;
-
-	s = nil;
-	as = StrAlloc;
-	ns = 0;
-	if(com)
-		s = emalloc(StrAlloc);
-	incom = 0;
-	for(; c = *headStr; headStr++){
-		switch(c){
-		case ' ':
-		case '\t':
-		case '\r':
-			c = ' ';
-			break;
-		case '\n':
-			c = headStr[1];
-			if(c != ' ' && c != '\t')
-				goto breakout;
-			c = ' ';
-			break;
-		case '\\':
-			if(com && incom)
-				s[ns++] = c;
-			c = headStr[1];
-			if(c == '\0')
-				goto breakout;
-			headStr++;
-			break;
-		case '(':
-			incom++;
-			if(incom == 1)
-				continue;
-			break;
-		case ')':
-			incom--;
-			if(com && !incom){
-				s[ns] = '\0';
-				return s;
-			}
-			break;
-		default:
-			if(!incom)
-				goto breakout;
-			break;
-		}
-		if(com && incom && (c != ' ' || ns > 0 && s[ns-1] != ' ')){
-			s[ns++] = c;
-			if(ns + 1 >= as){	/* leave room for \c or 0 */
-				as += StrAlloc;
-				s = erealloc(s, as);
-			}
-		}
-	}
-breakout:;
-	free(s);
-	return nil;
-}
-
-/*
- * return the next non-white character
- */
-static int
-headChar(int eat)
-{
-	int c;
-
-	headSkipWhite(0);
-	c = *headStr;
-	if(eat && c != '\0' && c != '\n')
-		headStr++;
-	return c;
-}
-
-static void
-headToEnd(void)
-{
-	uint8_t *s;
-	int c;
-
-	for(;;){
-		s = headStr;
-		c = *s++;
-		while(c == '\r')
-			c = *s++;
-		if(c == '\n'){
-			c = *s++;
-			if(c != ' ' && c != '\t')
-				return;
-		}
-		if(c == '\0')
-			return;
-		headStr = s;
-	}
-}
-
-static void
-headSkip(void)
-{
-	int c;
-
-	while(c = *headStr){
-		headStr++;
-		if(c == '\n'){
-			c = *headStr;
-			if(c == ' ' || c == '\t')
-				continue;
-			return;
-		}
-	}
-}

+ 0 - 183
sys/src/cmd/ip/imap4d/mutf7.c

@@ -1,183 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include "imap4d.h"
-
-/*
- * modified utf-7, as per imap4 spec
- * like utf-7, but substitues , for / in base 64,
- * does not allow escaped ascii characters.
- *
- * /lib/rfc/rfc2152 is utf-7
- * /lib/rfc/rfc1642 is obsolete utf-7
- *
- * test sequences from rfc1642
- *	'A≢Α.'		'A&ImIDkQ-.'
- *	'Hi Mom ☺!"	'Hi Mom &Jjo-!'
- *	'日本語'		'&ZeVnLIqe-'
- */
-
-static uint8_t mt64d[256];
-static char mt64e[64];
-
-static void
-initm64(void)
-{
-	int c, i;
-
-	memset(mt64d, 255, 256);
-	memset(mt64e, '=', 64);
-	i = 0;
-	for(c = 'A'; c <= 'Z'; c++){
-		mt64e[i] = c;
-		mt64d[c] = i++;
-	}
-	for(c = 'a'; c <= 'z'; c++){
-		mt64e[i] = c;
-		mt64d[c] = i++;
-	}
-	for(c = '0'; c <= '9'; c++){
-		mt64e[i] = c;
-		mt64d[c] = i++;
-	}
-	mt64e[i] = '+';
-	mt64d['+'] = i++;
-	mt64e[i] = ',';
-	mt64d[','] = i;
-}
-
-int
-encmutf7(char *out, int lim, char *in)
-{
-	Rune rr;
-	uint32_t r, b;
-	char *start = out;
-	char *e = out + lim;
-	int nb;
-
-	if(mt64e[0] == 0)
-		initm64();
-	for(;;){
-		r = *(uint8_t*)in;
-
-		if(r < ' ' || r >= Runeself){
-			if(r == '\0')
-				break;
-			if(out + 1 >= e)
-				return -1;
-			*out++ = '&';
-			b = 0;
-			nb = 0;
-			for(;;){
-				in += chartorune(&rr, in);
-				r = rr;
-				if(r == '\0' || r >= ' ' && r < Runeself)
-					break;
-				b = (b << 16) | r;
-				for(nb += 16; nb >= 6; nb -= 6){
-					if(out + 1 >= e)
-						return -1;
-					*out++ = mt64e[(b>>(nb-6))&0x3f];
-				}
-			}
-			for(; nb >= 6; nb -= 6){
-				if(out + 1 >= e)
-					return -1;
-				*out++ = mt64e[(b>>(nb-6))&0x3f];
-			}
-			if(nb){
-				if(out + 1 >= e)
-					return -1;
-				*out++ = mt64e[(b<<(6-nb))&0x3f];
-			}
-
-			if(out + 1 >= e)
-				return -1;
-			*out++ = '-';
-			if(r == '\0')
-				break;
-		}else
-			in++;
-		if(out + 1 >= e)
-			return -1;
-		*out = r;
-		out++;
-		if(r == '&')
-			*out++ = '-';
-	}
-
-	if(out >= e)
-		return -1;
-	*out = '\0';
-	return out - start;
-}
-
-int
-decmutf7(char *out, int lim, char *in)
-{
-	Rune rr;
-	char *start = out;
-	char *e = out + lim;
-	int c, b, nb;
-
-	if(mt64e[0] == 0)
-		initm64();
-	for(;;){
-		c = *in;
-
-		if(c < ' ' || c >= Runeself){
-			if(c == '\0')
-				break;
-			return -1;
-		}
-		if(c != '&'){
-			if(out + 1 >= e)
-				return -1;
-			*out++ = c;
-			in++;
-			continue;
-		}
-		in++;
-		if(*in == '-'){
-			if(out + 1 >= e)
-				return -1;
-			*out++ = '&';
-			in++;
-			continue;
-		}
-
-		b = 0;
-		nb = 0;
-		while((c = *in++) != '-'){
-			c = mt64d[c];
-			if(c >= 64)
-				return -1;
-			b = (b << 6) | c;
-			nb += 6;
-			if(nb >= 16){
-				rr = b >> (nb - 16);
-				nb -= 16;
-				if(out + UTFmax + 1 >= e && out + runelen(rr) + 1 >= e)
-					return -1;
-				out += runetochar(out, &rr);
-			}
-		}
-		if(b & ((1 << nb) - 1))
-			return -1;
-	}
-
-	if(out >= e)
-		return -1;
-	*out = '\0';
-	return out - start;
-}

+ 0 - 223
sys/src/cmd/ip/imap4d/nodes.c

@@ -1,223 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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, uint32_t max, int uids,
-	int (*f)(Box*, Msg*, int, void*), void *rock)
-{
-	Msg *m;
-	uint32_t 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(uint32_t 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);
-}

+ 0 - 253
sys/src/cmd/ip/imap4d/search.c

@@ -1,253 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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;
-}

+ 0 - 136
sys/src/cmd/ip/imap4d/store.c

@@ -1,136 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#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;
-}
-
-uint32_t
-mapFlag(char *name)
-{
-	return mapInt(flagMap, name);
-}

+ 0 - 191
sys/src/cmd/ip/imap4d/utils.c

@@ -1,191 +0,0 @@
-/*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
-
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <auth.h>
-#include "imap4d.h"
-
-/*
- * reverse string [s:e) in place
- */
-void
-strrev(char *s, char *e)
-{
-	int c;
-
-	while(--e > s){
-		c = *s;
-		*s++ = *e;
-		*e = c;
-	}
-}
-
-int
-isdotdot(char *s)
-{
-	return s[0] == '.' && s[1] == '.' && (s[2] == '/' || s[2] == '\0');
-}
-
-int
-issuffix(char *suf, char *s)
-{
-	int n;
-
-	n = strlen(s) - strlen(suf);
-	if(n < 0)
-		return 0;
-	return strcmp(s + n, suf) == 0;
-}
-
-int
-isprefix(char *pre, char *s)
-{
-	return strncmp(pre, s, strlen(pre)) == 0;
-}
-
-int
-ciisprefix(char *pre, char *s)
-{
-	return cistrncmp(pre, s, strlen(pre)) == 0;
-}
-
-char*
-readFile(int fd)
-{
-	Dir *d;
-	int32_t length;
-	char *s;
-
-	d = dirfstat(fd);
-	if(d == nil)
-		return nil;
-	length = d->length;
-	free(d);
-	s = binalloc(&parseBin, length + 1, 0);
-	if(s == nil || read(fd, s, length) != length)
-		return nil;
-	s[length] = '\0';
-	return s;
-}
-
-/*
- * create the imap tmp file.
- * it just happens that we don't need multiple temporary files.
- */
-int
-imapTmp(void)
-{
-	char buf[ERRMAX], name[MboxNameLen];
-	int tries, fd;
-
-	snprint(name, sizeof(name), "/mail/box/%s/mbox.tmp.imp", username);
-	for(tries = 0; tries < LockSecs*2; tries++){
-		fd = create(name, ORDWR|ORCLOSE|OCEXEC, DMEXCL|0600);
-		if(fd >= 0)
-			return fd;
-		errstr(buf, sizeof buf);
-		if(cistrstr(buf, "locked") == nil)
-			break;
-		sleep(500);
-	}
-	return -1;
-}
-
-/*
- * open a file which might be locked.
- * if it is, spin until available
- */
-int
-openLocked(char *dir, char *file, int mode)
-{
-	char buf[ERRMAX];
-	int tries, fd;
-
-	for(tries = 0; tries < LockSecs*2; tries++){
-		fd = cdOpen(dir, file, mode);
-		if(fd >= 0)
-			return fd;
-		errstr(buf, sizeof buf);
-		if(cistrstr(buf, "locked") == nil)
-			break;
-		sleep(500);
-	}
-	return -1;
-}
-
-int
-fqid(int fd, Qid *qid)
-{
-	Dir *d;
-
-	d = dirfstat(fd);
-	if(d == nil)
-		return -1;
-	*qid = d->qid;
-	free(d);
-	return 0;
-}
-
-uint32_t
-mapInt(NamedInt *map, char *name)
-{
-	int i;
-
-	for(i = 0; map[i].name != nil; i++)
-		if(cistrcmp(map[i].name, name) == 0)
-			break;
-	return map[i].v;
-}
-
-char*
-estrdup(char *s)
-{
-	char *t;
-
-	t = emalloc(strlen(s) + 1);
-	strcpy(t, s);
-	return t;
-}
-
-void*
-emalloc(uint32_t n)
-{
-	void *p;
-
-	p = malloc(n);
-	if(p == nil)
-		bye("server out of memory");
-	setmalloctag(p, getcallerpc(&n));
-	return p;
-}
-
-void*
-ezmalloc(uint32_t n)
-{
-	void *p;
-
-	p = malloc(n);
-	if(p == nil)
-		bye("server out of memory");
-	setmalloctag(p, getcallerpc(&n));
-	memset(p, 0, n);
-	return p;
-}
-
-void*
-erealloc(void *p, uint32_t n)
-{
-	p = realloc(p, n);
-	if(p == nil)
-		bye("server out of memory");
-	setrealloctag(p, getcallerpc(&p));
-	return p;
-}

+ 0 - 1
sys/src/cmd/ip/ip.json

@@ -10,7 +10,6 @@
 			"dhcpd/dhcpleases.json",
 			"ftpfs/ftpfs.json",
 			"httpd/httpd.json",
-			"imap4d/imap4d.json",
 			"ipconfig/ipconfig.json",
 			"ppp/ppp.json"
 		],

+ 0 - 97
sys/src/cmd/ip/mkfile

@@ -1,97 +0,0 @@
-</$objtype/mkfile
-
-TARG = 	6in4\
-	dhcpclient\
-	ftpd\
-	gping\
-	hogports\
-	httpfile\
-	linklocal\
-	ping\
-	pppoe\
-	pptp\
-	pptpd\
-	rarpd\
-	rexexec\
-	rip\
-	rlogind\
-	telnet\
-	telnetd\
-	tftpd\
-	traceroute\
-	udpecho\
-	wol\
-
-DIRS=ftpfs dhcpd httpd ipconfig ppp imap4d snoopy
-
-BIN=/$objtype/bin/ip
-HFILES=dhcp.h arp.h glob.h icmp.h telnet.h
-
-UPDATE=\
-	mkfile\
-	$HFILES\
-	${OFILES:%.$O=%.c}\
-	${TARG:%=%.c}\
-
-</sys/src/cmd/mkmany
-
-all:V:	$DIRS
-
-$DIRS:V:
-	for (i in $DIRS) @{
-		echo mk $i
-		cd $i
-		mk all
-	}
-
-install:V:	installdirs
-
-installdirs:V:
-	for (i in $DIRS) @{
-		echo mk $i
-		cd $i
-		mk install
-	}
-
-update:V:
-	update $UPDATEFLAGS $UPDATE
-	for (i in $DIRS) @{
-		echo update $i
-		cd $i
-		mk 'UPDATEFLAGS='$"UPDATEFLAGS update
-	}
-
-clean:V:
-	for (i in $DIRS) @{
-		echo clean $i
-		cd $i
-		mk clean
-	}
-	rm -f [$OS].* *.[$OS]
-
-nuke:V:
-	for (i in $DIRS) @{
-		echo nuke $i
-		cd $i
-		mk nuke
-	}
-	rm -f *.[$OS] y.tab.? y.debug y.output [$OS].$TARG $TARG
-
-$O.fakearp:	fakearp.$O getether.$O
-	$LD -o $target $prereq
-
-telnetd.$O:	telnet.h
-
-telnet.$O:	telnet.h
-
-$O.ftpd:	ftpd.$O glob.$O
-	$LD -o $target $prereq
-
-$BIN/telnet:V:	$O.telnet
-	cp $prereq /$objtype/bin/telnet
-
-$BIN/snoopy:V:	$O.snoopy
-	cp $prereq /$objtype/bin/snoopy
-
-$BIN/sniffer:V:	$O.sniffer
-	cp $prereq /$objtype/bin/sniffer