10 SUB GETFNA %TITLE "GETFNA" %IDENT "X0.15" ! http get filename from URI ! (c) 2012 by Johnny Billquist ! ! X0.0 2012-04-12 Johnny Billquist ! Initial coding ! ! X0.1 2012-06-05 Johnny Billquist ! Added MCR:, DCL: and CLI: as alternatives to CGI: ! ! X0.2 2012-08-16 Johnny Billquist ! Implemented redirects. ! Implement nested files for PUBLIC.LNK ! ! X0.3 2014-05-25 Johnny Billquist ! Implement changing default directory ! Implement wildcard file pattern matching ! ! X0.4 2014-06-17 Johnny Billquist ! Cleanup of code. ! ! X0.5 2014-11-28 Johnny Billquist ! Fix bug that if no matching patter is found in PUBLIC.TXT ! it erroneously returned the last target. ! Also general cleanup of code. ! ! X0.6 2016-03-16 Johnny Billquist ! Bugfix. If URL lead to a new file, and the globbing ! of the URM left us with an empty string, we got ! an error in the end. ! ! X0.7 2016-03-17 Johnny Billquist ! If no PUBLIC.TXT is found, use HTTPD$ROOT:DEFAULT.TXT ! Raise privileges when accessing CONTENTS.TXT ! ! X0.8 2016-03-20 Johnny Billquist ! Add handling of bad device (err 133). This is ! probably a configuration error, so give some hint ! on console, in addition to generating a proper ! error response. ! ! X0.9 2016-03-29 Johnny Billquist ! Cleanup code for handling err 5. ! ! X0.10 2017-03-10 Johnny Billquist ! When wildcard matching is done on URMs, ! we should not allow slashes in the part ! matching the wildcard. ! ! X0.11 4-Aug-2017 Johnny Billquist ! Changed to use BQTLIB ACNT functions. ! Added checking for user lookup actually successful. ! ! X0.12 9-Sep-2018 Johnny Billquist ! Added virtual host capabilities. ! ! X0.13 24-Sep-2019 Johnny Billquist ! When matching wildcard patterns, do not allow ! intermediate slashes in the wildcard matched part. ! ! X0.14 5-Sep-2022 Johnny Billquist ! Added $ as terminator in matching from PUBLIC.TXT ! as a way of indicating that something terminating ! with a wildcard should not allow slashes. ! ! X0.15 9-Sep-2022 Johnny Billquist ! Make URM and matches case insensitive by making ! them all uppercase. ! This module parses the URI requested, and sets up a number ! of values for further processing. ! ! F.CHUNK: If the response should be chunked or not. ! F.NOFILE: If no file should be output. ! F.CGI: True if CGI result. ! FNAM: The filename to use in processing. ! DIR: Default directory to use. ! CCMD: CLI to use. ! CONTENT: Content header string. ! UIC: UIC of user to run as. ! STATVAL: The status result for the reply. ! STATTXT: The status text for the reply. ! EXTRA: Extra headers for the reply. ! BODY: Extra body content for the reply. %INCLUDE "WWW.INC" %INCLUDE "LB:[1,1]BQTLIB.B2S" DECLARE STRING LN,TGT,EXT,USER,URM,MATCH,MATCHL,MATCHR,MATCHF DECLARE WORD I,J,X,F.EXT,LL,LR,LU,FULL,WILD,VALID,HIT,TERM DECLARE WORD F.PUBLIC DECLARE STRING OFIL DECLARE STRING CONSTANT VALIDCHR="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789." ON ERROR GOTO Hell F.EXT=0 DIR=PATH UIC=DEFUIC URM=TRM$(URI) ! If we have a host, check if that should result in ! some changed action... GOTO No_Vhost IF HOST="" F.PUBLIC=2 OFIL = "VHOST.TXT" OPEN OFIL FOR INPUT AS FILE TMPFIL, ACCESS READ, & ALLOW READ, ORGANIZATION SEQUENTIAL VARIABLE, & DEFAULTNAME DIR Vloop: WHILE 1 LINPUT #TMPFIL,LN LN = EDIT$(LN,4+8+16+128) I = INSTR(1,LN," ") ITERATE Vloop IF I=0 IF LEFT$(LN,I-1) = HOST THEN TGT = RIGHT$(LN,I+1) IF LEFT$(TGT,2)="->" THEN CLOSE #TMPFIL STATVAL=301 STATTXT="Use different host" EXTRA="Location: http://"+RIGHT$(TGT,3)+URM F.CHUNK=1 F.NOFILE=1 EXIT SUB END IF DIR = TGT EXIT Vloop END IF NEXT End_Vloop: CLOSE #TMPFIL No_Vhost: F.PUBLIC = 0 ! Parse out username, if it exists, and the things that ! follow from that. IF (LEFT$(URM,2)) = "/~" THEN I = INSTR(2,URM,"/") IF I=0 THEN STATVAL=301 STATTXT="Users should end with a slash" EXTRA="Location: "+URM+"/" F.CHUNK=1 F.NOFILE=1 EXIT SUB END IF USER = SEG$(URM,3,I-1) URM = RIGHT$(URM,I) I = ACNT(USER) IF I>0 THEN STATVAL=404 STATTXT="User "+USER+" does not exist." FNAM=PATH+"404.HTM" BODY="User "+USER+" does not exist." GOTO Content END IF UIC = G.UIC DIR = G.HDIR END IF ! Trim off parameters from path I = INSTR(1,URM,"?") URM = LEFT$(URM,I-1) IF I > 0 URM = EDIT$(URM,32) LU = LEN(URM) ! Try to find the URM in PUBLIC.TXT to find the matching ! filename to use. TGT="" EXT="" CALL NOPRIV(UIC) ! Become the right user, and drop privs. F.PUBLIC=1 OFIL="PUBLIC.TXT" OPEN OFIL FOR INPUT AS FILE TMPFIL, ACCESS READ, ALLOW READ, & ORGANIZATION SEQUENTIAL VARIABLE, DEFAULTNAME DIR GOTO Pub_Ok No_Public: F.PUBLIC=0 OFIL="DEFAULT.TXT" OPEN OFIL FOR INPUT AS FILE TMPFIL, ACCESS READ, ALLOW READ, & ORGANIZATION SEQUENTIAL VARIABLE, DEFAULTNAME PATH Pub_Ok: F.PUBLIC=0 Tgt_loop: WHILE 1 LINPUT #TMPFIL,LN LN = EDIT$(LN,4+8+16+128) I = INSTR(1,LN," ") ! If no space on line, it can't be a valid line. ITERATE Tgt_loop IF I=0 ! If the line don't start with slash, it's not valid either. ITERATE Tgt_loop UNLESS LEFT$(LN,1) = "/" MATCH = LEFT$(LN,I-1) TGT = RIGHT$(LN,I+1) ! We have a valid line to match against. Let's ! find wildcard if we have one. HIT = 0 FULL = 0 TERM = 0 WILD = INSTR(1,MATCH,"*") IF WILD=0 THEN MATCHL = MATCH MATCHR = "" ELSE MATCHL = LEFT$(MATCH,WILD-1) MATCHR = RIGHT$(MATCH,WILD+1) END IF IF MATCHR="$" THEN TERM=1 MATCHR="" END IF MATCHL = EDIT$(MATCHL,32) MATCHR = EDIT$(MATCHR,32) LL = LEN(MATCHL) LR = LEN(MATCHR) ! Time to see if we have a match... IF (WILD=0) AND (MATCHL=URM) THEN HIT = 1 FULL = 1 END IF IF (WILD>0) AND (LU>0) AND (LU>=LL) AND & (MATCHL=LEFT$(URM,LL)) AND & (MATCHR=RIGHT$(URM,LU-LR+1)) THEN ! We are matching the fixed parts... ! Check if the wildcard bits have any slash. ! We accept if no slash, or if there is no right part. IF (INSTR(1,SEG$(URM,LL+1,LU-LR), "/")=0) OR & ((LR=0) AND (TERM=0)) THEN HIT = 1 FULL = 1 END IF END IF ! Now see if the pattern match. IF HIT THEN MATCHF = "" MATCHF = SEG$(URM,LL+1,LU-LR) IF WILD>0 ! See if we should put part of URM into target. I = INSTR(1,TGT,"*") TGT = LEFT$(TGT,I-1)+MATCHF+RIGHT$(TGT,I+1) IF I>0 ! Now check if it is a redirect... IF LEFT$(TGT,2)="->" THEN CLOSE #TMPFIL STATVAL=301 STATTXT="Target tree moved" EXTRA="Location: "+RIGHT$(TGT,3) F.CHUNK=1 F.NOFILE=1 EXIT SUB END IF ! Check if we should continue in another file. IF LEFT$(TGT,1)="@" THEN CLOSE #TMPFIL URM = RIGHT$(URM,LL+1) URM = "/"+URM UNLESS LEFT$(URM,1)="/" LU = LEN(URM) TGT = RIGHT$(TGT,2) DIR = TGT IF INSTR(1,TGT,"[") > 0 OFIL=TGT OPEN TGT FOR INPUT AS FILE TMPFIL, & ACCESS READ, & ALLOW READ, & ORGANIZATION SEQUENTIAL VARIABLE, & DEFAULTNAME DIR ITERATE Tgt_Loop END IF ! If it's a complete match, we're done... EXIT Tgt_Loop IF FULL END IF NEXT TGT = EDIT$(TGT,4+8+16+128) End_Map: CLOSE #TMPFIL TGT = EDIT$(TGT,32) VALID=-1 IF WILD THEN ! We have a wildcard populated filename. Check that the ! wildcarding didn't introduce something nasty... MATCHF = EDIT$(MATCHF,32) FOR I=1 TO LEN(MATCHF) VALID=0 IF INSTR(1,VALIDCHR,MID$(MATCHF,I,1)) = 0 NEXT I END IF IF VALID=0 THEN FNAM=PATH+"404.HTM" STATVAL=404 STATTXT="Bad filename" BODY="Bad filename: "+TGT GOTO Content END IF ! Figure out CGI stuff... SELECT LEFT$(TGT,4) CASE "CGI:","MCR:","DCL:","CLI:" FNAM=RIGHT$(TGT,5) CCMD=LEFT$(TGT,3) CCMD="MCR" IF CCMD="CGI" STATVAL=200 STATTXT="OK" F.CGI=1 EXIT SUB END SELECT IF TGT = "" THEN ! No matching URI FNAM = PATH+"404.HTM" STATVAL=404 STATTXT="URL not found" BODY="File not found.

