/* ** ** Gopherexx v0.3 ** ** ** RFC 1436 ** The Internet Gopher Protocol ** (a distributed document search and retrieval protocol) ** ** ** Requriments ** ** rxsocket.library ** rmh.library ** rexxsupport.library ** rxs in c: ( from the rxsocket package ) ** ** installation ** ** copy gopherd.rexx to rexx: ** ** add the following to miamidx/amitcp inetd ** ** service stream protocol wait user server name args ** ------------------------------------------------------------------------------- ** ** gopher stream tcp nowait root c:rxs gopher rexx:gopherd.rexx ** ** add the following to miamidx/amitcp services ** ** temp name id protocol aliaes ** ------------------------------------------------------------------------------- ** gopher 70 tcp ** ** ** The format of the .names file follows the normal gopher selector format ** seperated by TAB( Ascii value 09 ) ** ** Type User_Name Tab Selector Tab Host Tab Port ** ** example. ** ** 1floodgap / gopher.floodgap.com 70 ** ** Currently the .names file are used for off site linking only, as the ** GopherD, is building the Selector string it self for any files or dirs ** it finds in its data directory. ** ** If your really really wants to define all selector your self. you can ** just edit the .cache file, hardy a ideal way of doing it, but until ** rexx gopherd grows smarts it`s the only way. ** ** ** ** ** todo: ** ** add alternativ selector nameing taken for the comments field ** Make the return_scan_dir function parse the .names file first ** and only build the selector strings for those files that the ** .names file don`t define. ** ** ** ** ** history: ** v0.1 first simple version ** ** v0.2 code cleanup and simplyfing ** ** v0.3 simple .names funktion ** ** ** ** ** ** ** ** email: Rachael_@gmx.net ** ** ** (C) 2001 Jacob Dahl Pind aka Rachael/Copy`n`Paste Tech. ** */ /* config space */ /* Path that shall be shared */ path_data="ram:" /* ** The Hostname/ip of the machine on which this is running ** And which port to run on ** */ hostname="192.168.1.3" hostport=70 /* ** End of config space */ /* */ fin="D0A"x /* */ tab="09"x /* ** selector types definition */ item_file = 0 ; item_dir = 1 ; item_cso = 2 ; item_error = 3 ; item_binhex = 4 ; item_dosbin = 5 ; item_uu = 6 ; item_index = 7 ; item_telnet = 8 ; item_bin = 9 /* ** ** max size of incomming request ** */ maxblock=16384 /* ** ** Load the needet shared libraries it they ain`t available in memory ** */ If ~show('L','rxsocket.library') then DO call addlib("rxsocket.library",0,-30,0) end If ~show('L','rexxsupport.library') then DO call addlib("rexxsupport.library",0,-30,0) end If ~show('L','rmh.library') then DO call addlib("rmh.library",0,-30,0) end /* ** main */ main: /* Get the socket from InetD */ socket=LastSocket() call IOCtlSocket(socket,"FIONBIO",1) /* ** Make sure we was called from InetD and not from cli ** */ IF socket < 0 THEN DO call err "No Socket found" exit END /* ** recive request */ if recv(socket,"BUF",maxblock)<0 then call err "error receiving" call return_call(buf) exit /* ** ** parse and handle the incomming request ** */ return_call: parse arg type SELECT WHEN type=FIN THEN call return_scan_dir(path_data) OTHERWISE call type_check(buf) END return /* ** ** type check to see if the object is a director or file ** */ type_check: parse arg object /* here we strip the trailing / and */ res=STATEF(path_data||Left(Right(object,(Length(object)-1)),Length(object)-3)) type=word(res,1) SELECT WHEN type="DIR" THEN call return_scan_dir(object) OTHERWISE CALL return_binary(object) END return /* ** ** Send file to client ** */ return_binary: parse arg file file=path_data||Left(Right(file,(Length(file)-1)),Length(file)-3) open(filehandler,file,R) DO WHILE ~eof(filehandler) anserw=ReadCH(filehandler,512) if anserw~="" THEN send(socket,anserw) END return /* ** Scan directory, build .cache file if there is none, and send list ** back to the client */ return_scan_dir: parse arg dir /* ** check if we are entering a subdir and change the dir string so that it matches */ add_path="" IF right(dir,2) = FIN THEN DO add_path=Left(Right(object,(Length(object)-1)),Length(object)-3)||"/" dir=path_data||add_path END /* ** check if there is a .cache file */ IF ~exists(dir||".cache") THEN DO call build_cachefile() return END open(cachefile,dir||".cache",R) DO WHILE ~eof(cachefile) anserw=ReadLN(cachefile) /* ** ** we have to appent to the line we just read from the .cache file ** as ReadLN strip those off ** */ if send(socket,anserw||FIN)<0 then call err "error sending" END RETURN built_cachefile: /* ** List directory and save it using a random name ** so that we can handle multiple access to the system, ** there is not real use for it, as after the first access ** all data has been writen to the .cache file and all ** request are anserwer by sending that .cache back */ ran=randu(Time('S')) /* ** scanning dir in sorted form and leaveout any . files */ Address command 'c:list 'dir'~(.#?) TO t:'||ran||' LFORMAT "%S %b" SORT N' /* ** Opening .cache file for writing ** the reason for opening it now is, this way the .cache will never show up in the ** directory list, so we dont have to worry about it ** */ open(cachefile,dir||".cache",W) SIGNAL ON IOERR Open('FLIST','T:'||ran||'',R) Do while 1 L=Readln('FLIST') if l='' then leave Parse var L Filename size /* ** parse the directory list, and build the selector string accordingly ** */ SELECT WHEN size = "Dir" THEN anserw=item_dir||filename||TAB||"/"||add_path||filename||TAB||hostname||TAB||hostport||FIN OTHERWISE anserw=item_file||filename||TAB||"/"||add_path||filename||TAB||hostname||TAB||hostport||FIN END /* write selector the string to the .cache file */ WriteCH(cachefile,anserw) /* send selector string to the client to client */ if send(socket,anserw)<0 then call err "error sending" End /* ** ** ** */ IF exists(dir||".names") THEN call parse_namesfile() /* end transmision with . */ call return_endtransfer() /* Close .cache file and the temporay file used for directory scanning */ Close(cachefile) Close(FLIST) /* delete the temporay file */ Address command 'c:delete t:'||ran||' >NIL:' Return /* ** ** parse the .names file for additional selectors and add those to the ** .cache file ** */ parse_namesfile: open(namesfile,dir||".names",R) DO WHILE ~eof(namesfile) anserw=ReadLN(namesfile)||FIN WriteCH(cachefile,anserw) if send(socket,anserw||FIN)<0 then call err "error sending" close(namesfile) RETURN /* ** ** Every transfer is ended with the transmision of . ** It is only used durring the first parse of the dir, when ** there is no .cache file. After that the . is includet ** in the .cache file ** */ return_endtransfer: anserw="."||FIN WriteCH(cachefile,anserw) if send(socket,anserw)<0 then call err "error sending" return /* error handling procedure */ err: Procedure Expose socket parse arg msg If IsLibOn('SOCKET') Then If errno() == 4 Then msg = 'timeout' Say 'error :' msg Exit . .