-- Transport Mechanism - fetches MTP distribution lists --

-- [Juniper]<DMS>MS>DLFetcher.mesa

-- Randy Gobbel		19-May-81 13:28:57 --
-- Mike Schroeder	September 19, 1980  12:08 PM --
-- Andrew Birrell	30-Sep-80 16:45:56 --

DIRECTORY
Ascii,
BodyDefs	USING[ maxRNameLength, RName, RNameSize ],
FTPDefs		USING[CommunicationError, FileHandle, FileInfo,
		      FilePrimitives, FilePrimitivesObject, FileType,
		      FileError, FileSystem, FTPCreateUser, FTPDestroyUser,
		      FTPError, FTPOpenConnection, FTPRetrieveFile, FTPUser,
		      Mode, ProtocolError, PupCommunicationPrimitives,
		      UnidentifiedError ],
HeapDefs	USING[ HeapAbandonWrite, HeapEndWrite, HeapStartRead,
		       HeapStartWrite, HeapWriteData, HeapWriteRName,
		       ObjectNumber, objectStart, ReaderHandle,
		       SetWriterOffset, WriterHandle ],
PupDefs		USING[ AppendPupAddress, PupAddress ],
PupTypes	USING[ mailSoc ],
ServerDefs	USING[ NoSuchServer, ServerAddr, ServerNotUp ],
SiteCacheDefs	USING[ RecipientInfo ],
String		USING[ AppendChar, AppendString, StringBoundsFault ];

DLFetcher: PROGRAM
IMPORTS BodyDefs, FTPDefs, HeapDefs, PupDefs, ServerDefs, String
EXPORTS SiteCacheDefs =

BEGIN

OPEN Ascii, FTPDefs, String;

FetchDL: PROCEDURE[dlName: BodyDefs.RName,
                   host: STRING,
                   owner: BodyDefs.RName]
	   RETURNS[dl: SiteCacheDefs.RecipientInfo] = BEGIN

--variables--

	ftpUser: FTPUser;
	myFilePrimitivesObject: FilePrimitivesObject;
	writer: HeapDefs.WriterHandle;
	reader: HeapDefs.ReaderHandle;

	SyntaxError: SIGNAL = CODE;

