/* 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 "tcp.h" #define FILE_NUM 37 /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */ /************************************************************************ * functions for i/o over a pseudo-telnet MacTcp stream * these functions are oriented toward CONVENIENCE, not performance ************************************************************************/ #pragma segment TcpTrans /************************************************************************ * private functions ************************************************************************/ #define TcpTrouble(stream,which,err) TT(stream,which,err,FNAME_STRN+FILE_NUM,__LINE__) int TT(TransStream stream,int which, int theErr, int file, int line); uLong RandomAddr(uLong *addrs); void NoteAddrGoodness(struct hostInfo *hip,uLong addr,short err); pascal void BindDone(struct hostInfo *hostInfoPtr, char *userData); pascal void TcpASR(StreamPtr tcpStream, unsigned short eventCode,UPtr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg); /********************************************************************** * ConnectTransLo - connect to a host **********************************************************************/ OSErr ConnectTrans(TransStream stream, UPtr serverName, long port, Boolean silently,uLong timeout) { Str255 realHost; PCopy(realHost,serverName); SplitPort(realHost,&port); return ConnectTransLo(stream,realHost,port,silently,timeout); } /********************************************************************** * SplitPort - Split host:port into components * returns true if port found **********************************************************************/ Boolean SplitPort(PStr host,long *port) { UPtr colon; long localPort; if (colon=PIndex(host,':')) { *colon = *host - (colon-host); *host = colon-host-1; StringToNum(colon,&localPort); ASSERT(localPort>0); if (localPort>0 && port) *port = localPort; return true; } return false; } /********************************************************************** * RandomAddr - pick a random address out of a set of addresses **********************************************************************/ uLong RandomAddr(uLong *addrs) { short count; if (!PrefIsSet(PREF_DNS_BALANCE)) return(addrs[0]); for (count=NUM_ALT_ADDRS;count;count--) if (addrs[count-1]) break; return(addrs[count<2?0:TickCount()%count]); } /********************************************************************** * NoteAddrGoodness - remove an address or all except an address **********************************************************************/ void NoteAddrGoodness(struct hostInfo *hip,uLong addr,short err) { short count,i; if (!PrefIsSet(PREF_DNS_BALANCE)) return; for (count=NUM_ALT_ADDRS;count;count--) if (hip->addr[count-1]==addr) break; if (err) { /* this addr didn't work. remove it */ for (i=count;iaddr[i-1] = hip->addr[i]; hip->addr[NUM_ALT_ADDRS-1] = 0; if (count==1) *hip->cname = 0; /* if used last addr, kill the name to force another lookup */ } else { /* mark the OTHERS as dead. */ WriteZero(hip->addr,sizeof(uLong)*NUM_ALT_ADDRS); hip->addr[0] = addr; } } #pragma segment Main /************************************************************************ * BindDone - report that the resolver has done its duty ************************************************************************/ pascal void BindDone(struct hostInfo *hostInfoPtr, char *userData) { *(short *)userData = hostInfoPtr->rtnCode; return; } #pragma segment TcpTrans /************************************************************************ * TcpTrouble - report an error with TCP and break the connection ************************************************************************/ int TT(TransStream stream, int which, int theErr, int file, int line) { if (stream==0 || (!stream->BeSilent && (!CommandPeriod || stream->Opening&&!PrefIsSet(PREF_OFFLINE)&&!PrefIsSet(PREF_NO_OFF_OFFER)))) { Str255 message; Str255 tcpMessage; Str63 debugStr; Str31 rawNumber; short realSettingsRef = SettingsRefN; NumToString(theErr,rawNumber); SettingsRefN = GetMainGlobalSettingsRefN(); GetRString(message, which); if (-23000>=theErr && theErr >=-23048) GetRString(tcpMessage,MACTCP_ERR_STRN-22999-theErr); else if (2<=theErr && theErr<=9) GetRString(tcpMessage,MACTCP_ERR_STRN+theErr+(23048-23000)); else *tcpMessage = 0; ComposeRString(debugStr,FILE_LINE_FMT,file,line); SettingsRefN = realSettingsRef; MyParamText(message,rawNumber,tcpMessage,debugStr); if (stream==0 || stream->Opening) { if (2==ReallyDoAnAlert(OPEN_ERR_ALRT,Caution)) SetPref(PREF_OFFLINE,YesStr); } else ReallyDoAnAlert(BIG_OK_ALRT,Caution); } return(stream ? (stream->streamErr=theErr) : theErr); } /************************************************************************ * TcpASR - asynchronous notification routine ************************************************************************/ pascal void TcpASR(StreamPtr tcpStream, unsigned short eventCode, UPtr userDataPtr, unsigned short terminReason, struct ICMPReport *icmpMsg) { //#pragma unused(userDataPtr) if (tcpStream==((TransStreamPtr)userDataPtr)->TcpStream) { if (eventCode==TCPDataArrival) ((TransStreamPtr)userDataPtr)->CharsAvail = 1; else if (eventCode==TCPICMPReceived) { ICMPAvail = 1; ICMPMessage = *icmpMsg; } else if (eventCode==TCPTerminate) { WhyTCPTerminated = terminReason; } } } #pragma segment TcpTrans /********************************************************************************* * Eudora might should take advantage of native Open Transport TCP/IP when it's * installed. The following functions implement the same as the MacTCP ones above, * but employ OT. There's also some PPP stuff here, too. *********************************************************************************/ OSErr pppErr = noErr; MyOTPPPInfoStruct MyOTPPPInfo; unsigned long connectionSelection = kOtherSelected; long oldDlogState = 0; Boolean needPPPConnection = false; //used to determine when PPP is down but should be up. Boolean OTinitialized = false; Boolean dialingThePhone = false; //set to true after we've dialed the phone. Boolean gUpdateTPWindow = false; // set to true when the connection method changes to invalidate the TP window //setup Boolean HasOTInetLib(void); Boolean OTSupported(void); OSErr OpenOTInternetServices(MyOTInetSvcInfo *myOTInfo); pascal void MyOTNotifyProc(MyOTInetSvcInfo *info, OTEventCode theEvent, OTResult theResult, void *theParam); OSErr CreateOTStream(TransStream stream); OSErr OTTCPOpen(TransStream stream, InetHost tryAddr, InetPort port,uLong timeout); void EnqueueMyTStream(MyOTTCPStreamPtr myStream); void DestroyMyTStream(MyOTTCPStreamPtr myStream); //communication OSErr OTGetHostByName(InetDomainName hostName, InetHostInfo *hostInfoPtr); OSErr OTGetHostByAddr(InetHost addr, InetHostInfo *domainNamePtr); InetHost OTRandomAddr(InetHostInfo *host); OSErr OTGetDomainMX(InetDomainName hostName, InetMailExchange *MXPtr, short *numMX); void GetPreferredMX(InetDomainName preferred, InetMailExchange *MXPtr, short numMX); short OTWaitForChars(TransStream stream, long timeout, UPtr line, long *size, OTResult *otResult); pascal void MyOTStreamNotifyProc (MyOTTCPStream *myStream, OTEventCode code, OTResult theResult, void *theParam); short SpinOnWithConnectionCheck(short *rtnCodeAddr,long maxTicks,Boolean allowCancel,Boolean forever); //error reporting OSErr OTTE(TransStream stream, OSErr generalError, OSErr specficError, short file, short line); #define OTTCPError(stream,generalError,specficError) OTTE(stream,generalError,specficError,FNAME_STRN+FILE_NUM,__LINE__) #define IsMyOTError(x) (x >= errOTInitFailed && x <= errMyLastOTErr) #define IsOTPPPError(x) (x <= kCCLErrorStart && x >= kCCLErrorEnd) //PPP functions OSErr OTVerifyOpen(TransStream stream); OSErr OTPPPConnect(Boolean *attemptedConnection); OSErr GetPPPConnectionState(EndpointRef endPoint, unsigned long *PPPState); pascal void MyPPPEndpointNotifier(void *context, OTEventCode code, OTResult result, void *cookie); OSErr TurnOnPPPConnectionDialog(EndpointRef endPoint, Boolean on); OSErr ResetPPPConnectionDialog(EndpointRef endPoint); OSErr WaitForOTPPPDisconnect(Boolean showStatus); OTResult NewPPPControlEndpoint(void); OSErr GetCurrentUInt32Option(EndpointRef endPoint, OTXTIName theOption, UInt32 *value); OSErr SetCurrentUInt32Option(EndpointRef endPoint, OTXTIName theOption, UInt32 value); OSErr OTPPPDialingInformation(Boolean *redial, unsigned long *numRedials, unsigned long *delay); Boolean SearchDirectoryForFile(CInfoPBRec *info, long dirToSearch, OSType type, OSType creator); void UpdateCachedConnectionMethodInfo(void); /********************************************************************************* * OTInitOpenTransport - initialize open transport if present. *********************************************************************************/ void OTInitOpenTransport(void) { //if we have OT && TCP, then try to initialize it if (OTIs) { if (!HasOTInetLib()) { gUseOT = false; // Only warn the user if we know OT works on this machine if (OTSupported()) OTTCPError(NULL,errOTInitFailed,errOTMissingLib); } else { gUseOT = true; //Initialize if we haven't already. if (!OTinitialized) { OSStatus osStatus; if ((osStatus = InitOpenTransportInContext(kInitOTForApplicationMask,nil)) != kOTNoError) { gUseOT = false; OTTCPError(NULL,errOTInitFailed,osStatus); } else //Open Transport is here. { //See if RemoteAcess/OT/PPP is present while we're here. long result; OSErr err = Gestalt(gestaltOpenTptRemoteAccess, &result); if ((err == noErr) && (result & (1 << gestaltOpenTptRemoteAccessPresent)) && (result & (1 << gestaltOpenTptPPPPresent))) { gHasOTPPP = true; } else { gHasOTPPP = false; } #ifdef USE_NETWORK_SETUP // check for the network setup library as well ... UseNetworkSetup(); #endif //UseNetworkSetup OTinitialized = true; //remember that we started OT. } } } } } /********************************************************************************* * HasOTInetLib - (CFM Only) returns true if the OpenTptInetLib functions are present. *********************************************************************************/ Boolean HasOTInetLib(void) { Boolean HasOTInetLib = true; #if TARGET_RT_MAC_CFM HasOTInetLib = (long)(&OTInetGetInterfaceInfo) != kUnresolvedCFragSymbolAddress && (long)(&OTInetStringToAddress) != kUnresolvedCFragSymbolAddress && (long)(&OTInetAddressToName) != kUnresolvedCFragSymbolAddress && (long)(&OTInetMailExchange) != kUnresolvedCFragSymbolAddress; #endif //TARGET_RT_MAC_CFM return(HasOTInetLib); } /********************************************************************************* * OTSupported - (CFM Only) returns true if OT can be used on this 68K machine *********************************************************************************/ Boolean OTSupported(void) { Boolean OTSupported = true; long result; #if TARGET_RT_MAC_CFM // OT can't be used if this is a 68K machine AND an OT version less than 1.2 is installed. OTSupported = false; if (Gestalt(gestaltOpenTptVersions, &result) == noErr) { if ((result & 0xFFFF0000) >= 0x01200000) OTSupported = true; } #endif //TARGET_RT_MAC_CFM return(OTSupported); } /********************************************************************************* * OTCloseOpenTransport - cleanup after Open Transport has been used *********************************************************************************/ void OTCleanUpAfterOpenTransport(void) { if (OTinitialized) { //shut down OT/PPP if appropriate ... if (gHasOTPPP) if (MyOTPPPInfo.weConnectedPPP == true) OTPPPDisconnect(false, true); CloseOpenTransportInContext(nil); } } /********************************************************************************* * OTTCPConnectTrans - connect to the remote host. This version uses OT TCP. *********************************************************************************/ OSErr OTTCPConnectTrans(TransStream stream, UPtr serverName, long port,Boolean silently,uLong timeout) { InetHostInfo hostInfo; Str255 scratch; InetMailExchange MXRecords[NUM_MX]; short numMX = NUM_MX; InetDomainName hostName; Boolean useMX = PrefIsSet(PREF_IGNORE_MX) && (port == GetRLong(SMTP_PORT)) && GetOSVersion()<0x1030; InetHost tryAddr; long receiveBufferSize = 0; OSErr err = noErr; #ifdef DEBUG if (BUG12) port += 10000; #endif ASSERT(stream); ASSERT(stream->OTTCPStream == 0); stream->streamErr = noErr; stream->Opening = true; stream->BeSilent = silently; //allocate memory for a new connection stream->OTTCPStream = New(MyOTTCPStream); if (stream->OTTCPStream != 0) { WriteZero(stream->OTTCPStream,sizeof(MyOTTCPStream)); ProgressMessageR(kpSubTitle,WHO_AM_I); //Make sure TCP is up and going. Connect with OTPPP or something at this point. if ((err=OTVerifyOpen(stream)) == noErr) { gActiveConnections++; // a TCP connection has been established PCat(GetRString(scratch, DNR_LOOKUP), serverName); ProgressMessage(kpSubTitle, scratch); strncpy(hostName,serverName+1,(*serverName > kMaxHostNameLen? kMaxHostNameLen : *serverName)); hostName[*serverName] = 0; //MX allows us to pick the best address to send mail to. if (useMX) { if ((err = OTGetDomainMX(hostName, MXRecords, &numMX)) != noErr) useMX = false; else GetPreferredMX(hostName, MXRecords, numMX); } if (err != userCancelled) { //Lookup the host by its name if ((err = OTGetHostByName(hostName, &hostInfo)) == noErr) { tryAddr = OTRandomAddr(&hostInfo); //OTGetHostByName succeeded. Create the myStream structure, and allocate a buffer ProgressMessageR(kpSubTitle,HOUSEKEEPING); if ((err = CreateOTStream(stream)) == noErr) { // allocate a buffer for tcp, and create the stream receiveBufferSize = GetRLong(RCV_BUFFER_SIZE); if ((stream->RcvBuffer=NuHTempOK(receiveBufferSize))!=nil) { //Create stream succeeded. Now try opening the connection. ComposeRString(scratch,CNXN_OPENING,serverName,tryAddr); ProgressMessageR(kpSubTitle,PREPARING_CONNECTION); ProgressMessage(kpMessage,scratch); // log the connection we're about to make ComposeLogS(LOG_PROTO,nil,"\pConnecting to %i:%d",tryAddr,port); if ((err = OTTCPOpen(stream, tryAddr, port, timeout)) == noErr) { //open succeeded. stream->RcvSpot = -1; ComposeLogS(LOG_PROTO,nil,"\pConnected to %i:%d",tryAddr,port); } else if (err != userCancelled) { OTTCPError(stream,errOpenStream, stream->streamErr); ComposeLogS(LOG_PROTO,nil,"\pConnection to %i:%d failed. Error %d",tryAddr,port,err); } } else WarnUser(MEM_ERR, stream->streamErr); // Memory error while creating buffer } else if (err != userCancelled) OTTCPError(stream,errCreateStream, stream->streamErr=err); } else { if (err != userCancelled) OTTCPError(stream,errDNR,stream->streamErr=err); // else OTTCPError(stream,errUserCancelledConnection,stream->streamErr=err); // Log the DNS Error ComposeLogS(LOG_PROTO,nil,"\pConnection failed. DNS error %d", err); } } } else { if (stream->streamErr == -7109) stream->streamErr = userCancelled; if (stream->streamErr != userCancelled) OTTCPError(stream,errPPPConnect, stream->streamErr); } } else WarnUser(OT_CON_MEM_ERR, stream->streamErr); stream->Opening = false; return ((OSErr)stream->streamErr); } #define SWING_THE_THING 60 /********************************************************************************* * OTTCPSendTrans - send some text to the remote host. This version uses OT TCP. *********************************************************************************/ OSErr OTTCPSendTrans(TransStream stream, UPtr text,long size, ...) { OTResult result = noErr; va_list extra_buffers; UPtr curBuf; long curSize = 0; long last = TickCount(); long now = 0; Boolean slow = False; ASSERT(stream); ASSERT(stream->OTTCPStream); stream->streamErr = noErr; if (CommandPeriod) return(userCancelled); if (size==0) return(noErr); // allow vacuous sends va_start(extra_buffers,size); //We'll send the text buffer first. curBuf = text; curSize = size; do { //send the current buffer. while (curSize > 0) { now = TickCount(); result = OTSnd(stream->OTTCPStream->ref, curBuf, curSize, 0); if (result >= 0) { //log what we sent if (LogLevel&LOG_TRANS && !stream->streamErr && result) CarefulLog(LOG_TRANS,LOG_SENT,curBuf,result); CycleBalls(); curSize -= result; curBuf += result; last = now; if (IsSendAudit(stream)) stream->bytesTransferred += result; } else //no data was sent { if (result != kOTFlowErr) //something happened to the connection. Stop the transfer { if (stream->OTTCPStream->otherSideClosed) { stream->streamErr = result = errLostConnection; } else stream->streamErr = errMiscSend; break; } else //just some flow control issue or something. Swing the thing, and check for a command-. { // check to see if we need ppp, but the connection is down if (needPPPConnection && MyOTPPPInfo.state != kPPPUp) return(stream->streamErr = result = errLostConnection); } } if (slow || (now - last) > SWING_THE_THING) { if (slow) YieldTicks = 0; MiniEvents(); if (CommandPeriod) return(stream->streamErr = userCancelled); if ((now - last) > SWING_THE_THING) { slow = True; CyclePendulum(); last = now - SWING_THE_THING/2; } } } //point to the next buffer to send if (!stream->streamErr) { curBuf = va_arg(extra_buffers,UPtr); curSize = va_arg(extra_buffers,long); } } while (!stream->streamErr && curBuf); va_end(extra_buffers); if (stream->streamErr) { if (stream->streamErr!=commandTimeout && stream->streamErr!=userCancelled) OTTCPError(stream,stream->streamErr,result); } return (stream->streamErr); } /********************************************************************************* * OTTCPRecvTrans - get some text from the remote host. This version uses OT TCP. *********************************************************************************/ OSErr OTTCPRecvTrans(TransStream stream, UPtr line,long *size) { OTResult result = noErr; Str31 scratch; long timeout = InAThread() ? GetRLong(THREAD_RECV_TIMEOUT) : GetRLong(RECV_TIMEOUT); ASSERT(stream); ASSERT(stream->OTTCPStream); stream->streamErr = noErr; // Spin until something comes in. do { // read characters from the network, or die trying. stream->streamErr = OTWaitForChars(stream, timeout, line, size, &result); if (stream->streamErr == noErr) { if (result >= 0) // got something *size = result; else if (stream->OTTCPStream->otherSideClosed) // lost connection stream->streamErr = errLostConnection; else if (result == kOTNoDataErr) // got nothing *size = 0; else // got some other error stream->streamErr = errMiscRec; } } while (stream->streamErr == commandTimeout && AlertStr(TIMEOUT_ALRT,Caution,GetRString(scratch,InAThread() ? THREAD_RECV_TIMEOUT : RECV_TIMEOUT))==1); if (stream->streamErr) { if (stream->streamErr!=commandTimeout && stream->streamErr!=userCancelled) OTTCPError(stream,stream->streamErr,(result < 0 ? result : kOTNoDataErr)); // Note: result could be 0 if OTWaitForChars fails. We want a real error message displayed in that case, too. } else { if (*size) ResetAlertStage(); if (*size && LogLevel&LOG_TRANS) CarefulLog(LOG_TRANS,LOG_GOT,line,*size); //log what we got ... if (*size && IsRecAudit(stream)) stream->bytesTransferred += *size; } return (stream->streamErr); } /********************************************************************************* * TCPDisTrans - disconnect from the remote host. This version uses OT TCP. *********************************************************************************/ OSErr OTTCPDisTrans(TransStream stream) { if (stream && stream->OTTCPStream) { stream->streamErr = noErr; //We're going to do an orderly disconnect on this stream. stream->OTTCPStream->weAreClosing = true; if ((stream->OTTCPStream->ref) && (!stream->OTTCPStream->ourSideClosed)) { //close our end if the other side has aborted. if (stream->OTTCPStream->otherSideClosed) { OTSndDisconnect(stream->OTTCPStream->ref, nil); stream->OTTCPStream->ourSideClosed = true; stream->OTTCPStream->releaseMe = true; //we can destroy this connection completely. } else { if (OTSndOrderlyDisconnect(stream->OTTCPStream->ref)!=noErr) stream->OTTCPStream->releaseMe = true; stream->OTTCPStream->ourSideClosed = true; stream->OTTCPStream->age = TickCount(); } } return (stream->streamErr); } else return (noErr); } /********************************************************************************* * OTTCPDestroyTrans - destroy the connection. This version uses OT TCP *********************************************************************************/ OSErr OTTCPDestroyTrans(TransStream stream) { OSStatus status = noErr; if ((stream == 0) || (stream->OTTCPStream == 0)) return (noErr); //We are destroying the stream without disconnecting it. An error occured, or the the user cancelled. //Do an abortive disconnect. if (!stream->OTTCPStream->weAreClosing) { DestroyMyTStream(stream->OTTCPStream); ZapPtr(stream->OTTCPStream); } else //otherwise, queue up the stream for an orderly disconnect. { if (stream->OTTCPStream) { //if both sides are closed, throw out this connection immediately if (stream->OTTCPStream->ourSideClosed && stream->OTTCPStream->otherSideClosed) stream->OTTCPStream->releaseMe = true; if (!stream->OTTCPStream->releaseMe) { //Queue it up in our queue of MyTStreams waiting to hear an orderly disconnect EnqueueMyTStream(stream->OTTCPStream); stream->OTTCPStream = 0; } else { //an error occurred disconnecting our stream. Just kill it. DestroyMyTStream(stream->OTTCPStream); ZapPtr(stream->OTTCPStream); } } } //dispose our receive buffer. if (stream->RcvBuffer) ZapHandle(stream->RcvBuffer); return (stream->streamErr); } /********************************************************************************* * OTTCPTransError - report our most recent error *********************************************************************************/ OSErr OTTCPTransError(TransStream stream) { ASSERT(stream); ASSERT(stream->OTTCPStream); return (stream->streamErr); } /********************************************************************************* * OTTCPSilenceTrans - turn off error reports from ot tcp routines *********************************************************************************/ void OTTCPSilenceTrans(TransStream stream, Boolean silence) { ASSERT(stream); stream->BeSilent = silence; } /******************************************************************************** * OTTCPWhoAmI - return the mac's tcp name. This version uses OT TCP. ********************************************************************************/ UPtr OTTCPWhoAmI(TransStream stream, Uptr who) { #pragma unused(stream) InetInterfaceInfo localInfo; MyOTInetSvcInfo myOTInfo; short len; OSErr whoAmIErr = noErr; if (!*MyHostname) { whoAmIErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); if (whoAmIErr == kOTNotFoundErr) //OT hasn't been loaded yet ... { if (whoAmIErr = OpenOTInternetServices(&myOTInfo)) return (who); whoAmIErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; } if (whoAmIErr != noErr) return (who); ComposeRString(who,TCP_ME,localInfo.fAddress); SetPref(PREF_LASTHOST,who); PCopy(MyHostname,who); } PCopy(who,MyHostname); //remove the '.' from the end of who, if there is one. len = strlen(who); if (who[len-1]=='.') who[len-1]=0; return(who); } /******************************************************************************** * DNSHostid - return the mac's dns server hostid. This version uses OT TCP. ********************************************************************************/ OSErr DNSHostid(uLong *dnsAddr) { InetInterfaceInfo localInfo; MyOTInetSvcInfo myOTInfo; OSErr dnsHostidErr = noErr; dnsHostidErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); if (dnsHostidErr == kOTNotFoundErr) //OT hasn't been loaded yet ... { if (!(dnsHostidErr = OpenOTInternetServices(&myOTInfo))) { dnsHostidErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; } } if (!dnsHostidErr) *dnsAddr = *(long*)&localInfo.fDNSAddr; return(dnsHostidErr); } /******************************************************************************** * OTMyHostid - return the mac's hostid. This version uses OT TCP. ********************************************************************************/ OSErr OTMyHostid(uLong *myAddr,uLong *myMask) { InetInterfaceInfo localInfo; MyOTInetSvcInfo myOTInfo; OSErr myHostidErr=noErr; myHostidErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); if (myHostidErr == kOTNotFoundErr) //OT hasn't been loaded yet ... { if (!(myHostidErr = OpenOTInternetServices(&myOTInfo))) { myHostidErr = OTInetGetInterfaceInfo(&localInfo, kDefaultInetInterface); OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; } } if (!myHostidErr) { *myAddr = *(long*)&localInfo.fAddress; *myMask = *(long*)&localInfo.fNetmask; } return(myHostidErr); } /******************************************************************************** * OTMyHostid - return the mac's hostid. This version uses OT TCP. ********************************************************************************/ OSErr GetMyHostid(uLong *addr,uLong *mask) { return(OTMyHostid(addr,mask)); } /************************************************************************ * OTGetHostByName - get host information, given a hostname * this routine maintains a small, unflushable cache. ************************************************************************/ OSErr OTGetHostByName(InetDomainName hostName, InetHostInfo *hostInfoPtr) { MyOTInetSvcInfo myOTInfo; OSErr nameErr = noErr; if (nameErr = OpenOTInternetServices(&myOTInfo)) return (nameErr); myOTInfo.status = inProgress; nameErr = OTInetStringToAddress(myOTInfo.ref,hostName,hostInfoPtr); if (nameErr == noErr) nameErr = SpinOnWithConnectionCheck(&(myOTInfo.status),0,True,False); if (nameErr == noErr) nameErr = myOTInfo.result; OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; //return without changing the name of the server we wish to look up. if (nameErr == noErr) strcpy(hostInfoPtr->name,hostName); // log the result of the name lookup. Be careful not to overrun buffers. if (LogLevel&LOG_PROTO) { short count; Str63 logHostName; MakePStr(logHostName,hostName,strlen(hostName)); ComposeLogS(LOG_PROTO,nil,"\pDNS Lookup of \"%p\"",logHostName); if (nameErr == noErr) { for (count=0;countaddrs[count]) ComposeLogS(LOG_PROTO,nil,"\p %i (%d)",hostInfoPtr->addrs[count],count+1); } else ComposeLogS(LOG_PROTO,nil,"\pLookup failed (error %d)",nameErr); } return (nameErr); } /********************************************************************************* * OTGetHostByAddr - get host information, given an address. This version uses OT * this routine maintains a small, unflushable cache. *********************************************************************************/ OSErr OTGetHostByAddr(InetHost addr, InetHostInfo *domainNamePtr) { MyOTInetSvcInfo myOTInfo; short len = 0; OSErr addrErr = noErr; domainNamePtr->addrs[0] = addr; if (addrErr = OpenOTInternetServices(&myOTInfo)) return (addrErr); myOTInfo.status = inProgress; addrErr = OTInetAddressToName(myOTInfo.ref,domainNamePtr->addrs[0],domainNamePtr->name); if (addrErr == noErr) addrErr = SpinOnWithConnectionCheck(&(myOTInfo.status),0,True,False); if (addrErr == noErr) addrErr = myOTInfo.result; OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; //remove the '.' from the end of name, if there is one. if (addrErr == noErr) { len = strlen(domainNamePtr->name); if (domainNamePtr->name[len-1]=='.') domainNamePtr->name[len-1]=0; } return (addrErr); } /********************************************************************** * OTRandomAddr - pick a random address out of a set of addresses we * got from our OT DNR lookip. **********************************************************************/ InetHost OTRandomAddr(InetHostInfo *host) { short count; if (!PrefIsSet(PREF_DNS_BALANCE)) return (host->addrs[0]); for (count=kMaxHostAddrs;count;count--) if (host->addrs[count-1]) break; return (host->addrs[count<2?0:TickCount()%count]); } /************************************************************************ * OTGetDomainMX - Get the mail exchange info for a particular host. ************************************************************************/ OSErr OTGetDomainMX(InetDomainName hostName, InetMailExchange *MXPtr, short *numMX) { OSErr err = noErr; MyOTInetSvcInfo myOTInfo; short i; if (MXPtr == 0 // must have a place to store the MX records || numMX == 0 // must have an idea of how many records will fit there || *numMX <= 0) // and must have allocated room for at least one return (paramErr); //clear out the MXPtr for (i = 0; i < *numMX; i++) (MXPtr[i]).exchange[0] = (MXPtr[i]).preference = 0; //do the MX lookup if ((err = OpenOTInternetServices(&myOTInfo)) == noErr) { myOTInfo.status = inProgress; err = OTInetMailExchange(myOTInfo.ref, hostName, numMX, MXPtr); if (err == noErr) err = SpinOnWithConnectionCheck(&(myOTInfo.status),0,True,False); OTCloseProvider(myOTInfo.ref); myOTInfo.ref = 0; if (err == noErr) if (MXPtr->exchange[0] == 0) return (errNoMXRecords); } return (err); } /********************************************************************** * GetPreferredMX - return the preferred host to send mail to. **********************************************************************/ void GetPreferredMX(InetDomainName preferredName, InetMailExchange *MXPtr, short numMX) { short lowestPref, preferredHost; short count = numMX - 1; short len; preferredHost = count; lowestPref = MXPtr[count].preference; for (count; count >= 0; count--) { if (MXPtr[count].preference < lowestPref) { lowestPref = MXPtr[count].preference; preferredHost = count; } } strcpy(preferredName, MXPtr[preferredHost].exchange); //remove the '.' from the end of preferredName, if there is one. len = strlen(preferredName); if (preferredName[len-1]=='.') preferredName[len-1]=0; } #pragma segment Main /********************************************************************************* * MyOTNotifyProc - This callback handles the results of all asynch OT calls. *********************************************************************************/ pascal void MyOTNotifyProc(MyOTInetSvcInfo *info, OTEventCode theEvent, OTResult theResult, void *theParam) { switch (theEvent) { case T_OPENCOMPLETE: // The OTAsyncOpenInternetServices function has completed case T_DNRSTRINGTOADDRCOMPLETE: // The OTInetStringToAddress function has finished case T_DNRADDRTONAMECOMPLETE: // The OTInetAddressToName function has finished case T_DNRMAILEXCHANGECOMPLETE: // The OTInetMailExchange function has finished info->status = 0; info->result = theResult; info->cookie = theParam; break; default: // All other network events can be ignored break; } } #pragma segment TcpTrans /********************************************************************************* * OpenOTInternetServices - Open up the internet service provider OT gives us. *********************************************************************************/ OSErr OpenOTInternetServices(MyOTInetSvcInfo *myOTInfo) { OSErr err = noErr; DECLARE_UPP(MyOTNotifyProc,OTNotify); INIT_UPP(MyOTNotifyProc,OTNotify); myOTInfo->status = inProgress; if ((err = OTAsyncOpenInternetServicesInContext(kDefaultInternetServicesPath, 0, MyOTNotifyProcUPP, myOTInfo,NULL)) == noErr) if ((err = SpinOnWithConnectionCheck(&(myOTInfo->status),0,True,False)) == noErr) { myOTInfo->ref = myOTInfo->cookie; } if (err != noErr && err != userCancelled) err = errOTInetSvcs; return (err); } #pragma segment Main /********************************************************************************* * MyOTStreamNotifyProc - my OT notifier proc for TCP streams. *********************************************************************************/ pascal void MyOTStreamNotifyProc (MyOTTCPStream *myStream, OTEventCode code, OTResult theResult, void *cookie) { OSStatus err; switch (code) { case T_DISCONNECT: // Other side has aborted myStream->otherSideClosed = true; myStream->status = 0; break; case T_ORDREL: // Other side has closed in an orderly fashion myStream->otherSideClosed = true; myStream->status = 0; err = OTRcvOrderlyDisconnect(myStream->ref); if (!myStream->ourSideClosed) { err = OTSndOrderlyDisconnect(myStream->ref); myStream->ourSideClosed = true; } myStream->releaseMe = true; // we don't need this stream anymore. break; case T_OPENCOMPLETE: // OTOpenAsyncEndpoint has finished case T_BINDCOMPLETE: // OTBind has finished case T_UNBINDCOMPLETE: // OTUnbind has finished case T_CONNECT: // OTConnect has finished case T_PASSCON: // state is now T_DATAXFER myStream->status = 0; myStream->code = code; myStream->result = theResult; myStream->cookie = cookie; break; } } #pragma segment TcpTrans /********************************************************************************* * CreateOTStream - create a MyOTTCPStreamPtr *********************************************************************************/ OSErr CreateOTStream(TransStream stream) { OSStatus OTErr = noErr; DECLARE_UPP(MyOTStreamNotifyProc,OTNotify); INIT_UPP(MyOTStreamNotifyProc,OTNotify); ASSERT(stream); stream->streamErr = noErr; //Open a TCP endpoint asynchronously stream->OTTCPStream->status = inProgress; stream->streamErr = OTAsyncOpenEndpointInContext(OTCreateConfiguration(kTCPName),0,0,MyOTStreamNotifyProcUPP,stream->OTTCPStream,nil); if (stream->streamErr == noErr) stream->streamErr = SpinOnWithConnectionCheck(&(stream->OTTCPStream->status),0,True,False); if ((stream->streamErr == noErr) || ((stream->streamErr = stream->OTTCPStream->result) == noErr)) { if (stream->OTTCPStream->code != T_OPENCOMPLETE) return (stream->streamErr = errOpenStream); stream->OTTCPStream->ref = stream->OTTCPStream->cookie; if (stream->OTTCPStream->ref == kOTInvalidEndpointRef) return (stream->streamErr = errOpenStream); //Initialize the MyOTTCPStreamPtr flags stream->OTTCPStream->weAreClosing = false; stream->OTTCPStream->otherSideClosed = false; stream->OTTCPStream->ourSideClosed = false; stream->OTTCPStream->releaseMe = false; } if (stream->streamErr != noErr) { if (stream->OTTCPStream->ref) OTCloseProvider(stream->OTTCPStream->ref); stream->OTTCPStream->ref = 0; } return (stream->streamErr); } /********************************************************************************* * OTTCPOpen - actually open a connection * * 3/18/99 no longer using OT memory allocation routines. *********************************************************************************/ OSErr OTTCPOpen(TransStream stream, InetHost tryAddr, InetPort port,uLong timeout) { InetAddress connectAddr; TCall sndCall; ASSERT(stream); ASSERT(stream->OTTCPStream); stream->streamErr = noErr; stream->OTTCPStream->status = inProgress; if (stream->streamErr = OTBind(stream->OTTCPStream->ref, 0, 0)) // OTBind doesn't need any parameters. This is an outgoing connection. jdboyd 04/11/02 return (stream->streamErr); if (stream->streamErr = SpinOnWithConnectionCheck(&(stream->OTTCPStream->status),60*timeout,True,False)) return (stream->streamErr); if (stream->OTTCPStream->code != T_BINDCOMPLETE) { stream->streamErr = stream->OTTCPStream->result; return (errOpenStream); } // set up the TCall structure needed to connect to tryAddr OTInitInetAddress(&connectAddr, port, tryAddr); WriteZero(&sndCall, sizeof(TCall)); sndCall.addr.len = sizeof(InetAddress); sndCall.addr.buf = (unsigned char*) &connectAddr; // now connect to the address asynchronously stream->OTTCPStream->status = inProgress; stream->streamErr = OTConnect(stream->OTTCPStream->ref, &sndCall, 0); if (stream->streamErr != noErr && stream->streamErr != kOTNoDataErr) return (stream->OTTCPStream->otherSideClosed ? errLostConnection : stream->streamErr); if (stream->streamErr = SpinOnWithConnectionCheck(&(stream->OTTCPStream->status),60*timeout,True,False)) return (stream->OTTCPStream->otherSideClosed ? errLostConnection : stream->streamErr); // did we connect? if (stream->OTTCPStream->code != T_CONNECT) { stream->streamErr = errLostConnection; return (errOpenStream); } if (stream->streamErr = OTRcvConnect(stream->OTTCPStream->ref, 0)) return (stream->streamErr); return (stream->streamErr); } /********************************************************************************* * OTWaitForChars - spin, giving everybody else time, until chars available *********************************************************************************/ short OTWaitForChars(TransStream stream, long timeout, UPtr line, long *size, OTResult *otResult) { EventRecord event; long ticks=TickCount(); static long waitTicks=0; long tookTicks; Boolean result = false; long timeoutTicks = ticks + 60*timeout; Boolean slow = False; OTFlags junkFlags = 0; ASSERT(stream); ASSERT(stream->OTTCPStream); if (!InBG) waitTicks = 0; // spin until we see data on the network ... do { // check to see if our connection is still up. Only care if remote end has closed. if (stream->OTTCPStream->otherSideClosed) return(errLostConnection); // check to see if we need ppp, but the connection is down if (needPPPConnection && MyOTPPPInfo.state != kPPPUp) return(errLostConnection); //check to see if cmd-. has been pressed. if (CommandPeriod) return (userCancelled); if (TickCount()-ticks > 10) { slow = True; CyclePendulum(); ticks=TickCount(); if (ticks >timeoutTicks) return(commandTimeout); } // receive size bytes and put it in line. We expect some data, so wait around until we see it. *otResult = OTRcv(stream->OTTCPStream->ref, line, *size, &junkFlags); // we'll spin until we see data or an error. if (*otResult == kOTNoDataErr) *otResult = 0; if (slow) YieldTicks = 0; // To speed up threaded xfers when app is in bg, don't call WaitNextEvent as often // also speeded up typing by changing NEED_YIELD -- it checks Typing when in a thread. (though I'm not quite sure how thread gets time when Typing is true) if (NEED_YIELD || ((*otResult == 0) && !stream->DontWait)) { tookTicks = TickCount(); // 11-13-97 change to fix cmd period bug when in main thread result = WNE(MINI_MASK|keyDownMask,&event,waitTicks); if (CommandPeriod) return(userCancelled); //was: // result = WNE(MINI_MASK,&event,waitTicks); tookTicks = TickCount()-tookTicks; if (InBG) if (tookTicks > waitTicks+1) waitTicks = MIN(120,tookTicks); else waitTicks = waitTicks>>1; else waitTicks = 1; if (result) (void) MiniMainLoop(&event); } } while ((*otResult == 0) && !stream->DontWait); stream->CharsAvail = 0; return(0); } /********************************************************************************* * OTFlushInput - dump all chars that arrive on a stream, until there are no * chars for a given period of time *********************************************************************************/ void OTFlushInput(TransStream stream,uLong timeout) { Str255 junk; OTResult result; long got; do { got = sizeof(junk); if (OTWaitForChars(stream, timeout, junk, &got, &result)) break; if (LogLevel&LOG_TRANS && !stream->streamErr && result>0) CarefulLog(LOG_TRANS,LOG_FLUSHED,junk,result); } while (result>0); } static short errorMessages[errMyLastOTErr-errOTInitFailed] = { OT_INIT_ERR, //errOTInitFailed, OT_INIT_ERR, //errOTInetSvcs, BIND_ERR, //errDNR, 0, //errNoMXRecords, TCP_TROUBLE, //errCreateStream, NO_SMTP_SERVER, //errOpenStream, TCP_TROUBLE, //errLostConnection, TCP_TROUBLE, //errMiscRec, TCP_TROUBLE, //errMiscSend OT_DIALUP_CONNECT_ERR,//errPPPConnect 0, //errPPPPrefNotFound 0, //errPPPStateUnknown OT_MISSING_LIBRARY //errOTMissingLib: }; static void OTTELo ( OSErr generalError, OSErr specificError, StringPtr message ) { if (IsMyOTError(generalError)) GetRString(message, errorMessages[generalError - errOTInitFailed]); else GetRString(message, TCP_TROUBLE); } /********************************************************************************* * OTTE - give the user some helpful information if an error occurs. * generalError contains a general description of the error, set by me. * specificError contains the actual error returned by the failed call *********************************************************************************/ OSErr OTTE(TransStream stream, OSErr generalError, OSErr specificError, short file, short line) { short errorString = 0; OSErr MacTCPErr = noErr; if ((generalError != noErr) && (stream==0 || !stream->BeSilent) && !AmQuitting && (!CommandPeriod || stream==0 || stream->Opening && !PrefIsSet(PREF_OFFLINE) && !PrefIsSet(PREF_NO_OFF_OFFER))) { Str255 message; Str255 tcpMessage; Str63 debugStr; Str31 rawNumber; short realSettingsRef = SettingsRefN; tcpMessage[0] = 0; NumToString(specificError,rawNumber); SettingsRefN = GetMainGlobalSettingsRefN(); OTErrorToString(specificError, tcpMessage); OTTELo ( generalError, specificError, message ); ComposeRString(debugStr,FILE_LINE_FMT,file,line); SettingsRefN = realSettingsRef; MyParamText(message,rawNumber,tcpMessage,debugStr); if (stream==0 || stream->Opening) { if (2==ReallyDoAnAlert(OPEN_ERR_ALRT,Caution)) SetPref(PREF_OFFLINE,YesStr); } else ReallyDoAnAlert(BIG_OK_ALRT,Caution); } return (generalError); } /********************************************************************************* * OTErrorToString - pick an error string that best describes the * specific error. *********************************************************************************/ void OTErrorToString(short specificError, Str255 tcpMessage) { short errorString = 0; tcpMessage[0] = 0; //Was this some error that I defined? if (IsMyOTError(specificError)) { switch (specificError) { case errOTInetSvcs: errorString = OT_INET_SVCS_ERR; break; case errLostConnection: specificError = TCPRemoteAbort; break; case errPPPStateUnknown: errorString = OT_PPP_STATE_ERR; break; case errPPPPrefNotFound: errorString = OT_TCPIP_PREF_ERR; break; case errOTMissingLib: errorString = OT_MISSING_LIBRARY; break; default: errorString = OT_UNKNOWN_ERR; break; } } if (errorString!=0) GetRString(tcpMessage,errorString); else { if (IsXTIError(specificError)) GetRString(tcpMessage,OTTCP_ERR_STRN + XTI2OSStatus(specificError)); else if (IsOTPPPError(specificError)) // was this an OT/PPP related error? GetRString(tcpMessage,OTPPP_ERR_STRN - specificError + kCCLErrorBaseCode - 1); else if (-23000>=specificError && specificError >=-23048) // was this a MacTCP error code? GetRString(tcpMessage,MACTCP_ERR_STRN-22999-specificError); else if (2<=specificError && specificError<=9) GetRString(tcpMessage,MACTCP_ERR_STRN+specificError+(23048-23000)); else tcpMessage = 0; } } /********************************************************************************* * DestroyMyTStream - deallocate everything a MyStream grabs for itself. *********************************************************************************/ void DestroyMyTStream(MyOTTCPStreamPtr myStream) { OSErr destroyErr = noErr; if (myStream) { //clean up after the myStream if (myStream->ref != 0) { //make sure the connection is closed. if (!myStream->otherSideClosed || !myStream->ourSideClosed) OTSndDisconnect(myStream->ref, nil); //unbind the endpoint myStream->status = inProgress; destroyErr = OTUnbind(myStream->ref); if (destroyErr == noErr) destroyErr = SpinOnWithConnectionCheck(&(myStream->status),0,True,False); // remove the endpoint's notifier. We won't be needing it anymore. OTRemoveNotifier(myStream->ref); // now we can kill the endpoint. destroyErr = OTCloseProvider(myStream->ref); myStream->ref = 0; } if (gActiveConnections) gActiveConnections--; } } /********************************************************************************* * EnqueueMyTStream - put myStream in the queue of streams waiting to close. *********************************************************************************/ void EnqueueMyTStream(MyOTTCPStreamPtr myStream) { MyOTTCPStreamPtr queueScan = 0; OSStatus status = noErr; ASSERT(myStream); if (pendingCloses == 0) //this is the only stream in the queue { pendingCloses = myStream; myStream->next = 0; myStream->prev = 0; } else //there are some other streams waiting to close { queueScan = pendingCloses; while (queueScan->next != 0) queueScan = queueScan->next; queueScan->next = myStream; myStream->prev = queueScan; myStream->next = 0; } } /********************************************************************************* * KillDeadMyTStreams - deallocate memory and TStreams that have received an * orderly disconnect. destroy determines whether to kill all the streams, or * only the ones that have been disconnected in an orderly fashion. *********************************************************************************/ void KillDeadMyTStreams(Boolean destroy) { MyOTTCPStreamPtr queueScan = pendingCloses; MyOTTCPStreamPtr releaseThisOne = 0; //loop through and find stream that can be released. while (queueScan != 0) { OSStatus queueScanStatus; if (queueScan->ref == 0) queueScan->releaseMe = true; //this should never happen ... else { queueScanStatus = OTLook(queueScan->ref); //if this endpoint has some data waiting to be read, read it. if (queueScanStatus == T_DATA || queueScanStatus == T_EXDATA) { do { queueScanStatus = OTRcv(queueScan->ref, queueScan->dummyBuffer, sizeof(queueScan->dummyBuffer), nil); } while (queueScanStatus >= 0); queueScan->age = TickCount(); //this stream made a noise, earning it a new minute. } if ((TickCount() - queueScan->age) > 3600) queueScan->releaseMe = true; //keep silent connections around for 1 minute. } //the MyOTStream callback will set releaseMe once this stream can die quietly if (destroy || queueScan->releaseMe) { releaseThisOne = queueScan; queueScan = queueScan->next; if (releaseThisOne == pendingCloses) pendingCloses = releaseThisOne->next; if (releaseThisOne->prev) (releaseThisOne->prev)->next = releaseThisOne->next; if (releaseThisOne->next) (releaseThisOne->next)->prev = releaseThisOne->prev; DestroyMyTStream(releaseThisOne); ZapPtr(releaseThisOne); } else queueScan = queueScan->next; } } /********************************************************************************* * OTVerifyOpen - Make sure OT TCP is ready to go. May have to conenct with PPP * or SLIP, depending on what is selected in the TCP/IP control panel *********************************************************************************/ OSErr OTVerifyOpen(TransStream stream) { Boolean weAttemptedPPP = false; stream->streamErr = noErr; //signal everyone else that, no, PPP is not needed for the connection needPPPConnection = false; gPPPConnectFailed = false; if (!PrefIsSet(PREF_IGNORE_PPP)) //this pref lets us ignore MacSLIP/PPP if we want { if ((stream->streamErr = SelectedConnectionMode(&connectionSelection, true)) == noErr) { if (dialingThePhone) { //sit and wait for a connection. if (connectionSelection == kPPPSelected) { ProgressMessageR(kpSubTitle,OT_PPP_WAIT); do { MiniEvents(); if (CommandPeriod) return (stream->streamErr = userCancelled); } while (dialingThePhone); if (PPPDown()) return(stream->streamErr = errPPPConnect); } } else if (connectionSelection == kPPPSelected) { dialingThePhone = true; needPPPConnection = true; //let everyone know the connection is being made over PPP. gConnecting = true; //likewise, tell the world we're dialing the phone. if (stream->streamErr = OTPPPConnect(&weAttemptedPPP)) { gPPPConnectFailed = true; //flag to tell the world that our PPP connection attempt failed. // Holy crap, Batman! This has been broken all these years. Only force clase the connection // if we were the fools to think we could connect it to begin with. -JDB 12/16/98 OTPPPDisconnect(weAttemptedPPP, true); } gConnecting = false; dialingThePhone = false; } } } return (stream->streamErr); } /********************************************************************************* * OTPPPConnect - Cause an OTPPP connection to happen. I will create one single * OTPPP control endpoint, and keep it around. Apple says there's a bug with the * current OT/PPP that causes OTCloseProvider() to crash when closing a PPP control * endpoint. * * 6/18/97 This routine can be called from multiple threads. The first call * will initiate the PPP connection. Subsequent calls will wait for the PPP * connection. * * 12/16/98 Added attemotedConnection paramter to let caller know whether we make * the connection attempt or not. There might be one underway, in which case * we wait for it. * * May, 1999 Added a delay after (a) the connection is made, or (b) the connection * we were waiting for finishes. We delat for seconds * to allow ARA to actually connect. *********************************************************************************/ OSErr OTPPPConnect(Boolean *attemptedConnection) { OSErr err = noErr; OTResult result = kOTNoError; unsigned long PPPState = 0; Boolean PPPInForeGround = PrefIsSet(PREF_PPP_FOREGROUND); Str255 scratch; Boolean redial = false; long numRedials = 0; long delay = 0; long dialCount = 0; Boolean doDelay = false; // do we need to delay for stupid ARA? #ifdef DEBUG ASSERT(attemptedConnection); // must have this parameter #endif pppErr = noErr; *attemptedConnection = false; //Make a new PPP endpoint. We're only going to keep one around. if (MyOTPPPInfo.ref == 0) result = NewPPPControlEndpoint(); if ((result == kOTNoError) && (MyOTPPPInfo.ref != kOTInvalidEndpointRef)) { // Check the current state of the PPP connection. err = GetPPPConnectionState(MyOTPPPInfo.ref, &PPPState); if (err == noErr) { // have we already connected PPP at some point in the past? if (MyOTPPPInfo.weConnectedPPP==true && MyOTPPPInfo.state==kPPPUp && PPPState==kPPPStateOpened) return noErr; //If we don't already have an open connection, open one. if ((PPPState != kPPPStateOpened && PPPState != kPPPStateOpening) || MyOTPPPInfo.state == kPPPClosing) { // remember that we're the one starting this connection *attemptedConnection = true; // we'll have to delay for ARA doDelay = true; // Get the redial information if (!PPPInForeGround) { err = OTPPPDialingInformation(&redial, &numRedials, &delay); if (err != noErr || numRedials < 1) redial = false; // couldn't get redial data. Ignore it. dialCount = redial ? numRedials + 1 : 1; } else dialCount = 1; //redials are handled in the PPP connection dialog already. // Establish a new connection TurnOnPPPConnectionDialog(MyOTPPPInfo.ref, PPPInForeGround); // force the current connection to close if it hasn't been to to already if (PPPState == kPPPStateOpened || PPPState == kPPPStateOpening) { OTPPPDisconnect(true, false); if (pppErr = WaitForOTPPPDisconnect(true)) return (pppErr); MyOTPPPInfo.state = kPPPDown; } MyOTPPPInfo.result = noErr; ProgressMessageR(kpSubTitle,OT_PPP_CONNECT); if (!PPPInForeGround) { GetRString(scratch, OT_PPP_CONNECT_MESSAGE); ProgressMessage(kpMessage,scratch); } // Actually connect OT/PPP MyOTPPPInfo.code = 0; MyOTPPPInfo.state = kPPPOpening; result = kCCLErrorLineBusyErr; pppErr = OTIoctl(MyOTPPPInfo.ref, I_OTConnect, NULL); // Bug 1648 - if we're connecting PPP with the connection dialog, we should sit and spin until it finishes dialig the phone. if (PPPInForeGround) { MyOTPPPInfo.status = inProgress; if (pppErr == noErr) pppErr = SpinOn(&(MyOTPPPInfo.status),0,True,False); } // Spin until we can do TCP/IP over the connection while (dialCount > 0 && result == kCCLErrorLineBusyErr && !PPPInForeGround && pppErr == noErr) { while (true) { MyOTPPPInfo.event = 0; MyOTPPPInfo.result = noErr; MyOTPPPInfo.status = inProgress; if (pppErr == noErr) pppErr = SpinOn(&(MyOTPPPInfo.status),0,True,False); result = MyOTPPPInfo.result; if (MyOTPPPInfo.state == kPPPUp || MyOTPPPInfo.state == kPPPDown || pppErr != noErr || MyOTPPPInfo.result < 0) break; // Update the progress dialog. GetRString(scratch, MyOTPPPInfo.event); ProgressMessage(kpMessage,scratch); } dialCount--; if (redial && result == kCCLErrorLineBusyErr) ProgressMessageR(kpSubTitle,OT_PPP_REDIALING); } } else { //there's a connection current trying to open if (PPPState == kPPPStateOpening) { // do the ARA delay ... doDelay = true; //Spin until we can talk TCP over the connection ProgressMessageR(kpSubTitle,OT_PPP_CONNECT); GetRString(scratch, OT_PPP_CONNECT_MESSAGE); ProgressMessage(kpMessage,scratch); MyOTPPPInfo.state = kPPPOpening; while (true) { MyOTPPPInfo.event = 0; MyOTPPPInfo.result = noErr; MyOTPPPInfo.status = inProgress; err = SpinOn(&(MyOTPPPInfo.status),0,True,False); result = MyOTPPPInfo.result; if (MyOTPPPInfo.state == kPPPUp || MyOTPPPInfo.state == kPPPDown || err != noErr || MyOTPPPInfo.result < 0) break; // Update the progress dialog. GetRString(scratch, MyOTPPPInfo.event); ProgressMessage(kpMessage,scratch); } if (err == noErr) err = MyOTPPPInfo.result; } if (err == noErr) { MyOTPPPInfo.state = kPPPUp; *attemptedConnection = false; // PPP is up, but we didn't connect it. pppErr = noErr; } else return (pppErr = err); } } else //could not determine the state of the PPP connection { return (pppErr = errPPPStateUnknown); } } else return (pppErr = result); // The connection is up because we connected it, or someone else did if (pppErr == noErr) { if (MyOTPPPInfo.state == kPPPUp) { // must we delay for ARA? if (doDelay) { // PPP Race Condition Hack. // // Since ARA and PPP have been combined, it's now possible to get the kPPPConnectCompletedEvent // *before* ARA does. This will cause the first network operation to fail. So, let's Pause // for a while, and let ARA know about it's successful connection. long delay = GetRLong(OT_PPP_RACE_HACK); Str255 delayStr; if (delay > 0) { NumToString(delay, delayStr); ComposeRString(scratch,OT_PPP_SMART_ASS,delayStr); ProgressMessage(kpMessage,scratch); Pause(60*delay); } } MyOTPPPInfo.weConnectedPPP = *attemptedConnection; // remember if we started the connections ourselves or not. } else { if (!PPPInForeGround) pppErr = MyOTPPPInfo.result ? MyOTPPPInfo.result : kCCLErrorGeneric; //return some sort of error else pppErr = userCancelled; //errors are handled in the PPP connection dialog } } return (pppErr); } /********************************************************************************* * NewPPPControlEndpoint - Set up the global PPP enpoint we use to control PPP. *********************************************************************************/ OTResult NewPPPControlEndpoint(void) { OTResult result = kOTNoError; TEndpointInfo epInfo; short oldResFile = CurResFile(); DECLARE_UPP(MyPPPEndpointNotifier,OTNotify); INIT_UPP(MyPPPEndpointNotifier,OTNotify); MyOTPPPInfo.ref = OTOpenEndpointInContext(OTCreateConfiguration(kPPPControlName),0, &epInfo, &result,nil); UseResFile(oldResFile); // bug in Modem control panel resets curresfile SD 8/5/98 if((result == kOTNoError) && (MyOTPPPInfo.ref != kOTInvalidEndpointRef)) { result = OTInstallNotifier(MyOTPPPInfo.ref, MyPPPEndpointNotifierUPP, (void *)NULL); if(result == kOTNoError) { if ((result = OTIoctl(MyOTPPPInfo.ref, I_OTGetMiscellaneousEvents, (void*)1)) == kOTNoError ) { MyOTPPPInfo.state = kPPPDown; //kPPPDown means PPP has not been touched by us yet. } OTSetAsynchronous(MyOTPPPInfo.ref); } } //if some error occurred, return 0, but don't close the endpoint. if (result != kOTNoError) MyOTPPPInfo.ref = kOTInvalidEndpointRef; return (result); } /********************************************************************************* * TurnOnPPPConnectionDialog - make the PPP connection a modal event * * If on is true, then we will turn the connection dialog ON. If on is false, * we will restore the conenction dialog to its previous state. *********************************************************************************/ OSErr TurnOnPPPConnectionDialog(EndpointRef endPoint, Boolean on) { OSErr err = noErr; long dlogState = 0; err = GetCurrentUInt32Option(endPoint, OPT_ALERTENABLE, &dlogState); if (err == noErr) { if (oldDlogState == 0) oldDlogState = dlogState; if (on) //set the kPPPConnectionStatusDialogsFlag dlogState = dlogState | kPPPConnectionStatusDialogsFlag; else //turn the kPPPConnectionStatusDialogsFlag off. dlogState = dlogState & (kPPPAllAlertsEnabledFlag - kPPPConnectionStatusDialogsFlag); if (dlogState != oldDlogState) err = SetCurrentUInt32Option(endPoint, OPT_ALERTENABLE, dlogState); } return (err); } /********************************************************************************* * ResetPPPConnectionDialog - rest the statusDialogFlag to what it was before we * started messing with it. *********************************************************************************/ OSErr ResetPPPConnectionDialog(EndpointRef endPoint) { OSErr err = noErr; long dlogState = 0; err = GetCurrentUInt32Option(endPoint, OPT_ALERTENABLE, &dlogState); if (err == noErr) { if (dlogState != oldDlogState) { if (!(oldDlogState&kPPPConnectionStatusDialogsFlag)) //if it was off to begin with, turn it off to reset dlogState = dlogState & (kPPPAllAlertsEnabledFlag - kPPPConnectionStatusDialogsFlag); else //if it was on before, turn it back on to reset. dlogState = dlogState | kPPPConnectionStatusDialogsFlag; err = SetCurrentUInt32Option(endPoint, OPT_ALERTENABLE, dlogState); } oldDlogState = 0; } return (err); } /********************************************************************************* * GetPPPConnectionState - determine the state of PPP *********************************************************************************/ OSErr GetPPPConnectionState(EndpointRef endPoint, unsigned long *PPPState) { OSErr err = noErr; err = GetCurrentUInt32Option(endPoint, PPP_OPT_GETCURRENTSTATE, PPPState); if (err != noErr) *PPPState = kPPPStateInitial; return (err); } /********************************************************************************* * GetCurrentUInt32Option - gets a UInt32 option from a PPP control point. *********************************************************************************/ OSErr GetCurrentUInt32Option(EndpointRef endPoint, OTXTIName theOption, UInt32 *value) { OSErr err = noErr; unsigned char buf[sizeof(TOption)]; TOption *option; TOptMgmt command; *value = 0; command.opt.buf = buf; command.opt.len = sizeof(TOption); command.opt.maxlen = sizeof(TOption); command.flags = T_CURRENT; option = (TOption *)buf; option->len = sizeof(TOption); option->level = COM_PPP; option->name = theOption; option->status = 0; option->value[0] = 0; //get the current alert flags err = OTOptionManagement(endPoint, &command, &command); if ((err != noErr) || (option->status == T_FAILURE) || (option->status == T_NOTSUPPORT)) *value = 0L; else *value = option->value[0]; return (err); } /********************************************************************************* * SetCurrentUInt32Option - sets a UInt32 option for PPP control point options. *********************************************************************************/ OSErr SetCurrentUInt32Option(EndpointRef endPoint, OTXTIName theOption, UInt32 value) { OSErr err = noErr; unsigned char buf[sizeof(TOption)]; TOption *option; TOptMgmt command; command.opt.buf = buf; command.opt.len = sizeof(TOption); command.opt.maxlen = sizeof(TOption); command.flags = T_NEGOTIATE; option = (TOption *)buf; option->len = sizeof(TOption); option->level = COM_PPP; option->name = theOption; option->status = 0; option->value[0] = value; //get the current alert flags err = OTOptionManagement(endPoint, &command, &command); return (err); } /********************************************************************************* * OTPPPDisconnect - disconnect OTPPP if Eudora connected it in the first place. * if (forceDisconnect) then we disconnect no matter who connected PPP * if (endConnectionAttempt) then we reset the PPP connection dialog. *********************************************************************************/ OSErr OTPPPDisconnect(Boolean forceDisconnect, Boolean endConnectionAttempt) { OSErr err = noErr; if ((gHasOTPPP == true) && ((MyOTPPPInfo.ref && PrefIsSet(PREF_PPP_DISC) && MyOTPPPInfo.weConnectedPPP) || forceDisconnect)) { err = OTIoctl(MyOTPPPInfo.ref, I_OTDisconnect, 0); if (endConnectionAttempt) { ResetPPPConnectionDialog(MyOTPPPInfo.ref); } MyOTPPPInfo.weConnectedPPP = false; MyOTPPPInfo.status = MyOTPPPInfo.result = 0; } return (err); } /********************************************************************************* * OTPPPConnectForLink - connect OT/PPP to follow a link. Ignore the fact that * we connected it, so we don't go try to close the connection later. *********************************************************************************/ OSErr OTConnectForLink(void) { OSErr err = userCanceledErr; unsigned long conMethod = 0; Boolean weAttemptedPPP = false; // Only makes sense to do this when OT is present if (gUseOT) { // How are we connecting to the internet? Read from the preference files or NS database err = SelectedConnectionMode(&conMethod,true); // do we have PPP? if (gHasOTPPP) { // Is OT/PPP the mode of connection? if (conMethod == kPPPSelected) { dialingThePhone = true; needPPPConnection = true; gConnecting = true; if (err = OTPPPConnect(&weAttemptedPPP)) { OTPPPDisconnect(weAttemptedPPP, true); } gConnecting = false; dialingThePhone = false; // Forget about the fact that we connected OT/PPP. MyOTPPPInfo.weConnectedPPP = false; } } } return (err); } /********************************************************************************* * SelectedConnectionMode - determine the connection mode set in the TCP/IP control * panel. * * 12-16-98 JDB * Added the forceRead paramter to read from the TCP file, even if cache time * has not yet run out. We really want to know the state of TCP/IP at each * connection attempt. We don't care as much at idle time for things like * the next check menu item. * * 5-19-99 JDB * Added calls to the Network Setup library. This should prevent breakage. * * 7-16-99 JDB * Use the cached connection method unless told not to * * 12-3-99 JDB * Use the cached connection method if we just read the preferences *********************************************************************************/ OSErr SelectedConnectionMode(unsigned long *connectionSelection, Boolean forceRead) { OSErr err = noErr; char currentPortName[kMaxProviderNameSize]; char junk[kMaxProviderNameSize]; Boolean enabled; static uLong method; static uLong lastRead = 0; if (HaveOSX()) { // there's no way to tell if we're dialed up or not under OS X. *connectionSelection = kOtherSelected; return (noErr); } // do NOT call this unless OT is installed if (OTIs == false) return (fnfErr); // did we just read the preferences recently? if (!forceRead && (lastRead>0) && ((TickCount()-lastRead) < GetRLong(TCP_PREF_REUSE_INTERVAL))) { *connectionSelection = method; return (noErr); } // are we falling asleep? Then return value from the last time, no matter what. if (UserIdle(TICKS2MINS) && (lastRead>0)) { *connectionSelection = method; return (noErr); } #ifdef DEBUG Log(LOG_TRANS,"\pReading TCP/IP preferences from the disk now."); #endif #ifdef USE_NETWORK_SETUP if (UseNetworkSetup()) { if (IsNetworkSetupAvailable()) { err = GetConnectionModeFromDatabase(connectionSelection); lastRead = TickCount(); method = *connectionSelection; } else { // we have to use the Network Setup Library, but it's not available. // Assume non-PPP and non-MacSLIP connection. lastRead = TickCount(); method = *connectionSelection = kOtherSelected; } } else { #endif *connectionSelection = kOtherSelected; // read the port name from the TCP/IP preference file err = GetCurrentPortNameFromFile(currentPortName, junk, &enabled); if (err == noErr) { if (StringSame(currentPortName,PPP_NAME)) *connectionSelection = kPPPSelected; } if (!err) { lastRead = TickCount(); method = *connectionSelection; } #ifdef USE_NETWORK_SETUP } #endif return (err); } /******************************************************************************* * UserIdle - see if the user has been idle. *******************************************************************************/ Boolean UserIdle(uLong ticks) { Boolean result = false; #ifdef HAVE_GETLASTACTIVITY static uLong idleTicks; ActivityInfo info; OSErr err = noErr; if (((GestaltBits(gestaltPowerMgrAttr)&(1< ticks) result = true; } #endif return (result); } /******************************************************************************* * CanCheckPPPState - is it possible for us to check PPP's state? *******************************************************************************/ Boolean CanCheckPPPState(void) { return !PrefIsSet(PREF_IGNORE_PPP) && (HaveTheDiseaseCalledOSX()||!gMissingNSLib); } /******************************************************************************* * CanChangePPPState - is it possible for us to change PPP's state? *******************************************************************************/ Boolean CanChangePPPState(void) { return !PrefIsSet(PREF_IGNORE_PPP) && !gMissingNSLib && !HaveTheDiseaseCalledOSX(); } /******************************************************************************* * PPPDown - is PPP installed, the selected mode of connection & disconnected? *******************************************************************************/ Boolean PPPDown(void) { unsigned long con = 0; static Boolean ret = false; static uLong lastCheck; ASSERT(!PrefIsSet(PREF_IGNORE_PPP)); if (HaveTheDiseaseCalledOSX()) { if (TickCount()-lastCheck > GetRLong(TCP_PREF_REUSE_INTERVAL)/10+1) { Str255 host; // grab a specific host to check; if there // isn't one, use the mailhost if (!*GetRString(host,PPP_REACHABLE_HOST)) GetPOPInfo(nil,host); // check it if (*host && CHostUnreachableByPPP(host+1)) ret = true; else ret = false; lastCheck = TickCount(); } } else if (gHasOTPPP) { SelectedConnectionMode(&con,false); if (con == kPPPSelected) { if (MyOTPPPInfo.ref == 0) NewPPPControlEndpoint(); GetPPPConnectionState(MyOTPPPInfo.ref, &con); if (con != kPPPStateOpened) ret = true; } } return (ret); } #pragma segment Main /********************************************************************************* * MyPPPEndpointNotifier - notifier function for PPP endpoint events. It directly * modifies the MyOTPPPInfo global structure. *********************************************************************************/ pascal void MyPPPEndpointNotifier(void *context, OTEventCode code, OTResult result, void *cookie) { if (code > kPPPEvent && code <= kPPPDCECallFinishedEvent) { MyOTPPPInfo.status = 0; MyOTPPPInfo.result = (OTResult)cookie; if ((code == kPPPConnectCompleteEvent) && (MyOTPPPInfo.result != kOTNoError)) MyOTPPPInfo.event = OTPPP_MSG_STRN + (kPPPDCECallFinishedEvent + 1) - kPPPEvent + 1; else MyOTPPPInfo.event = OTPPP_MSG_STRN + code - kPPPEvent + 1; } if (code == kPPPDisconnectCompleteEvent) // Disconnect has completed. MyOTPPPInfo.state = MyOTPPPInfo.result ? kPPPClosing : kPPPDown; if (code == kPPPLowerLayerDownEvent) //remote server isn't responding added this 11-21-97 MyOTPPPInfo.state = kPPPDown; else if ((code == kPPPConnectCompleteEvent) && (MyOTPPPInfo.result == kOTNoError)) MyOTPPPInfo.state = kPPPUp; } #pragma segment TcpTrans /********************************************************************************* * WaitForOTPPPDisconnect - sit and spin until the PPP state is kPPPStateInitial *********************************************************************************/ OSErr WaitForOTPPPDisconnect(Boolean showStatus) { OSErr err = noErr; long ticks=TickCount(); long startTicks=ticks+120; long now; Str255 scratch; unsigned long PPPState; //wait for the connection to close before we start another if (showStatus) { GetRString(scratch, OT_PPP_DISCONNECT); ProgressMessage(kpMessage,scratch); } do { now = TickCount(); if (now>startTicks && now-ticks>10) { CyclePendulum(); ticks=now; } MiniEvents(); if (CommandPeriod) return(pppErr = userCancelled); err = GetPPPConnectionState(MyOTPPPInfo.ref, &PPPState); } while ((PPPState != kPPPStateInitial) && (err == noErr)); if (err != noErr && err != userCancelled) err = errPPPStateUnknown; return (err); } /********************************************************************************* * OTPPPDialingInformation - retrieve the redial options from the PPP settings file. * * - Look in Preferences folder for lzcn/rmot, the Remote Access Connections file * - Open the resource fork * - Fetch 'cdia' id 128, which contains the info we need. * * the fourth long in this resource contains 3 or 4 if we are to redial. * the fifth long contains the number of redials to do before giving up. * the sixth long contains 1000*number of seconds to delay. * * If any of this fails, we continue on as if nothing happened. Redialing just won't * happen. * * The spec locating the rmot preference file is cached. This way, we can check * the preference file periodically, and make sure the setting hasn't changed. * * This will break in a future OT. * * 5-19-99 JDB * Added calls to the Network Setup library. This should prevent breakage. *********************************************************************************/ OSErr OTPPPDialingInformation(Boolean *redial, unsigned long *numRedials, unsigned long *delay) { OSErr err = noErr; short vRef = 0; long dirId = 0; CInfoPBRec hfi; Str31 name; short refNum = 0; Handle probe = 0; short oldRes; oldRes = CurResFile(); //do NOT call this unless OT/PPP is installed and being used if (gHasOTPPP == false) return (fnfErr); *numRedials = *delay = 0; *redial = false; #ifdef USE_NETWORK_SETUP // grok the settings from the TCP/IP preference file using the Network Setup Library. if (UseNetworkSetup()) { err = GetPPPDialingInformationFromDatabase(redial, numRedials, delay); return (err); } #endif if (PPPprefFileSpec.name[0] == 0) //have we not yet already located the PPP pref file? { /* Locate the PPP preferences file */ //find the active Preferences folder err = FindFolder(kOnSystemDisk,kPreferencesFolderType,False,&vRef,&dirId); if (err == noErr) { hfi.hFileInfo.ioNamePtr = name; hfi.hFileInfo.ioVRefNum = vRef; SearchDirectoryForFile(&hfi, dirId, PPP_PREF_FILE_TYPE, PPP_PREF_FILE_CREATOR); } } if (PPPprefFileSpec.name[0] != 0) //have we located the PPP pref file? { refNum = FSpOpenResFile(&PPPprefFileSpec,fsRdPerm); err = ResError(); if (err == noErr && refNum >= 0) { //this could breka in future versions of OT probe = Get1Resource(DIAL_RESOURCE,DIAL_RESOURCE_ID); err = ResError(); if (err == noErr && probe != 0) { //this is agonna break in future versions ot fer sure if (((long *)*probe)[3] == 2) { *redial = false; *numRedials = *delay = 0; } else { *redial = true; *numRedials = ((long *)*probe)[4]; *delay = ((long *)*probe)[5]/1000; } } } CloseResFile(refNum); } if ((err != noErr) || (PPPprefFileSpec.name[0] == 0)) err = errPPPPrefNotFound; UseResFile(oldRes); return (err); } /********************************************************************************* * SearchDirectoryForFile - Recursively search a directory for a file with a * given creator and file type. This is an expensive function to call. *********************************************************************************/ Boolean SearchDirectoryForFile(CInfoPBRec *info, long dirToSearch, OSType type, OSType creator) { short index = 1; OSErr err = noErr; Boolean static foundIt; foundIt = false; do { info->hFileInfo.ioFDirIndex = index; info->hFileInfo.ioDirID = dirToSearch; err = PBGetCatInfoSync(info); if (err == noErr) { //found a directory if ((info->hFileInfo.ioFlAttrib & ioDirMask) != 0) { SearchDirectoryForFile(info, info->hFileInfo.ioDirID, type, creator); err = noErr; } else //found a file { if ((info->hFileInfo.ioFlFndrInfo.fdType == type && info->hFileInfo.ioFlFndrInfo.fdCreator == creator)) { // Found it. Stop the search! foundIt = true; FSMakeFSSpec(info->hFileInfo.ioVRefNum, info->hFileInfo.ioFlParID, info->hFileInfo.ioNamePtr, &PPPprefFileSpec); } } ++index; } } while (err == noErr && !foundIt); return (foundIt); } /********************************************************************************* * SpinOnWithConnectionCheck - spin until a return code is not inProgress. Check * connection while spinning. *********************************************************************************/ short SpinOnWithConnectionCheck(short *rtnCodeAddr,long maxTicks,Boolean allowCancel,Boolean forever) { long ticks=TickCount(); long startTicks=ticks+120; long now; #ifdef CTB extern ConnHandle CnH; #endif Boolean oldCommandPeriod = CommandPeriod; Boolean slow = False; static short slowThresh; if (!slowThresh) slowThresh = GetRLong(SPIN_LENGTH); if (allowCancel) YieldTicks = 0; if (allowCancel || *rtnCodeAddr==inProgress) { CommandPeriod = False; do { // check to see if we need ppp, but the connection is down if (needPPPConnection && MyOTPPPInfo.state != kPPPUp) return(errLostConnection); now = TickCount(); if (now>startTicks && now-ticks>slowThresh) {slow = True;if (!InAThread()) CyclePendulum(); else MyYieldToAnyThread();ticks=now;} MiniEvents(); if (CommandPeriod && !forever) return(userCancelled); if (maxTicks && startTicks+maxTicks < now+120) break; } while (*rtnCodeAddr == inProgress); if (CommandPeriod) return(userCancelled); CommandPeriod = oldCommandPeriod; } return(*rtnCodeAddr); } //Some sticky stuff /********************************************************************************* * GetHostByAddr - Call either TCPGetHostByAddr or OTGetHostByAddr, depending on * whether OT is installed or not. *********************************************************************************/ OSErr GetHostByAddr(struct hostInfo *hostInfoPtr,long addr) { // Do we have a NAT? if (0x0A000000 == (addr&0xff000000) || 0xAC100000 == (addr&0xfff00000) || 0xC0A80000 == (addr&0xffff0000)) { Str31 literal; ComposeRString(literal,NAT_FMT,addr); literal[*literal+1] = 0; strcpy(hostInfoPtr->cname,literal+1); hostInfoPtr->addr[1] = addr; hostInfoPtr->rtnCode = 0; return noErr; } if (gUseOT == true) // OT is installed { InetHostInfo domainName; OSErr err = OTGetHostByAddr(addr, &domainName); if (err == noErr) // our caller expects the hostInfoPtr to point a hostInfo struct. { short count; strcpy(hostInfoPtr->cname,domainName.name); for (count = 0; count < MIN(NUM_ALT_ADDRS,kMaxHostAddrs); count ++) hostInfoPtr->addr[count] = domainName.addrs[count]; hostInfoPtr->rtnCode = 0; } return (err); } ASSERT ( false ); return unimpErr; /* unreachable, I think */ } /********************************************************************************* * GetHostByName - Call either TCPGetHostByName or OTGetHostByName, depending on * whether OT is installed or not. * * The caller expects hostInfoPtr to be pointing to a TCP hostInfo struct. So * if we do the OT thing, point hostInfoPtr at a hostInfo struct we fill with * the results of the OTGetHostByName call. *********************************************************************************/ int GetHostByName(UPtr name, struct hostInfo **hostInfoPtr) { static struct hostInfo trickCaller; if (gUseOT == true) // OT is installed { InetHostInfo domainName; int err = noErr; short count; InetDomainName hostName; PtoCcpy(hostName,name); err = OTGetHostByName(hostName, &domainName); if (err == noErr) // our caller expects the hostInfoPtr to point a hostInfo struct. { *hostInfoPtr = &trickCaller; strcpy(trickCaller.cname,domainName.name); for (count = 0; count < MIN(NUM_ALT_ADDRS,kMaxHostAddrs); count ++) trickCaller.addr[count] = domainName.addrs[count]; trickCaller.rtnCode = 0; } return (err); } ASSERT ( false ); return unimpErr; /* unreachable, I think */ } /********************************************************************************* * PPPIsMostDefinitelyUpAndRunning - return true if we're connected with PPP, or * it's not an issue. *********************************************************************************/ Boolean PPPIsMostDefinitelyUpAndRunning(void) { Boolean connected = true; unsigned long con = 0; if (gHasOTPPP) { SelectedConnectionMode(&con,false); if ((con == kPPPSelected) && (MyOTPPPInfo.state != kPPPUp)) connected = false; } return (connected); } /********************************************************************************* * UpdateCachedTCPIPPrefInfo - read from the preference files or NS Library now *********************************************************************************/ void UpdateCachedConnectionMethodInfo(void) { unsigned long con = 0; static uLong method; if (SelectedConnectionMode(&con, true)==noErr) { // return true if the method has changed if (con != method) { method = con; gUpdateTPWindow = true; // update the TP window } } } /********************************************************************************* * NeedToUpdateTP - do we need to adjust the next check time in the TP window? *********************************************************************************/ Boolean NeedToUpdateTP(void) { Boolean updateIt = false; if (gUpdateTPWindow) { updateIt = true; gUpdateTPWindow = false; } return (updateIt); } /********************************************************************************* * AutoCheckOKWithDBRead - read from the preference files or NS Library and see * if an autocheck is appropriate. *********************************************************************************/ Boolean AutoCheckOKWithDBRead(Boolean updatePers) { Boolean result = false; OSErr err = noErr; // are we set up to not check when not connected? if (gUseOT && !PrefIsSet(PREF_IGNORE_PPP) && PrefIsSet(PREF_PPP_NOAUTO)) { // make sure we're really, truly connected UpdateCachedConnectionMethodInfo(); result = AutoCheckOK(); // we're not. Tell this personality to cram it. if (!result && updatePers) { PersSkipNextCheck(); gUpdateTPWindow = true; } } else { // not set to not check when not connected. Do the normal thing. result = AutoCheckOK(); } return (result); } #ifdef ESSL // Declare the routine to set up the TransVector for doing SSL TransVector ESSLSetupVector(TransVector theTrans); #endif TransVector GetTCPTrans() { TransVector theTrans; theTrans = OTTCPTrans; #ifdef ESSL return ESSLSetupVector(theTrans); #else return theTrans; #endif } /************************************************************************ * TcpFastFlush - run through the queue, killing off defunct streams * Call KillDeadKyTStreams if we happen to be using open transport. ************************************************************************/ void TcpFastFlush(Boolean destroy) { static Boolean flushing = false; // are we already flushing streams from somewhere? if (flushing) return; // kill defunct streams flushing = true; if (gUseOT) KillDeadMyTStreams(destroy); // we're done flushing streams for now. flushing = false; } /********************************************************************** * CheckConnectionSettings - Attempt to connect to a host/port pair. **********************************************************************/ OSErr CheckConnectionSettings ( UPtr host, long port, StringPtr errorMessage ) { OSErr err = noErr; TransStream stream = NULL; Boolean oldPref = PrefIsSet(PREF_IGNORE_PPP); // Init the TransStream if ( noErr == ( err = NewTransStream ( &stream ))) { // See if the host is there.... SetPref(PREF_IGNORE_PPP,YesStr); err = ConnectTrans ( stream, host, port, true, GetRLong(SHORT_OPEN_TIMEOUT)); SetPref(PREF_IGNORE_PPP,oldPref ? YesStr : NoStr); // Grab an error message if we failed if ( noErr != err && errorMessage != NULL ) { short realSettingsRef = SettingsRefN; SettingsRefN = GetMainGlobalSettingsRefN(); OTTELo ( errOpenStream, stream->streamErr, errorMessage ); SettingsRefN = realSettingsRef; } // That's all we wanted. Cleanup. if ( noErr == err ) DestroyTrans(stream); ZapTransStream ( &stream ); } return err; }