/* REXX: get http document (result incl. http headers to stdout), */ /* see also */ /* CAVEAT: do not use this script for "bulk" downloads - it does */ /* not support the ROBOTS protocol and must not be used as robot. */ /* v0.8: fresh attempt to unify RxSock interface in REXX scripts. */ /* v0.7: return string if used as function, result 1 is an error. */ /* v0.6: new TOPEN supports connections to IPs without host name. */ /* v0.5: instead of GET /path HTTP/1.0 send OPTIONS * HTTP/1.1 if */ /* the specified path is * (only a quick hack to test this) */ /* v0.4: unquote quoted argument, this allows to use "&" in URLs */ /* on the command line */ /* v0.3: use T.... procedures copied from RxIdent.CMD, support a */ /* port number as in login:sword@www.example:port/document */ /* v0.2: optional authorization@www.test instead of www.test for */ /* "Authorization:" header, use "Connection: Close" header, */ /* avoid chunked data by specifying HTTP/1.0 instead of 1.1 */ signal on novalue name TRAP ; signal on syntax name TRAP signal on failure name TRAP ; signal on halt name TRAP CRLF = x2c( 0D0A ) ; EXPO = 'TSOCK CRLF' TEXT = strip( strip( strip( arg( 1 )),, '"' )) select when arg() > 2 then exit USAGE( 'unexpected arg.s' ) when arg() = 2 then parse arg PEER , TEXT when words( TEXT ) > 2 then exit USAGE( 'unexpected arg.s' ) when words( TEXT ) = 2 then parse var TEXT PEER TEXT when words( TEXT ) = 1 then parse var TEXT PEER '/' TEXT otherwise exit USAGE() end parse var PEER AUTH '@' PEER if PEER = '' then parse var AUTH PEER '@' AUTH parse var PEER PEER ':' PORT if PORT = '' then PORT = 80 if TEXT <> '*' then if right( TEXT, 1 ) = '*' then do TEXT = left( TEXT, length( TEXT ) - 1 ) TEXT = 'OPTIONS /' || TEXT 'HTTP/1.1' end else TEXT = 'GET /' || TEXT 'HTTP/1.0' else TEXT = 'OPTIONS * HTTP/1.1' TEXT = TEXT || CRLF || 'Host:' PEER || BASIC( AUTH ) TEXT = TEXT || CRLF || 'Pragma: no-cache' TEXT = TEXT || CRLF || 'Connection: close' TEXT = TEXT || CRLF || 'User-Agent: RxGetUrl.cmd/0.8' TEXT = TEXT || CRLF || CRLF TEXT = GETIT( PEER, PORT, TEXT ) parse source . PORT . if PORT = 'COMMAND' then return charout( /**/, TEXT ) else return TEXT USAGE: procedure expose (EXPO) /* show usage or error message */ parse source . . THIS /* (sorry, only SAY implemented) */ TEXT = 'or :' THIS 'login:sword@host:port/file' TEXT = 'or :' THIS 'host/file' || CRLF || TEXT TEXT = 'usage:' THIS 'host file' || CRLF || TEXT if arg( 1, 'e' ) then TEXT = 'error:' arg( 1 ) || CRLF || TEXT say TEXT ; return 1 BASIC: procedure expose (EXPO) /* basic HTTP authorization line */ if arg( 1 ) == '' then return '' B64 = 'abcdefghijklmnopqrstuvwxyz' B64 = translate( B64 ) || B64 || '0123456789+/' SRC = x2b( c2x( arg( 1 ))) ; DST = '' ADD = ( length( SRC ) / 4 ) // 3 SRC = SRC || copies( '00', ADD ) do while SRC <> '' parse var SRC N 7 SRC ; N = x2d( b2x( N )) DST = DST || substr( B64, N + 1, 1 ) end DST = DST || copies( '=', ADD ) return CRLF || 'Authorization: Basic' DST GETIT: procedure expose (EXPO) /* TCP send query and get answer */ signal on novalue name TFAIL ; signal on syntax name TFAIL signal on failure name TFAIL ; signal on halt name TFAIL parse arg PEER, PORT, TEXT ; MORE = '' select when PEER = '' then exit USAGE( 'missing host' ) when TOPEN( PEER, PORT ) then exit 1 when TSEND( TEXT ) then exit TFAIL() otherwise do N = 0 until TEXT = '' TEXT = TREAD() ; MORE = MORE || TEXT if N = 0 then do if TEXT = '' then exit TFAIL() call TSEND end end N end call SockClose TSOCK ; return MORE /* -------------------------------------------------------------- */ /* RXsock.dll interface (TOPEN, TREAD, TSEND, TFAIL) + gen. RXMSG */ TOPEN: procedure expose (EXPO) /* TCP connect with HOST at PORT */ if RxFuncQuery( 'SockLoadFuncs' ) then do call RxFuncAdd 'SockLoadFuncs', 'RXSOCK', 'SockLoadFuncs' call SockLoadFuncs 'N' /* TRAP if RXSOCK.DLL not found */ end if datatype( value( 'TSOCK' ), 'w' ) then call SockClose TSOCK if sign( verify( arg( 1 ), '0.123456789' )) = 0 then do if SockGetHostByAddr( arg( 1 ), 'PEER.' ) = 0 then do PEER.ADDR = arg( 1 ) ; PEER.HOST = arg( 1 ) end /* support IP without host name: */ end /* SockConnect() handles bad IP */ else if SockGetHostByName( arg( 1 ), 'PEER.' ) = 0 then return RXMSG( 'unknown host' arg( 1 ) value( 'h_errno' )) PEER.PORT = arg( 2 ) ; PEER.FAMILY = 'AF_INET' TSOCK = SockSocket( PEER.FAMILY, 'SOCK_STREAM', 'IPPROTO_TCP' ) if 0 <= TSOCK then do if SockConnect( TSOCK, 'PEER.' ) = 0 then return 0 end /* 0: okay, connected with TSOCK */ return TFAIL() /* 1: error shown, socket closed */ TREAD: procedure expose (EXPO) /* TCP read line (or data block) */ READ = '' do until N < 2000 | sign( pos( x2c( 0A ), READ )) N = SockRecv( TSOCK, 'DATA', 2000 ) if N > 0 then READ = READ || left( DATA, N ) end return READ TSEND: procedure expose (EXPO) /* TCP send complete data block */ if arg( 1, 'e' ) /* 1: any error, 0: sent / close */ then return length( arg( 1 )) <> SockSend( TSOCK, arg( 1 )) else return SockShutDown( TSOCK, 1 ) <> 0 RXMSG: procedure expose (EXPO) /* show error message & return 1 */ parse source . . THIS ; signal on syntax name RXMSG.TRAP call RxMessageBox arg( 1 ), THIS, /**/, 'HAND' ; return 1 RXMSG.TRAP: call SockPSock_Errno arg( 1 ) ; return 1 TFAIL: /* close sockets and handle TRAP */ signal on novalue name TRAP ; signal on syntax name TRAP signal on failure name TRAP ; signal on halt name TRAP TRAP = RXMSG( 'socket error' SockSock_Errno() value( 'errno' )) if symbol( 'TSOCK' ) = 'VAR' then TRAP = SockClose( TSOCK ) if condition() = '' then return 1 /* drop into normal TRAP handler, 'sigl' + 'result' preserved: */ /* see , (c) F. Ellermann */ TRAP: /* select REXX exception handler */ call trace 'O' ; trace N /* don't trace interactive */ parse source TRAP /* source on separate line */ TRAP = x2c( 0D ) || right( '+++', 10 ) TRAP || x2c( 0D0A ) TRAP = TRAP || right( '+++', 10 ) /* = standard trace prefix */ TRAP = TRAP strip( condition( 'c' ) 'trap:' condition( 'd' )) select when wordpos( condition( 'c' ), 'ERROR FAILURE' ) > 0 then do if condition( 'd' ) > '' /* need an additional line */ then TRAP = TRAP || x2c( 0D0A ) || right( '+++', 10 ) TRAP = TRAP '(RC' rc || ')' /* any system error codes */ if condition( 'c' ) = 'FAILURE' then rc = -3 end when wordpos( condition( 'c' ), 'HALT SYNTAX' ) > 0 then do if condition( 'c' ) = 'HALT' then rc = 4 if condition( 'd' ) > '' & condition( 'd' ) <> rc then do if condition( 'd' ) <> errortext( rc ) then do TRAP = TRAP || x2c( 0D0A ) || right( '+++', 10 ) TRAP = TRAP errortext( rc ) end /* future condition( 'd' ) */ end /* may use errortext( rc ) */ else TRAP = TRAP errortext( rc ) rc = -rc /* rc < 0: REXX error code */ end when condition( 'c' ) = 'NOVALUE' then rc = -2 /* dubious */ when condition( 'c' ) = 'NOTREADY' then rc = -1 /* dubious */ otherwise /* force non-zero whole rc */ if datatype( value( 'RC' ), 'W' ) = 0 then rc = 1 if rc = 0 then rc = 1 if condition() = '' then TRAP = TRAP arg( 1 ) end /* direct: TRAP( message ) */ TRAP = TRAP || x2c( 0D0A ) || format( sigl, 6 ) signal on syntax name TRAP.SIGL /* throw syntax error 3... */ if 0 < sigl & sigl <= sourceline() /* if no handle for source */ then TRAP = TRAP '*-*' strip( sourceline( sigl )) else TRAP = TRAP '+++ (source line unavailable)' TRAP.SIGL: /* ...catch syntax error 3 */ if abbrev( right( TRAP, 2 + 6 ), x2c( 0D0A )) then do TRAP = TRAP '+++ (source line unreadable)' ; rc = -rc end select when 1 then do /* in pipes STDERR: output */ parse version TRAP.REXX /* REXX/Personal: \dev\con */ if abbrev( TRAP.REXX, 'REXXSAA ' ) | /**/ , 6 <= word( TRAP.REXX, 2 ) then TRAP.REXX = 'STDERR' else TRAP.REXX = '\dev\con' signal on syntax name TRAP.FAIL call lineout TRAP.REXX , TRAP /* fails if no more handle */ end when 0 then do /* OS/2 PM or ooREXX on NT */ signal on syntax name TRAP.FAIL call RxMessageBox translate( TRAP, ' ', x2c( 0D )), /**/ , 'Trap' time(),, 'ERROR' end otherwise say TRAP ; trace ?L /* interactive Label trace */ end if condition() = 'SIGNAL' then signal TRAP.EXIT TRAP.CALL: return rc /* continue after CALL ON */ TRAP.FAIL: say TRAP ; rc = 0 - rc /* force TRAP error output */ TRAP.EXIT: exit rc /* exit for any SIGNAL ON */