DIRECTORY BasicTime USING [earliestGMT, GMT, Now, Period], Endian USING [FFromCard, FWORD, HWORD, CardFromF], GVBasics USING [Connect, ItemHeader, ItemLength, MakeKey, RName, Password, 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, IO USING [CharsAvail, CreateStreamProcs, CreateStream, EndOfStream, STREAM, StreamProcs, UnsafeBlock], Process USING [SecondsToTicks, SetTimeout], Pup USING [Address, nullAddress, nullSocket, Socket], PupBuffer USING [Buffer], PupName USING [Error, HisAddresses, NameLookup], PupSocket USING [AllocBuffer, AppendRope, CopyRope, CreateEphemeral, Destroy, FreeBuffer, Get, GetLocalAddress, GetUserBytes, Kick, Send, SetNoErrors, SetUserHWords, Socket], PupStream USING [ConsumeMark, StreamClosing, Timeout], PupType USING [], PupWKS USING [misc], Rope USING [Find, Length, ROPE, Substr]; GVRetrieveImpl: CEDAR MONITOR LOCKS handle USING handle: Handle IMPORTS BasicTime, Endian, GVBasics, GVNames, GVProtocol, IO, Process, PupName, PupSocket, PupStream, Rope EXPORTS GVRetrieve = { MBXState: TYPE = GVRetrieve.MBXState; RName: TYPE = GVBasics.RName; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; MBXPtr: TYPE = REF MBXData; MBXData: TYPE = RECORD[ next: MBXPtr, type: GVRetrieve.ServerType, state: GVRetrieve.ServerState, addrState: { unknown, known, bad }, replyWanted: BOOLEAN, addr: Pup.Address, name: GVBasics.RName ]; Handle: TYPE = REF HandleObject; HandleObject: PUBLIC TYPE = MONITORED RECORD[ MBXChain: MBXPtr, mbxKnown: BOOLEAN, -- whether user's mailbox sites are known notEmptyMBXCount: CARDINAL, unknownMBXCount: CARDINAL, registry: GVRetrieve.ServerType, state: -- position in legal call sequences { -- GV states beforeMBX, beforeTOCr, beforeBody, inBody, beforeTOCw, afterMessage, afterMBX, end, beginning, message, lastItem, block, lastBlock, lastMessage }, spareByte: BOOLEAN, -- GV padding, or MTP odd-byte mess spareByteValue: CHARACTER, -- for MTP odd-byte mess header: GVBasics.ItemHeader, -- header of current item; length field is decremented as we go itemLength: INT, -- total length of current item (for IO.GetLength) currentMBX: MBXPtr, -- mailbox being read messages: CARDINAL, -- number of messages in the mailbox currentStr: GVProtocol.Handle, -- stream to mailbox being read mbxState: GVRetrieve.MBXState, polling: BOOLEAN, pollWanted: BOOLEAN, newPollWanted: BOOLEAN, pollReplying: BOOLEAN, mbxStateChange: CONDITION, pollCond: CONDITION, pollID: Endian.FWORD, sendPoll: PROCESS, pollStarted: BasicTime.GMT, -- real time when poll last started interval: INT, -- polling interval, in seconds changes: PROCEDURE[GVRetrieve.MBXState], userName: GVBasics.RName, userPwd: Rope.ROPE, userKey: GVBasics.Password ]; 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 before 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 _ NIL; handle.mbxKnown _ FALSE; handle.notEmptyMBXCount _ 0; handle.unknownMBXCount _ 0; handle.registry _ GV; handle.spareByte _ FALSE; handle.currentMBX _ NIL; handle.messages _ 0; handle.currentStr _ NIL; handle.mbxState _ badName; handle.polling _ FALSE; handle.pollWanted _ FALSE; handle.newPollWanted _ FALSE; handle.pollReplying _ FALSE; handle.pollID _ Endian.FFromCard[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 = NIL 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: PupSocket.Socket = PupSocket.CreateEphemeral[remote: Pup.nullAddress]; socketAddr: Pup.Address = PupSocket.GetLocalAddress[socket]; replyPoll: PROCESS = FORK PollReplyProcess[handle, socket]; transmissions: CARDINAL _ 0; -- number of retransmissions so far handle.pollReplying _ TRUE; TRUSTED { Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ]; }; 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: MBXPtr _ handle.MBXChain, this.next WHILE this # NIL 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; Process.SetTimeout[@handle.pollCond, Process.SecondsToTicks[retransmitDelay] ]; } 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, socket, socketAddr]; transmissions _ transmissions + 1; WAIT handle.pollCond; --wait for reply IF transmissions >= transmitLimit THEN { IF handle.mbxState = unknown THEN --no reply 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: MBXPtr _ handle.MBXChain, this.next WHILE this # NIL DO IF this.replyWanted THEN { b: PupBuffer.Buffer = PupSocket.AllocBuffer[socket]; PupSocket.CopyRope[b, handle.userName]; b.type _ mailCheckLaurel; b.id _ handle.pollID; b.dest _ [this.addr.net, this.addr.host, IF this.type = MTP THEN PupWKS.misc ELSE GVProtocol.GetSocket[MSPoll] ]; PupSocket.Send[socket, b, b.dest]; finished _ FALSE; }; ENDLOOP; IF finished THEN transmissions _ transmitLimit -- all have replied ELSE WAIT handle.pollCond; IF transmissions >= transmitLimit THEN { FOR this: MBXPtr _ handle.MBXChain, this.next WHILE this # NIL DO IF this.addrState = unknown OR this.replyWanted THEN NoteChangedMBX[handle, this, unknown]; ENDLOOP; IF handle.MBXChain = NIL THEN SetMBXState[handle, allEmpty]; }; }; ENDCASE => transmissions _ transmitLimit; -- couldn't authenticate ENDLOOP; PupSocket.Kick[socket]; WHILE handle.pollReplying DO WAIT handle.pollCond; ENDLOOP; JOIN replyPoll; PupSocket.SetNoErrors[socket]; PupSocket.Destroy[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 = NIL 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 = NIL 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: Pup.Address _ 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 [Pup.Address] = { IF handle.currentMBX = NIL 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: 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: MBXPtr _ handle.MBXChain, this.next WHILE this # NIL 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]; { -- Registrys with only one address are MTP addresses: LIST OF Pup.Address; handle.registry _ GV; -- default if registry isn't in NLS addresses _ PupName.HisAddresses[registry, Pup.nullSocket ! PupName.Error => IF code = errorFromServer THEN CONTINUE ELSE GOTO noReg ]; IF addresses # NIL AND addresses.rest = NIL THEN handle.registry _ MTP; }; IF handle.MBXChain # NIL 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: 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 _ PupName.NameLookup[connect, ! PupName.Error => 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: PupBuffer.Buffer] RETURNS [BOOL] = TRUSTED { IF NOT handle.pollWanted THEN { handle.pollReplying _ FALSE; BROADCAST handle.pollCond; RETURN [FALSE] }; IF b # NIL AND b.id = handle.pollID THEN { mbx: MBXPtr; FOR mbx _ handle.MBXChain, mbx.next UNTIL mbx = NIL 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.type FROM userAuthOk => { mbx.replyWanted _ TRUE; IF handle.mbxState = unknown THEN SetMBXState[handle, userOK]; handle.pollStarted _ BasicTime.earliestGMT; --force new poll for mbx's }; 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 b.error.code FROM noSocket, cantGetThere, hostDown, tooManyHops => 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 _ Endian.FFromCard[Endian.CardFromF[handle.pollID] + 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 # NIL THEN { GVClose[handle]; handle.currentMBX _ NIL; }; handle.unknownMBXCount _ handle.notEmptyMBXCount _ 0; SetMBXState[handle, unknown]; handle.mbxKnown _ FALSE; handle.MBXChain _ NIL; }; 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: MBXPtr] = { last: MBXPtr _ NIL; FOR old: MBXPtr _ handle.MBXChain, old.next UNTIL old = NIL DO last _ old; ENDLOOP; this _ NEW[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 _ NIL; 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, socket: PupSocket.Socket, socketAddr: Pup.Address] = TRUSTED { this: MBXPtr = handle.MBXChain; IF this.type # MTP THEN ERROR; IF this.addrState # known THEN SetMBXState[handle, cantAuth] ELSE { b: PupBuffer.Buffer = PupSocket.AllocBuffer[socket]; b.hWord[0] _ Rope.Length[handle.userName]; b.hWord[1] _ LAST[CARDINAL]; PupSocket.SetUserHWords[b, 2]; PupSocket.AppendRope[b, handle.userName]; { pos: INT = (PupSocket.GetUserBytes[b] +1)/SIZE[Endian.HWORD]; -- Round UP b.hWord[pos] _ Rope.Length[handle.userPwd]; b.hWord[pos+1] _ LAST[CARDINAL]; PupSocket.SetUserHWords[b, pos+2]; PupSocket.AppendRope[b, handle.userPwd]; }; b.type _ userAuthReq; b.dest _ [this.addr.net, this.addr.host, PupWKS.misc]; b.id _ handle.pollID; PupSocket.Send[socket, b, b.dest]; }; }; 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: PupSocket.Socket] = TRUSTED { DO b: PupBuffer.Buffer = PupSocket.Get[socket]; IF NOT ConsiderPollReply[handle, b] THEN EXIT; IF b # NIL THEN PupSocket.FreeBuffer[b]; ENDLOOP; }; }. GVRetrieveImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Created by Andrew Birrell Russ Atkinson (RRA) October 18, 1985 6:45:10 pm PDT Hal Murray, June 3, 1986 3:45:33 pm PDT The client's mailboxes are represented by a chain: The overall state of mail is represented by a HandleObject list of the user's mailboxes user's registry type Current state of mail reading: MTP states State of mailbox polling: Global information supplied by the client: 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 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 Êܘcodešœ™Kšœ Ïmœ1™K˜Kšœ™K˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœž œ˜Kšœ ž œ˜Kšœžœ˜Kšœ žœ˜KšœžœŸ#˜?K˜Kšœ*™*Kšœ žœŸ˜.Kšœ ž œ˜(K˜Kšœžœ˜K˜—K˜K˜—head2™šœžœžœžœ˜9K˜#Kšœ˜Kšœ˜K˜Kšœ˜Kšœ!˜!K˜K˜—KšœžœŸ&˜EKšœžœŸ!œ˜BK˜—™š œžœžœ&žœžœ˜GK˜—šœžœžœ˜ K˜——™šÏnœžœžœžœžœžœžœ˜rKšœ žœ˜šœ˜Kšœžœ˜Kšœžœ˜K˜K˜K˜Kšœžœ˜K˜Kšœžœ˜Kšœžœ˜K˜Kšœžœ˜K˜K˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜K˜$K˜%K˜K˜"K˜Kšœžœ˜Kšœžœ˜K˜Kšœ˜—Kšœ˜K™——šœžœ ™š   œžœžœžœžœžœ˜NKšžœžœžœ˜šž˜šžœž˜Kšœžœ˜.Kšžœžœ˜—Kšžœ˜—Kšžœ˜Kšœ˜K˜—š   œžœžœžœžœžœ˜NKšžœžœžœžœ˜:Kšžœ˜ Kšœ˜K˜—š  œžœžœžœ˜3Kšžœžœžœžœ˜GKšœ˜K˜—š œžœžœžœ˜-K˜Kšœžœ˜Kšœžœ˜Kšœ˜K˜—š  œžœžœžœ#žœ˜FK˜šžœ˜Kšžœ˜!šžœžœ˜Kšžœ˜ šžœ˜K˜K˜K˜2K˜Kšœ˜———Kšœ˜K˜—š  œžœžœžœ˜5Kšžœ)žœžœ˜@Kšžœžœžœ˜CKšžœžœžœ˜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˜ šžœ!žœžœž˜6šžœ˜Kšžœ˜Kšžœ˜!šžœ˜Kšœžœ˜%Kšœžœ ˜+Kšœžœ ˜(šžœž˜šœ˜Kšœ™Kšœžœ˜Kšžœžœ˜>Kšœ,Ÿ˜FKšœ˜—˜Kšžœ˜KšžœŸ œ˜-—˜ K˜&—˜ K˜#—˜ K˜#—˜šžœž˜K˜VKšžœžœ˜——Kšžœžœ˜Kšœ˜———Kšžœ˜—Kšœ˜—Kšžœžœ˜ Kšœ˜K™——™š œžœžœžœC˜|Kšžœžœžœ˜CKšžœžœžœ˜Kšœ ˜ Kšžœ˜—Kšœžœ ˜Kšžœžœžœžœ˜@Kšœ˜Kš œ žœžœžœžœžœ˜3Kšœ žœ˜Kšœ)žœ˜.K˜4Kšžœžœ˜?K˜Kšœ˜K˜—š  œžœžœ˜1Kšœžœ˜Kš œžœžœ žœžœ˜)Kšžœžœžœ˜8Kšžœžœ/˜GKšœžœ˜šžœžœžœ˜+Kšœžœžœ˜0šœ,žœžœ˜OKšœ9˜9—Kšœ5˜5Kšœ˜Kšžœ˜—Kšœ˜K˜—š  œžœžœGžœ˜jK˜Kšžœ žœžœžœ˜šžœ˜Kšžœ˜"šžœ˜Kšœ4˜4Kšœ*˜*Kšœ žœžœ˜K˜Kšœ)˜)šœ˜Kšœžœ"žœžœŸ ˜IKšœ+˜+Kšœžœžœ˜ Kšœ"˜"Kšœ+˜+—K˜K˜6K˜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˜—š œžœ-žœ˜MKšœ%™%šž˜Kšœ,˜,Kšžœžœžœžœ˜.Kšžœžœžœ˜(Kšžœ˜—Kšœ˜K˜——Kšœ˜K˜K˜K˜—…—dŠˆf