// IfsMailJob.bcpl -- sorts and forwards mail // Copyright Xerox Corporation 1979, 1980, 1981, 1982 // Last modified April 9, 1982 12:33 PM by Taft get "Ifs.decl" get "IfsMail.decl" get "IfsDirs.decl" get "IfsFtpProt.decl" get "AltoFileSys.d" external [ // outgoing procedures MailJob; EnumerateMailFile; EnumerateMB; ReadMB // incoming procedures from other mail modules SortNewMail; ForwardMail; StartUndelivMsg; FinishUndelivMsg // incoming procedures from IFS dirs LookupIFSFile; NextFD; DestroyFD; DeleteFileFromFD; GetBufferForFD LockTransferLeaderPage; OpenIFSStream; CloseIFSStream; CloseIFSFile // incoming procedures - misc DestroyJob; JobOK; IFSError; IFSPrintError ReadBlock; WriteBlock; Closes; Endofs; FilePos; SetFilePos DoubleIncrement; DoubleUsc; DoubleAdd; Mul SysAllocate; SysFree; ReadCalendar; Zero; PutTemplate // incoming statics mail; debugFlag ] //--------------------------------------------------------------------------- let MailJob(ctx) be // a context //--------------------------------------------------------------------------- [ ctx>>RSCtx.userInfo = mail>>Mail.ui [ // repeat mail>>Mail.workToDo = false EnumerateMailDir("<Mail>New>Mail!**", SortNewMail, minSortInterval) EnumerateMailDir("<Mail>Fwd>**!1", ForwardMail, minForwardInterval) ] repeatwhile mail>>Mail.enabled ne 0 & mail>>Mail.workToDo ne 0 & JobOK(jobTypeMail) mail>>Mail.wake = jobInterval/eventInterval mail>>Mail.ctx = 0 DestroyJob() ] //---------------------------------------------------------------------------- and EnumerateMailDir(dirName, Proc, minInterval) be //---------------------------------------------------------------------------- // Enumerates all files matching dirName, opens a stream and calls Proc. // Skips over a file if it was last checked less than minInterval seconds ago, // unless it has been written more recently than that. // If Proc returns true, the file is then deleted. [ let fd = LookupIFSFile(dirName, lcMultiple) if fd eq 0 return [ // repeat // Continue or stop? if mail>>Mail.enabled eq 0 break unless JobOK(jobTypeMail) do [ mail>>Mail.workToDo = true; break ] // We should look closely at this file if it has been written since // last read or if it hasn't been read for at least minInterval. let time = vec 1; ReadCalendar(time) DoubleIncrement(time, -minInterval) let ld = GetBufferForFD(fd) let ok = LockTransferLeaderPage(fd, ld) eq 0 & (DoubleUsc(lv ld>>LD.written, lv ld>>LD.read) gr 0 % DoubleUsc(time, lv ld>>LD.read) ge 0) SysFree(ld) unless ok loop // Open the file and see what's inside. let stream = OpenIFSStream(fd, 0, modeReadWrite) if stream ne 0 then [ let delete = Proc(stream) // Do not unlock file if we're going to delete it. CloseIFSStream(stream, delete) if delete then if DeleteFileFromFD(fd, false, true) ne 0 then CloseIFSFile(fd) ] ] repeatwhile NextFD(fd) // Done enumeration -- cleanup and go away DestroyFD(fd) ] //---------------------------------------------------------------------------- and EnumerateMailFile(stream, Proc) = valof //---------------------------------------------------------------------------- // Enumerates all msgs in the stream, calling Proc for each one. // Returns true if the file behind stream can be deleted. [ let delete = true let msg = vec lenMsg; Zero(msg, lenMsg) msg>>Msg.stream = stream [ // read next message in file SetFilePos(stream, lv msg>>Msg.posEnd) if Endofs(stream) break // if the message is damaged, skip it. if ReadBlock(stream, lv msg>>Msg.mh, lenMH) ne lenMH then [ if debugFlag then IFSError(ecEofInHdr, msg); break ] if msg>>Msg.seal ne mhSeal then [ if debugFlag then IFSError(ecBadMsgSeal, msg); break ] if msg>>Msg.numActive eq 0 loop Proc(msg) // Check long-term timeout and handle any Exception blocks. let timeout = vec 1; Mul(0, 3600, deliveryTimeout, timeout) DoubleAdd(timeout, lv msg>>Msg.date) let now = vec 1; ReadCalendar(now) EnumerateMB(msg, Undeliv, DoubleUsc(now, timeout) ge 0) // if there is one, finish off the undeliverable msg msg if msg>>Msg.uMsg then FinishUndelivMsg(msg) // rewrite message header if msg>>Msg.numActive ne 0 then delete = false SetFilePos(stream, lv msg>>Msg.posBegin) WriteBlock(stream, lv msg>>Msg.mh, lenMH) ] repeat resultis delete ] //---------------------------------------------------------------------------- and Undeliv(mb, msg, timeout) = valof //---------------------------------------------------------------------------- // Called from EnumeratMB in EnumerateMailFile. // If 'timeout' then converts all active blocks into exception blocks. // Generates an undeliverable message message for all exception blocks. [ let rewrite, type = false, mb>>MB.type if timeout & type ne mbTypeFree & type ne mbTypeSndr & type ne mbTypeExcp then [ mb>>MB.type = mbTypeExcp mb>>MNB.ec = ecDeliveryTimeout rewrite = true ] if mb>>MB.type ne mbTypeExcp resultis false if msg>>Msg.uMsg eq 0 then StartUndelivMsg(msg) if msg>>Msg.uMsg ne 0 then [ let uStream = msg>>Msg.uMsg>>Msg.stream PutTemplate(uStream, "*N$S - ", lv mb>>MNB.name) IFSPrintError(uStream, mb>>MNB.ec) mb>>MB.type = mbTypeFree rewrite = true ] resultis rewrite ] //---------------------------------------------------------------------------- and EnumerateMB(msg, Proc, arg) be //---------------------------------------------------------------------------- // Enumerates the Blocks in msg calling Proc for each one. // If Proc returns true, the block is rewritten. // Changing the length of the block is forbidden. [ let stream = msg>>Msg.stream SetFilePos(stream, lv msg>>Msg.posBlk) msg>>Msg.numActive = 0 let mb = SysAllocate(maxLenMB) [ let thisPos = vec 1; FilePos(stream, thisPos) if DoubleUsc(thisPos, lv msg>>Msg.posTxt) ge 0 break ReadMB(msg, mb) let nextPos = vec 1; FilePos(stream, nextPos) test Proc(mb, msg, arg) //rewrite block? ifnot SetFilePos(stream, nextPos) //Proc may have changed stream pos. ifso //back up and rewrite the block [ SetFilePos(stream, thisPos) WriteBlock(stream, mb, mb>>MB.length) ] unless mb>>MB.type eq mbTypeFree % mb>>MB.type eq mbTypeSndr do msg>>Msg.numActive = msg>>Msg.numActive +1 ] repeat SysFree(mb) ] //---------------------------------------------------------------------------- and ReadMB(msg, mb; numargs na) = valof //---------------------------------------------------------------------------- [ let temp = nil ReadBlock(msg>>Msg.stream, lv temp, 1) if na ls 2 % mb eq 0 then mb = SysAllocate(temp<<MB.length) mb!0 = temp if ReadBlock(msg>>Msg.stream, mb+1, mb>>MB.length-1) ne mb>>MB.length-1 then IFSError(ecEofInBlk, msg) resultis mb ]