-- Name: Socket interface for IRCBOT (now orx-irc) -- Written by Moritz Hoffmann 2006, part of the orx-irc project -- Released under the BSD license -- TODO: -- -Clean the code up ::CLASS socket_init ::METHOD init CLASS rc = RxFuncAdd("SockLoadFuncs","rxsock","SockLoadFuncs") rc = SockLoadFuncs(0) rc = SockInit() ::CLASS socket PUBLIC ::method init expose host connected use arg host connected = .false ::method uninit self~disconnect ::method host UNGUARDED expose host return host ::method connect expose host socket connected -- this method connects to the server socket = SockSocket("AF_INET","SOCK_STREAM","0") -- Don't change this! !temp.!family = "AF_INET" !temp.!port = host~port !temp.!addr = host~ip -- say 'Trying to connect to server, timeout is 75s.' ret = SockConnect(socket,"!temp.!") if ret \= 0 then return ret -- say '_Errno:' SockSock_Errno() 'errno' errno 'h_errno' h_errno '!!!!' -- Say 'Connected to server '||host~ip||' on socket '||socket -- Say 'Setting IO...' ret = Sockioctl(socket,'FIONBIO',1) if ret = -1 & errno \= 'EWOULDBLOCK' then do Say 'Error Connecting...' connected = .false return -1 end connected = .true return 0 ::METHOD connected ATTRIBUTE -- Disconnects the socket ::Method disconnect unguarded expose socket connected if connected = .true then rc = SockClose(socket) else rc = 0 connected = .false Return rc -- Readable: returns true if there's data available to be read. ::Method Readable UNGUARDED expose socket connected -- if connected \= .true then return .false r.0=1;r.1=socket w.0=1;w.1=socket e.0=1;e.1=socket rc = SockSelect('r.','w.','e.',10) -- ret = charout(,((r.1 = socket)&(rc>0))||' '||rc r.1'='socket' ') if rc < 1 then do self~disconnect return .false end return (r.1 = socket)&(rc>0) -- reads input from the socket. the length may be given. default is 512. ::Method read expose Socket connected readq if connected \= .true then return -1 if datatype(arg(1)) = 'NUM' then len = arg(1) else len = 512 input = '' ret = SockRecv(socket,"recvstr",len) if ret > 0 then input = left(recvstr,ret) if ret = -1 & errno \= 'EWOULDBLOCK' then do Say 'Error while reading from socket...' errno say SockSock_Errno() sockPSock_Errno() errno h_errno self~disconnect return '' end return input -- writes a string on the socket ::Method Write UNGUARDED expose socket connected if connected \= .true then return -1 parse arg StringToSend ret = 0 -- say 'Writing "'StringToSend'" on Socket #'Socket ret = SockSend(socket,stringtosend) if ret = -1 then do say SockSock_Errno() sockPSock_Errno() errno h_errno if rc < 1 then do self~disconnect return -1 end end return ret ::CLASS lnsocket PUBLIC SUBCLASS socket -- Method Init accepts 2 parameters: A Host object and a client object. -- The client object needs to have a public method named AVAILABLE. -- It's called with the line (string) as parameter. -- The client also needs to have a DISCONNECT method to indicate disconnects. ::method init expose client use arg host, client forward class (super) array (host) ::method connect result = self~connect:super reply result self~readserv ::Method Readserv PRIVATE unguarded expose connected line_available client reply input = '' do while self~connected if self~readable then input = input||self~read do until input~pos('0d0a'x) = 0 parse var input line '0d0a'x input if input~length = 0 then do line = '' input = '' end else do if line~strip~length > 0 then do client~available(line) end end end Call SysSleep .25 end return ::class host public ::method sethost expose ip name port use arg iparg parse var iparg local.host':'local.port parse var local.host num.1'.'num.2'.'num.3'.'num.4 IsIP = .true do i = 1 to 4 IsIP = IsIP & datatype(num.i,'Number') end -- IsIP will be .true if all of the num elements are numbers. If IsIP is -- false then we have to look for the IP number. if IsIP is .true the user supplied an -- IP number. if \ IsIP then do -- test if the string was a hostname -- Say 'Trying to resolve '||local.host||'. This may take some time.' rc = SockGetHostByName(local.host,'host.') if rc <> 0 then do -- say 'Resolved '||host.name||' to '||host.addr||'. RC was:'||rc -- do i = 1 to host.alias.0 -- say 'Aliases: '||host.alias.i -- end -- do i = 1 to host.addr.0 -- say 'Addresses: '||host.addr.i -- end ip = host.addr end else return -1 end else ip = local.host if (space(local.port) = '') & \Datatype(local.port,'Number'), then port = 6667 else port = local.port name = local.host return 0 ::method ip expose ip return ip ::method port expose port return port ::method name expose name return name