📄 imap4.c
字号:
#include "common.h"#include <ctype.h>#include <plumb.h>#include <libsec.h>#include <auth.h>#include "dat.h"#pragma varargck argpos imap4cmd 2#pragma varargck type "Z" char*int doublequote(Fmt*);int pipeline = 1;static char Eio[] = "i/o error";typedef struct Imap Imap;struct Imap { char *freep; // free this to free the strings below char *host; char *user; char *mbox; int mustssl; int refreshtime; int debug; ulong tag; ulong validity; int nmsg; int size; char *base; char *data; vlong *uid; int nuid; int muid; Thumbprint *thumb; // open network connection Biobuf bin; Biobuf bout; int fd;};static char*removecr(char *s){ char *r, *w; for(r=w=s; *r; r++) if(*r != '\r') *w++ = *r; *w = '\0'; return s;}//// send imap4 command//static voidimap4cmd(Imap *imap, char *fmt, ...){ char buf[128], *p; va_list va; va_start(va, fmt); p = buf+sprint(buf, "9X%lud ", imap->tag); vseprint(p, buf+sizeof(buf), fmt, va); va_end(va); p = buf+strlen(buf); if(p > (buf+sizeof(buf)-3)) sysfatal("imap4 command too long"); if(imap->debug) fprint(2, "-> %s\n", buf); strcpy(p, "\r\n"); Bwrite(&imap->bout, buf, strlen(buf)); Bflush(&imap->bout);}enum { OK, NO, BAD, BYE, EXISTS, STATUS, FETCH, UNKNOWN,};static char *verblist[] = {[OK] "OK",[NO] "NO",[BAD] "BAD",[BYE] "BYE",[EXISTS] "EXISTS",[STATUS] "STATUS",[FETCH] "FETCH",};static intverbcode(char *verb){ int i; char *q; if(q = strchr(verb, ' ')) *q = '\0'; for(i=0; i<nelem(verblist); i++) if(verblist[i] && strcmp(verblist[i], verb)==0){ if(q) *q = ' '; return i; } if(q) *q = ' '; return UNKNOWN;}static voidstrupr(char *s){ for(; *s; s++) if('a' <= *s && *s <= 'z') *s += 'A'-'a';}static voidimapgrow(Imap *imap, int n){ int i; if(imap->data == nil){ imap->base = emalloc(n+1); imap->data = imap->base; imap->size = n+1; } if(n >= imap->size){ // friggin microsoft - reallocate i = imap->data - imap->base; imap->base = erealloc(imap->base, i+n+1); imap->data = imap->base + i; imap->size = n+1; }}//// get imap4 response line. there might be various // data or other informational lines mixed in.//static char*imap4resp(Imap *imap){ char *line, *p, *ep, *op, *q, *r, *en, *verb; int i, n; static char error[256]; while(p = Brdline(&imap->bin, '\n')){ ep = p+Blinelen(&imap->bin); while(ep > p && (ep[-1]=='\n' || ep[-1]=='\r')) *--ep = '\0'; if(imap->debug) fprint(2, "<- %s\n", p); strupr(p); switch(p[0]){ case '+': if(imap->tag == 0) fprint(2, "unexpected: %s\n", p); break; // ``unsolicited'' information; everything happens here. case '*': if(p[1]!=' ') continue; p += 2; line = p; n = strtol(p, &p, 10); if(*p==' ') p++; verb = p; if(p = strchr(verb, ' ')) p++; else p = verb+strlen(verb); switch(verbcode(verb)){ case OK: case NO: case BAD: // human readable text at p; break; case BYE: // early disconnect // human readable text at p; break; // * 32 EXISTS case EXISTS: imap->nmsg = n; break; // * STATUS Inbox (MESSAGES 2 UIDVALIDITY 960164964) case STATUS: if(q = strstr(p, "MESSAGES")) imap->nmsg = atoi(q+8); if(q = strstr(p, "UIDVALIDITY")) imap->validity = strtoul(q+11, 0, 10); break; case FETCH: // * 1 FETCH (uid 8889 RFC822.SIZE 3031 body[] {3031} // <3031 bytes of data> // ) if(strstr(p, "RFC822.SIZE") && strstr(p, "BODY[]")){ if((q = strchr(p, '{')) && (n=strtol(q+1, &en, 0), *en=='}')){ if(imap->data == nil || n >= imap->size) imapgrow(imap, n); if((i = Bread(&imap->bin, imap->data, n)) != n){ snprint(error, sizeof error, "short read %d != %d: %r\n", i, n); return error; } if(imap->debug) fprint(2, "<- read %d bytes\n", n); imap->data[n] = '\0'; if(imap->debug) fprint(2, "<- %s\n", imap->data); imap->data += n; imap->size -= n; p = Brdline(&imap->bin, '\n'); if(imap->debug) fprint(2, "<- ignoring %.*s\n", Blinelen(&imap->bin), p); }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){ *r = '\0'; q++; n = r-q; if(imap->data == nil || n >= imap->size) imapgrow(imap, n); memmove(imap->data, q, n); imap->data[n] = '\0'; imap->data += n; imap->size -= n; }else return "confused about FETCH response"; break; } // * 1 FETCH (UID 1 RFC822.SIZE 511) if(q=strstr(p, "RFC822.SIZE")){ imap->size = atoi(q+11); break; } // * 1 FETCH (UID 1 RFC822.HEADER {496} // <496 bytes of data> // ) // * 1 FETCH (UID 1 RFC822.HEADER "data") if(strstr(p, "RFC822.HEADER") || strstr(p, "RFC822.TEXT")){ if((q = strchr(p, '{')) && (n=strtol(q+1, &en, 0), *en=='}')){ if(imap->data == nil || n >= imap->size) imapgrow(imap, n); if((i = Bread(&imap->bin, imap->data, n)) != n){ snprint(error, sizeof error, "short read %d != %d: %r\n", i, n); return error; } if(imap->debug) fprint(2, "<- read %d bytes\n", n); imap->data[n] = '\0'; if(imap->debug) fprint(2, "<- %s\n", imap->data); imap->data += n; imap->size -= n; p = Brdline(&imap->bin, '\n'); if(imap->debug) fprint(2, "<- ignoring %.*s\n", Blinelen(&imap->bin), p); }else if((q = strchr(p, '"')) && (r = strchr(q+1, '"'))){ *r = '\0'; q++; n = r-q; if(imap->data == nil || n >= imap->size) imapgrow(imap, n); memmove(imap->data, q, n); imap->data[n] = '\0'; imap->data += n; imap->size -= n; }else return "confused about FETCH response"; break; } // * 1 FETCH (UID 1) // * 2 FETCH (UID 6) if(q = strstr(p, "UID")){ if(imap->nuid < imap->muid) imap->uid[imap->nuid++] = ((vlong)imap->validity<<32)|strtoul(q+3, nil, 10); break; } } if(imap->tag == 0) return line; break; case '9': // response to our message op = p; if(p[1]=='X' && strtoul(p+2, &p, 10)==imap->tag){ while(*p==' ') p++; imap->tag++; return p; } fprint(2, "expected %lud; got %s\n", imap->tag, op); break; default: if(imap->debug || *p) fprint(2, "unexpected line: %s\n", p); } } snprint(error, sizeof error, "i/o error: %r\n"); return error;}static intisokay(char *resp){ return strncmp(resp, "OK", 2)==0;}//// log in to IMAP4 server, select mailbox, no SSL at the moment//static char*imap4login(Imap *imap){ char *s; UserPasswd *up; imap->tag = 0; s = imap4resp(imap); if(!isokay(s)) return "error in initial IMAP handshake"; if(imap->user != nil) up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q user=%q", imap->host, imap->user); else up = auth_getuserpasswd(auth_getkey, "proto=pass service=imap server=%q", imap->host); if(up == nil) return "cannot find IMAP password"; imap->tag = 1; imap4cmd(imap, "LOGIN %Z %Z", up->user, up->passwd); free(up); if(!isokay(s = imap4resp(imap))) return s; imap4cmd(imap, "SELECT %Z", imap->mbox); if(!isokay(s = imap4resp(imap))) return s; return nil;}//// push tls onto a connection//intmypushtls(int fd){ int p[2]; char buf[10]; if(pipe(p) < 0) return -1; switch(fork()){ case -1: close(p[0]); close(p[1]); return -1; case 0: close(p[1]); dup(p[0], 0); dup(p[0], 1); sprint(buf, "/fd/%d", fd); execl("/bin/tlsrelay", "tlsrelay", "-f", buf, nil); _exits(nil); default: break; } close(fd); close(p[0]); return p[1];}//// dial and handshake with the imap server//static char*imap4dial(Imap *imap){ char *err, *port; uchar digest[SHA1dlen]; 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 geterrstr();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -