<> <> <> <> <> 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; }; }.