// IfsMailRetrieve.bcpl - IFS Mail Server Retrieve command
// Copyright Xerox Corporation 1979, 1980, 1981, 1982
// Last modified May 13, 1982 2:21 PM by Taft
get "Ifs.decl"
get "IfsDirs.decl"
get "IfsFiles.decl"
get "IfsFtpProt.decl"
get "IfsMail.decl"
external
[
// outgoing procedures
MtpSRetrieve; MtpSRetrieveMail; MtpSRetrieveCleanup
// incoming procedures from other FTP and Mail modules
FtpSCheckAccess; FtpSSendMark
FlipCursor; DiskToNet; FreePList; InitPList; MailStatDelay
// incoming procedures from IFS Dirs
IFSOpenFile; OpenIFSStream; CloseIFSStream
DestroyFD; DeleteFileFromFD; StreamsFD
// incoming procedures - miscellaneous
SysAllocateZero; SysFree; ExtractSubstring; ConcatenateStrings; RNamesEqual
SetTimer; Dismiss; TimerHasExpired; SetFilePos
Resets; Closes; Endofs; ReadBlock; TruncateDiskStream
MoveBlock; Zero
IFSError
// incoming statics
CtxRunning; mail; debugFlag
]
//----------------------------------------------------------------------------
let MtpSRetrieve(remotePL, localPL) = valof
//----------------------------------------------------------------------------
[
localPL = FreePList(localPL)
if CtxRunning>>FtpCtx.msg eq 0 then //first time for this Retrieve
[
// Check out his pList
unless FtpSCheckAccess(remotePL) resultis 0
let mailbox = remotePL>>PL.MLBX
if mailbox eq 0 resultis FtpSSendMark(markNo, ecMlbxRequired)
for i = mailbox>>String.length to 1 by -1 do
if mailbox>>String.char↑i eq $. then
[ mailbox>>String.length = i-1; break ]
unless RNamesEqual(mailbox, remotePL>>PL.UNAM) %
RNamesEqual(mailbox, remotePL>>PL.CNAM) do
resultis FtpSSendMark(markNo, ecMlbxNotUnamOrCnam)
// Is there a mailbox?
// IFSOpenFile checks access, so switch to our "Mail" identity.
let ui = CtxRunning>>FtpCtx.userInfo
CtxRunning>>FtpCtx.userInfo = mail>>Mail.ui
mailbox = ConcatenateStrings("<Mail>Box>", mailbox)
let ec, stream = 0, 0
let timer = nil; SetTimer(lv timer, openTimeout*100)
[ // repeat
stream = IFSOpenFile(mailbox, lv ec, modeReadWrite, 0, lcVHighest)
if stream ne 0 % ec ne ecFileBusy % TimerHasExpired(lv timer) break
Dismiss(100) // Busy. Wait 1 second, then try again
] repeat
CtxRunning>>FtpCtx.userInfo = ui // switch back to user's identity
SysFree(mailbox)
if stream eq 0 then
resultis FtpSSendMark(markNo,
(ec eq ecFileNotFound % ec eq ecDirNotFound? ecNoMailbox, ec))
let msg = SysAllocateZero(lenMsg)
msg>>Msg.stream = stream
CtxRunning>>FtpCtx.msg = msg
]
let msg = CtxRunning>>FtpCtx.msg
SetFilePos(msg>>Msg.stream, lv msg>>Msg.posEnd)
if Endofs(msg>>Msg.stream) resultis -1
// If the next message is damaged, then say 'no more messages',
// which will case the file to be emptied thereby healing it.
if ReadBlock(msg>>Msg.stream, lv msg>>Msg.mh, lenMH) ne lenMH then
[ if debugFlag then IFSError(ecEofInHdr, msg); resultis -1 ]
if msg>>Msg.seal ne mhSeal then
[ if debugFlag then IFSError(ecBadMsgSeal, msg); resultis -1 ]
// Set up the pList for the next message.
localPL = InitPList()
MoveBlock(lv localPL>>PL.LGTH, lv msg>>Msg.lenTxt, 2)
MoveBlock(lv localPL>>PL.RCVD, lv msg>>Msg.date, 2)
// ready to transfer message body
resultis localPL
]
//----------------------------------------------------------------------------
let MtpSRetrieveMail(nil, nil) = valof
//----------------------------------------------------------------------------
[
let msg = CtxRunning>>FtpCtx.msg
let bytes = vec 1; MoveBlock(bytes, lv msg>>Msg.lenTxt, 2)
SetFilePos(msg>>Msg.stream, lv msg>>Msg.posTxt)
let ec = DiskToNet(CtxRunning>>FtpCtx.bspStream, msg>>Msg.stream, bytes)
if ec eq 0 then
[
// time in seconds message waited in mailbox
MailStatDelay(msTypeRetr, lv msg>>Msg.date)
resultis true
]
FtpSSendMark(markNo, ec)
resultis false
]
//----------------------------------------------------------------------------
and MtpSRetrieveCleanup(remotePL, ok) = valof
//----------------------------------------------------------------------------
[
let msg = CtxRunning>>FtpCtx.msg
let stream = msg>>Msg.stream
if ok then
[ // ok to flush mail from mailbox
let fd = StreamsFD(stream)
let ui = CtxRunning>>FtpCtx.userInfo
let mbxName = ExtractSubstring(lv fd>>FD.dr>>DR.pathName,
fd>>FD.lenSubDirString+1, fd>>FD.lenBodyString-1)
test ui>>UserInfo.capabilities.mail eq 0 &
RNamesEqual(mbxName, ui>>UserInfo.userName)
ifso
[ // User no longer has mail capability; destroy mailbox.
// To avoid a race with another process attempting to store new
// mail, we exploit the feature of DeleteFileFromFD that permits
// the caller to pass in the FD for an open file.
CloseIFSStream(stream, true) // just destroy stream; FD stays open
CtxRunning>>FtpCtx.userInfo = mail>>Mail.ui
unless DeleteFileFromFD(fd, false, true) eq 0 do
IFSError(ecMlbxDelFailed)
CtxRunning>>FtpCtx.userInfo = ui
DestroyFD(fd)
stream = 0
]
ifnot
[ // Normal case: make mailbox empty
Resets(stream)
TruncateDiskStream(stream)
]
SysFree(mbxName)
]
if msg ne 0 then
[
if stream ne 0 then Closes(stream)
SysFree(msg)
CtxRunning>>FtpCtx.msg = 0
]
resultis ok
]