-- my file system --

	MyCreateFileSystem: PROCEDURE [bufferSize: CARDINAL]
			RETURNS [fileSystem: FileSystem] = {RETURN[NIL]};

	MyDestroyFileSystem: PROCEDURE [fileSystem: FileSystem] = {};

	MyCloseFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle,
			aborted: BOOLEAN] = {};

	MyOpenFile: PROCEDURE [fileSystem: FileSystem, file: STRING, mode: Mode,
			fileTypePlease: BOOLEAN, info: FileInfo] RETURNS [fileHandle: FileHandle,
			fileType: FileType] = BEGIN
		IF info.author # NIL AND info.author.length # 0
		AND owner # NIL
			THEN AppendString[owner, info.author ! StringBoundsFault => CONTINUE];
		RETURN[NIL, unknown];
		END;

	MyWriteFile: PROCEDURE [fileSystem: FileSystem, fileHandle: FileHandle,
			receiveBlock: PROC [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL], 
			receiveBlockData: UNSPECIFIED] = BEGIN
		colonSeen, semicolonSeen, boundFault: BOOLEAN ← FALSE;
		position, bytes: CARDINAL;
		name: BodyDefs.RName = [BodyDefs.maxRNameLength];
		bufferLength: CARDINAL = 100;
		buffer: PACKED ARRAY [0..2*bufferLength) OF CHARACTER;
		rListLength: CARDINAL ← 0;
		NextChar: PROCEDURE RETURNS [ch: CHARACTER] =
			BEGIN
				IF bytes = 0
					THEN ch ← NUL
					ELSE BEGIN
						ch ← buffer[position];
						position ← position + 1;
						IF position = bytes THEN BEGIN
							bytes ← receiveBlock[receiveBlockData,@buffer,bufferLength];
							position ← 0
							END;
						END;
			END; --NextChar--
		position ← 0;
		bytes ← receiveBlock[receiveBlockData,@buffer,bufferLength];
		DO
			c: CHARACTER;
			name.length ← 0;
			DO
				SELECT (c ← NextChar[]) FROM
					NUL, ', => IF boundFault THEN SIGNAL SyntaxError ELSE EXIT;
					'; => IF (NOT colonSeen) OR semicolonSeen
							THEN SIGNAL SyntaxError
							ELSE {semicolonSeen ← TRUE; EXIT};
					': => IF colonSeen
							THEN SIGNAL SyntaxError
							ELSE {boundFault ← FALSE; name.length ← 0; colonSeen ← TRUE};
					SP, TAB, CR => NULL;
					'( => UNTIL ([] ← NextChar[]) = ') DO ENDLOOP;
					ENDCASE => AppendChar[name, c ! StringBoundsFault =>
									{name.length ← 0; boundFault ← TRUE; CONTINUE}];
			ENDLOOP;
		IF name.length = 0
			THEN BEGIN
				IF c # NUL THEN LOOP; 
				HeapDefs.SetWriterOffset[writer, HeapDefs.objectStart];
				SetRListLength[rListLength];
				EXIT -- done
				END
			ELSE BEGIN --process name
				rListLength ← rListLength + BodyDefs.RNameSize[name];
				HeapDefs.HeapWriteRName[writer, name];
				END;
		ENDLOOP;
		END; -- of MyWriteFile --

	SaveReader: PROCEDURE[obj: HeapDefs.ObjectNumber] =
		{reader ← HeapDefs.HeapStartRead[obj]};

	SetRListLength: PROCEDURE[l: CARDINAL] = INLINE
		{HeapDefs.HeapWriteData[writer, [@l, SIZE[CARDINAL]]];};

	myFilePrimitivesObject.CreateFileSystem ← MyCreateFileSystem;
	myFilePrimitivesObject.OpenFile ← MyOpenFile;
	myFilePrimitivesObject.WriteFile ← MyWriteFile;
	myFilePrimitivesObject.CloseFile ← MyCloseFile;
	myFilePrimitivesObject.DestroyFileSystem ← MyDestroyFileSystem;
	ftpUser ← FTPCreateUser[@myFilePrimitivesObject, PupCommunicationPrimitives[]];
	BEGIN -- for exits --
		FTPOpenConnection[ftpUser, host, files, NIL !
			FTPError =>  SELECT ftpError FROM
				-- noSuchHost can't happen --
				IN CommunicationError, IN ProtocolError, IN UnidentifiedError =>
					{dl ← [allDown[]]; GOTO done;};
				ENDCASE => NULL
			];
		writer ← HeapDefs.HeapStartWrite[temp];
		SetRListLength[0];
		[] ← FTPRetrieveFile[ftpUser, NIL, dlName, unknown !
			SyntaxError => BEGIN
				HeapDefs.HeapAbandonWrite[writer];
				dl ← [notFound[]]; GOTO done;
				END;
			FTPError => BEGIN
				HeapDefs.HeapAbandonWrite[writer];
				SELECT ftpError FROM
					illegalFilename, noSuchFile => {dl ← [notFound[]]; GOTO done;};
					IN CommunicationError, IN FileError, IN ProtocolError,
						IN UnidentifiedError => {dl ← [allDown[]]; GOTO done;};
					ENDCASE => NULL;
				END ];
		HeapDefs.HeapEndWrite[writer, SaveReader];
		dl ← [foreign[reader]];
		IF owner.length # 0 THEN FOR i:CARDINAL IN [0..dlName.length) DO
			IF dlName[i] = '↑ THEN BEGIN
				 FOR j:CARDINAL IN [i+1..dlName.length) DO
					AppendChar[owner, dlName[j]];
					ENDLOOP;
					EXIT;
				END;
			REPEAT FINISHED => owner.length ← 0;
			ENDLOOP;
		EXITS
			done => NULL;
		END;
	FTPDestroyUser[ftpUser];
	END;

ForeignDL: PUBLIC PROC[ who: BodyDefs.RName,
                        oldInfo: SiteCacheDefs.RecipientInfo ]
               RETURNS[ newInfo: SiteCacheDefs.RecipientInfo ] =
   BEGIN
   -- If "who" is "thing↑.where", oldInfo is info for "where.foreign" --
   WITH old: oldInfo SELECT FROM
     found =>
       BEGIN
       addr: PupDefs.PupAddress ← ServerDefs.ServerAddr[old.server !
          ServerDefs.NoSuchServer => {newInfo ← [notFound[]]; GOTO noAddr };
          ServerDefs.ServerNotUp => {newInfo ← [allDown[]]; GOTO noAddr } ];
       host: STRING = [11] --377#377#377--;
       author: BodyDefs.RName ← [BodyDefs.maxRNameLength];
       addr.socket ← PupTypes.mailSoc;
       PupDefs.AppendPupAddress[host, addr];
       newInfo ← FetchDL[who, host, author];
       EXITS noAddr => NULL;
       END;
   ENDCASE => newInfo ← oldInfo;
   END;

END.