/************************************************************************ * cucipop - The Cubic Circle POP3 daemon implementation * * * * A fully compliant RFC1939 POP3 daemon implementation. * * Features: light load, fast query path, NFS resistant, * * in-situ mailbox rewrite, no tempfiles, short locking, * * standard BSD mailbox format, SysV Content-Length extension * * * * Copyright (c) 1996-1998, S.R. van den Berg, The Netherlands * * #include "README" * ************************************************************************/ #ifdef RCS static /*const*/char rcsid[]= "$Id: cucipop.c,v 1.31 1998/05/13 16:57:39 srb Exp $"; #endif #include "patchlevel.h" #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #endif #include "cucipop.h" #include "atotime.h" #include "authenticate.h" #include "locking.h" #include "dbops.h" #include "hsort.h" #ifdef APOP #include #endif #define MD5_DIGLEN 16 #define BIN2HEXLEN (MD5_DIGLEN*2) #define Signal(sig,fn) signal(sig,(void(*)())(fn)) #define Vuser (auth_logname?auth_logname:user) #define Bintohex(arg,checkendian) (sizeof(arg)>MD5_DIGLEN?\ (fail(#arg" too large"),terminate(EX_SOFTWARE),""):\ bintohex(&(arg),sizeof(arg),checkendian)) static const char*const nullp,From_[]=FROM,XFrom_[]=XFROM,cucipopn[]="cucipop", Received[]= "Received: by %s (mbox %s)\r\n (with Cubic Circle's %s (%s) %.24s)\r\n", version[]=VERSION,bulletins_path[]=BULLETINS_PATH; static char linebuf[LINEBUFLEN+1],*arg1,*arg2,user[ARGLEN+1],*host,*timestamp; static FILE*fmbox,*sockout; static size_t hostnduser; FILE*sockin; long spoofcheck; #ifdef INET6 static struct sockaddr_storage peername; #else static struct sockaddr_in peername; #endif static int namelen; struct msg { size_t order; off_t start; int file; time_t mtime; #ifdef UIDL uidl_t uidl; unsigned statskip; #endif long virtsize,realsize; unsigned char deleted,tooold; }*msgs; #define MSGS(i) (msgs[msgs[i].order]) static size_t msgs_filled,priv_msgs_filled,msgs_max,totmsgs,transfmsgs; static long totsize,transfsize; static off_t oldend; static unsigned audit,berkeleyformat; static time_t tsabotage,tsessionstart; static const char*mailbox; static struct stateinfo si; #define TBUL_READ(x) (si.readbulletin[(x)/8]&1<<((x)&7)) #define SBUL_READ(x) (si.readbulletin[(x)/8]|=1<<((x)&7)) #define SBUL_UNREAD(x) (si.readbulletin[(x)/8]&=~(1<<((x)&7))) static void bailout(void); static void zombiecollect(void) { while(waitpid((pid_t)-1,(int*)0,WNOHANG)>0); /* collect any zombies */ } void blocksignals(void) { signal(SIGHUP,SIG_IGN);signal(SIGINT,SIG_IGN);signal(SIGTERM,SIG_IGN); signal(SIGPIPE,SIG_IGN);signal(SIGALRM,SIG_IGN); #ifdef SIGURG signal(SIGURG,SIG_IGN); #endif } void setsignals(void) { Signal(SIGHUP,bailout);Signal(SIGINT,bailout);Signal(SIGTERM,bailout); Signal(SIGPIPE,bailout);Signal(SIGALRM,bailout); #ifdef SIGURG Signal(SIGURG,bailout); #endif } static size_t findmsg(void) { size_t i; return arg1&&(i=strtol(arg1,(char**)0,10)-1)sin_addr.s_addr; sprintf(ip,"%d.%d.%d.%d",p[0],p[1],p[2],p[3]); return ip; } static const char*peer(void) { return iptoascii(&peername); } #endif void logfinal(int level,int lost,unsigned autodel) { if(!*user||transfsize||totmsgs||audit) syslog(level,"%s%s %s %ld, %s%ld (%ld), %ld (%ld)",lost?"lost ":"",Vuser, peer(),(long)(time((time_t*)0)-tsessionstart),autodel?"-":"", (long)transfmsgs,transfsize,(long)totmsgs,totsize); } static void terminate(int code) { unlockmbox(); if(transfmsgs) { struct stateinfo*sip; if(sip=getstate(Vuser,tsessionstart)) si=*sip; si.lastabort=tsessionstart; if(!si.lastsuccess) si.lastsuccess=tsessionstart; if(si.brokenstate+1>si.brokenstate) si.brokenstate++; putstate(Vuser,&si); } closedb();exitappdb();logfinal(LOG_NOTICE,1,0);exit(code); } static void bailout(void) { const static char abnormal[]="-ERR Abnormal shutdown\r\n"; blocksignals();write(1,abnormal,STRLEN(abnormal));terminate(EX_NOINPUT); } void castlower(char*p) { for(;*p=tolower(*p);p++); } static int cmpbuf(const char*p) { return!strcmp(p,linebuf); } static int readcommand(unsigned autodel) { fflush(sockout);alarm(TIMEOUT); if(!fgets(linebuf,LINEBUFLEN+1,sockin)) { if(autodel) return 0; terminate(EX_PROTOCOL); } alarm(TIMEOUT);arg2=0; if((arg1=strchr(linebuf,'\r'))||(arg1=strchr(linebuf,'\n'))) *arg1='\0'; if(arg1=strchr(linebuf,' ')) { *arg1++='\0'; if(arg2=strchr(arg1,' ')) { *arg2++='\0'; if(strlen(arg2)>ARGLEN) arg2[ARGLEN]='\0'; } /* some silly popclient called E-mail connection */ else if(!*arg1) /* adds spurious spaces */ { arg1=0; /* at the end of the command line */ goto noarg1; } if(strlen(arg1)>ARGLEN) arg1[ARGLEN]='\0'; } noarg1: castlower(linebuf); return 1; } static void fail(const char*msg) { fprintf(sockout,"-ERR %s\r\n",msg); } static void loginvalcmd(void) { static const char noarg[]="-"; syslog(LOG_DEBUG,"Invalid command %s %s %s",linebuf,arg1?arg1:noarg, arg2?arg2:noarg); } static void invalcmd(const char*cmds) { loginvalcmd(); fprintf(sockout,"-ERR Invalid command, try one of: %s\r\n",cmds); } static void outofmem(void) { syslog(LOG_ALERT,"out of memory"); if(sockout) fail("Out of memory"); terminate(EX_OSERR); } void wipestring(char*string) { while(*string) *string++='\0'; } static const auth_identity*transmogrify(const auth_identity*pass) { initappdb();opendb(); return setuid(auth_whatuid(pass))?(auth_identity*)0:pass; } static const auth_identity*checkpw(const char*pw) { const auth_identity*pass;int valid=0; if(!(pass=auth_finduser(user,fileno(sockin)))||auth_whatuid(pass)1000 #error Increase the buf array length if you want to do this #endif sprintf(buf,"%02d",bullno); return buf; } FILE*openbulletin(const int extrafile,FILE*fp) { if(extrafile>=0) fp=fopen(bullname(extrafile),"rb"); return fp; } static void scanfile(const int extrafile,FILE*fp,const time_t agecutoff) { size_t virtsize,extra,omf;int fdsockin;off_t start,end;uidl_t uidl;long clen; unsigned inheader,statskip; clen=end=start=statskip=0;fdsockin=-1;omf=msgs_filled; virtsize=extra=STRLEN(XFrom_)-STRLEN(From_)+STRLEN(Received)+hostnduser-2*2+ STRLEN(cucipopn)-2+STRLEN(version)-2+24-5; if(!(fp=openbulletin(extrafile,fp))) if(extrafile<0) goto nomailbox; else return; if(extrafile<0) end=oldend,fdsockin=fileno(sockin); else { struct stat stbuf; fstat(fileno(fp),&stbuf);end=stbuf.st_size; } fcntl(fdsockin,F_SETFL,(long)O_NONBLOCK); /* non-blocking spoof enable */ for(;;) { unsigned newlines=1; if(clen<=0) { const char*p;int c; for(p=From_;;) { if(!end) goto nomailbox; end--; if(*p!=(c=getc(fp))) { ungetc(c,fp);end++; break; } newlines=2; if(!*++p) { struct msg*msp; inheader=1;addblock(start);(msp=msgs+msgs_filled-1)->deleted=0; msp->tooold=1;msp->file=extrafile;msp->mtime=agecutoff; if(msgs_filled-omf>1) { msp--; totsize+=msp->virtsize=virtsize+ (msp->realsize=start-msp->start); #ifdef UIDL msp->uidl=uidl;msp->statskip=statskip; #endif } statskip=0;uidl=tsabotage;virtsize=extra; #define DATELEN 20 if(end>DATELEN+4) { char circ[DATELEN],circo[DATELEN+1];unsigned rot,frot; for(circo[DATELEN]=frot=rot=0;;rot=(rot+1)%DATELEN) { if(!end) goto nomailbox; end--;virtsize++;start++; if((c=getc(fp))=='\n') break; circ[rot]=c;frot++; } if(frot>=DATELEN) { for(frot=0;frot=0) { if(tsabotage) uidl=0; msp->tooold=0; } msp->mtime=mtime; } } } break; } } start+=p-From_; } nomailbox: ;{ off_t oend;register int c; oend=end; do { if(!end--) goto thatsall; if(!inheader) clen--; c=getc(fp); if(spoofcheck>=0&&!(++spoofcheck&SPOOFMASK)) { char c; spoofcheck=0; switch(read(fdsockin,&c,1)) { case 1:spoofcheck=-1;ungetc(c,sockin); break; case 0: goto thatsall; } } trynl: #ifdef UIDL uidl=uidl*67067L+(unsigned char)c; #endif if(c=='\n') isnl: { virtsize++; if(--newlines&&inheader) { static const char contlength[]=CONTLENGTH, statfield[]=STATUSFIELD,xstatfield[]=XSTATUSFIELD; const char*p,*ps,*psx;unsigned skiplen;uidl_t luidl; p=berkeleyformat?0:contlength;ps=statfield;psx=xstatfield; for(skiplen=0,luidl=uidl;;) { if(!end) goto thatsall; end--;c=getc(fp); if(p&&*p!=tolower(c)) p=0; if(ps&&*ps!=tolower(c)) ps=0; if(psx&&*psx!=tolower(c)) psx=0; if(!p&&!ps&&!psx) { uidl=luidl; goto trynl; } #ifdef UIDL luidl=luidl*67067L+(unsigned char)c; #endif newlines=2;skiplen++; if(ps&&!*++ps||psx&&!*++psx) { statskip+=skiplen+1; do { if(!end) goto thatsall; end--;statskip++; } while(fgetc(fp)!='\n'); goto isnl; } if(p&&!*++p) { for(;;) { if(!end) goto thatsall; end--; switch(c=fgetc(fp)) { case ' ':case '\t':continue; } break; } for(clen=0;;end--,c=fgetc(fp)) { if(c<'0'||c>'9') goto trynl; clen=((unsigned long)clen*10)+c-'0'; if(!end) /* memory requirements to a perfect fit */ thatsall: { struct msg*msp; fcntl(fdsockin,F_SETFL,0L); /* block again */ if(clen>0) syslog(LOG_WARNING,"%s mailbox %ld short", mailbox,(long)clen); msp=msgs+msgs_filled;start+=oend;msp->deleted=1; if(msgs_filled>omf) { msp--; totsize+=msp->virtsize=virtsize+ (msp->realsize=start-msp->start); #ifdef UIDL msp->uidl=uidl;msp->statskip=statskip; #endif } if(extrafile>=0) fclose(fp); return; } } } } } if(!newlines&&!inheader&&clen>0) newlines=1; } else newlines=2; } while(newlines); inheader=0;start+=oend-end; } } } static int mcmp(const struct msg*a,const struct msg*b) { a=msgs+a->order;b=msgs+b->order; switch(si.brokenstate) { case 0: return -(a->orderorder); case 1: return -(a->realsizerealsize); default: if(tsessionstart-a->mtimemtimemtime>b->mtime); case 2: { unsigned sa=a->realsize/BSIZECATEGORY,sb=b->realsize/BSIZECATEGORY; return sasb?1:-(a->mtime>b->mtime); } } } static void mswap(struct msg*a,struct msg*b) { int t; t=a->order;a->order=b->order;b->order=t; } static int openmailbox(const auth_identity*pass, const time_t agecutoff) { FILE*fp; alarm(0); if(!(fmbox=fp=fopen(mailbox=auth_mailboxname((auth_identity*)pass),"r+b"))) { if(errno==ENOENT) goto nomailbox; /* no mailbox found */ return 0; } ungetc(fgetc(fp),fp); /* prime atime */ ;{ struct stat stbuf;int fd; fstat(fd=fileno(fp),&stbuf); if(stbuf.st_atime-stbuf.st_mtime<32) /* modification in progress? */ { if(!lockmbox(fd,mailbox)) return 0; fstat(fd,&stbuf);unlockmbox();setsignals(); } oldend=stbuf.st_size; } nomailbox: ;{ struct stateinfo*sip; if(sip=getstate(Vuser,tsessionstart)) { unsigned i; si=*sip; for(i=priv_msgs_filled;i--;) { unsigned b=msgs[i].file; if(TBUL_READ(b)) if(msgs[i].mtime-si.lastsuccess>=0) SBUL_UNREAD(b); else { msgs[i]=msgs[--priv_msgs_filled];msgs[i].order=i; --msgs_filled; } } } } scanfile(-1,fp,agecutoff); if(si.brokenstate>0) hsort(msgs,msgs_filled,sizeof*msgs,mcmp,mswap); totmsgs=msgs_filled; return 1; } void checkbulletins() { if(!chdir(bulletins_path)) { struct stat stbuf;static time_t lastmods;static int nextextrafile=-1; if(nextextrafile>=0) { if(!stat(bullname(nextextrafile),&stbuf)&&stbuf.st_mtime>lastmods) goto scanem; } else { int bul; scanem: for(msgs_filled=0,bul=MAXBULLETINS;bul--;) if(!stat(bullname(bul),&stbuf)) { time_t mtime=stbuf.st_mtime; if(!lastmods||mtime-lastmods>0) lastmods=mtime; scanfile(bul,0,mtime); } priv_msgs_filled=msgs_filled; } if(nextextrafile<=0) nextextrafile=MAXBULLETINS; nextextrafile--; } chdir(ROOTDIR); } static int rread(const int fd,char*a,const unsigned len) { int i;size_t left=len; do while(0>(i=read(fd,a,left))&&errno==EINTR); while(i>0&&(a+=i,left-=i)); return len-left; } static int rwrite(const int fd,char*a,const unsigned len) { int i;size_t left=len; do while(0>(i=write(fd,a,left))&&errno==EINTR); while(i>0&&(a+=i,left-=i)); return len-left; } #ifdef UIDL static void printuidl(size_t i) { size_t sz=MSGS(i).virtsize-hostnduser-MSGS(i).statskip; (sizeof MSGS(i).uidl+sizeof MSGS(i).virtsize)*2>UIDLLEN? fail("uidl too long"),terminate(EX_SOFTWARE),0:0; fprintf(sockout,"%ld %s",(long)(i+1),Bintohex(MSGS(i).uidl,1)); fprintf(sockout,"%s\r\n",Bintohex(sz,1)); } #endif void printusg(void) { fprintf(stderr,USAGE,cucipopn); } main(int argc,const char*const argv[]) { unsigned loginattempts=0,quiet=0,debug=0;short port=POP3_PORT; unsigned douser=1,douidl=1,doapop=1,dotop=1,autodel=0,sabotage=0; static const char tdotnl[]=".\r\n";time_t agecutoff=-AGETOLERANCE; openlog(cucipopn,LOG_PID,LOG_FACILITY);sockin=stdin;sockout=stdout; if(!(msgs=malloc(GROWSTEP*sizeof*msgs))) outofmem(); msgs_max=GROWSTEP; #ifdef SIGXCPU signal(SIGXCPU,SIG_IGN); #endif #ifdef SIGXFSZ signal(SIGXFSZ,SIG_IGN); #endif if(argc) { const char*chp; #define Qnext_arg() if(!*chp&&!(chp=*++argv))goto usg while(chp=*++argv) { if(*chp++!='-') goto usg; for(;;) { switch(argc=*chp++) { case DEBUG:debug=1; continue; case QUIET:quiet=1; continue; case AUDIT:audit=1; continue; case AUTODELETE:autodel=1; continue; case BERKELEYFORMAT:berkeleyformat=1; continue; case SABOTAGE_UIDL:sabotage=1; continue; case USERF:douser=0; continue; case UIDLF:douidl=0; continue; case APOPF:doapop=0; continue; case TOPF:dotop=0; continue; case FVERSION: fprintf(stderr,"%s %s, Cubic Circle's POP3 daemon (RFC1939)" FULLVERSION "Locking strategies:\t" #ifdef USEdot_lock "dotlocking" #endif #ifdef USEfcntl_lock " fcntl()" #endif #ifdef USElockf " lockf()" #endif #ifdef USEflock " flock()" #endif "\nOptional commands:\t" #ifdef USER "USER/PASS" #endif #ifdef APOP " APOP" #endif #ifdef TOP " TOP" #endif #ifdef UIDL " UIDL" #endif "\nSystem mailbox:\t\t%s\n" #ifdef USE_DB "Virtual pop database:\t%s\n" "Virtual pop user:\t%s\n" "Housekeeping tree:\t%s\n" "Bulletin subdirectory:\t%s\t00-%02d\n" "State database:\t\t%s/%s\n" #endif ,cucipopn,version, auth_mailboxname(auth_finduid(getuid(),0)),virtualhostdb, virtualuser,cplib,bulletins_path,MAXBULLETINS-1,cplib, statedbfile); return EXIT_SUCCESS; usg: default:printusg(); return EX_USAGE; case HELP1:case HELP2:printusg(); fprintf(stderr,XUSAGE,POP3_PORT); return EX_USAGE; case EXPIRE:Qnext_arg();agecutoff=atosec(chp,&chp); continue; case PORT:Qnext_arg();port=strtol(chp,(char**)&chp,10); continue; case '\0':; } break; } } } ;{ namelen=sizeof peername; if(getpeername(fileno(sockin),(struct sockaddr*)&peername,&namelen)&& !debug&&(errno==ENOTSOCK||errno==EINVAL)) { int serverfd,curfd; signal(SIGHUP,SIG_IGN);signal(SIGPIPE,SIG_IGN);fclose(stdin); fclose(stdout);serverfd=socket(PF_INET,SOCK_STREAM,TCP_PROT); #ifdef INET6 /* XXX assumes INET */ memset(&peername, 0, sizeof(peername)); ((struct sockaddr_in *)&peername)->sin_family = AF_INET; ((struct sockaddr_in *)&peername)->sin_port = htons(port); #else peername.sin_family=AF_INET;peername.sin_addr.s_addr=INADDR_ANY; peername.sin_port=htons(port); #endif namelen = sizeof(struct sockaddr_in); curfd=-1; setsockopt(serverfd,SOL_SOCKET,SO_REUSEADDR,&curfd,sizeof curfd); if(bind(serverfd,(struct sockaddr*)&peername,namelen)) { syslog(LOG_CRIT,"unable to bind socket %d",POP3_PORT); fprintf(stderr,"%s: Can't bind socket %d\n",cucipopn,POP3_PORT); return EX_OSFILE; } fclose(stderr); if(fork()>0) return EXIT_SUCCESS; setsid();listen(serverfd,LISTEN_BACKLOG); sprintf(linebuf,PIDFILE,cucipopn); ;{ FILE*pidf; if(pidf=fopen(linebuf,"w")) fprintf(pidf,"%ld\n",(long)getpid()),fclose(pidf); else syslog(LOG_ERR,"can't write %s",linebuf); } initappdb();exitappdb(); /* prime database system */ do { close(curfd); if(namelen<0) sleep(LOCKSLEEP); namelen=sizeof peername;checkbulletins();zombiecollect(); if((curfd=accept(serverfd,(struct sockaddr*)&peername,&namelen))<0) continue; zombiecollect(); } while(namelen=fork()); close(serverfd); if(!(sockout=fdopen(curfd,"wb"))||!(sockin=fdopen(curfd,"rb"))) if(errno==ENOMEM) outofmem(); else terminate(EX_PROTOCOL); } else checkbulletins(),fclose(stderr); } chdir(bulletins_path);tsessionstart=time((time_t*)0);setsignals(); ;{ struct utsname name; uname(&name); ;{ size_t hlen; if(!(host=malloc(hlen=strlen(name.nodename)+1))|| !(timestamp=malloc(1+sizeof(pid_t)*2+sizeof(time_t)*2+1+hlen+1))) outofmem(); } ;{ pid_t pid=getpid(); sprintf(timestamp,"<%s",Bintohex(pid,1)); } if(sabotage) tsabotage=tsessionstart; agecutoff=tsessionstart-agecutoff; sprintf(timestamp+1+sizeof(pid_t)*2,"%s@%s>",Bintohex(tsessionstart,1), strcpy(host,name.nodename)); } fprintf(sockout,"+OK Cubic Circle's %s POP3 ready %s" #ifdef LOG_FAILED_PASSWORD "." #endif "\r\n",version, timestamp); for(;;) { const auth_identity*pass; readcommand(0); if(!arg1&&cmpbuf("quit")) { fprintf(sockout,"+OK Not really your day, is it?\r\n"); return EXIT_SUCCESS; } /* still allow for trailing spaces */ if(douser&&(!arg2||!*arg2)&&cmpbuf("user")) if(!*user) #ifdef USER if(arg1&&*arg1) fprintf(sockout,"+OK %s selected\r\n",strcpy(user,arg1)); else #endif fail("Invalid username"); else goto aluser; else if(cmpbuf("pass")) { if(*user) { if(arg2) arg2[-1]=' '; /* put back a literal space in the passwd */ if(arg1&&*arg1&&(pass=checkpw(arg1))) { wipestring(arg1); goto okpass; } #ifdef LOG_FAILED_PASSWORD if(audit) syslog(LOG_DEBUG,"Password attempted: \"%s\"",arg1?arg1:""); #endif goto failpass; } fail("Select a username first"); goto wipeuser; } else if(doapop&&cmpbuf("apop")) { if(!*user) { if(arg1&&*arg1&&arg2&&*arg2) { strcpy(user,arg1); if(pass=checkchallenge(arg2)) okpass: { free(timestamp); hostnduser=strlen(host)+strlen(Vuser); if(openmailbox(pass,agecutoff)) fprintf(sockout,"+OK Congratulations!\r\n"); else { syslog(LOG_ALERT,"Error opening %s's mailbox",Vuser); fail("Error opening your mailbox");terminate(EX_NOINPUT); } break; } else failpass: { syslog(LOG_ERR,"authentication failure %s %s",Vuser,peer()); sleep(++loginattempts); fprintf(sockout, "-ERR %s: Invalid password or username (check case)\r\n", cucipopn); } } else fail("Pop me harder"); wipeuser: *user='\0'; } else aluser: fail("Already selected a username"); } else if(arg1&&!arg2&&cmpbuf("auth")&&!strcmp(arg1,"twinkie")) { fail("Forget those... Try oreo cookies (and nice red uniforms :-)"); if(audit) loginvalcmd(); } else invalcmd( #ifdef USER "USER name, PASS string, " #endif #ifdef APOP "APOP name digest, " #endif "QUIT"); } for(;;) { if(!readcommand(autodel)) goto prequitstate; if(!arg1&&cmpbuf("quit")) break; if(!arg1&&cmpbuf("stat")) fprintf(sockout,"+OK %ld %ld\r\n",(long)totmsgs,totsize); else if(!arg2&&cmpbuf("list")) { if(arg1) { size_t i; if(i=findmsg()) fprintf(sockout,"+OK %ld %ld\r\n", (long)i,MSGS(i-1).virtsize); else goto nomsg; } else { size_t i; fprintf(sockout,"+OK %ld messages (%ld octets)\r\n",(long)totmsgs, (long)totsize); for(i=0;ifile,fmbox))) { int newline=1,lines=-1,inbody=0;size_t dodel=0; alarm(0);fseek(fp,pvm->start,SEEK_SET); if(STRLEN(From_)!=fread(linebuf,1,STRLEN(From_),fp)|| memcmp(From_,linebuf,STRLEN(From_))) corrupted(pvm->start); if(arg2) lines=strtol(arg2,(char**)0,10); else { transfmsgs++; if(autodel) dodel=i+1; } fprintf(sockout,"+OK %ld octets%s\r\n",pvm->virtsize, dodel?" (autodeleted)":""); transfsize+=pvm->virtsize;i=pvm->realsize-STRLEN(From_); ;{ time_t lasttime; lasttime=time((time_t*)0); fprintf(sockout,Received,host,Vuser,cucipopn,version, ctime(&lasttime)); } fprintf(sockout,XFrom_); while(i--) { unsigned c; switch(c=getc(fp)) { case '\n': if(newline) inbody=1; if(EOF==fputc('\r',sockout)) { if(autodel) prequitstate: { autodel=2; for(i=0;istart+msp->realsize); } if(oldend>(source=lseek(fd,(off_t)0L,SEEK_END))) goto corrupt; addblock(source);msgs[--msgs_filled].deleted=1; for(target=msgs[i].start;iCOPYBUF?(size_t)COPYBUF:slen))< STRLEN(From_)|| memcmp(From_,buf,STRLEN(From_))) goto corrupt; goto jin; do { lseek(fd,source,SEEK_SET); if(!(readin= rread(fd,buf,slen>COPYBUF?(size_t)COPYBUF:slen))) corrupt: corrupted(source); jin: source+=readin;lseek(fd,target,SEEK_SET); if(readin!=rwrite(fd,buf,readin)) goto writerr; target+=readin; } while(slen-=readin); } } free(buf);lseek(fd,(off_t)0L,SEEK_SET); if(ftruncate(fd,target)) goto writerr; unlockmbox(); if(close(fd)) writerr: { syslog(LOG_CRIT,"write error mailbox %s at %ld",Vuser,(long)target); fail("Error writing mailbox");terminate(EX_OSERR); } } if(!quiet) logfinal(LOG_INFO,autodel==2,autodel); fprintf(sockout,"+OK Was it as good for you, as it was for me? ("); fprintf(sockout,target?">%ld bytes left)\r\n": "clean as a baby)\r\n",(long)target); } ;{ unsigned i; for(i=priv_msgs_filled;i--;) if(msgs[i].deleted&1) { unsigned b=msgs[i].file; SBUL_READ(b); } } si.lastsuccess=tsessionstart; if(transfmsgs) si.brokenstate=0; if(!si.lastabort) si.lastabort=tsessionstart; blocksignals();putstate(Vuser,&si);closedb();exitappdb(); return EXIT_SUCCESS; }