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;
}
/*
* parse the address in the unix header
* last line of defence, so must return something
*/
static Maddr *
unixfrom(char *s)
{
char *e, *t;
Maddr *a;
if(s == nil)
return nil;
headstr = (uchar*)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;
}
/*
* retrieve information from the unixheader file
*/
static int
msgunix(Msg *m, int top)
{
char *s, *ss;
Tm tm;
/*
* make sure the message has valid mime structure
* and sub-messages
*/
int
msgstruct(Msg *m, int top)
{
char buf[12];
int fd, ns, max;
Msg *k, head, *last;
if(m->kids != nil)
return 1;
if(m->expunged
|| !msginfo(m)
|| !msgheader(m, &m->mime, "mimeheader")){
msgdead(m);
return 0;
}
/* gack. we need to get the header from the subpart here. */
if(msgis822(&m->mime)){
free(m->ibuf);
m->info[0] = 0;
m->efs = seprint(m->efs, m->efs + 5, "/1/");
if(!msginfo(m)){
msgdead(m);
return 0;
}
}
if(!msgunix(m, top)
|| !msgbodysize(m)
|| (top || msgis822(&m->mime) || msgismulti(&m->mime)) && !msgheader(m, &m->head, "rawheader")){
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) && !msgis822(&m->head) && !msgis822(&m->mime)){
k = MKZ(Msg);
k->id = 1;
k->fsdir = m->fsdir;
k->parent = m->parent;
ns = m->efs - m->fs;
k->fs = emalloc(ns + (Filelen + 1));
memmove(k->fs, m->fs, ns);
k->efs = k->fs + ns;
*k->efs = '\0';
k->size = m->size;
m->kids = k;
return 1;
}
/*
* if kids fail, just whack them
*/
top = top && (msgis822(&m->head) || msgismulti(&m->head));
for(k = m->kids; k != nil; k = k->next)
if(!msgstruct(k, top)){
debuglog("kid fail %p %s", k, k->fs);
for(k = m->kids; k != nil; ){
last = k;
k = k->next;
freemsg(0, last);
}
m->kids = nil;
break;
}
return 1;
}
/*
* read in the message body to count \n without a preceding \r
*/
static int
msgbodysize(Msg *m)
{
char buf[Bufsize + 2], *s, *se;
uint length, size, lines, needr;
int n, fd, c;
Dir *d;
/*
* 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;
int dated, c;
long ns;
uint lines, n, nn;
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')
*t = ' ';
if(c != '\n')
continue;
if(t == s || t[-1] != '\r')
n++;
lines++;
}
if(t > s && t[-1] != '\n'){
if(t[-1] != '\r')
n++;
n++;
}
if(n > 0)
n += 2;
h->buf = emalloc(n + 1);
h->size = n;
h->lines = lines;
/*
* 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)
{
char *s, *e, *w, *personal;
uchar *hs;
int c;
Maddr *addr;
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);
/*
* 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;
}
/*
* find the ! in domain!rest, where domain must have at least
* one internal '.'
*/
static char*
dombang(char *s)
{
int dot, c;
/*
* 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++;
}
/*
* 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;
/*
* 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;
/*
* headtext : contents of rest of header line
*/
static char *
headtext(void)
{
uchar *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 done;
c = ' ';
break;
case '\\':
if(com && incom)
s[ns++] = c;
c = headstr[1];
if(c == '\0')
goto done;
headstr++;
break;
case '(':
incom++;
if(incom == 1)
continue;
break;
case ')':
incom--;
if(com && !incom){
s[ns] = '\0';
return s;
}
break;
default:
if(!incom)
goto done;
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);
}
}
}
done:
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)
{
uchar *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;
}
}