URI: "+TRM$(URI) ELSE FNAM = TGT STATVAL=200 STATTXT="OK" END IF Content: ! Now figure out type of content for response. I = INSTR(1,FNAM,".") IF I=0 THEN EXT = "." ELSE EXT = RIGHT$(TRM$(FNAM),I) END IF F.EXT=1 CALL BEPRIV(DEFUIC) OFIL=PATH+"CONTENT.TXT" OPEN OFIL FOR INPUT AS FILE TMPFIL, & ACCESS READ, ALLOW READ Ext_Loop: WHILE 1 LINPUT #TMPFIL,LN LN = EDIT$(LN,4+8+16+128) I = INSTR(1,LN," ") ITERATE Ext_Loop IF I<1 CONTENT = RIGHT$(LN,I+1) LN = EDIT$(LEFT$(LN,I-1),32) EXIT Ext_Loop IF LN="*" OR LN=EXT NEXT End_Content: CLOSE #TMPFIL CALL NOPRIV(UIC) Ret: IF ECNT>10 THEN ON ERROR GO BACK CAUSE ERROR 14 END IF EXIT SUB Hell: RESUME End_Content IF ECNT>10 SELECT ERR CASE 11 ! End of file... RESUME End_Vloop IF F.PUBLIC=2 TGT="" ! We have no target. WILD = 0 RESUME End_Map IF F.EXT=0 RESUME End_Content CASE 5 ! Failed to open file. RESUME No_Vhost IF F.PUBLIC=2 RESUME No_Public IF F.PUBLIC=1 CALL WWWERR(500,"Config problem", "", & "Config file not found.

Filename: "+OFIL) RESUME End_Map IF F.EXT=0 RESUME End_Content CASE 133 PRINT "WWW - Failed to open file '"+OFIL+"'." PRINT "WWW - Probably a configuration error."+ & " (Check "+TRM$(PATH)+")." PRINT "WWW - The URI was "+TRM$(URI) CALL WWWERR(500,"Configuration problem", & "","Config file '"+OFIL+"' not found."+CR+LF) F.NOFILE=1 F.KAF=0 RESUME End_Content CASE ELSE ON ERROR GO BACK END SELECT END SUB