<<>> <> <> <> <> <> <<>> <> <<>> DIRECTORY Ascii, BasicTime USING [FromNSTime, GMT], Convert, IO, MailBasics, MailBasicsFileTypes, MailBasicsItemTypes, MailBasicsMoreItemTypes, MailRetrieve, MailUtils USING [GeneratePostmark, GetTimeFromPostmark], MSBasics, MSMessage USING [GetHeaderRope, headingBodyPart], MSRetrieve, MSUtils, PFS, RefText, Rope, SerializedFiling, SystemNames USING [UserName], UserProfile, XNSCHName USING [NameFromRope, RopeFromName]; MSMailRetrieveImpl: CEDAR PROGRAM IMPORTS Ascii, BasicTime, Convert, IO, MailRetrieve, MailUtils, MSMessage, MSRetrieve, MSUtils, PFS, RefText, Rope, SerializedFiling, SystemNames, UserProfile, XNSCHName ~ BEGIN STREAM: TYPE ~ IO.STREAM; ROPE: TYPE ~ Rope.ROPE; Handle: TYPE ~ MailRetrieve.Handle; MBXState: TYPE ~ MailRetrieve.MBXState; ServerState: TYPE ~ MailRetrieve.ServerState; ServerType: TYPE ~ MailRetrieve.ServerType; FailureReason: TYPE ~ MailRetrieve.FailureReason; <> <> <> MSInfo: TYPE ~ REF MSInfoRec; MSInfoRec: TYPE ~ RECORD [ user: MSBasics.CHName, -- user/mailbox name envelope: MSBasics.Envelope, -- envelope of current message postmark: MailBasics.Timestamp, -- needed later if there is an attachment sender: ROPE, -- needed later if there is an attachment newMsg: BOOL, -- TRUE when envelope needs to be returned systemMessage: ROPE, -- derived from envelope if appropriate msH: MSRetrieve.Handle, -- XNS Mail Service handle for mailbox handle: Handle, -- for reproting state changes plainTextFromHeader: ROPE, -- if in header, non-nil until returned as item formattingFromHeader: ROPE, -- if in header, non-nil until returned as item bodyAnnotSize: CARD32 ¬ 0, -- size of extra text in body of message lastBodyPartType: MailBasicsItemTypes.ItemType ¬ MailBasicsItemTypes.header, -- for bodyAnnotSize fix reportChanges: PROC[Handle, MBXState] ]; <> msInfoList: LIST OF MSInfo ¬ NIL; tiogaTextOK: BOOL ¬ TRUE; AttributeType: TYPE = SerializedFiling.AttributeType; lastInterlispType: AttributeType = 4919; -- from MailFormatP1516V3 <> MSClose: PROC [ref: REF] RETURNS [] ~ { <> msInfo: MSInfo = NARROW[ref]; MSRetrieve.Close[msInfo.msH]; }; <<>> MSNewUser: PROC [handle: Handle, user: MailBasics.RName, password: ROPE, pollingInterval: CARDINAL, reportChanges: PROC[Handle, MBXState] ] RETURNS [REF] ~ { <> msInfo: MSInfo ¬ NEW[MSInfoRec]; msInfo.user ¬ XNSCHName.NameFromRope[user.name]; msInfo.msH ¬ MSRetrieve.Create[pollingInterval, IF reportChanges#NIL THEN ChangeReportFromMS ELSE NIL]; msInfo.reportChanges ¬ reportChanges; msInfo.handle ¬ handle; MSRetrieve.NewUser[msInfo.msH, msInfo.user, password]; msInfoList ¬ CONS[msInfo, msInfoList]; RETURN[msInfo]; }; <<>> MSMailboxState: PROC [ref: REF] RETURNS [mboxState: MBXState] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; mboxState ¬ FromMSMboxState[MSRetrieve.MailboxState[msInfo.msH]]; }; <<>> MSNextServer: PROC [ref: REF] RETURNS [noMore: BOOLEAN, state: ServerState, type: ServerType] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; msState: MSRetrieve.ServerState; noMore ¬ TRUE; state ¬ unknown; type ¬ $unknown; [noMore, msState] ¬ MSRetrieve.NextServer[msInfo.msH]; IF NOT noMore THEN { state ¬ FromMSServerState[msState]; type ¬ $xns; }; }; <<>> MSServerName: PROC [ref: REF] RETURNS [serverName: MailBasics.RName] ~ { <> msInfo: MSInfo = NARROW[ref]; serverName.ns ¬ $xns; serverName.name ¬ XNSCHName.RopeFromName[MSRetrieve.ServerName[msInfo.msH]]; }; <<>> MSUserName: PROC [ref: REF] RETURNS [userName: MailBasics.RName] ~ { <> msInfo: MSInfo = NARROW[ref]; userName.ns ¬ $xns; userName.name ¬ XNSCHName.RopeFromName[msInfo.user]; }; <<>> MSNextMessage: PROC [ref: REF] RETURNS [msgExists, archived, deleted, read: BOOLEAN] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; [msgExists, read] ¬ MSRetrieve.NextMessage[msInfo.msH]; archived ¬ FALSE; deleted ¬ FALSE; IF msgExists THEN { msInfo.envelope ¬ MSRetrieve.GetMessageEnvelope[msInfo.msH]; msInfo.systemMessage ¬ MSUtils.SystemMessageFromEnvelope[msInfo.envelope]; msInfo.newMsg ¬ TRUE; }; }; <<>> MSStartMessage: PROC [ref: REF] RETURNS [postmark: MailBasics.Timestamp, sender: MailBasics.RName, returnTo: MailBasics.RName] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; msInfo.postmark ¬ postmark ¬ RopeFromMSPostmark[msInfo.envelope]; sender ¬ [ns: $xns, name: MSUtils.RopeFromMSRName[msInfo.envelope.originator]]; msInfo.sender ¬ sender.name; <> returnTo ¬ sender; }; <<>> boilerPlate: ROPE ~ "\r===========> msg from XNSMailUser <===========\r"; <<>> MSNextItem: PROC [ref: REF] RETURNS [ih: MailBasics.ItemHeader, currentItem: STREAM] ~ { <> <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; bp: MSBasics.BodyPartInfo; isTiogaText: BOOL ¬ FALSE; DoAttachment: PROC[baseName: ROPE, fileType: PFS.FileType] = { msg: ROPE; this: ROPE ¬ MSGetItemAsRope[ref]; <> IF UserProfile.Boolean["XNSMail.FlushMsgsWithAttachments", FALSE] THEN { who: ROPE = SystemNames.UserName[]; -- unix name needed wDir: ROPE ¬ UserProfile.Token["XNSMail.attachmentsDir", IO.PutFR1["/tilde/%g/.cedar/xnsMailAttachments/", [rope[who]]] ]; fileName: ROPE ¬ IO.PutFR["%g-%g.txt", [rope[baseName]], [rope[Intelligible[msInfo.postmark, msInfo.sender]]] ]; fullName: PFS.PATH ¬ PFS.AbsoluteName[short: PFS.PathFromRope[fileName], wDir: PFS.PathFromRope[wDir] ]; fileLength: INT ¬ 0; outStream: STREAM; outStream ¬ PFS.StreamOpen[fileName: fullName, accessOptions: create ! PFS.Error => CONTINUE]; IF outStream = NIL THEN { fullName ¬ PFS.AbsoluteName[short: PFS.PathFromRope[fileName], wDir: PFS.PathFromRope[IO.PutFR1["/tilde/%g/", [rope[who]] ]] ]; outStream ¬ PFS.StreamOpen[fileName: fullName, accessOptions: create ! PFS.Error => CONTINUE]; IF outStream = NIL THEN ERROR MailRetrieve.Failed[$couldntCreateFile, PFS.RopeFromPath[fullName]]; }; outStream.PutRope[this]; fileLength ¬ outStream.GetLength[]; outStream.Close[]; msg ¬ IO.PutFLR["%g==> %g stored as:\r\t%g (%g bytes) <==\r\r", LIST[[rope[boilerPlate]], [rope[baseName]], [rope[PFS.RopeFromPath[fullName]]], [integer[fileLength]]] ]; ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsMoreItemTypes.hasAttachment; } ELSE { msg ¬ IO.PutFR["%g\r %g attachment not retrieved because XNSMail.FlushMsgsWithAttachments was FALSE\r\r", [rope[boilerPlate]], [rope[baseName]] ]; ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.multinationalNote; }; currentItem ¬ IO.RIS[msg]; ih.length ¬ msg.Length[]; }; GetNSTextFile: PROC RETURNS[note, plainTextR, formattingR: ROPE] = { sf: SerializedFiling.SerializedFile; len: INT ~ msInfo.bodyAnnotSize; sf ¬ SerializedFiling.GetSerializedFile[MSRetrieve.GetBodyPart[msInfo.msH]]; note ¬ SerializedFiling.GetRopeContents[sf]; IF ( len # 0 ) THEN { msInfo.bodyAnnotSize ¬ 0; IF len < note.Length[] THEN note ¬ note.Substr[len]; }; plainTextR ¬ SerializedFiling.GetRopeAttribute[sf, lastInterlispType-1]; formattingR ¬ SerializedFiling.GetRopeAttribute[sf, lastInterlispType]; }; IF msInfo.newMsg THEN { -- return the envelope ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.envelope; [currentItem, ih.length] ¬ BuildMSEnvelopeItem[msInfo.envelope]; msInfo.newMsg ¬ FALSE; RETURN; }; IF msInfo.systemMessage # NIL THEN { -- return the system message ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.systemMessage; currentItem ¬ IO.RIS[msInfo.systemMessage]; ih.length ¬ Rope.Length[msInfo.systemMessage]; msInfo.systemMessage ¬ NIL; RETURN; }; IF msInfo.plainTextFromHeader # NIL THEN { ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.plainTextForFormatting; currentItem ¬ IO.RIS[msInfo.plainTextFromHeader]; ih.length ¬ Rope.Length[msInfo.plainTextFromHeader]; msInfo.plainTextFromHeader ¬ NIL; RETURN; }; IF msInfo.formattingFromHeader # NIL THEN { ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.tioga1; currentItem ¬ IO.RIS[msInfo.formattingFromHeader]; ih.length ¬ Rope.Length[msInfo.formattingFromHeader]; msInfo.formattingFromHeader ¬ NIL; RETURN; }; bp ¬ MSRetrieve.NextBodyPart[msInfo.msH]; ih.type ¬ msInfo.lastBodyPartType ¬ MapMSBodyPartType[bp.type]; SELECT ih.type FROM MailBasicsItemTypes.nsTextFile => IF UserProfile.Boolean["XNSMail.nsTextFileAsFile", FALSE] THEN DoAttachment["NSTextFile", [3] ] ELSE { <> tempBodies: ROPE; [tempBodies, , ] ¬ GetNSTextFile[]; currentItem ¬ IO.RIS[tempBodies]; ih.type ¬ msInfo.lastBodyPartType ¬ MailBasicsItemTypes.multinationalNote; ih.length ¬ tempBodies.Length[]; }; MailBasicsItemTypes.vpDocument => DoAttachment["VPDocument", MailBasicsFileTypes.vpDocFileType]; MailBasicsItemTypes.vpFolder => DoAttachment["VPFolder", [3] ]; MailBasicsItemTypes.otherNSFile => DoAttachment["OtherNSFile", [3] ]; MailBasicsItemTypes.interpress => DoAttachment["Interpress", [3] ]; MailBasicsItemTypes.postscript => DoAttachment["Postscript", [3] ]; ENDCASE => [currentItem, ih.length] ¬ MapMSItemContents[bp, msInfo]; <<[currentItem, ih.length] _ MapMSItemContents[bp, msInfo];>> }; <<>> MSGetItem: PROC [ref: REF] RETURNS [STREAM] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; RETURN[MSRetrieve.GetBodyPart[msInfo.msH]]; }; <<>> MSGetItemAsRope: PROC [ref: REF] RETURNS [rp: ROPE] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; rp ¬ MSRetrieve.GetBodyPartAsRope[msInfo.msH]; IF ( msInfo.lastBodyPartType = MailBasicsItemTypes.multinationalNote ) AND ( msInfo.bodyAnnotSize # 0 ) THEN { ln: INT ¬ msInfo.bodyAnnotSize; msInfo.bodyAnnotSize ¬ 0; IF ln < rp.Length[] THEN RETURN[rp.Substr[ln]] ELSE RETURN[rp]; } ELSE RETURN[rp]; }; <<>> MSGetItemViaCallback: PROC [ref: REF, proc: MailRetrieve.GetItemCallback] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; CheckForAbort: PROC RETURNS [BOOL] ~ { RETURN[FALSE]; }; msInfo: MSInfo = NARROW[ref]; MSRetrieve.GetBodyPartViaCallback[msInfo.msH, proc]; }; <<>> MSMarkMessage: PROC [ref: REF] RETURNS [] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; MSRetrieve.MarkMessage[msInfo.msH]; }; MSDeleteMessage: PROC [ref: REF] RETURNS [] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; MSRetrieve.DeleteMessage[msInfo.msH]; }; MSAccept: PROC [ref: REF] RETURNS [] ~ { <> ENABLE MSRetrieve.Failed => ERROR MailRetrieve.Failed[FromMSFailureReason[why], text]; msInfo: MSInfo = NARROW[ref]; MSRetrieve.Accept[msInfo.msH]; }; <> RopeFromMSPostmark: PROC [env: MSBasics.Envelope] RETURNS [id: ROPE] = { RETURN [MailUtils.GeneratePostmark[gmt: BasicTime.FromNSTime[env.postmark.time], machine: MSUtils.RopeFromMSRName[env.postmark.postedAt]] ]; }; Intelligible: PROC[timeStamp: MailBasics.Timestamp, sender: ROPE] RETURNS[ROPE] = { nameCount: INT ¬ 0; timeBuf.length ¬ 0; FOR i: INT IN [0..sender.Length[]) DO c: CHAR = sender.Fetch[i]; IF ~(Ascii.Letter[c] OR Ascii.Digit[c]) THEN LOOP; timeBuf ¬ RefText.AppendChar[timeBuf, c]; IF (nameCount ¬ nameCount + 1) >= 8 THEN EXIT; ENDLOOP; timeBuf ¬ RefText.AppendChar[timeBuf, '-]; -- after name timeBuf ¬ Convert.AppendTimeRFC822[timeBuf, MailUtils.GetTimeFromPostmark[timeStamp]]; FOR i: NAT IN [0..timeBuf.length) DO SELECT timeBuf[i] FROM ' , '\t => timeBuf[i] ¬ '-; ': => timeBuf[i] ¬ '.; ENDCASE; ENDLOOP; RETURN[Rope.FromRefText[timeBuf]]; }; timeBuf: REF TEXT ¬ NEW[TEXT[RefText.line]]; <> <> highXNS: MSBasics.BodyPartType = 499; <> <> MapMSBodyPartType: PROC [type: MSBasics.BodyPartType] RETURNS [mapped: MailBasics.ItemType] ~ { SELECT type FROM MSBasics.lastBodyPart => mapped ¬ MailBasicsItemTypes.lastItem; -- last item; < highXNS => mapped ¬ type; -- direct map ENDCASE => mapped ¬ highXNS; -- hopefully won't see these for awhile }; <<>> <> <<>> MapMSItemContents: PROC [bp: MSBasics.BodyPartInfo, msInfo: MSInfo] RETURNS [item: STREAM ¬ NIL, length: INT] ~ { <> SELECT bp.type FROM MSMessage.headingBodyPart => { -- convert X.400 header to text header, plainText, formatting: ROPE; item ¬ MSRetrieve.GetBodyPart[msInfo.msH]; [header, plainText, formatting, msInfo.bodyAnnotSize] ¬ MSMessage.GetHeaderRope[item]; length ¬ Rope.Length[header]; item ¬ IO.RIS[header]; IF plainText # NIL AND ( tiogaTextOK ) THEN { msInfo.plainTextFromHeader ¬ plainText; msInfo.formattingFromHeader ¬ formatting; }; }; ENDCASE => { -- return body part as is length ¬ bp.sizeInBytes; item ¬ NIL; -- get it from server if requested }; }; <<>> <> <<>> BuildMSEnvelopeItem: PROC [envelope: MSBasics.Envelope] RETURNS [item: STREAM, length: INT] ~ { rope: ROPE ¬ MSUtils.RopeFromEnvelope[envelope]; item ¬ IO.RIS[rope]; length ¬ Rope.Length[rope]; }; <> FromMSMboxState: PROC [msState: MSRetrieve.MboxState] RETURNS [state: MBXState] ~ { state ¬ VAL[ORD[msState]]; }; FromMSServerState: PROC [msState: MSRetrieve.ServerState] RETURNS [state: ServerState] ~ { state ¬ VAL[ORD[msState]]; }; FromMSFailureReason: PROC [msWhy: MSRetrieve.FailureReason] RETURNS [why: FailureReason] ~ { why ¬ SELECT msWhy FROM $RPC => $communicationFailure, $Protocol => $unknownFailure, $Service => $connectionRejected, $Authentication => $badCredentials, ENDCASE => $unknownFailure; }; <> ChangeReportFromMS: MSRetrieve.ChangeReportProc ~ { <<[state: MboxState, handle: Handle]>> FOR mL: LIST OF MSInfo ¬ msInfoList, mL.rest UNTIL mL=NIL DO IF mL.first.msH = handle THEN mL.first.reportChanges[mL.first.handle, FromMSMboxState[state] ]; ENDLOOP; }; InitProcs: PROC = { mailRetrieveProcs: MailRetrieve.MailRetrieveProcsRef ¬ NEW[MailRetrieve.MailRetrieveProcs ¬ [ which: $xns, Close: MSClose, NewUser: MSNewUser, MailboxState: MSMailboxState, NextServer: MSNextServer, ServerName: MSServerName, UserName: MSUserName, NextMessage: MSNextMessage, StartMessage: MSStartMessage, NextItem: MSNextItem, GetItem: MSGetItem, GetItemAsRope: MSGetItemAsRope, GetItemViaCallback: MSGetItemViaCallback, MarkMessage: MSMarkMessage, DeleteMessage: MSDeleteMessage, Accept: MSAccept ]]; MailRetrieve.RegisterMailRetrieveProcs[mailRetrieveProcs]; }; InitProcs[]; END.