/* Copyright (c) 2017, Computer History Museum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "pop.h" #include "myssl.h" #define FILE_NUM 30 /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */ /* 5/29/97 cwong NOTE: You should use the following macros when accessing POPD resources in the settings file: GetResourceMainThread_ ZapSettingsResourceMainThread AddMyResourceMainThread_ They access the main thread's Settings file (not the background thread's copy.) */ /************************************************************************ * functions for dealing with a pop 2 server ************************************************************************/ #ifdef KERBEROS #include #endif #pragma segment POP #define CMD_BUFFER 514 #define PSIZE (UseCTB ? 256 : 4096) #define errNotFound -2 #define MatchPOPD(old,oldSpot,hash) \ ((hash) && (*(old))[oldSpot].uidHash==(hash)) #define POP_TERM(buffer,size) ((size)==2 && (buffer)[0]=='.' && (buffer)[1]=='\015') /************************************************************************ * private routines ************************************************************************/ void POPDelDup(POPDHandle popDH); OSErr POPPreFetch(TransStream stream,POPDHandle popDH,short message,Boolean *capabilities); int POPGetReplyLo(TransStream stream,short cmd, UPtr buffer, long *size,AccuPtr resAcc); #define POPGetReply(stream,cmd,buffer,size) POPGetReplyLo(stream,cmd,buffer,size,nil) void RelatedNote(FSSpecPtr spec,HeaderDHandle hdh,PStr theMessage); int POPByeBye(TransStream stream); int POPCmdLo(TransStream stream, short cmd, UPtr args, AccuPtr argsAcc); #define POPCmd(stream,cmd,args) POPCmdLo(stream,cmd,args,nil) int POPGetMessage(TransStream,long messageNumber,short *gotSome,POPDHandle popDH,Boolean *capabilities); int DupHeader(short refN,UPtr buff,long bSize,long offset,long headerSize); int SaveAndSplit(TransStream stream,short refN,long estSize,HeaderDHandle *hdhp,Boolean isIMAP); Boolean StackLowErr = false; Boolean PopConnected; int FirstUnread(TransStream stream,int count); Boolean HasBeenRead(TransStream stream,short msgNum,short count); void StampPartNumber(MSumPtr sum,short part,short count); UPtr ExtractStamp(UPtr stamp,UPtr banner); short POPLast(TransStream,short *lastRead); POPLineType ReadPlainBody(TransStream stream,short refN,char *buf,long bSize,long estSize); short SplitMessage(short refN, long hStart, long hEnd, long msgEnd); void DisposePOPD(POPDHandle *popDH); OSErr BuildPOPD(TransStream stream,POPDHandle *popDH,short count,XferFlags *flags,Boolean *capabilities); void FillPOPD(POPDPtr pdp,HeaderDHandle hdh); short CountFetch(POPDHandle popDH); PStr HeaderMsgId(HeaderDHandle hdh,PStr msgId); uLong FakeMIDHash(HeaderDHandle hdh); void SetFetchDel(POPDHandle popDH,short from, short to,Boolean fetch, Boolean delete); void SetFetched(POPDHandle popDH,short from, short to); void SetBeforeAfter(POPDHandle popDH,uLong gmt,short *after,short *before); OSErr POPMsgSize(short messageNumber,long *msgsize); short FindExistSpot(POPDHandle popDH,uLong hash); OSErr DeletePOPMessage(TransStream stream,short number,long uidHash); OSErr FillWithUidl(TransStream stream,POPDHandle popDH); OSErr FillWithTop(TransStream stream,POPDHandle new, POPDHandle old); OSErr FillSizesWithList(TransStream stream,POPDHandle popDH); short FindUndelete(POPDHandle popDH,uLong gmt,uLong hash); OSErr FillPOPDFromServer(TransStream stream,POPDHandle popDH,short spot); OSErr InitKerberos(); OSErr KerbGetTicket(PStr popName,PStr host,PStr realm,PStr version,UHandle *ticket); OSErr SendPOPTicket(TransStream stream); void LogPOPD(PStr intro,POPDHandle newDH); void Log1POPD(PStr intro, PStr which, POPDHandle popDH); Boolean NoClearPass(Boolean *capabilities,UPtr response, short len); void PrunePOPD(OSType listType,short listId, POPDHandle onServer); OSErr ReapCmds(TransStream stream, short cmd); void PopCapabilities(TransStream stream, Boolean *capabilities,SASLEnum *mechPtr); int POPSasl(TransStream stream, Boolean *capabilities, SASLEnum mech, UPtr buffer, long *size); OSErr FixLongFilename(HeaderDHandle hdh,FSSpecPtr spec); PStr Un2184Append(PStr dest,short sizeofDest,PStr orig,PStr charset,Boolean isEncoded); PStr Un2184(PStr dest, PStr orig, PStr charset); /* stack sniffer defines */ // 5k seems to work for ppc. may need to tweak it some more. s/b smaller for 68k? #define kLowStackSize ((GetCurrentISA() == kPowerPCISA) ? (5 K) : (4 K)) #define kMoreStackSpace (10 K) /* Globals */ Boolean gPOPKerbInited = false; // true when Kerberos has been initialized for POP KClientSessionInfo gSession; // session info KClientKey gPrivateKey; // private key /************************************************************************ * GetMyMail - the biggie; transfers mail into In mailbox ************************************************************************/ short GetMyMail(TransStream stream,Boolean quietly,short *gotSome,struct XferFlags *flags) { #pragma unused(quietly) int messageCount; Str255 msgname; Str63 hostName; long port; TOCHandle tocH; short err; short fetchCount, message, fetched; POPDHandle popDH=nil; Boolean built = True; int beforeBytes, actualBytes, approxBytes; #ifdef BATCH_DELIVERY_ON Boolean inThread=InAThread(); #endif Boolean capabilities[pcapaLimit+1]; WriteZero(capabilities,sizeof(capabilities)); // we don't know if we have them yet!!! if (Prr=StackInit(sizeof(short),&POPCmds)) return(Prr); *gotSome = 0; #ifdef ESSL stream->ESSLSetting = GetPrefLong(PREF_SSL_POP_SETTING); if(stream->ESSLSetting & esslUseAltPort) port = GetRLong(POP_SSL_PORT); else #endif port = PrefIsSet(PREF_KERBEROS)?GetRLong(KERB_POP_PORT):GetRLong(POP_PORT); if (Prr=GetPOPInfoLo(msgname,hostName,&port)) return(Prr); if ((err=StartPOP(stream,hostName,port))==noErr) { messageCount = POPIntroductions(stream, msgname, capabilities); #ifdef DEBUG if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif if (capabilities[0] && !capabilities[pcapaUIDL]) { (*CurPers)->noUIDL = true; Log(LOG_LMOS,"\pCAPA says no UIDL"); } if (!Prr) { if (messageCount==0) { FixServers = FixServers || nil!=GetResource_(CUR_POPD_TYPE,POPD_ID); ZapSettingsResourceMainThread_(CUR_POPD_TYPE,POPD_ID); #ifdef TWO ZapSettingsResourceMainThread_(CUR_POPD_TYPE,DELETE_ID); ZapSettingsResourceMainThread_(CUR_POPD_TYPE,FETCH_ID); #endif } else if (!BuildPOPD(stream,&popDH,messageCount,flags,capabilities)) { CanPipeline = (capabilities[0] ? capabilities[pcapaPipelining] : PrefIsSet(PREF_CAN_PIPELINE)) && !(*CurPers)->noUIDL; ComposeLogR(LOG_RETR,nil,START_POP_LOG,hostName,port,messageCount); if (tocH=GetInTOC()) { /* * run through the pure deletes */ for (message=0;messagecount-1); fetched--; ETLDeleteRequest = false; } #ifdef BATCH_DELIVERY_ON if (inThread && (fetched % batchNum == 0)) { tocH = RenameInTemp(tocH); // This is bad. We don't know why this happens, // and if this codebase had a future, we would need // to find out. But for now, we're just going to stop // the crashing and feel ashamed. SD 5/2005 if (!tocH) break; #ifdef THIS_CODE_HAD_A_FUTURE #error FIX ME! #endif } #endif } #ifdef BATCH_DELIVERY_ON if (inThread) RenameInTemp(tocH); #endif } if (CommandPeriod) Prr = userCancelled; } else Prr = 1; if (!Prr) { ProgressMessageR(kpSubTitle,CLEANUP_CONNECTION); } PrunePOPD(CUR_POPD_TYPE,DELETE_ID,popDH); PrunePOPD(CUR_POPD_TYPE,FETCH_ID,popDH); DisposePOPD(&popDH); } else /* popd build failed */ ZapHandle(popDH); } } if (!err) err = Prr; if (!err && messageCount==0) ZapSettingsResourceMainThread_(CUR_POPD_TYPE,POPD_ID); ProgressMessageR(kpSubTitle,CLEANUP_CONNECTION); if (AttachedFiles) SetHandleBig_(AttachedFiles,0); err = Prr; EndPOP(stream); ZapHandle(POPCmds); return(err); } #ifdef BATCH_DELIVERY_ON /********************************************************************** * RenameInTemp **********************************************************************/ TOCHandle RenameInTemp(TOCHandle tocH) { Str63 name; FSSpec deliverSpec, inSpec, deliverFolder; FSSpec deliverTOCSpec, tocSpec; CInfoPBRec hfi; long maxFileNum=0, fileNum; OSErr err; if (!tocH || !(*tocH)->count) return tocH; if (err=SubFolderSpec(DELIVERY_FOLDER,&deliverFolder)) { Aprintf(OK_ALRT,Note,THREAD_SUBFOLDER_ERR,DELIVERY_FOLDER,err); return tocH; } // make sure the toc is written if (TOCIsDirty(tocH) || (*tocH)->reallyDirty) if (err=WriteTOC(tocH)) return tocH; /* find highest-numbered file in delivery folder */ Zero(hfi); hfi.hFileInfo.ioNamePtr = name; hfi.hFileInfo.ioFDirIndex=0; while(!DirIterate(deliverFolder.vRefNum,deliverFolder.parID,&hfi)) { if (hfi.hFileInfo.ioFlFndrInfo.fdType==MAILBOX_TYPE) { StringToNum(name, &fileNum); if (fileNum > maxFileNum) maxFileNum = fileNum; } } // Make name for new mailbox inSpec = GetMailboxSpec(tocH,-1); NumToString(maxFileNum+1, name); while (*name<6) PInsertC(name,sizeof(name),'0',name+1); FSMakeFSSpec(deliverFolder.vRefNum,deliverFolder.parID,name,&deliverSpec); // toc file? tocSpec = inSpec; PCatR(tocSpec.name,TOC_SUFFIX); if (!FSpExists(&tocSpec)) { FSMakeFSSpec(deliverFolder.vRefNum,deliverFolder.parID,name,&deliverTOCSpec); PCatR(&deliverTOCSpec.name,TOC_SUFFIX); } else *tocSpec.name = 0; // Move files if ((*tocH)->win) CloseMyWindow(GetMyWindowWindowPtr((*tocH)->win)); if (err=SpecMoveAndRename(&inSpec,&deliverSpec)) { Aprintf(OK_ALRT,Note,THREAD_DELIVER_CREATE_ERR,deliverSpec.name,err); } else { if (*tocSpec.name) { if (err=SpecMoveAndRename(&tocSpec,&deliverTOCSpec)) { Aprintf(OK_ALRT,Note,THREAD_DELIVER_CREATE_ERR,deliverTOCSpec.name,err); FSpDelete(&tocSpec); // hell with it. We can rebuild it } } // Ok, we have moved the temp.in. Make a new one if (err=MakeResFile(inSpec.name,inSpec.vRefNum,inSpec.parID,CREATOR,MAILBOX_TYPE)) { Aprintf(OK_ALRT,Note,THREAD_DELIVER_CREATE_ERR,inSpec.name,err); // this is bad } NeedToFilterIn++; // some filtering to be done } tocH = GetTempInTOC(); return tocH; } #endif /********************************************************************** * POPPreFetch **********************************************************************/ OSErr POPPreFetch(TransStream stream, POPDHandle popDH,short message,Boolean *capabilities) { short messageCount = HandleCount(popDH); Str63 args; Str15 top; OSErr err = noErr; short cmd; for (;messagepopSecure = False; goto done; } #endif do { size = sizeof(buffer)-1; Prr = RecvLine(stream,buffer+1,&size); if (Prr) goto done; buffer[0] = MIN(size,127); ProgressMessage(kpMessage,buffer); buffer[0] = MIN(size,255); } while (buffer[1]!='+' && buffer[1]!='-'); PopConnected = size && (buffer[1]=='+' || buffer[1]=='-'); if (buffer[1] != '+') { Prr = buffer[1]; POPCmdError(-1,nil,buffer); if (kerb4 && !NoClearPass(capabilities,buffer,size)) KerbDestroy(); goto done; } if (capabilities) PopCapabilities(stream,capabilities,&mech); #ifdef ESSL if( ShouldUseSSL(stream) && !(stream->ESSLSetting & esslSSLInUse)) { if (!capabilities || !capabilities[pcapaSTLS]) { if(!(stream->ESSLSetting & esslOptional)) { Prr = unimpErr; ComposeStdAlert ( Note, ALRTStringsStrn+NO_SERVER_SSL ); goto done; } } else { StringPtr errStr; errStr = buffer; size = sizeof(buffer)-1; Prr = POPCmdGetReply(stream,kpcStls,nil,buffer,&size); if(!Prr) { Prr = ESSLStartSSL(stream); if(Prr) { DoSSLErrString : GetRString(buffer,SSL_ERR_STRING); errStr = buffer + 1; goto DoSSLErr; } else if(stream->ESSLSetting & esslSSLInUse) PopCapabilities(stream,capabilities,&mech); else { // Cyrus sucks. // After a failed TLS negotiation, Cyrus will issue a bogus // -ERR response to the NEXT command. They shouldn't be issuing // any protocol-level response at all. bxxxxxxs OTFlushInput(stream,GetRLong(FLUSH_TIMEOUT)); } } else DoSSLErr : { if(stream->ESSLSetting & esslOptional) Prr = noErr; else { POPCmdError(kpcStls,nil,errStr); goto done; } } } } #endif ProgressMessageR(kpSubTitle,LOGGING_IN); if (mech && !kerb4) { // SASL ahoy! size = sizeof(buffer)-1; Prr = POPSasl(stream,capabilities,mech,buffer,&size); } else { if (useAPOP) {PCopy(args,(*CurPers)->password);useAPOP = GenDigest(buffer,args,digest);} if (useAPOP) { size = sizeof(buffer)-1; if (PrefIsSet(PREF_POP_SENDHOST)) GetPOPPref(args); else PCopy(args,user); PCatC(args,' '); PCat(args,digest); Prr = POPCmdGetReply(stream,kpcApop,args,buffer,&size); } else { if (PrefIsSet(PREF_POP_SENDHOST)) GetPOPPref(args); else PCopy(args,user); size = sizeof(buffer)-1; Prr = POPCmdGetReply(stream,kpcUser,args,buffer,&size); if (Prr || *buffer != '+') { if (!Prr) POPCmdError(kpcUser,args,buffer); Prr = '-'; goto done; } #ifdef TWO if (kerb4) GetRString(args,KERBEROS_FAKE_PASS); else #endif PCopy(args,(*CurPers)->password); size = sizeof(buffer)-1; Prr = POPCmdGetReply(stream,kpcPass,args,buffer,&size); } } if (Prr || *buffer != '+') { if (!Prr) { (*CurPers)->popSecure = False; POPCmdError(kpcPass,nil,buffer); if (!NoClearPass(capabilities,buffer,size)) InvalidatePasswords(False,True,False); } Prr = '-'; goto done; } (*CurPers)->popSecure = True; SetPrefLong(PREF_POP_LAST_AUTH,GMTDateTime()); ProgressMessageR(kpSubTitle,LOOK_MAIL); size = sizeof(buffer)-1; Prr = POPCmdGetReply(stream,kpcStat,nil,buffer,&size); if (Prr || *buffer != '+') { if (!Prr) POPCmdError(kpcStat,nil,buffer); Prr = '-'; goto done; } result = Atoi(buffer+3); done: return(result); } /************************************************************************ * PopCapabilities - what can our pop server do? ************************************************************************/ void PopCapabilities(TransStream stream, Boolean *capabilities, SASLEnum *mechPtr) { short i; Str255 buffer; long size; UPtr spot; Str31 token; Str31 service; for (i=0;i<=pcapaLimit;i++) capabilities[i]=0; *mechPtr = 0; if (*GetRString(buffer,POPCmdsStrn+kpcCapa)<=1) return; // hack, but hey GetRString(service,K5_POP_SERVICE); size = sizeof(buffer); if (!POPCmdGetReply(stream,kpcCapa,nil,buffer,&size) && *buffer=='+') { capabilities[0] = 1; // we have them! for(;;) { size = sizeof(buffer)-1; if (RecvLine(stream,buffer+1,&size)) break; buffer[0] = MIN(size,255); if (buffer[*buffer]=='\015') --*buffer; if (buffer[0]==1 && buffer[1]=='.') break; spot = buffer+1; if (PToken(buffer,token,&spot," ")) { i = FindSTRNIndex(POPCapaStrn,token); if (i && i1 && (*respAcc.data)[1]!=' ') { // We win! We win! Prr = 0; } else { Prr = ' '; AccuAddFromHandle(&chalAcc,respAcc.data,1,respAcc.offset-1); // clean it out respAcc.offset = 0; } } } } } while (Prr==' '); if (Prr || **respAcc.data!='+') { (*CurPers)->popSecure = False; AccuToStr(&respAcc,scratch); POPCmdError(kpcAuth,nil,scratch); if (!NoClearPass(capabilities,scratch,*size)) smtpEquivCode = 535; } else smtpEquivCode = 237; // auth succeeded! // Let the sasl mechanism know how it all came out SASLDone(service,mech,rounds,&state,smtpEquivCode); AccuZap(chalAcc); AccuZap(respAcc); return Prr; } /********************************************************************** * NoClearPass - is a pass error one that should not reset the password? **********************************************************************/ Boolean NoClearPass(Boolean *capabilities,UPtr response, short len) { Str255 string; short i; // If the pop server has the AUTH_RESP_CODE caapability, // then errors do NOT clear the password unless // [auth] appears in them if (capabilities && capabilities[pcapaAuthRespCode]) { GetRString(string,POP3_AUTH_RESP_CODE); return !PFindSub(string,response); } // without the capability, we have to refer to // our list of non-clearing errors for (i=1;*GetRString(string,NoClearPassStrn+i);i++) if (PPtrFindSub(string,response,len)) return(True); // Have we ever auth'ed using this password? If so, // let's assume this is a server problem and not an authentication // problem. if (GetPrefLong(PREF_POP_LAST_AUTH)) return true; // all else as failed. Sigh. return(False); } /************************************************************************ * POPByeBye - tell the POP server we're leaving ************************************************************************/ int POPByeBye(TransStream stream) { char buffer[CMD_BUFFER]; long size=sizeof(buffer); if (!PopConnected) return(noErr); if (PrefIsSet(PREF_SLOW_QUIT)) Prr = POPCmdGetReply(stream,kpcQuit,nil,buffer,&size); else {Prr = POPCmd(stream,kpcQuit,nil); *buffer = '+'; /* fast TCP disconnect */} return (Prr || *buffer != '+'); } /************************************************************************ * POPCmd - Send a command to the POP server ************************************************************************/ int POPCmdLo(TransStream stream, short cmd, UPtr args, AccuPtr argsAcc) { char buffer[CMD_BUFFER]; short err; /* * reap outstanding commands */ if (!CanPipeline || POPCmds && (*POPCmds)->elCount>=15) { err=ReapCmds(stream, -1); if (err==fnfErr) err = noErr; if (err) return(Prr=err); } if (CanPipeline && cmd==kpcQuit) ReapCmds(stream, 0); if (cmd) GetRString(buffer,POP_STRN+cmd); if (cmd==kpcPass || cmd==kpcAuth) ProgressMessage(kpMessage,buffer); if (cmd==kpcAuth) *buffer = 0; if (args && *args) PCat(buffer,args); if (cmd!=kpcAuth && cmd!=kpcPass && cmd!=kpcRetr && cmd!=kpcTop) ProgressMessage(kpMessage,buffer); if (cmd==kpcRetr || cmd==kpcDele) Log(LOG_LMOS,buffer); if (!argsAcc) PCat(buffer,NewLine); else if (cmd && cmd!=kpcAuth) PCatC(buffer,' '); err=SendTrans(stream,buffer+1,*buffer,nil); if (!err && argsAcc) { // add a newline to the accumulator AccuAddStr(argsAcc,NewLine); // send the data err = SendTrans(stream,LDRef(argsAcc->data),argsAcc->offset,nil); // erase what we did to the accumulator UL(argsAcc->data); argsAcc->offset -= *NewLine; } if (!err && POPCmds) err = StackQueue(&cmd,POPCmds); return(err); } /********************************************************************** * ReapCmds - reap commands until the named command is at the top * of the stack, ready to be handled **********************************************************************/ OSErr ReapCmds(TransStream stream, short cmd) { Str255 buffer; long size; OSErr err=noErr; short thisCmd=0; if (!POPCmds) return(noErr); while ((*POPCmds)->elCount) { if (cmd!=-1) { StackTop(&thisCmd,POPCmds); if (cmd==thisCmd) return(noErr); } StackPop(&thisCmd,POPCmds); do { size = sizeof(buffer); err = RecvLine(stream,buffer,&size); } while (!err && *buffer!='+' && *buffer!='-'); if (thisCmd==cmd) return(noErr); if (thisCmd==kpcTop || thisCmd==kpcRetr) { do { size = sizeof(buffer); err = RecvLine(stream,buffer,&size); } while (!err && !POP_TERM(buffer,size)); } if (cmd==-1) return(noErr); } if (err) Prr=err; return(err ? err : fnfErr); } /************************************************************************ * POPCmdGetReply - send a POP command and get a reply ************************************************************************/ int POPCmdGetReply(TransStream stream, short cmd, UPtr args, UPtr buffer, long *size) { if (cmd>=0 && (Prr=POPCmd(stream,cmd,args))) return(Prr); /* error in transmission */ return(POPGetReply(stream,cmd,buffer,size)); } /************************************************************************ * POPGetReply - get a reply to a POP command ************************************************************************/ int POPGetReplyLo(TransStream stream, short cmd, UPtr buffer, long *size, AccuPtr resAcc) { long rSize; // So what's errChar? Well, two things: // 1. Way Back When, when we did serial lines, some POP servers echoed; // errChar is used to detect and ignore echoes. // 2. Some POP servers add extraneous blank lines (not that I'm naming // names, but if someone were to nuke a company named "ipswitch", we // might not have this problem anymore), and we can skip those, too // So when we see an actual valid POP error indicator (either + or -), we // know the true response has begun. Hence, errChar. Byte errChar = 0; if (Prr=ReapCmds(stream, cmd)) return(Prr); if (resAcc) resAcc->offset = 0; do { rSize = *size; Prr = RecvLine(stream,buffer,&rSize); if (!rSize) { errChar = '-'; break; } if (!errChar && (*buffer=='+' || *buffer=='-')) errChar = *buffer; if (!Prr && errChar && POPCmds && buffer[rSize-1]=='\r') StackPop(nil,POPCmds); if (errChar && resAcc) AccuAddPtr(resAcc,buffer,rSize); } while (!Prr && !CommandPeriod && !(errChar && buffer[rSize-1]=='\r')); *size = rSize; buffer[0] = errChar; return(Prr); } /************************************************************************ * POPGetMessage - get a message from the POP server ************************************************************************/ int POPGetMessage(TransStream stream, long messageNumber,short *gotSome,POPDHandle popDH,Boolean *capabilities) { char buffer[CMD_BUFFER]; long size = sizeof(buffer); short count; TOCHandle tocH = GetInTOC(); /* shd already be in memory, so NBD to grab it here */ POPDesc pd; long msgsize; Boolean notFetched = False; /* * if there's no room at all, we won't even try */ if (RoomForMessage(0)) return(WarnUser(NOT_ENOUGH_ROOM,Prr=dskFulErr)); pd = (*popDH)[messageNumber]; msgsize = pd.msgSize; POPPreFetch(stream, popDH,CanPipeline?messageNumber+1:messageNumber,capabilities); /* * stub or retr */ if (pd.stub) { msgsize *= -1; /* let everyone down the line know what's going down */ size = sizeof(buffer); Prr = POPGetReply(stream,kpcTop,buffer,&size); NoAttachments = True; /* don't do BinHex */ RemIdFromPOPD(CUR_POPD_TYPE,DELETE_ID,(*popDH)[messageNumber].uidHash); /* and clear the force del flag if set */ } else { NoAttachments = pd.error ? True : False; refetch: size = sizeof(buffer); Prr=POPGetReply(stream,kpcRetr,buffer,&size); } if (Prr) return(Prr); if (*buffer!='+') { POPCmdError(kpcRetr,nil,buffer); return(Prr=1); } /* * command issued and accepted - now read the message */ BadBinHex = False; BadEncoding = 0; #ifdef DEBUG if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif count=FetchMessageText(stream,msgsize,&pd,messageNumber,nil); #ifdef DEBUG if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif if (CommandPeriod && StackLowErr) { StackLowErr = false; #ifdef THREADING_ON // increase thread stack size for next try if (InAThread()) { WarnUser(THREAD_LOW_STACK,0); } else #endif // try lowering appllimit for non-threaded operation???? // ...Will look into if users frequently experience WarnUser(LOW_STACK,0); } /* * did it work? */ if (!Prr && !CommandPeriod) { /* * ask user what to do about encoding errors */ #ifdef BAD_ENCODING_HANDLING if (BadBinHex || BadEncoding) { pd.delete = False; pd.stubbed = True; pd.error = True; notFetched = True; } #endif if (pd.delete) { Prr = DeletePOPMessage(stream,messageNumber,pd.uidHash); if (!Prr) pd.deleted = True; } if (pd.stub) pd.stubbed = True; else if (!notFetched && pd.retr) { pd.retred = True; RemIdFromPOPD(CUR_POPD_TYPE,FETCH_ID,pd.uidHash); } } (*popDH)[messageNumber] = pd; if (!Prr) (*gotSome)+=count; return(Prr); } /************************************************************************ * DeletePOPMessage - delete a message from the POP server ************************************************************************/ OSErr DeletePOPMessage(TransStream stream, short number,long uidHash) { Str255 buffer; Str63 args; long size; NumToString(number+1,args); size = sizeof(buffer); Prr=POPCmd(stream,kpcDele,args); if (!Prr) RemIdFromPOPD(CUR_POPD_TYPE,DELETE_ID,uidHash); return(Prr); } /************************************************************************ * FillSizesWithList - fill message sizes with the LIST command ************************************************************************/ OSErr FillSizesWithList(TransStream stream,POPDHandle popDH) { Str127 buffer; long size = sizeof(buffer); short msgNum; UPtr spot; short n = HandleCount(popDH); if (Prr=POPCmdGetReply(stream,kpcList,nil,buffer,&size)) return(Prr); if (*buffer != '+') { Prr = *buffer; POPCmdError(kpcList,nil,buffer); return(Prr); } // if (n>100) ByteProgress(nil,0,n); for (size=sizeof(buffer); !(Prr=RecvLine(stream,buffer,&size)) && !POP_TERM(buffer,size); size=sizeof(buffer)) { CycleBalls(); if (CommandPeriod) break; spot = strtok(buffer," \t"); if (!spot) continue; msgNum = Atoi(spot); spot = strtok(nil," \t"); if (!spot) continue; size = Atoi(spot); if (msgNum<1 || msgNum>n) continue; // if (n>100) ByteProgress(nil,-1,0); (*popDH)[msgNum-1].msgSize = size; } if (CommandPeriod && !Prr) Prr = userCancelled; // if (n > 100 && !Prr) ByteProgress(nil,1,1); Progress(NoBar,0,nil,nil,nil); return(Prr); } /************************************************************************ * POPCmdError - report an error for an POP command ************************************************************************/ int POPCmdError(short cmd, UPtr args, UPtr message) { Str255 theCmd; Str255 theError; int err; *theCmd = 0; GetRString(theCmd,POP_STRN+cmd); if (args && *args) PCat(theCmd,args); strcpy(theError+1,message); *theError = strlen(theError+1); if (theError[*theError]=='\012') (*theError)--; if (theError[*theError]=='\015') (*theError)--; MyParamText(theCmd,theError,"\pPOP",""); err = ReallyDoAnAlert(PROTO_ERR_ALRT,Note); return(err); } /************************************************************************ * FetchMessageText - read in the body of a message ************************************************************************/ int FetchMessageText(TransStream stream,long estSize,POPDPtr pdp,short messageNumber,TOCHandle useTocH) { return (FetchMessageTextLo(stream, estSize, pdp, messageNumber, useTocH, false, false)); } /************************************************************************ * FetchMessageText - read in the body of a message ************************************************************************/ int FetchMessageTextLo(TransStream stream,long estSize,POPDPtr pdp,short messageNumber,TOCHandle useTocH,Boolean imap,Boolean import) { UPtr text=nil; TOCHandle tocH; MSumType sum; long eof,chopHere; Str255 name; short count=0,part; HeaderDHandle hdh = nil; LineIOD lid; OSErr err; FSSpec spec; extern OSErr ImportErr; Str63 savedSub; /* * make the message summary */ WriteZero(&sum,sizeof(MSumType)); *savedSub = 0; /* * haven't seen any rich text yet */ AnyRich = AnyHTML = AnyFlow = AnyCharset = AnyDelSP = False; ZapHandle(LastAttSpec); /* or attachments */ ETLDeleteRequest = False; /* and no translators have been run on this message yet */ /* * grab the destination mailbox (usually "In") */ tocH = useTocH ? useTocH : GetInTOC(); if (!tocH) {Prr=-108;return(0);} spec = GetMailboxSpec(tocH,-1); PCopy(name,spec.name); // if we're adding IMAP messages or importing mail, we've taken care of opening the mailbox already if (!imap && !import) { Prr = BoxFOpen(tocH); if (Prr) {FileSystemError(OPEN_MBOX,name,Prr); goto done;} } eof = FindTOCSpot(tocH,estSize); Prr = SetFPos((*tocH)->refN, fsFromStart, eof); if (Prr) {FileSystemError(WRITE_MBOX,name,Prr); goto done;} #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// count = SaveAndSplit(stream,(*tocH)->refN,estSize,&hdh,(*tocH)->imapTOC); #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// done: if (!Prr && !GetFPos((*tocH)->refN,&chopHere)) SetEOF((*tocH)->refN,chopHere); // if we're adding IMAP messages, or importing mail, we'll close and flush later. if (Prr || (!imap && !import)) { Prr = BoxFClose(tocH,true) || Prr; if (Prr || !count) return(0); } /* * now, read it back from the file */ if (Prr=OpenLine(spec.vRefNum,spec.parID,name,(imap||import)?fsRdPerm:fsRdWrPerm,&lid)) {FileSystemError(READ_MBOX,name,Prr);return(0);} if (Prr=SeekLine(eof,&lid)) {FileSystemError(READ_MBOX,name,Prr);return(0);} ReadSum(nil,False,&lid,True); for (part=1;!(err=ReadSum(&sum,False,&lid,True));part++) { if (!*savedSub) PSCopy(savedSub,sum.subj); if (part==1 && pdp) { FillPOPD(pdp,hdh); DBNoteUIDHash(sum.uidHash,pdp->uidHash); sum.uidHash = pdp->uidHash; } else { DBNoteUIDHash(sum.uidHash,kNoMessageId); sum.uidHash = kNoMessageId; } if (!(*hdh)->isMIME) { TransLitString(sum.from); TransLitString(sum.subj); } else sum.tableId = ViewTable(hdh); if (part==1) sum.msgIdHash = (*hdh)->msgIdHash; if (!ValidHash(sum.uidHash)) sum.uidHash = sum.msgIdHash; #ifdef BAD_ENCODING_HANDLING if ((estSize<0) || BadBinHex || BadEncoding) sum.flags |= FLAG_SKIPPED; #else if ((estSize<0)) sum.flags |= FLAG_SKIPPED; #endif // set or clear html/enriched flags. Clear is necessary because toc build might give // false positive if (count==1 && AnyRich) sum.flags |= FLAG_RICH; else sum.flags &= ~FLAG_RICH; if (count==1 && AnyHTML) sum.opts |= OPT_HTML; else sum.opts &= ~OPT_HTML; if (count==1 && AnyFlow) sum.opts |= OPT_FLOW; else sum.opts &= ~OPT_FLOW; if (count==1 && AnyDelSP) sum.opts |= OPT_DELSP; else sum.opts &= ~OPT_DELSP; if (count==1 && AnyCharset) sum.opts |= OPT_CHARSET; else sum.opts &= ~OPT_CHARSET; if ((*hdh)->hasMDN) sum.opts |= OPT_RECEIPT; if (LastAttSpec) sum.flags |= FLAG_HAS_ATT; if (!sum.seconds) sum.seconds = GMTDateTime(); if (count>1) StampPartNumber(&sum,part,count); sum.spamScore = 0; sum.arrivalSeconds = GMTDateTime(); if (Prr) break; if (useTocH && !import) // create a new summary if we're importing { MSumType newSum; newSum = (*tocH)->sums[messageNumber]; newSum.offset = sum.offset; newSum.length = sum.length; newSum.bodyOffset = sum.bodyOffset; newSum.flags |= sum.flags; newSum.opts |= sum.opts; newSum.msgIdHash = sum.msgIdHash; if (!newSum.priority) newSum.priority = sum.priority; PSCopy(newSum.subj,sum.subj); if (!(newSum.opts & OPT_IMAP_SENT)) PSCopy(newSum.from,sum.from); RemoveUTF8FromSum(&newSum); (*tocH)->sums[messageNumber] = newSum; } else if (Prr=!SaveMessageSum(&sum,&tocH)) { if (import) ImportErr = memFullErr; // stop if we're importing. break; } } ReadSum(nil,False,&lid,True); if (err!=fnfErr) Prr = err; Prr = Prr || part<=count; ZapHeaderDesc(hdh); CloseLine(&lid); #ifdef DEBUG if (ETLDeleteRequest) ComposeLogS(LOG_PLUG,nil,"\pA plugin has requested the deletion of '%p' in '%p'",savedSub,spec.name); #endif if (BadBinHex || BadEncoding) { Str255 hex, enc, errorStr; if (BadBinHex) GetRString(hex,BAD_HEX_MSG); else *hex = 0; if (BadEncoding) ComposeRString(enc,BAD_ENC_MSG,BadEncoding,BadEncoding); else *enc = 0; ComposeRString(errorStr,(imap?IMAP_BAD_HEXBIN_ERR_TEXT:BAD_HEXBIN_ERR_TEXT),hex,enc); if (imap) // add the message error to the IMAP message we just downloaded AddMesgError (tocH,messageNumber,errorStr,-1); else // add it to the last message added to the mailbox. OK for POP. AddMesgError (tocH,(*tocH)->count-1,errorStr,-1); } if (Prr) { WarnUser(READ_MBOX,Prr); return(0); } count = part-1; InvalSum(tocH,useTocH?messageNumber:(*tocH)->count-1); if (!PrefIsSet(PREF_CORVAIR) && !((*tocH)->count%5)) { Prr = WriteTOC(tocH); FlushVol(nil,spec.vRefNum); } MakeMessTitle(name,tocH,useTocH?messageNumber:(*tocH)->count-count,False); ComposeLogR(LOG_RETR,nil,MSG_GOT,name,count); if (!imap) UpdateNumStatWithTime(kStatReceivedMail,1,(*tocH)->sums[useTocH?messageNumber:(*tocH)->count-1].seconds+ZoneSecs()); return(Prr ? 0:count); } /************************************************************************ * SaveAndSplit - read a message, (possibly) splitting it into parts and * saving it. ************************************************************************/ int SaveAndSplit(TransStream stream,short refN,long estSize,HeaderDHandle *hdhp,Boolean isIMAP) { Str255 buf; short count=0; HeaderDHandle hdh=NewHeaderDesc(nil); long fromSize; long end; long oldStart; short lastHeaderTokenType; long ticks = TickCount(); if (!hdh) {Prr=MemError(); return(0);} // if (estSize>0) ByteProgress(nil,0,estSize); if (Prr=PutOutFromLine(refN,&fromSize)) return(0); reRead: if (!hdh) {Prr=MemError(); return(0);} lastHeaderTokenType = ReadHeader(stream,hdh,estSize,refN,False); #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",lastHeaderTokenType); #endif //DEBUG ////////////////////////// if (lastHeaderTokenType!=EndOfHeader && lastHeaderTokenType!=EndOfMessage) { Prr = 1; goto done; } if (fromSize) { (*hdh)->diskStart -= fromSize; /* count the envelope as part of the header */ fromSize = 0; /* in case we pass this way again. */ } else (*hdh)->diskStart = oldStart; if (!Prr) { /* * I've wanted to do this for years. say who it's from! */ PCopy(buf,(*hdh)->who); *buf = MIN(*buf,31); // not too long here... PCatC(buf,','); PCatC(buf,' '); PSCat(buf,(*hdh)->subj); if (!(*hdh)->isMIME) TransLitString(buf); ProgressMessage(kpMessage,buf); // regenerate full info for comment PCopy(buf,(*hdh)->who); PCatC(buf,','); PCatC(buf,' '); PSCat(buf,(*hdh)->subj); PSCopy((*hdh)->summaryInfo,buf); /* * now, go save the body */ if (lastHeaderTokenType!=EndOfMessage) ReadEitherBody(stream,refN,hdh,buf,sizeof(buf),estSize,EMSF_ON_ARRIVAL); else FSWriteP(refN,"\p\015\015"); #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// /* * darn encapsulated stuf */ if (Prr == '82') { oldStart = (*hdh)->diskStart; ZapHeaderDesc(hdh); hdh = NewHeaderDesc(nil); PSCopy((*hdh)->summaryInfo,buf); Prr = noErr; goto reRead; } /* * ok, got real body now */ #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// EnsureNewline(refN); ticks = TickCount()-ticks + 1; { long rate = (estSize*600)/(ticks*1024); ComposeLogS(LOG_TPUT,nil,"\p%dK in %d.%d sec; %d.%d KBps",estSize/1024,ticks/60,(ticks/6)%10,rate/10,rate%10); } #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d %d;file %x;sc;g",Prr,GetFPos(refN,&end),refN); #endif //DEBUG ////////////////////////// if (!Prr && !(Prr=GetFPos(refN,&end))) { #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// TruncOpenFile(refN,end); #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d e %d ds %d st %d;sc;g",Prr,end,(*hdh)->diskStart,GetRLong(SPLIT_THRESH)); #endif //DEBUG ////////////////////////// if (!isIMAP && (end-(*hdh)->diskStart>GetRLong(SPLIT_THRESH))) count = SplitMessage(refN,(*hdh)->diskStart,(*hdh)->diskEnd,end); else count = 1; #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// } } #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// done: *hdhp = hdh; if (Prr) return(0); return(estSize<0 && GetPrefLong(PREF_POP_MODE)==popRStatus && *(*hdh)->status ? 0 : count); } /************************************************************************ * ReadEitherBody - read the body of a message from a pop-3 server ************************************************************************/ short ReadEitherBody(TransStream stream,short refN,HeaderDHandle hdh,char *buf,long bSize,long estSize,long context) { MIMESHandle mimeSList=nil; BoundaryType endType; /* * is our MIME converter interested in this thing? */ if (!NoAttachments && estSize>=0) { mimeSList = NewMIMES(stream,hdh,False,context); if (!mimeSList) return(Prr = MemError()); if (mimeSList == kMIMEBoring) mimeSList = nil; else if ((*mimeSList)->readBody==READ_MESSAGE) { ZapMIMES(mimeSList); return(Prr = '82'); } } /* * call the proper body reading function */ endType = mimeSList ? (*(*mimeSList)->readBody)(stream,refN,mimeSList,buf,bSize,ReadPOPLine) : ReadPlainBody(stream,refN,buf,bSize,estSize); if (endType == btError) Prr = 1; #ifdef DEBUG //////////////////////////// if (BUG15) Dprintf("\p%d;sc;g",Prr); #endif //DEBUG ////////////////////////// ZapMIMES(mimeSList); return(Prr); } /********************************************************************** * RoomForMessage - make sure there's room for a message on both the * attachments folder volume and the in box volume **********************************************************************/ OSErr RoomForMessage(long msgsize) { FSSpec attSpec; OSErr err=noErr; GetAttFolderSpec(&attSpec); err = VolumeMargin(MailRoot.vRef,msgsize); if (!err && MailRoot.vRef==attSpec.vRefNum) return(noErr); if (!err) err = VolumeMargin(attSpec.vRefNum,msgsize); return(err); } /************************************************************************ * ReadPlainBody - handle the body of a non-MIME message. ************************************************************************/ POPLineType ReadPlainBody(TransStream stream,short refN,char *buf,long bSize,long estSize) { long size; Boolean hexing, singling, pgping; POPLineType lineType; #ifdef OLDPGP PGPContext pgp; #endif /* * prepare converters */ if (!NoAttachments) { BeginHexBin(nil); BeginAbomination("",nil); #ifdef OLDPGP BeginPGP(&pgp); #endif hexing = singling = pgping = False; } ReadPOPLine(stream,nil,0,nil); /* * main processing loop */ for (lineType=ReadPOPLine(stream,buf,bSize,&size); lineType!=plError && lineType!=plEndOfMessage; lineType=ReadPOPLine(stream,buf,bSize,&size)) { /* * give each converter a crack at the line */ if (!NoAttachments) { if (!(singling || pgping)) hexing = ConvertHexBin(refN,buf,&size,lineType,estSize); if (!(hexing || pgping)) singling = ConvertUUSingle(refN,buf,&size,lineType,estSize,nil,nil); #ifdef OLDPGP if (!(singling || hexing)) pgping = ConvertPGP(refN,buf,&size,lineType,estSize,&pgp); #endif } /* * write the line */ if (size && (Prr=AWrite(refN,&size,buf))) break; } /* * record skipped message */ if (!Prr && lineType == plEndOfMessage && estSize < 0) { Str255 msg; long count; #ifdef TWO if (Headering || PrefIsSet(PREF_NO_BIGGIES)) ComposeRString(msg,BIG_MESSAGE_MSG2,-estSize); else ComposeRString(msg,NOSPACE_SKIP,-estSize); #else ComposeRString(msg,BIG_MESSAGE_MSG,-estSize); #endif count = *msg; Prr=AWrite(refN,&count,msg+1); } /* * close converters */ if (!NoAttachments) { EndHexBin(); SaveAbomination(nil,0); #ifdef OLDPGP EndPGP(&pgp); #endif /* * write attachment notes, if any */ WriteAttachNote(refN); } /* * report error (if any) */ if (Prr) FileSystemError(WRITE_MBOX,"",Prr); // else if (!UUPCIn) Progress(100,NoChange,nil,nil,nil); return(Prr?btError : btEndOfMessage); } /************************************************************************ * PutOutFromLine - write an envelope ************************************************************************/ int PutOutFromLine(short refN,long *fromLen) { Str255 fromLine; long len; *fromLen = len = SumToFrom(nil,fromLine); if (Prr=AWrite(refN,&len,fromLine)) return(FileSystemError(WRITE_MBOX,"",Prr)); return(noErr); } /************************************************************************ * DupHeader - copy the header of a split message ************************************************************************/ int DupHeader(short refN,UPtr buff,long bSize,long offset,long headerSize) { long currentOffset; long readBytes,writeBytes; long copied; if (Prr=GetFPos(refN,¤tOffset)) return(FileSystemError(READ_MBOX,"",Prr)); for (copied=0; copied", message * has been read ************************************************************************/ Boolean HasBeenRead(TransStream stream,short msgNum,short count) { Str127 scratch; Boolean unread=False, statFound=False; Str31 terminate; Str31 status; UPtr cp; long size; if (msgNum>count) return(0); Progress((msgNum*100)/count,NoBar,nil,GetRString(scratch,FIRST_UNREAD),nil); GetRString(terminate,ALREADY_READ); GetRString(status,STATUS); NumToString(msgNum,scratch); PLCat(scratch,1); POPCmd(stream,kpcTop,scratch); for (size=sizeof(scratch); !(Prr=RecvLine(stream,scratch,&size)) && !POP_TERM(scratch,size); size=sizeof(scratch)) if (!unread && !statFound && !striscmp(scratch,status+1)) { statFound = True; for (cp=scratch;cp scratch+size-*terminate; break; } } } ComposeLogS(LOG_LMOS,nil,"\pHasBeenRead: %d sf %d un %d %p",msgNum,statFound,!unread,statFound&&!unread?"\pREAD":"\pUNREAD"); return(statFound && !unread); } /************************************************************************ * StampPartNumber - put the part number on a mail message ************************************************************************/ void StampPartNumber(MSumPtr sum,short part,short count) { char *spot; short i, digits, len; if (part==1) sum->flags |= FLAG_FIRST; else sum->flags |= FLAG_SUBSEQUENT; for (i=count,digits=0;i;i/=10,digits++); len = 2*digits+2; spot=sum->subj+MIN(*sum->subj+len,sizeof(sum->subj)-1); *sum->subj = spot-sum->subj; for (i=digits;i;i--,count/=10) *spot-- = '0'+count%10; *spot-- = '/'; for (i=digits;i;i--,part/=10) *spot-- = '0'+part%10; *spot = ' '; } /************************************************************************ * RecordAttachment - note that we've attached a file ************************************************************************/ OSErr RecordAttachment(FSSpecPtr spec,HeaderDHandle hdh) { Str255 theMessage; OSErr err; Boolean deleted = false; if(FileTypeOf(spec) == REG_FILE_TYPE) { if(!hdh || AAFetchResData((*hdh)->contentAttributes,AttributeStrn+aRegFile,theMessage)) { FSpDelete(spec); deleted = true; GetRString(theMessage, STOLEN_REG_FILE); } else { if(!gRegFiles) gRegFiles = NuHandle(0); if(gRegFiles) PtrPlusHand_(spec, gRegFiles, sizeof(FSSpec)); } } // Long filename? if (hdh && !(*hdh)->relatedPart) FixLongFilename(hdh,spec); /* * update global fsspec record */ if (!deleted) { if (!LastAttSpec) LastAttSpec = NewH(FSSpec); if (LastAttSpec) **(FSSpecHandle)LastAttSpec = *spec; } // only record top-level files if (AttFolderStack && !SameSpec(&CurrentAttFolderSpec,&AttFolderSpec)) return noErr; if(!deleted) { if (hdh && (*hdh)->relatedPart) RelatedNote(spec,hdh,theMessage); else AttachNoteLo(spec,theMessage); } /* * tack on the note */ if (!AttachedFiles) AttachedFiles = NuHandle(0); if (AttachedFiles) PtrPlusHand_(theMessage+1,AttachedFiles,*theMessage); if (err=MemError()) { WarnUser(BINHEX_MEM,err); CommandPeriod = true; return(err); } if(deleted) return(noErr); RecordTransAttachments(spec); /* * add a comment? */ if (hdh && !PrefIsSet(PREF_NO_ATT_COMMENT)) DTSetComment(spec,PCopy(theMessage,(*hdh)->summaryInfo)); /* * is there a date? */ if (hdh && !AAFetchResData((*hdh)->contentAttributes,AttributeStrn+aModDate,theMessage)) { uLong mod; long zone; if (mod=BeautifyDate(theMessage,&zone)) if (mod>(long)GetRLong(TOO_EARLY_FILE)) AFSpSetMod(spec,mod+ZoneSecs()); } // record for attachment received statistics UpdateNumStatWithTime(kStatReceivedAttach,1,hdh?(*hdh)->gmtSecs+ZoneSecs():LocalDateTime()); return(noErr); } /************************************************************************ * FixLongFilename - attach a long filename to an FSSpec made from a shorter * filename ************************************************************************/ OSErr FixLongFilename(HeaderDHandle hdh,FSSpecPtr spec) { Str127 longFilename; Str31 filenameAtt; Str255 part; Str255 charset; *longFilename = *charset = 0; // is there a filename at all? if (AAFetchData((*hdh)->contentAttributes,GetRString(filenameAtt,AttributeStrn+aFilename),longFilename)) { // no "filename". Is there a "filename*"? PCatC(filenameAtt,'*'); if (!AAFetchData((*hdh)->contentAttributes,filenameAtt,part)) Un2184Append(longFilename,sizeof(longFilename),part,charset,true); else { // no "filename*'. short i; for (i=0;;i++) { // Is there a "filename*0"? if (!AAFetchData((*hdh)->contentAttributes,ComposeRString(filenameAtt,AttributeStrn+aFilename,i),part)) { if (i==0) GetRString(charset,UNSPECIFIED_CHARSET); Un2184Append(longFilename,sizeof(longFilename),part,charset,false); } // Is there a "filename*0*"? else if (!AAFetchData((*hdh)->contentAttributes,PCat(filenameAtt,"\p*"),part)) Un2184Append(longFilename,sizeof(longFilename),part,charset,true); else break; } } } // check for applesingle if (EqualStrRes(LDRef(hdh)->contentSubType,MIME_APPLEFILE)) if (longFilename[1]=='%' && *longFilename>1) BMD(longFilename+2,longFilename+1,--*longFilename); UL(hdh); // is it a short filename? if (*longFilename <= 31 && !AnyHighBits(longFilename+1,*longFilename)) return noErr; // Ok, it's a long filename. Set the name of the file to it // Make sure that the file is unique (void) MakeUniqueLongFileName ( spec->vRefNum, spec->parID, longFilename, kTextEncodingUnknown, sizeof ( longFilename ) - 1 ); return FSpSetLongName(spec,kTextEncodingUnknown,longFilename,spec); } /************************************************************************ * Un2184Append - undo some 2184, and append to an existing string ************************************************************************/ PStr Un2184Append(PStr dest,short sizeofDest,PStr orig,PStr charset,Boolean isEncoded) { Str255 decoded; short spaceNeeded; short leftAppend = GetRLong(MIN_LEFT_APPEND); Str31 elide; if (isEncoded) Un2184(decoded,orig,charset); else PCopy(decoded,orig); spaceNeeded = 1+*dest+*decoded - sizeofDest; if (spaceNeeded > 0) { // add in space for the ellipsis spaceNeeded += *GetRString(elide,ELIDE_2184_STRING); // Make sure we leave at least 64 characters of the original string if (*dest > leftAppend) { // we can get all we want from the excess of dest if (spaceNeeded <= *dest - leftAppend) *dest -= spaceNeeded; else { // take some from dest... spaceNeeded -= *dest - leftAppend; // and take the rest from the RIGHT side of decoded BMD(decoded+1,decoded+1+spaceNeeded,*decoded-spaceNeeded); *decoded -= spaceNeeded; } } // copy the elision string PCat(dest,elide); } // copy the decoded string PCat(dest,decoded); return dest; } /************************************************************************ * Un2184 - undo 2184 encoding ************************************************************************/ PStr Un2184(PStr dest, PStr orig, PStr charset) { short tableID; Boolean found; // copy it in PCopy(dest,orig); // do we have a charset? if (!*charset) { if (PIndex(orig,'\'')) { UPtr spot = orig+1; PToken(orig,charset,&spot,"'"); // grab charset PToken(orig,dest,&spot,"'"); // skip language PToken(orig,dest,&spot,"'"); // put the rest into dest } if (!*charset) GetRString(charset,UNSPECIFIED_CHARSET); } // undo the QP FixURLString(dest); // transliterate tableID = FindMIMECharsetLo(charset,&found); if (!found) tableID = FindMIMECharset(GetRString(charset,UNSPECIFIED_CHARSET)); TransLitRes(dest+1,*dest,tableID); return dest; } /************************************************************************ * AttachNoteLo - format the attachment note ************************************************************************/ void AttachNoteLo(FSSpecPtr spec,PStr theMessage) { Str31 folderName; Str15 typeString, creatorString; FInfo info; long fid; Str31 fidStr; if (FSpIsItAFolder(spec)) { info.fdCreator = kSystemIconsCreator; info.fdType = kGenericFolderIcon; fid = SpecDirId(spec); } else { FSpGetFInfo(spec,&info); FSMakeFID(spec,&fid); } BMD(&info.fdCreator,creatorString+1,4); BMD(&info.fdType,typeString+1,4); *creatorString = *typeString = 4; SanitizeFN(creatorString,creatorString,ATTCONV_BAD_CHARS,ATTCONV_REP_CHARS,false); SanitizeFN(typeString,typeString,ATTCONV_BAD_CHARS,ATTCONV_REP_CHARS,false); GetMyVolName(spec->vRefNum,folderName); ComposeRString(theMessage,FILE_FOLDER_FMT, folderName,spec->name,typeString,creatorString,Long2Hex(fidStr,fid)); } /************************************************************************ * RelatedNote - format the related note ************************************************************************/ void RelatedNote(FSSpecPtr spec,HeaderDHandle hdh,PStr theMessage) { Str31 folderName; long fid; Str255 quoteName; FSMakeFID(spec,&fid); PCopy(quoteName,spec->name); GetMyVolName(spec->vRefNum,folderName); ComposeRString(theMessage,RELATED_FMT,MIME_RELATED, folderName,URLEscape(quoteName),fid,(*hdh)->cidHash,(*hdh)->relURLHash,(*hdh)->absURLHash); } /************************************************************************ * AddAttachInfo - attach a note about problems with the enclosure ************************************************************************/ void AddAttachInfo(short theIndex, long result) { Str255 theMessage; ComposeString(theMessage,"\p%r%d\015", theIndex, result); PtrPlusHand_(theMessage+1,AttachedFiles,*theMessage); } /************************************************************************ * WriteAttachNote - write the attachment note ************************************************************************/ OSErr WriteAttachNote(short refN) { long size; short err = noErr; if (AttachedFiles && *AttachedFiles && (size=GetHandleSize_(AttachedFiles))) { if (!(err=EnsureNewline(refN))) { err = AWrite(refN,&size,LDRef(AttachedFiles)); UL(AttachedFiles); SetHandleBig_(AttachedFiles,0); } } return(err); } /************************************************************************ * POPLast - give the LAST command to find the last unread message ************************************************************************/ short POPLast(TransStream stream,short *lastRead) { Str127 buffer; UPtr spot; long size = sizeof(buffer); if (Prr=POPCmdGetReply(stream,kpcLast,nil,buffer,&size)) return(Prr); ComposeLogS(LOG_LMOS,nil,"\pLast: %s",buffer); if (*buffer != '+') Prr = *buffer; else { strtok(buffer," "); /* skip ok */ if (spot=strtok(nil," \015")) *lastRead = Atoi(spot); /* read message size */ else return(1); } return(0); } /************************************************************************ * ReadPOPLine - read a line from the POP server. * Returns the kind of line it is: * plComplete - a line that began with a newline * plPartial - the remainder of a line * plBlank - a blank line * plEndOfMessage - the message is OVER. * plError - there has been an error (recorded in global Prr) * Also removes the "." that escapes lines beginning with ".", * and adds a ">" to escape envelopes ************************************************************************/ POPLineType ReadPOPLine(TransStream stream,UPtr buf, long bSize, long *len) { static wasNl; POPLineType returnType; long freeStack; // sniff the stack to see if we're low #ifdef THREADING_ON if (InAThread()) { ThreadID threadID; GetCurrentThread (&threadID); ThreadCurrentStackSpace(threadID, &freeStack); } else #endif freeStack = StackSpace(); if (freeStack < kLowStackSize) { CommandPeriod = true; StackLowErr = true; } /* * nil buffer initializes */ if (!buf) { wasNl = True; return(plEndOfMessage); } /* * grab the line */ *len = bSize-1; /* allow extra char for escaped envelopes */ if (Prr = RecvLine(stream,buf,len)) return(plError); /* * update progress indicator */ ByteProgress(nil,-*len,0); /* * are we looking at the beginning of a line? */ if (wasNl) { returnType = plComplete; if (buf[0] == '.') /* leading period */ { if (buf[1]=='.') /* if dot doubled, was in message data */ { BMD(buf+1,buf,*len); --*len; } else if (buf[1] == '\015') /* if dot followed by \015, end of message */ returnType = plEndOfMessage; } else if (IsFromLine(buf)) /* is envelope? */ { BMD(buf,buf+1,*len); /* escape with '>' */ ++*len; buf[0] = '>'; } } else returnType = plPartial; wasNl = *len ? buf[*len-1] == '\015' : True; /* set for next go-round */ return(returnType); } /************************************************************************ * SplitMessage - split a message into pieces ************************************************************************/ short SplitMessage(short refN, long hStart, long hEnd, long msgEnd) { long splitSize = GetRLong(FRAGMENT_SIZE); long bodySplit; short err; short count; short headerNl=0; long *froms=nil; long *tos=nil; Boolean *reals=nil; short i; Boolean headerReal; if (hEnd-hStart > splitSize) { /* uh-oh. the header is waaaaaaay big */ if (err = HuntNewline(refN,hStart+4096,&hEnd,&headerReal)) goto done; headerNl = headerReal?1:2; } /* * how many splits do we need? */ bodySplit = splitSize - (hEnd-hStart); /* how much of the body goes into each split */ count = (msgEnd-hEnd+bodySplit-1)/bodySplit; /* how many splits do we need? */ bodySplit = (msgEnd-hEnd)/count; /* divide them evenly */ /* * Ok, now let's find all the split locations */ if (!(froms = NuPtr((count+1)*sizeof(long *))) || !(tos = NuPtr((count+1)*sizeof(long *))) || !(reals = NuPtr((count+1)*sizeof(Boolean)))) { WarnUser(MEM_ERR,err=MemError()); goto done; } for (i=1,*froms=hEnd;i=0;i--) { if (tos[i] != froms[i]) { /* * copy body bytes */ if (err = CopyFBytes(refN,froms[i],froms[i+1]-froms[i],refN,tos[i])) {WarnUser(WRITE_MBOX,err); goto done;} if (i) { /* * copy the header bytes */ if (err = CopyFBytes(refN,hStart,hEnd-hStart,refN,tos[i]-(hEnd-hStart+headerNl))) {FileSystemError(WRITE_MBOX,"",err); goto done;} } /* * do we need to add an ending newline to the header? */ if (headerNl) { if (err = SetFPos(refN,fsFromStart,tos[i]-headerNl)) {FileSystemError(WRITE_MBOX,"",err); goto done;} if (err = FSWriteP(refN,headerReal?Cr:"\p\015\015")) {FileSystemError(WRITE_MBOX,"",err); goto done;} } } /* * do we need to add an ending newline to the body? */ if (!reals[i+1]) { if (err = SetFPos(refN,fsFromStart,tos[i]+froms[i+1]-froms[i])) {FileSystemError(WRITE_MBOX,"",err); goto done;} if (err = FSWriteP(refN,Cr)) {FileSystemError(WRITE_MBOX,"",err); goto done;} } } TruncOpenFile(refN,tos[count]); done: if (tos) ZapPtr(tos); if (froms) ZapPtr(froms); if (reals) ZapPtr(reals); Prr = err; return(err ? 0 : count); } #ifdef POPSECURE /************************************************************************ * VetPOP - make sure the 's POP account is ok. ************************************************************************/ short VetPOP(void) { Str255 , host; long port; short err; if (UUPCIn && !UUPCOut) {WarnUser(UUPC_SECURE,0);return(1);} GetPOPInfo(,host); port = GetRLong(POP_PORT); if ((err=StartPOP(host,port))==noErr) { (void) POPIntroductions(); if (Prr) err=Prr; } EndPOP(); if (UseCTB && !err) err = CTBNavigateSTRN(NAVMID); return(err); } #endif #pragma segment MD5 static char hex[]="0123456789abcdef"; /************************************************************************ * GenDigest - generate a digest for APOP ************************************************************************/ Boolean GenDigest(UPtr banner,UPtr secret,UPtr digest) { Str255 stamp; MD5_CTX md5; short i; if (!*ExtractStamp(stamp,banner)) return(False); MD5Init(&md5); MD5Update(&md5,stamp+1,*stamp); MD5Update(&md5,secret+1,*secret); MD5Final(&md5); for (i=0;i>4)&0xf]; digest[2*i+2] = hex[md5.digest[i]&0xf]; } digest[0] = 2*sizeof(md5.digest); return(True); } #define kmd5opad (0x5C) #define kmd5ipad (0x36) #define kmd5Len (64) /************************************************************************ * GenKeyedDigest - generate a keyed digest for APOP ************************************************************************/ Boolean GenKeyedDigest(UPtr banner,UPtr secret,UPtr digest) { Str255 stamp; MD5_CTX /*ipadMD5,*/ md5; short i; /* Str255 localS; */ if (!*ExtractStamp(stamp,banner)) return(False); /* Zero(localS); BMD(secret+1,localS,*secret); for (i=0;i>4)&0xf]; digest[2*i+2] = hex[md5.digest[i]&0xf]; } digest[0] = 2*sizeof(md5.digest); return(True); } /************************************************************************ * ExtractStamp - grab the timestamp out of a POP banner ************************************************************************/ UPtr ExtractStamp(UPtr stamp,UPtr banner) { UPtr cp1,cp2; *stamp = 0; banner[*banner+1] = 0; if (cp1=strchr(banner+1,'<')) if (cp2=strchr(cp1+1,'>')) { *stamp = cp2-cp1+1; strncpy(stamp+1,cp1,*stamp); } return(stamp); } /************************************************************************ * New LMOS strategy ************************************************************************/ /************************************************************************ * FillPOPD - fill the pop descriptor with important info from a header ************************************************************************/ void FillPOPD(POPDPtr pdp,HeaderDHandle hdh) { Str255 msgId; uLong hash; if (pdp->uidHash==0) { pdp->receivedGMT = GMTDateTime(); if (pdp->uidHash==kNeverHashed || pdp->uidHash==kNoMessageId) { if (*HeaderMsgId(hdh,msgId)) hash = MIDHash(msgId+1,*msgId); else hash = FakeMIDHash(hdh); pdp->uidHash = hash; } } } /************************************************************************ * BuildPOPD - build the descriptor of the work we have to do with the * pop server * -- HERE BE DRAGONS -- * be careful with this code. It does things in a specific order for a reason ************************************************************************/ OSErr BuildPOPD(TransStream stream,POPDHandle *popDH,short count,XferFlags *flags,Boolean *capabilities) { POPDesc new, old; short i; short spot; Boolean room; Boolean anyRoom = True; /* we wouldn't be here if there weren't at least a little room */ long skipSize = GetRLong(BIG_MESSAGE) K; POPDHandle oldDH = nil; Boolean sbm = PrefIsSet(PREF_NO_BIGGIES); Boolean lmos = PrefIsSet(PREF_LMOS); long spaceNeeded = 0; short lastRead = 0; /* for the status or last methods */ uLong age = GetPrefLong(PREF_LMOS_XDAYS); Boolean onFetch, onDelete; Boolean aged; Boolean plentyRoom; short popMode = GetPrefLong(PREF_POP_MODE); int total = 0; #ifdef THREADING_ON TOCHandle tempInTocH = nil; if (InAThread()) tempInTocH = GetTempInTOC(); #endif age = (age&&lmos) ? (GMTDateTime()-24*3600*age) : 0; /* * allocate it */ if (!(*popDH = ZeroHandle(NuHandle(count*sizeof(POPDesc))))) return(WarnUser(MEM_ERR,Prr=MemError())); if (Prr=FillSizesWithList(stream,*popDH)) return(Prr); if (popMode!=popRUIDL) lastRead = FirstUnread(stream,count)-1; if (!(*CurPers)->noUIDL && (Prr = FillWithUidl(stream,*popDH))) return(Prr); ASSERT(CurResFile()==SettingsRefN); UseResFile(SettingsRefN); // make sure! if (oldDH = (void*)Get1Resource(CUR_POPD_TYPE,POPD_ID)) HNoPurge((Handle)oldDH); else { Prr = ResError(); if (Prr==resNotFound) Prr = noErr; if (Prr) return(WarnUser(BUILD_POPD,Prr)); } if (LogLevel & LOG_LMOS) Log1POPD("\pBuildPOPD","\pOld",oldDH); if ((*CurPers)->noUIDL && (Prr = FillWithTop(stream,*popDH,oldDH))) return(Prr); for (i=0;icount - 1)) { DeleteSum (tempInTocH, sum); old.retred = False; old.stubbed = False; } else { old.retred = True; new.retred = True; } } } } #endif } else { old = (*oldDH)[spot]; new = old; new.retr = False; new.stub = False; new.delete = False; if (popMode!=popRUIDL) { old.stubbed = old.stubbed || inukeHard) new.delete = True; /* * just stub? */ else if (flags->stub) {new.head = new.stub = True;} /* * is this message supposed to be toast? */ else if (old.deleted) new.delete = True; /* yes. Just kill it. */ else { /* * ok, message is not just to be killed. Make a rough pass on whether * or not it should be fetched or deleted. We'll refine our decision * later */ /* * check lists */ onFetch = IdIsOnPOPD(CUR_POPD_TYPE,FETCH_ID,new.uidHash); onDelete = IdIsOnPOPD(CUR_POPD_TYPE,DELETE_ID,new.uidHash); /* * is there room? */ room = anyRoom && (plentyRoom || !RoomForMessage(spaceNeeded+new.msgSize)); if (room) new.big2 = False; /* clear old flag */ /* * Should we fetch it? (ROUGH) */ if (!old.retred) new.retr = flags->check; /* * should we delete it? (ROUGH) */ aged = age && new.receivedGMT && age>new.receivedGMT; new.delete = !lmos || flags->servDel && onDelete ||/* user told us to */ aged && new.retred; /* * ok, now we fine-tune the process. Check that there is room for * the message, and that we don't want to skip it because of the * skip big messages preference. */ /* * if we're skipping big messages, see if we want to skip this one */ if (!sbm) new.skip = False; /* clear old flag */ else if (new.msgSize>skipSize && skipSize > 0) new.skip = True; else new.skip = False; if (new.retr && new.skip && !onFetch) { new.retr = False; if (!old.stubbed) { new.stub = True; new.delete = False; /* don't delete, because only fetching a stub */ } } /* * under what circumstances do we not fetch when we already have a stub? * We get stubs from: * Fetching headers - such stubs are never expanded except manually * Skip big messages - if the pref changes, the messages will be fetched * Not enough room - if the user makes room, the messages will be fetched */ if (old.stubbed && new.retr) { if (new.head || new.error) new.retr = False; // if the user fetched headers, must request fetch } new.retr = new.retr || flags->servFetch && onFetch; /* * If we want the whole message, do we have room? */ if (new.retr && !room) { new.big2 = True; new.delete = False; /* since we want to fetch it but can't, don't delete it */ new.retr = False; if (!old.stubbed) new.stub = True; /* shd we fetch the stub? */ } } /* * maybe we don't even have room for the stub? */ if (!anyRoom && new.stub) { new.stub = False; /* no room at the inn */ new.delete = False; /* again, since we want it but can't get it, don't delete it */ } /* * ok, we did it! Now, record how much disk this message will use. */ if (new.stub) spaceNeeded += 3 K; /* arbitrary conservative guess at stub size */ else if (new.retr) spaceNeeded += new.msgSize; anyRoom = anyRoom && (plentyRoom || !RoomForMessage(spaceNeeded)); /* * special processing */ if (flags->nuke) new.delete = True; /* * last sanity check */ if (!(new.retred || new.retr) && (!onDelete || onFetch) && !flags->nukeHard && !old.deleted) new.delete = False; /* * store it back */ (**popDH)[i] = new; if (new.retr) total+=new.msgSize; else if (new.stub) total+=3 K; } ByteProgress(nil,0,total); POPDelDup(*popDH); if (LogLevel & LOG_LMOS) LogPOPD("\pBUILT",*popDH); return(noErr); } /********************************************************************** * POPDelDup - delete duplicate messages before downloading **********************************************************************/ void POPDelDup(POPDHandle popDH) { short i,j,n; short killme; n = HandleCount(popDH); for (i=0;i(*popDH)[j].msgSize) killme = j; else killme = i; (*popDH)[killme].retr = (*popDH)[killme].stub = False; (*popDH)[killme].delete = True; } } /************************************************************************ * FillWithTop - fill up the descriptor without uidl. Not fun. ************************************************************************/ OSErr FillWithTop(TransStream stream,POPDHandle new, POPDHandle old) { short oldCount; short newCount = HandleCount(new); short oldSpot, newSpot; short oldUndelCount; short oldUndelSpot; if (old) { oldCount = HandleCount(old); /* * find last undeleted message in old descriptor, and count them */ oldUndelCount = 0; for (oldSpot=0;oldSpot=oldUndelCount) { Prr = FillPOPDFromServer(stream,new,oldUndelCount-1); if (Prr) return(Prr); if (MatchPOPD(old,oldUndelSpot,(*new)[oldUndelCount-1].uidHash)) { /* * ok, now we make the big leap of faith. Assume that since we found * this one where we expected it, the others will be there, too */ ComposeLogS(LOG_LMOS,nil,"\pCopy old to %d",oldUndelCount); for (newSpot=oldSpot=0;oldSpotfunFields,InterestHeadStrn+hMessageId,scratch)) PCopyTrim(msgId,scratch,128); else *msgId = 0; return(msgId); } /************************************************************************ * FakeMIDHash - fake a message-id for something that doesn't have one ************************************************************************/ uLong FakeMIDHash(HeaderDHandle hdh) { Str255 scratch; /* * no message-id in this message. Look for other headers of interest, * and add them to our string one at a time */ *scratch = 0; AAFetchResData((*hdh)->funFields,InterestHeadStrn+hReceived,scratch); if (!*scratch) AAFetchResData((*hdh)->funFields,InterestHeadStrn+hDate,scratch); LDRef(hdh); PSCat(scratch,(*hdh)->who); PSCat(scratch,(*hdh)->subj); UL(hdh); return(Hash(scratch)); } /************************************************************************ * CountFetch - count the number of messages to be fetched ************************************************************************/ short CountFetch(POPDHandle popDH) { short n; short fetch = 0; if (*popDH) { n = HandleCount(popDH); while (n--) { if ((*popDH)[n].retr || (*popDH)[n].stub) fetch++; } } return(fetch); } /************************************************************************ * AddIdToPOPD - add a message to a POPD list ************************************************************************/ OSErr AddIdToPOPD(OSType theType,short listId, uLong uidHash,Boolean dupOk) { POPDHandle resH; short n; short i; POPDesc popd; OSErr err; if (!ValidHash(uidHash)) return(errNotFound); resH = GetResourceMainThread_(theType,listId); if (!resH) { resH = NuHandle(0); if (resH) AddMyResourceMainThread_(resH,theType,listId,""); else return(resNotFound); } HNoPurge_(resH); n = HandleCount(resH); for (i=0;inoUIDL = True; return(noErr); } // if (n>100) ByteProgress(nil,0,n); for (size=sizeof(buffer); !(Prr=RecvLine(stream,buffer,&size)) && !POP_TERM(buffer,size); size=sizeof(buffer)) { MiniEvents(); if (CommandPeriod) break; CycleBalls(); if (*buffer!=' ' && *buffer!='\t') { msgNum = Atoi(buffer); if (msgNum<1 || msgNum>n) continue; // if (n>100) ByteProgress(nil,-1,0); #ifdef DEBUG if (RunType!=Production && ++i!=msgNum) AlertStr(OK_ALRT,Stop,"\pBad UIDL!"); #endif end = buffer+size; while (end[-1]<=' ') end--; for (spot=buffer;spot 100 && !Prr) ByteProgress(nil,1,1); for (i=0;i",intro,which); else { HNoPurge_(popDH); count = GetHandleSize_(popDH)/sizeof(POPDesc); for (i=0;iaddr[0])) return(err); CtoPCpy(host,hi.cname); /* * build the service name */ spot = host+1; PToken(host,shortHost,&spot,"."); EscapeChars(host,GetRString(fmt,KERBEROS_ESCAPES)); EscapeChars(serviceName,GetRString(fmt,KERBEROS_ESCAPES)); GetRString(fmt,KERBEROS_SERVICE_FMT); utl_PlugParams(fmt,fullName,serviceName,host,realm,shortHost); ProgressMessage(kpMessage,ComposeRString(scratch,KERBEROS_TICK_FMT,fullName)); // Null terminate these, KCLient expects C-Strings version[*version+1] = 0; fullName[*fullName+1] = 0; bufLen = GetRLong(KERBEROS_BSIZE); if (!(*ticket = NuHandle(bufLen))) return(MemError()); /* * call the driver */ LDRef(*ticket); err = KClientMakeSendAuthCompat(&gSession, fullName+1, **ticket, &bufLen, GetRLong(KERBEROS_CHECKSUM), version+1); UL(*ticket); /* * adjust the ticket size */ if (err) ZapHandle(*ticket); else SetHandleSize(*ticket,bufLen); /* * Cleanup */ return(err); } /********************************************************************** * InitKerberos - get ready to start making Kerberos calls **********************************************************************/ OSErr InitKerberos() { OSErr err = noErr; if (!gPOPKerbInited) { // make sure Kerberos is present before we start calling it. if ((Ptr) (KClientNewSessionCompat) == (Ptr) (kUnresolvedCFragSymbolAddress)) { // Kerberos is not installed. Warn and crap out. WarnUser(NO_KERBEROS,err); err = fnfErr; } else { // Kerberos is here. Try to start a new session. err = KClientNewSessionCompat(&gSession, 1, GetRLong(POP_PORT), 2, GetRLong(POP_PORT)); if (err != noErr) return (err); else gPOPKerbInited = true; } } return (err); } /********************************************************************** * SendPOPTicket - send a ticket to the Pop server **********************************************************************/ OSErr SendPOPTicket(TransStream stream) { Str63 popName,host,realm,version; Handle ticket=nil; OSErr err; if (err=GetPOPInfo(popName,host)) return(err); GetRString(popName,KERBEROS_POP_SERVICE); GetPref(realm,PREF_REALM); GetRString(version,KERBEROS_VERSION); if (err=KerbGetTicket(popName,host,realm,version,&ticket)) return(WarnUser(NO_KERBEROS,err)); err=SendTrans(stream,LDRef(ticket),GetHandleSize(ticket),nil); ZapHandle(ticket); return(err); }