DIRECTORY BasicTime USING [earliestGMT, GMT, Now, Period], GVBasics USING [Connect, ItemHeader, ItemLength, MakeKey, RName, Timestamp], GVNames USING [AuthenticateInfo, AuthenticateKey, ConnectInfo, Expand, ExpandInfo, GetConnect, NameType, RListHandle], GVProtocol USING [Close, CreateStream, Failed, FailureReason, GetSocket, Handle, ReceiveAck, ReceiveBoolean, ReceiveByte, ReceiveBytes, ReceiveCount, ReceiveItemHeader, ReceiveRemark, ReceiveRName, ReceiveTimestamp, SendMSOperation, SendNow, SendPassword, SendRemark, SendRName], GVRetrieve USING [FailureReason, MBXState, ServerState, ServerType], GVRetrieveInternal USING [Handle, HandleObject, MBXData, MBXPtr, noMBX], IO USING [CharsAvail, CreateStreamProcs, CreateStream, EndOfStream, STREAM, StreamProcs, UnsafeBlock], Process USING [SecondsToTicks, SetTimeout], PupDefs USING [AppendRopeToPupBuffer, EnumeratePupAddresses, GetFreePupBuffer, GetPupAddress, GetPupContentsBytes, MoveRopeToPupBuffer, PupBuffer, PupNameTrouble, PupRouterSendThis, PupSocket, PupSocketDestroy, PupSocketKick, PupSocketMake, ReturnFreePupBuffer, SetPupContentsWords, veryLongWait], PupStream USING [ConsumeMark, StreamClosing, TimeOut], PupTypes USING [fillInSocketID, miscSrvSoc, PupAddress, userAuthBad, userAuthOk, userAuthReq], Rope USING [Find, Length, ROPE, Substr]; GVRetrieveImpl: CEDAR MONITOR LOCKS handle USING handle: GVRetrieveInternal.Handle IMPORTS BasicTime, GVBasics, GVNames, GVProtocol, IO, Process, PupDefs, PupStream, Rope EXPORTS GVRetrieve, GVRetrieveInternal = { Handle: PUBLIC TYPE = GVRetrieveInternal.Handle; HandleObject: PUBLIC TYPE = GVRetrieveInternal.HandleObject; MBXState: TYPE = GVRetrieve.MBXState; PupAddress: TYPE = PupTypes.PupAddress; RName: TYPE = GVBasics.RName; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; myStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[ variety: $input, class: $Grapevine, getChar: GVGetChar, endOf: GVEndOf, getLength: GVGetLength, charsAvail: GVCharsAvail, unsafeGetBlock: GVUnsafeGetBlock, close: GVCloseStream ]; transmitLimit: CARDINAL _ 5 -- max number of transmissions of poll --; retransmitDelay: CARDINAL _ 10 -- seconds bfore re-transmittting --; Failed: PUBLIC ERROR[why: GVRetrieve.FailureReason, text: ROPE] = CODE; WrongCallSequence: ERROR = CODE; Create: PUBLIC PROC [pollingInterval: CARDINAL, reportChanges: PROC [MBXState] _ NIL] RETURNS [handle: Handle] = { handle _ NEW[HandleObject]; { handle.MBXChain _ GVRetrieveInternal.noMBX; handle.mbxKnown _ FALSE; handle.notEmptyMBXCount _ 0; handle.unknownMBXCount _ 0; handle.registry _ GV; handle.spareByte _ FALSE; handle.currentMBX _ GVRetrieveInternal.noMBX; handle.messages _ 0; handle.currentStr _ NIL; handle.mbxState _ badName; handle.polling _ FALSE; handle.pollWanted _ FALSE; handle.newPollWanted _ FALSE; handle.pollReplying _ FALSE; handle.pollID _ [0,1]; handle.pollStarted _ BasicTime.Now[]; handle.interval _ pollingInterval; handle.changes _ reportChanges; handle.userName _ NIL; handle.userPwd _ NIL; handle.userKey _ [0,0,0,0]; }; }; MailboxState: PUBLIC ENTRY PROC [handle: Handle] RETURNS [state: MBXState] = { ENABLE UNWIND => NULL; DO SELECT handle.mbxState FROM unknown, userOK => WAIT handle.mbxStateChange; ENDCASE => EXIT; ENDLOOP; RETURN [handle.mbxState] }; ServerName: PUBLIC ENTRY PROC [handle: Handle] RETURNS [serverName: RName] = { IF handle.currentMBX = GVRetrieveInternal.noMBX THEN ERROR WrongCallSequence[]; RETURN [handle.currentMBX.name]; }; WaitForMail: PUBLIC ENTRY PROC [handle: Handle] = { WHILE handle.mbxState # notEmpty DO WAIT handle.mbxStateChange ENDLOOP; }; Close: PUBLIC ENTRY PROC [handle: Handle] = { UnsetMailboxes[handle]; handle.userName _ NIL; handle.userPwd _ NIL; }; NewUser: PUBLIC ENTRY PROC [ handle: Handle, user, password: ROPE] = { UnsetMailboxes[handle]; IF user.Length[] = 0 THEN SetMBXState[handle, badName] ELSE IF password.Length[] = 0 THEN SetMBXState[handle, badPwd] ELSE { handle.userName _ user; handle.userPwd _ password; handle.userKey _ GVBasics.MakeKey[handle.userPwd]; RestartPoll[handle]; }; }; DeleteMessage: PUBLIC ENTRY PROC [handle: Handle] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; WHILE handle.state = inBody DO [] _ InnerNextItem[handle]; ENDLOOP; IF handle.state # beforeBody THEN ERROR WrongCallSequence[]; GVProtocol.SendMSOperation[handle.currentStr, deleteMessage]; GVProtocol.SendNow[handle.currentStr]; GVProtocol.ReceiveAck[handle.currentStr]; handle.state _ beforeBody; }; Accept: PUBLIC ENTRY PROC [handle: Handle] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; IF handle.state # afterMBX THEN ERROR WrongCallSequence[]; GVProtocol.SendMSOperation[handle.currentStr, flushMBX]; GVProtocol.SendNow[handle.currentStr]; GVProtocol.ReceiveAck[handle.currentStr]; NoteChangedMBX[handle, handle.currentMBX, empty]; }; SendPollProcess: PUBLIC ENTRY PROC [handle: Handle] = TRUSTED { socket: PupDefs.PupSocket = PupDefs.PupSocketMake[ PupTypes.fillInSocketID, , PupDefs.veryLongWait]; socketAddr: PupAddress = socket.getLocalAddress[]; replyPoll: PROCESS = FORK PollReplyProcess[handle, socket]; transmissions: CARDINAL _ 0; -- number of retransmissions so far SetPollTimeout: PROC = TRUSTED { Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ]; }; handle.pollReplying _ TRUE; SetPollTimeout[]; WHILE handle.pollWanted DO now: BasicTime.GMT = BasicTime.Now[]; elapsedSecs: INT = BasicTime.Period[from: handle.pollStarted, to: now]; IF handle.newPollWanted OR elapsedSecs >= handle.interval THEN { handle.pollStarted _ now; transmissions _ 0; IF handle.mbxState NOT IN [userOK..notEmpty] THEN handle.mbxState _ unknown --retry authentication-- ELSE { IF handle.notEmptyMBXCount = 0 AND handle.unknownMBXCount # 0 THEN handle.mbxState _ userOK --was someEmpty or allDown--; FOR this: GVRetrieveInternal.MBXPtr _ handle.MBXChain, this.next WHILE this # GVRetrieveInternal.noMBX DO IF this.addrState = unknown THEN FindAddress[handle, this]; IF this.addrState = known AND (handle.newPollWanted OR this.state # notEmpty) THEN this.replyWanted _ TRUE; ENDLOOP; }; handle.newPollWanted _ FALSE; BROADCAST handle.mbxStateChange; }; IF transmissions >= transmitLimit THEN TRUSTED { Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[handle.interval-elapsedSecs]]; WAIT handle.pollCond; SetPollTimeout[]; } ELSE SELECT handle.mbxState FROM unknown => { IF NOT handle.mbxKnown THEN FindRegistryAndMailboxes[handle]; IF handle.mbxState = unknown THEN IF handle.registry = MTP THEN { SendAuthReq[handle, socketAddr]; transmissions _ transmissions + 1; WAIT handle.pollCond--wait for reply--; IF transmissions >= transmitLimit THEN { IF handle.mbxState = unknown --no reply-- THEN SetMBXState[handle, cantAuth]; }; } ELSE { info: GVNames.AuthenticateInfo = GVNames.AuthenticateKey[handle.userName, handle.userKey]; SetMBXState[handle, SELECT info FROM individual => userOK, badPwd => badPwd, group, notFound => badName, allDown => cantAuth, ENDCASE => ERROR]; } --ELSE "FindRegistryAndMailboxes" failed--; }; IN [userOK..notEmpty] => --authenticated-- { finished: BOOL _ TRUE; --whether all have replied-- transmissions _ transmissions + 1; FOR this: GVRetrieveInternal.MBXPtr _ handle.MBXChain, this.next WHILE this # GVRetrieveInternal.noMBX DO IF this.replyWanted THEN { b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; PupDefs.MoveRopeToPupBuffer[b, handle.userName]; b.pupType _ mailCheckLaurel; b.pupID _ handle.pollID; b.dest _ [this.addr.net, this.addr.host, IF this.type = MTP THEN PupTypes.miscSrvSoc ELSE GVProtocol.GetSocket[MSPoll] ]; b.source _ socketAddr; PupDefs.PupRouterSendThis[b]; finished _ FALSE; }; ENDLOOP; IF finished THEN transmissions _ transmitLimit -- all have replied ELSE WAIT handle.pollCond; IF transmissions >= transmitLimit THEN { FOR this: GVRetrieveInternal.MBXPtr _ handle.MBXChain, this.next WHILE this # GVRetrieveInternal.noMBX DO IF this.addrState = unknown OR this.replyWanted THEN NoteChangedMBX[handle, this, unknown]; ENDLOOP; IF handle.MBXChain = GVRetrieveInternal.noMBX THEN SetMBXState[handle, allEmpty]; }; }; ENDCASE => -- couldn't authenticate transmissions _ transmitLimit; ENDLOOP; PupDefs.PupSocketKick[socket]; WHILE handle.pollReplying DO WAIT handle.pollCond ENDLOOP; JOIN replyPoll; PupDefs.PupSocketDestroy[socket]; handle.polling _ FALSE; NOTIFY handle.pollCond; }; NextServer: PUBLIC ENTRY PROC [handle: Handle] RETURNS [noMore: BOOL, state: GVRetrieve.ServerState, type: GVRetrieve.ServerType] = { ENABLE UNWIND => NULL; DO SELECT handle.mbxState FROM unknown, userOK => WAIT handle.mbxStateChange; ENDCASE => EXIT; ENDLOOP; IF handle.currentMBX = GVRetrieveInternal.noMBX THEN { handle.currentMBX _ handle.MBXChain; handle.newPollWanted _ TRUE; BROADCAST handle.pollCond; WHILE handle.newPollWanted DO WAIT handle.mbxStateChange ENDLOOP; } ELSE { GVClose[handle]; handle.currentMBX _ handle.currentMBX.next; }; IF handle.currentMBX = GVRetrieveInternal.noMBX THEN { noMore _ TRUE; } ELSE { noMore _ FALSE; WHILE handle.currentMBX.replyWanted DO WAIT handle.mbxStateChange ENDLOOP; state _ handle.currentMBX.state; IF handle.currentMBX.type = MTP THEN { handle.state _ beginning; type _ MTP; } ELSE { handle.header _ [type: LastItem, length: 0]; handle.spareByte _ FALSE; handle.state _ beforeMBX; type _ GV; }; }; }; NextMessage: PUBLIC ENTRY PROC [handle: Handle] RETURNS [msgExists: BOOL, archived: BOOL, deleted: BOOL] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; IF handle.state = beforeMBX THEN { addr: PupAddress _ ServerAddress[handle]; IF handle.currentStr # NIL THEN ERROR; IF handle.currentMBX.type # GV THEN ERROR Failed[communicationFailure, "Can't access MTP mailboxes from Cedar"]; handle.currentStr _ GVProtocol.CreateStream[handle.currentMBX.addr, MSRetrieve]; GVProtocol.SendMSOperation[handle.currentStr, openMBX]; GVProtocol.SendRName[handle.currentStr, handle.userName]; GVProtocol.SendPassword[str:handle.currentStr, pw:handle.userKey]; GVProtocol.SendNow[handle.currentStr]; { info: GVNames.AuthenticateInfo = LOOPHOLE[GVProtocol.ReceiveByte[handle.currentStr]]; SELECT info FROM allDown => ERROR Failed[communicationFailure, "You mailbox server can't contact an authentication server"]; notFound, group, badPwd => ERROR Failed[badCredentials, "Your mailbox server doesn't like your name or password"]; individual => NULL; ENDCASE => ERROR Failed[unknownFailure, "Unknown return code from mailbox"]; }; handle.messages _ GVProtocol.ReceiveCount[handle.currentStr]; handle.state _ afterMessage; }; WHILE handle.state = inBody DO [] _ InnerNextItem[handle]; ENDLOOP; IF handle.state = beforeBody THEN handle.state _ afterMessage; IF handle.messages = 0 THEN { handle.state _ afterMBX; RETURN [FALSE, FALSE, FALSE]; } ELSE { handle.messages _ handle.messages-1; GVProtocol.SendMSOperation[handle.currentStr, nextMessage]; GVProtocol.SendNow[handle.currentStr]; msgExists _ GVProtocol.ReceiveBoolean[handle.currentStr]; archived _ GVProtocol.ReceiveBoolean[handle.currentStr]; deleted _ GVProtocol.ReceiveBoolean[handle.currentStr]; IF msgExists THEN handle.state _ beforeBody ELSE ERROR; }; }; ReadTOC: PUBLIC ENTRY PROC [handle: Handle] RETURNS [ROPE] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; WHILE handle.state = inBody DO [] _ InnerNextItem[handle]; ENDLOOP; IF handle.state # beforeBody THEN ERROR WrongCallSequence[]; GVProtocol.SendMSOperation[handle.currentStr, readTOC]; GVProtocol.SendNow[handle.currentStr]; RETURN [GVProtocol.ReceiveRemark[handle.currentStr]]; }; StartMessage: PUBLIC ENTRY PROC [handle: Handle] RETURNS [postmark: GVBasics.Timestamp, sender: RName, returnTo: RName] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; [postmark, sender, returnTo] _ InnerStartMessage[handle]; }; NextItem: PUBLIC ENTRY PROC [handle: Handle] RETURNS [itemHeader: GVBasics.ItemHeader] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; IF handle.state # inBody THEN [] _ InnerStartMessage[handle]; RETURN [ InnerNextItem[handle] ] }; GetItem: PUBLIC ENTRY PROC [handle: Handle] RETURNS [STREAM] = { IF handle.state # inBody THEN ERROR WrongCallSequence[]; RETURN [IO.CreateStream[myStreamProcs, handle] ]; }; WriteTOC: PUBLIC ENTRY PROC [handle: Handle, entry: ROPE] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; WHILE handle.state = inBody DO [] _ InnerNextItem[handle]; ENDLOOP; IF handle.state # beforeBody THEN ERROR WrongCallSequence[]; GVProtocol.SendMSOperation[handle.currentStr, writeTOC]; GVProtocol.SendRemark[handle.currentStr, entry]; GVProtocol.SendNow[handle.currentStr]; GVProtocol.ReceiveAck[handle.currentStr]; handle.state _ beforeBody; }; ServerAddress: PUBLIC INTERNAL PROC [handle: Handle] RETURNS [PupAddress] = { IF handle.currentMBX = GVRetrieveInternal.noMBX THEN ERROR WrongCallSequence[]; IF handle.currentMBX.addrState = unknown THEN FindAddress[handle, handle.currentMBX]; SELECT handle.currentMBX.addrState FROM unknown => ERROR Failed[communicationFailure, "Can't find mailbox server address"]; bad => ERROR Failed[noSuchServer, "Your mailbox site name is not valid"]; known => RETURN [handle.currentMBX.addr]; ENDCASE => ERROR; }; GVClose: PUBLIC INTERNAL PROC [handle: Handle] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; IF handle.currentStr # NIL THEN GVProtocol.Close[handle.currentStr]; handle.currentStr _ NIL; handle.state _ beforeMBX; }; NoteChangedMBX: PUBLIC INTERNAL PROC [handle: Handle, mbx: GVRetrieveInternal.MBXPtr, new: GVRetrieve.ServerState] = { mbx.replyWanted _ FALSE; BROADCAST handle.mbxStateChange; IF new # mbx.state THEN SELECT new FROM unknown => { IF mbx.state = notEmpty THEN handle.notEmptyMBXCount _ handle.notEmptyMBXCount - 1; handle.unknownMBXCount _ handle.unknownMBXCount + 1; }; empty => { SELECT mbx.state FROM unknown => handle.unknownMBXCount _ handle.unknownMBXCount - 1; notEmpty => handle.notEmptyMBXCount _ handle.notEmptyMBXCount - 1; ENDCASE => NULL; }; notEmpty => { IF mbx.state = unknown THEN handle.unknownMBXCount _ handle.unknownMBXCount - 1; handle.notEmptyMBXCount _ handle.notEmptyMBXCount + 1; }; ENDCASE => ERROR; mbx.state _ new; IF new = unknown THEN mbx.addrState _ unknown; { complete: BOOL _ TRUE; emptyFound: BOOL _ FALSE; FOR this: GVRetrieveInternal.MBXPtr _ handle.MBXChain, this.next WHILE this # GVRetrieveInternal.noMBX DO IF this.state = empty THEN emptyFound _ TRUE; IF this.replyWanted THEN complete _ FALSE; ENDLOOP; SetMBXState[ handle, SELECT TRUE FROM (handle.notEmptyMBXCount # 0) => notEmpty, (handle.unknownMBXCount = 0) => allEmpty, (NOT complete) => userOK, emptyFound => someEmpty ENDCASE => allDown ]; }; }; SetMBXState: PUBLIC INTERNAL PROC [handle: Handle, state: MBXState] = { BROADCAST handle.mbxStateChange; IF state # userOK AND handle.changes # NIL THEN handle.changes[state]; handle.mbxState _ state; }; FindRegistryAndMailboxes: PUBLIC INTERNAL PROC [handle: Handle] = { length: INT = handle.userName.Length[]; firstRegChar: INT = handle.userName.Find["."]+1; -- =0 if no dot found registry: ROPE = handle.userName.Substr[firstRegChar, length-firstRegChar]; { called: BOOL _ FALSE; Work: INTERNAL PROC [addr:PupAddress] RETURNS [stop:BOOL] = { IF called THEN { handle.registry _ GV; stop _ TRUE } ELSE { handle.registry _ MTP; called _ TRUE; stop _ FALSE }; }; handle.registry _ GV; -- default if registry isn't in NLS [] _ PupDefs.EnumeratePupAddresses[registry, Work ! PupDefs.PupNameTrouble => IF code = errorFromServer THEN CONTINUE ELSE GOTO noReg ]; }; IF handle.MBXChain # GVRetrieveInternal.noMBX THEN ERROR; IF handle.registry = MTP THEN { FindAddress[handle, AddMBX[handle, registry]]; handle.mbxKnown _ TRUE; } ELSE FindGVMailboxes[handle]; EXITS noReg => SetMBXState[handle, cantAuth]; }; FindAddress: PUBLIC INTERNAL PROC [handle: Handle, mbx: GVRetrieveInternal.MBXPtr] = { connect: GVBasics.Connect; info: GVNames.ConnectInfo; IF mbx.addrState # unknown THEN ERROR; IF mbx.type = GV THEN { [info, connect] _ GVNames.GetConnect[mbx.name]; SELECT info FROM individual => NULL; allDown => GOTO noAddr; group, notFound => GOTO badConnect; ENDCASE => ERROR; } ELSE connect _ mbx.name; mbx^.addr _ PupDefs.GetPupAddress[[0,0], connect ! PupDefs.PupNameTrouble => IF code = errorFromServer THEN GOTO badConnect ELSE GOTO noAddr ]; mbx.addrState _ known; EXITS badConnect => { NoteChangedMBX[handle,mbx,empty]; mbx.addrState _ bad }; noAddr => NULL; }; EntryGetChar: ENTRY PROC [handle: Handle] RETURNS [CHAR] = { ENABLE { GVProtocol.Failed => Fail[why, text]; UNWIND => NULL }; IF handle.header.length = 0 THEN ERROR IO.EndOfStream[handle.currentStr]; RETURN [ LOOPHOLE[GVProtocol.ReceiveByte[handle.currentStr]] ] }; EntryUnsafeGetBlock: ENTRY UNSAFE PROC [handle: Handle, block: IO.UnsafeBlock] RETURNS [nBytesRead: INT] = UNCHECKED { ENABLE { GVProtocol.Failed => CHECKED{ Fail[why, text] }; UNWIND => NULL }; amount: INT = MIN[block.count, handle.header.length]; IF handle.state # inBody THEN ERROR WrongCallSequence[]; GVProtocol.ReceiveBytes[handle.currentStr, [block.base, block.startIndex, block.startIndex+amount]]; handle.header.length _ handle.header.length - amount; RETURN [amount] }; EntryEndOf: ENTRY PROC [handle: Handle] RETURNS [BOOL] = { RETURN [handle.header.length = 0]; }; EntryGetLength: ENTRY PROC [handle: Handle] RETURNS [length: INT] = { RETURN [handle.itemLength]; }; EntryCharsAvail: ENTRY PROC [handle: Handle] RETURNS [BOOL] = { RETURN [handle.header.length > 0 AND handle.currentStr.CharsAvail[]>0]; }; EntryCloseStream: ENTRY PROC [handle: Handle, abort: BOOL _ FALSE] = { InnerSkipItem[handle]; }; ConsiderPollReply: ENTRY PROC [handle: Handle, b: PupDefs.PupBuffer] RETURNS [BOOL] = TRUSTED { IF NOT handle.pollWanted THEN { handle.pollReplying _ FALSE; BROADCAST handle.pollCond; RETURN [FALSE] }; IF b # NIL AND b.pupID = handle.pollID THEN { mbx: GVRetrieveInternal.MBXPtr; FOR mbx _ handle.MBXChain, mbx.next UNTIL mbx = GVRetrieveInternal.noMBX DO IF mbx.addrState = known AND mbx.addr.net = b.source.net AND mbx.addr.host = b.source.host THEN { noProcessPupErrorCode: CARDINAL = 2B; cantGetTherePupErrorCode: CARDINAL = 1002B; eightHopsPupErrorCode: CARDINAL = 1004B; SELECT b.pupType FROM PupTypes.userAuthOk => { mbx.replyWanted _ TRUE; IF handle.mbxState = unknown THEN SetMBXState[handle, userOK]; handle.pollStarted _ BasicTime.earliestGMT; --force new poll for mbx's }; PupTypes.userAuthBad => IF handle.mbxState = unknown THEN SetMBXState[handle, badPwd--badName?--]; mailIsNew => NoteChangedMBX[handle, mbx, notEmpty]; mailNotNew => NoteChangedMBX[handle, mbx, empty]; mailError => NoteChangedMBX[handle, mbx, empty]; error => SELECT LOOPHOLE[b.errorCode, CARDINAL] FROM noProcessPupErrorCode, cantGetTherePupErrorCode, eightHopsPupErrorCode => NoteChangedMBX[handle, mbx, unknown]; ENDCASE => NULL; ENDCASE => NULL; }; ENDLOOP; }; RETURN [TRUE] }; InnerStartMessage: INTERNAL PROC [handle: Handle] RETURNS [postmark: GVBasics.Timestamp, sender: RName, returnTo: RName] = { WHILE handle.state = inBody DO [] _ InnerNextItem[handle]; ENDLOOP; IF handle.state # beforeBody THEN ERROR WrongCallSequence[]; GVProtocol.SendMSOperation[handle.currentStr, readMessage]; GVProtocol.SendNow[handle.currentStr]; handle.state _ inBody; handle.header _ GVProtocol.ReceiveItemHeader[handle.currentStr ]; IF handle.header.type # PostMark THEN ERROR; postmark _ GVProtocol.ReceiveTimestamp[handle.currentStr]; handle.header _ GVProtocol.ReceiveItemHeader[handle.currentStr]; IF handle.header.type # Sender THEN ERROR; sender _ GVProtocol.ReceiveRName[handle.currentStr]; handle.header _ GVProtocol.ReceiveItemHeader[handle.currentStr]; IF handle.header.type # ReturnTo THEN ERROR; returnTo _ GVProtocol.ReceiveRName[handle.currentStr]; handle.header.length _ 0; -- no more data in this item -- }; InnerNextItem: INTERNAL PROC [handle: Handle] RETURNS [itemHeader: GVBasics.ItemHeader] = { IF handle.state # inBody THEN ERROR WrongCallSequence[]; IF handle.header.length > 0 OR handle.spareByte THEN InnerSkipItem[handle]; handle.header _ GVProtocol.ReceiveItemHeader[handle.currentStr]; handle.itemLength _ handle.header.length; handle.spareByte _ handle.header.length MOD 2 # 0; IF handle.header.type = LastItem THEN { IF handle.header.length > 0 THEN InnerSkipItem[handle]; [] _ PupStream.ConsumeMark[handle.currentStr ! PupStream.StreamClosing => Fail[communicationError, text]; PupStream.TimeOut => Fail[communicationError, "Mailbox server not sending data"]]; handle.state _ beforeBody; }; itemHeader _ handle.header; }; RestartPoll: INTERNAL PROC [handle: Handle] = { handle.pollID.b _ handle.pollID.b + 1; --to ignore old poll replies-- IF NOT handle.polling THEN handle.sendPoll _ FORK SendPollProcess[handle]; handle.polling _ handle.pollWanted _ TRUE; handle.newPollWanted _ TRUE; BROADCAST handle.pollCond; }; UnsetMailboxes: INTERNAL PROC [handle: Handle] = { IF handle.polling THEN { sendPoll: PROCESS _ NIL; handle.pollWanted _ FALSE; BROADCAST handle.pollCond; WHILE handle.polling DO WAIT handle.pollCond ENDLOOP; sendPoll _ handle.sendPoll; IF sendPoll # NIL THEN TRUSTED { handle.sendPoll _ NIL; JOIN sendPoll; }; }; IF handle.currentMBX # GVRetrieveInternal.noMBX THEN { GVClose[handle]; handle.currentMBX _ GVRetrieveInternal.noMBX; }; handle.unknownMBXCount _ handle.notEmptyMBXCount _ 0; SetMBXState[handle, unknown]; handle.mbxKnown _ FALSE; handle.MBXChain _ GVRetrieveInternal.noMBX; }; FindGVMailboxes: INTERNAL PROC [handle: Handle] = { IF handle.registry # GV THEN ERROR ELSE TRUSTED { info: GVNames.ExpandInfo = GVNames.Expand[handle.userName]; WITH info SELECT FROM allDown => SetMBXState[handle, cantAuth]; notFound => SetMBXState[handle, badName]; group => handle.mbxKnown _ TRUE; individual => { FOR site: GVNames.RListHandle _ sites, site.rest UNTIL site = NIL DO FindAddress[handle, AddMBX[handle, site.first] ]; ENDLOOP; handle.mbxKnown _ TRUE; }; ENDCASE => ERROR; }; }; AddMBX: INTERNAL PROC [handle: Handle, site: RName] RETURNS [this: GVRetrieveInternal.MBXPtr] = { last: GVRetrieveInternal.MBXPtr _ NIL; FOR old: GVRetrieveInternal.MBXPtr _ handle.MBXChain, old.next UNTIL old = NIL DO last _ old; ENDLOOP; this _ NEW[GVRetrieveInternal.MBXData]; IF last = NIL THEN handle.MBXChain _ this ELSE last.next _ this; this.name _ site; this.type _ IF site.Find["."] < 0 THEN MTP ELSE GV; this.next _ GVRetrieveInternal.noMBX; this.state _ unknown; this.replyWanted _ TRUE; handle.unknownMBXCount _ handle.unknownMBXCount + 1; IF handle.mbxState = allEmpty THEN SetMBXState[handle, userOK]; this.addrState _ unknown; }; InnerSkipItem: INTERNAL PROC [handle: Handle] = { length: CARDINAL = 128; buffer: PACKED ARRAY [0..length) OF CHAR; IF handle.state # inBody THEN ERROR WrongCallSequence[]; IF handle.spareByte THEN handle.header.length _ handle.header.length+1; handle.spareByte _ FALSE; WHILE handle.header.length > 0 DO TRUSTED { wanted: INT = MIN[handle.header.length, length]; GVProtocol.ReceiveBytes[handle.currentStr, [LOOPHOLE[LONG[@buffer]], 0, wanted] ! GVProtocol.Failed => Fail[communicationError, text] ]; handle.header.length _ handle.header.length - wanted; }; ENDLOOP; }; SendAuthReq: INTERNAL PROC [handle: Handle, socketAddr: PupAddress] = TRUSTED { this: GVRetrieveInternal.MBXPtr = handle.MBXChain; IF this.type # MTP THEN ERROR; IF this.addrState # known THEN SetMBXState[handle, cantAuth] ELSE { b: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; b.pupWords[0] _ handle.userName.Length[]; b.pupWords[1] _ LAST[CARDINAL]; PupDefs.SetPupContentsWords[b, 2]; PupDefs.AppendRopeToPupBuffer[b, handle.userName]; { pos: INT = (PupDefs.GetPupContentsBytes[b] +1)/2; b.pupWords[pos] _ handle.userPwd.Length[]; b.pupWords[pos+1] _ LAST[CARDINAL]; PupDefs.SetPupContentsWords[b, pos+2]; PupDefs.AppendRopeToPupBuffer[b, handle.userPwd]; }; b.pupType _ PupTypes.userAuthReq; b.dest _ [this.addr.net, this.addr.host, PupTypes.miscSrvSoc]; b.pupID _ handle.pollID; b.source _ socketAddr; PupDefs.PupRouterSendThis[b]; }; }; Fail: PROC [why: GVProtocol.FailureReason, text: ROPE] = { ERROR Failed[IF why = protocolError THEN unknownFailure ELSE communicationFailure, text]; }; GVGetChar: PROC [self: STREAM] RETURNS [CHAR] = { RETURN [EntryGetChar[NARROW[self.streamData]]]; }; GVUnsafeGetBlock: UNSAFE PROC [self: STREAM, block: IO.UnsafeBlock] RETURNS [ nBytesRead: INT] = UNCHECKED { RETURN [EntryUnsafeGetBlock[NARROW[self.streamData], block]]; }; GVEndOf: PROC [self: STREAM] RETURNS [BOOL] = { RETURN [EntryEndOf[NARROW[self.streamData]]]; }; GVGetLength: PROC [self: STREAM] RETURNS [length: INT] = { RETURN [EntryGetLength[NARROW[self.streamData]]]; }; GVCharsAvail: PROC [self: STREAM, wait: BOOL] RETURNS [INT] = { RETURN [IF EntryCharsAvail[NARROW[self.streamData]] THEN 1 ELSE 0]; }; GVCloseStream: PROC [self: STREAM, abort: BOOL _ FALSE] = { EntryCloseStream[NARROW[self.streamData], abort]; }; PollReplyProcess: PROC [handle: Handle, socket: PupDefs.PupSocket] = TRUSTED { DO b: PupDefs.PupBuffer = socket.get[]; IF NOT ConsiderPollReply[handle, b] THEN EXIT; IF b # NIL THEN PupDefs.ReturnFreePupBuffer[b]; ENDLOOP; }; }. GVRetrieveImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Created by Andrew Birrell Russ Atkinson (RRA) February 11, 1986 4:04:39 pm PST Global variables Errors Public procedures Public ENTRY procedures main program for sending polls authenticate poll the mailboxes special case for user with no mailboxes we exited by a signal if the address wasn't available handle.state is now afterMessage or afterMBX Public INTERNAL procedures if server is down, its address may change! consider whether poll is complete definitive calculation of global state! Private ENTRY procedures returns 0 forever if we're at the end of the item MTP authentication The following LOOPHOLE brought to you courtesy of the Alto version of PupTypes has PupErrorCode: TYPE = RECORD[WORD] and the Pilot version has PupErrorCode: TYPE = MACHINE DEPENDENT { .... }. Private INTERNAL procedures arrange for InnerSkip to include padding byte RRA: Wait for the sendPoll process to finish. Also show that we do not need another JOIN. this case includes individual with forwarding skip to end of mailbox chain Private utilities main program for replies to the polls ΚH˜codešœ™Kšœ Οmœ7™BKšœ™K™4K˜šΟk ˜ Kšœ žœžœ˜0Kšœ žœ>˜LKšœžœi˜vKšœ žœ‡˜—Kšœ žœ4˜DKšœžœ0˜HKšžœžœ<žœ˜fKšœžœ˜+Kšœžœœ˜©Kšœ žœ'˜6Kšœ žœP˜^Kšœžœžœ ˜(——headšœžœž˜Kšžœžœ"˜4Kšžœ+žœ#˜WKšžœ!žœ˜*—˜Kšœžœžœ˜0Kšœžœžœ#˜Kšœ,™,—šžœ˜šžœ˜Kšœ˜Kšžœžœžœžœ˜Kšœ˜—šžœ˜K˜$K˜;K˜&K˜9K˜8K˜7Kšžœ žœžœžœ˜7Kšœ˜——Kšœ˜K˜—š  œžœžœžœžœžœ˜>Kšžœ)žœžœ˜@Kšžœžœžœ˜CKšžœžœžœ˜Kšœ˜K˜—š œžœžœžœžœžœžœž œ˜vKšœ1™1Kšžœžœžœžœ˜KKšœžœžœ$˜5Kšžœžœžœ˜8Kšœd˜dKšœ5˜5Kšžœ ˜Kšœ˜K˜—š   œžœžœžœžœ˜:Kšžœ˜"Kšœ˜K˜—š  œžœžœžœ žœ˜EKšžœ˜Kšœ˜K˜—š  œžœžœžœžœ˜?Kšžœžœ#˜GKšœ˜K˜—š  œžœžœžœžœ˜FKšœ˜Kšœ˜K˜—š  œžœžœ'žœžœžœ˜_šžœžœžœ˜Kšœžœž œ˜7Kšžœžœ˜Kšœ˜—šžœžœžœžœ˜-K˜šžœ!žœž˜Kšžœ˜Kšžœ˜Kšžœ˜!šžœ˜Kšœžœ˜%Kšœžœ ˜+Kšœžœ ˜(šžœ ž˜šœ˜Kšœ™Kšœžœ˜Kšžœžœ˜>Kšœ,Ÿ˜FKšœ˜—˜šžœž˜!KšœŸ œ˜(——˜ K˜&—˜ K˜#—˜ K˜#—K˜KšœB™BKšœ?™?šœ<™<šžœžœžœž˜+K˜oKšžœžœ˜——Kšžœžœ˜Kšœ˜———Kšžœ˜—Kšœ˜—Kšžœžœ˜ Kšœ˜K™——™š œžœžœžœC˜|Kšžœžœžœ˜CKšžœžœžœ˜K˜K˜K˜Kšœ˜——Kšœ˜K˜——™š œžœ'žœ˜:šžœžœ˜#Kšžœ˜Kšžœ˜!—Kšœ˜K˜—š   œžœžœžœžœ˜1Kšžœžœ˜/Kšœ˜K˜—š œžœžœžœ žœ žœžœž œ˜lKšžœžœ˜=Kšœ˜K˜—š  œžœžœžœžœ˜/Kšžœ žœ˜-Kšœ˜K˜—š   œžœžœžœ žœ˜:Kšžœžœ˜1Kšœ˜K˜—š   œžœžœžœžœžœ˜?Kš žœžœžœžœžœ˜CKšœ˜K˜—š   œžœžœ žœžœ˜;Kšœžœ˜1Kšœ˜K˜—š œžœ.žœ˜NKšœ%™%šž˜K˜$Kšžœžœžœžœ˜.Kšžœžœžœ ˜/Kšžœ˜—Kšœ˜K˜——Kšœ˜K˜K˜K˜—…—bv„N