<> <> <> <> <<>> DIRECTORY Basics USING [ BITSHIFT, BITXOR ], ConvertUnsafe USING [ ToRope ], GVBasics USING [ GVString, maxGVStringLength, oldestTime, Password, RName, Timestamp], GVProtocol USING [ bpw, FailureReason, GVSocket, Handle, ReceiveCount, ReceiveRC, ReceiveRName, ReceiveTimestamp, ReturnCode, RSOperation, SendCount, SendRName, SendRSOperation, SendTimestamp, StringSize ], IO USING [ EndOfStream, Flush, GetChar, PutChar, PutFR, PutRope, STREAM, UnsafeBlock, UnsafeGetBlock, UnsafePutBlock ], PupDefs USING [ AnyLocalPupAddress, PupPackageMake, SecondsToTocks ], PupStream USING [ PupAddress, PupByteStreamCreate, StreamClosing, TimeOut ], PupTypes USING [ Byte, fillInSocketID, PupAddress, PupSocketID ], Rope USING [ Fetch, Length, ROPE ]; GVProtocolImpl: CEDAR MONITOR IMPORTS Basics, ConvertUnsafe, GVProtocol, IO, PupDefs, PupStream, Rope EXPORTS GVBasics, GVProtocol = { OPEN GVProtocol; MakeKey: PUBLIC PROC [password: Rope.ROPE] RETURNS [ key: GVBasics.Password ] = { key _ ALL[0]; FOR i: CARDINAL IN CARDINAL[0..password.Length[]) DO j: [0..LENGTH[key]) = (i/bpw) MOD LENGTH[key]; c: WORD = LOOPHOLE[ IF password.Fetch[i] IN ['A..'Z] THEN password.Fetch[i] - 'A + 'a ELSE password.Fetch[i] ]; key[j] _ Basics.BITXOR[ key[j], Basics.BITSHIFT[c, IF (i MOD 2) = 0 THEN 9 ELSE 1]]; ENDLOOP; }; RopeFromTimestamp: PUBLIC PROC [stamp: GVBasics.Timestamp] RETURNS [Rope.ROPE] = { RETURN [ IO.PutFR["%b#%b@%g", [integer[stamp.net]], [integer[stamp.host]], [cardinal[stamp.time]] ] ]; }; bpw: INT = GVProtocol.bpw; SocketsArray: TYPE = ARRAY GVProtocol.GVSocket OF PupTypes.PupSocketID; sockets: REF SocketsArray _ NEW[SocketsArray _ [ none: [0,0], RSEnquiry: [0, 50B], RSPoll: [0, 52B], Lily: [0, 53B], MSPoll: [0, 54B], MSForward: [0, 55B], MSSend: [0, 56B], MSRetrieve: [0, 57B] ]]; SetTestingMode: PUBLIC ENTRY PROC = { FOR s: GVProtocol.GVSocket IN GVProtocol.GVSocket DO sockets[s].a _ 1 ENDLOOP; }; GetSocket: PUBLIC ENTRY PROC [gv: GVSocket] RETURNS [PupTypes.PupSocketID] = { RETURN [ sockets[gv] ]; }; myAddr: PupTypes.PupAddress; IsLocal: PUBLIC PROC [addr: PupTypes.PupAddress] RETURNS [BOOL] = { RETURN [ addr.net = myAddr.net AND addr.host = myAddr.host ] }; Failed: PUBLIC SIGNAL [why: FailureReason, text: Rope.ROPE] = CODE; CreateStream: PUBLIC PROC [host: PupTypes.PupAddress, socket: GVProtocol.GVSocket, secs: CARDINAL _ 120] RETURNS [GVProtocol.Handle] = { ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text]; IF socket # none THEN host.socket _ sockets[socket]; RETURN [ PupStream.PupByteStreamCreate[host, PupDefs.SecondsToTocks[secs] ] ] }; SendNow: PUBLIC PROC [str: GVProtocol.Handle] = { ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text]; str.Flush[]; }; Byte: TYPE = [0..256); SendByte: PUBLIC PROC [str: GVProtocol.Handle, byte: Byte] = { ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text]; str.PutChar[LOOPHOLE[byte]]; }; ReceiveByte: PUBLIC PROC [str: GVProtocol.Handle] RETURNS [byte: Byte] = { ENABLE { PupStream.StreamClosing => ERROR Failed[communicationError, text]; PupStream.TimeOut => SIGNAL Failed[noData, "Server not sending data"]; IO.EndOfStream => ERROR Failed[protocolError, "Unexpected \"mark\""]; }; byte _ LOOPHOLE[str.GetChar[]]; }; SendBytes: PUBLIC PROC [str: GVProtocol.Handle, block: IO.UnsafeBlock] = { ENABLE PupStream.StreamClosing => ERROR Failed[communicationError, text]; str.UnsafePutBlock[block]; }; ReceiveBytes: PUBLIC UNSAFE PROC [str: GVProtocol.Handle, block: IO.UnsafeBlock] = TRUSTED { ENABLE { PupStream.StreamClosing => ERROR Failed[communicationError, text]; PupStream.TimeOut => SIGNAL Failed[noData, "Server not sending data"]; IO.EndOfStream => ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""]; }; nBytes: INT = str.UnsafeGetBlock[block]; IF nBytes # block.count THEN ERROR Failed[protocolError, "Protocol error: unexpected \"mark\""]; }; SendGVString: PUBLIC PROC [str: GVProtocol.Handle, rope: GVBasics.GVString] = { length: INT = rope.Length[]; IF length > GVBasics.maxGVStringLength THEN ERROR Failed[clientError, "Excessive rope length"]; GVProtocol.SendCount[str, length]; GVProtocol.SendCount[str, 0--ignored--]; SendRope[str, rope]; IF length MOD 2 # 0 THEN SendByte[str,0]; }; ReceiveGVString: PUBLIC PROC [str: GVProtocol.Handle] RETURNS [GVBasics.GVString] = TRUSTED { string: STRING = [GVBasics.maxGVStringLength]; string.length _ GVProtocol.ReceiveCount[str]; [] _ GVProtocol.ReceiveCount[str]; IF string.length > string.maxlength THEN ERROR Failed[protocolError, "Protocol error: too many characters"]; ReceiveBytes[str, [LOOPHOLE[LONG[@string.text]], 0, bpw*(SIZE[StringBody[string.length]] - SIZE[StringBody[0]])]]; RETURN [ConvertUnsafe.ToRope[string]] }; SendRope: PUBLIC PROC [str: GVProtocol.Handle, rope: Rope.ROPE] = { str.PutRope[rope ! PupStream.StreamClosing => ERROR Failed[communicationError, text]]; }; Enquire: PUBLIC PROC [str: GVProtocol.Handle, op: GVProtocol.RSOperation, name: GVBasics.RName, oldStamp: GVBasics.Timestamp _ GVBasics.oldestTime] RETURNS [rc: GVProtocol.ReturnCode, stamp: GVBasics.Timestamp] = { GVProtocol.SendRSOperation[str, op]; GVProtocol.SendRName[str, name]; IF op IN [Expand .. CheckStamp] THEN GVProtocol.SendTimestamp[str, oldStamp]; SendNow[str]; rc _ GVProtocol.ReceiveRC[str]; IF rc.code = done AND op IN [Expand .. CheckStamp] THEN stamp _ GVProtocol.ReceiveTimestamp[str] ELSE stamp _ oldStamp; }; ReceiveRList: PUBLIC PROC [str: GVProtocol.Handle, work: PROC [GVBasics.RName]] = { length: INT _ ReceiveCount[str]; WHILE length > 0 DO name: GVBasics.RName = GVProtocol.ReceiveRName[str]; length _ length - GVProtocol.StringSize[name]; work[name]; ENDLOOP; }; TRUSTED { PupDefs.PupPackageMake[]; myAddr _ PupDefs.AnyLocalPupAddress[PupTypes.fillInSocketID]; }; }.