Esempio n. 1
0
//
// get capability list, possibly start tls
//
static char*
pop3capa(Pop *pop)
{
	char *s;
	int hastls;

	pop3cmd(pop, "CAPA");
	if(!isokay(pop3resp(pop)))
		return nil;

	hastls = 0;
	for(;;){
		s = pop3resp(pop);
		if(strcmp(s, ".") == 0 || strcmp(s, "unexpected eof") == 0)
			break;
		if(strcmp(s, "STLS") == 0)
			hastls = 1;
		if(strcmp(s, "PIPELINING") == 0)
			pop->pipeline = 1;
		if(strcmp(s, "EXPIRE 0") == 0)
			return "server does not allow mail to be left on server";
	}

	if(hastls && !pop->notls){
		pop3cmd(pop, "STLS");
		if(!isokay(s = pop3resp(pop)))
			return s;
		if((s = pop3pushtls(pop)) != nil)
			return s;
	}
	return nil;
}
Esempio n. 2
0
//
// log in to IMAP4 server, select mailbox, no SSL at the moment
//
static char*
imap4login(Imap *imap)
{
    char *s;
    UserPasswd *up;

    imap->tag = 0;
    s = imap4resp(imap);
    if(!isokay(s))
        return "error in initial IMAP handshake";

    if(imap->user != nil)
        up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user);
    else
        up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host);
    if(up == nil)
        return "cannot find IMAP password";

    imap->tag = 1;
    imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd);
    free(up);
    if(!isokay(s = imap4resp(imap)))
        return s;

    imap4cmd(imap, "SELECT %Z", imap->mbox);
    if(!isokay(s = imap4resp(imap)))
        return s;

    return nil;
}
Esempio n. 3
0
//
// dial and handshake with the imap server
//
static char*
imap4dial(Imap *imap)
{
    char *err, *port;
    int sfd;
    TLSconn conn;

    if(imap->fd >= 0) {
        imap4cmd(imap, "noop");
        if(isokay(imap4resp(imap)))
            return nil;
        close(imap->fd);
        imap->fd = -1;
    }

    if(imap->mustssl)
        port = "imaps";
    else
        port = "imap";

    if((imap->fd = dial(netmkaddr(imap->host, "net", port), 0, 0, 0)) < 0)
        return imaperrstr(imap->host, port);

    if(imap->mustssl) {
        sfd = starttls(imap, &conn);
        if (sfd < 0) {
            free(conn.cert);
            return imaperrstr(imap->host, port);
        }
        if(imap->debug) {
            char fn[128];
            int fd;

            snprint(fn, sizeof fn, "%s/ctl", conn.dir);
            fd = open(fn, ORDWR);
            if(fd < 0)
                fprint(2, "opening ctl: %r\n");
            if(fprint(fd, "debug") < 0)
                fprint(2, "writing ctl: %r\n");
            close(fd);
        }
    }
    Binit(&imap->bin, imap->fd, OREAD);
    Binit(&imap->bout, imap->fd, OWRITE);

    if(err = imap4login(imap)) {
        close(imap->fd);
        return err;
    }

    return nil;
}
Esempio n. 4
0
//
// sync mailbox
//
static void
imap4purge(Imap *imap, Mailbox *mb)
{
    int ndel;
    Message *m, *next;

    ndel = 0;
    for(m=mb->root->part; m!=nil; m=next) {
        next = m->next;
        if(m->deleted && m->refs==0) {
            if(m->inmbox && (ulong)(m->imapuid>>32)==imap->validity) {
                imap4cmd(imap, "UID STORE %lud +FLAGS (\\Deleted)", (ulong)m->imapuid);
                if(isokay(imap4resp(imap))) {
                    ndel++;
                    delmessage(mb, m);
                }
            } else
                delmessage(mb, m);
        }
    }
Esempio n. 5
0
//
// delete marked messages
//
static void
pop3purge(Pop *pop, Mailbox *mb)
{
	Message *m, *next;

	if(pop->pipeline){
		switch(rfork(RFPROC|RFMEM)){
		case -1:
			fprint(2, "rfork: %r\n");
			pop->pipeline = 0;

		default:
			break;

		case 0:
			for(m = mb->root->part; m != nil; m = next){
				next = m->next;
				if(m->deleted && m->refs == 0){
					if(m->inmbox)
						Bprint(&pop->bout, "DELE %d\r\n", m->mesgno);
				}
			}
			Bflush(&pop->bout);
			_exits(nil);
		}
	}
	for(m = mb->root->part; m != nil; m = next) {
		next = m->next;
		if(m->deleted && m->refs == 0) {
			if(m->inmbox) {
				if(!pop->pipeline)
					pop3cmd(pop, "DELE %d", m->mesgno);
				if(isokay(pop3resp(pop)))
					delmessage(mb, m);
			} else
				delmessage(mb, m);
		}
	}
}
Esempio n. 6
0
//
// download a single message
//
static char*
imap4fetch(Mailbox *mb, Message *m)
{
    int i;
    char *p, *s, sdigest[2*SHA1dlen+1];
    Imap *imap;

    imap = mb->aux;

    imap->size = 0;

    if(!isokay(s = imap4resp(imap)))
        return s;

    p = imap->base;
    if(p == nil)
        return "did not get message body";

    removecr(p);
    free(m->start);
    m->start = p;
    m->end = p+strlen(p);
    m->bend = m->rbend = m->end;
    m->header = m->start;

    imap->base = nil;
    imap->data = nil;

    parse(m, 0, mb, 1);

    // digest headers
    sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
    for(i = 0; i < SHA1dlen; i++)
        sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
    m->sdigest = s_copy(sdigest);

    return nil;
}
Esempio n. 7
0
//
// check for new messages on pop server
// UIDL is not required by RFC 1939, but 
// netscape requires it, so almost every server supports it.
// we'll use it to make our lives easier.
//
static char*
pop3read(Pop *pop, Mailbox *mb, int doplumb)
{
	char *s, *p, *uidl, *f[2];
	int mesgno, ignore, nnew;
	Message *m, *next, **l;

	// Some POP servers disallow UIDL if the maildrop is empty.
	pop3cmd(pop, "STAT");
	if(!isokay(s = pop3resp(pop)))
		return s;

	// fetch message listing; note messages to grab
	l = &mb->root->part;
	if(strncmp(s, "+OK 0 ", 6) != 0) {
		pop3cmd(pop, "UIDL");
		if(!isokay(s = pop3resp(pop)))
			return s;

		for(;;){
			p = pop3resp(pop);
			if(strcmp(p, ".") == 0 || strcmp(p, "unexpected eof") == 0)
				break;

			if(tokenize(p, f, 2) != 2)
				continue;

			mesgno = atoi(f[0]);
			uidl = f[1];
			if(strlen(uidl) > 75)	// RFC 1939 says 70 characters max
				continue;

			ignore = 0;
			while(*l != nil) {
				if(strcmp((*l)->uidl, uidl) == 0) {
					// matches mail we already have, note mesgno for deletion
					(*l)->mesgno = mesgno;
					ignore = 1;
					l = &(*l)->next;
					break;
				} else {
					// old mail no longer in box mark deleted
					if(doplumb)
						mailplumb(mb, *l, 1);
					(*l)->inmbox = 0;
					(*l)->deleted = 1;
					l = &(*l)->next;
				}
			}
			if(ignore)
				continue;

			m = newmessage(mb->root);
			m->mallocd = 1;
			m->inmbox = 1;
			m->mesgno = mesgno;
			strcpy(m->uidl, uidl);

			// chain in; will fill in message later
			*l = m;
			l = &m->next;
		}
	}

	// whatever is left has been removed from the mbox, mark as deleted
	while(*l != nil) {
		if(doplumb)
			mailplumb(mb, *l, 1);
		(*l)->inmbox = 0;
		(*l)->deleted = 1;
		l = &(*l)->next;
	}

	// download new messages
	nnew = 0;
	if(pop->pipeline){
		switch(rfork(RFPROC|RFMEM)){
		case -1:
			fprint(2, "rfork: %r\n");
			pop->pipeline = 0;

		default:
			break;

		case 0:
			for(m = mb->root->part; m != nil; m = m->next){
				if(m->start != nil)
					continue;
				Bprint(&pop->bout, "LIST %d\r\nRETR %d\r\n", m->mesgno, m->mesgno);
			}
			Bflush(&pop->bout);
			_exits(nil);
		}
	}

	for(m = mb->root->part; m != nil; m = next) {
		next = m->next;

		if(m->start != nil)
			continue;

		if(s = pop3download(pop, m)) {
			// message disappeared? unchain
			fprint(2, "download %d: %s\n", m->mesgno, s);
			delmessage(mb, m);
			mb->root->subname--;
			continue;
		}
		nnew++;
		parse(m, 0, mb, 1);

		if(doplumb)
			mailplumb(mb, m, 0);
	}
	if(pop->pipeline)
		waitpid();

	if(nnew || mb->vers == 0) {
		mb->vers++;
		henter(PATH(0, Qtop), mb->name,
			(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
	}

	return nil;	
}
Esempio n. 8
0
//
// download a single message
//
static char*
pop3download(Pop *pop, Message *m)
{
	char *s, *f[3], *wp, *ep;
	char sdigest[SHA1dlen*2+1];
	int i, l, sz;

	if(!pop->pipeline)
		pop3cmd(pop, "LIST %d", m->mesgno);
	if(!isokay(s = pop3resp(pop)))
		return s;

	if(tokenize(s, f, 3) != 3)
		return "syntax error in LIST response";

	if(atoi(f[1]) != m->mesgno)
		return "out of sync with pop3 server";

	sz = atoi(f[2])+200;	/* 200 because the plan9 pop3 server lies */
	if(sz == 0)
		return "invalid size in LIST response";

	m->start = wp = emalloc(sz+1);
	ep = wp+sz;

	if(!pop->pipeline)
		pop3cmd(pop, "RETR %d", m->mesgno);
	if(!isokay(s = pop3resp(pop))) {
		m->start = nil;
		free(wp);
		return s;
	}

	s = nil;
	while(wp <= ep) {
		s = pop3resp(pop);
		if(strcmp(s, "unexpected eof") == 0) {
			free(m->start);
			m->start = nil;
			return "unexpected end of conversation";
		}
		if(strcmp(s, ".") == 0)
			break;

		l = strlen(s)+1;
		if(s[0] == '.') {
			s++;
			l--;
		}
		/*
		 * grow by 10%/200bytes - some servers
		 *  lie about message sizes
		 */
		if(wp+l > ep) {
			int pos = wp - m->start;
			sz += ((sz / 10) < 200)? 200: sz/10;
			m->start = erealloc(m->start, sz+1);
			wp = m->start+pos;
			ep = m->start+sz;
		}
		memmove(wp, s, l-1);
		wp[l-1] = '\n';
		wp += l;
	}

	if(s == nil || strcmp(s, ".") != 0)
		return "out of sync with pop3 server";

	m->end = wp;

	// make sure there's a trailing null
	// (helps in body searches)
	*m->end = 0;
	m->bend = m->rbend = m->end;
	m->header = m->start;

	// digest message
	sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
	for(i = 0; i < SHA1dlen; i++)
		sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
	m->sdigest = s_copy(sdigest);

	return nil;
}
Esempio n. 9
0
//
// log in using APOP if possible, password if allowed by user
//
static char*
pop3login(Pop *pop)
{
	int n;
	char *s, *p, *q;
	char ubuf[128], user[128];
	char buf[500];
	UserPasswd *up;

	s = pop3resp(pop);
	if(!isokay(s))
		return "error in initial handshake";

	if(pop->user)
		snprint(ubuf, sizeof ubuf, " user=%q", pop->user);
	else
		ubuf[0] = '\0';

	// look for apop banner
	if(pop->ppop==0 && (p = strchr(s, '<')) && (q = strchr(p+1, '>'))) {
		*++q = '\0';
		if((n=auth_respond(p, q-p, user, sizeof user, buf, sizeof buf, auth_getkey, "proto=apop role=client server=%q%s",
			pop->host, ubuf)) < 0)
			return "factotum failed";
		if(user[0]=='\0')
			return "factotum did not return a user name";

		if(s = pop3capa(pop))
			return s;

		pop3cmd(pop, "APOP %s %.*s", user, n, buf);
		if(!isokay(s = pop3resp(pop)))
			return s;

		return nil;
	} else {
		if(pop->ppop == 0)
			return "no APOP hdr from server";

		if(s = pop3capa(pop))
			return s;

		if(pop->needtls && !pop->encrypted)
			return "could not negotiate TLS";

		up = auth_getuserpasswd(auth_getkey, "proto=pass service=pop dom=%q%s",
			pop->host, ubuf);
		if(up == nil)
			return "no usable keys found";

		pop3cmd(pop, "USER %s", up->user);
		if(!isokay(s = pop3resp(pop))){
			free(up);
			return s;
		}
		pop3cmd(pop, "PASS %s", up->passwd);
		free(up);
		if(!isokay(s = pop3resp(pop)))
			return s;

		return nil;
	}
}
Esempio n. 10
0
//
// check for new messages on imap4 server
// download new messages, mark deleted messages
//
static char*
imap4read(Imap *imap, Mailbox *mb, int doplumb)
{
    char *s;
    int i, ignore, nnew, t;
    Message *m, *next, **l;

    imap4cmd(imap, "STATUS %Z (MESSAGES UIDVALIDITY)", imap->mbox);
    if(!isokay(s = imap4resp(imap)))
        return s;

    imap->nuid = 0;
    imap->uid = erealloc(imap->uid, imap->nmsg*sizeof(imap->uid[0]));
    imap->muid = imap->nmsg;

    if(imap->nmsg > 0) {
        imap4cmd(imap, "UID FETCH 1:* UID");
        if(!isokay(s = imap4resp(imap)))
            return s;
    }

    l = &mb->root->part;
    for(i=0; i<imap->nuid; i++) {
        ignore = 0;
        while(*l != nil) {
            if((*l)->imapuid == imap->uid[i]) {
                ignore = 1;
                l = &(*l)->next;
                break;
            } else {
                // old mail, we don't have it anymore
                if(doplumb)
                    mailplumb(mb, *l, 1);
                (*l)->inmbox = 0;
                (*l)->deleted = 1;
                l = &(*l)->next;
            }
        }
        if(ignore)
            continue;

        // new message
        m = newmessage(mb->root);
        m->mallocd = 1;
        m->inmbox = 1;
        m->imapuid = imap->uid[i];

        // add to chain, will download soon
        *l = m;
        l = &m->next;
    }

    // whatever is left at the end of the chain is gone
    while(*l != nil) {
        if(doplumb)
            mailplumb(mb, *l, 1);
        (*l)->inmbox = 0;
        (*l)->deleted = 1;
        l = &(*l)->next;
    }

    // download new messages
    t = imap->tag;
    if(pipeline)
        switch(rfork(RFPROC|RFMEM)) {
        case -1:
            sysfatal("rfork: %r");
        default:
            break;
        case 0:
            for(m = mb->root->part; m != nil; m = m->next) {
                if(m->start != nil)
                    continue;
                if(imap->debug)
                    fprint(2, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
                           t, (ulong)m->imapuid);
                Bprint(&imap->bout, "9X%d UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
                       t++, (ulong)m->imapuid);
            }
            Bflush(&imap->bout);
            _exits(nil);
        }

    nnew = 0;
    for(m=mb->root->part; m!=nil; m=next) {
        next = m->next;
        if(m->start != nil)
            continue;

        if(!pipeline) {
            Bprint(&imap->bout, "9X%lud UID FETCH %lud (UID RFC822.SIZE BODY[])\r\n",
                   (ulong)imap->tag, (ulong)m->imapuid);
            Bflush(&imap->bout);
        }

        if(s = imap4fetch(mb, m)) {
            // message disappeared?  unchain
            fprint(2, "download %lud: %s\n", (ulong)m->imapuid, s);
            delmessage(mb, m);
            mb->root->subname--;
            continue;
        }
        nnew++;
        if(doplumb)
            mailplumb(mb, m, 0);
    }
    if(pipeline)
        waitpid();

    if(nnew || mb->vers == 0) {
        mb->vers++;
        henter(PATH(0, Qtop), mb->name,
        (Qid) {
            PATH(mb->id, Qmbox), mb->vers, QTDIR
        }, nil, mb);
    }
    return nil;
}