2
// This file is part of the KVirc irc client distribution
3
// Copyright (C) 1999-2000 Szymon Stefanek (stefanek@tin.it)
5
// This program is FREE software. You can redistribute it and/or
6
// modify it under the terms of the GNU General Public License
7
// as published by the Free Software Foundation; either version 2
8
// of the License, or (at your opinion) any later version.
10
// This program is distributed in the HOPE that it will be USEFUL,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
// See the GNU General Public License for more details.
15
// You should have received a copy of the GNU General Public License
16
// along with this program. If not, write to the Free Software Foundation,
17
// Inc. ,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
#define _KVI_DEBUG_CHECK_RANGE_
22
#include "kvi_scriptsocket.h"
23
#include "kvi_error.h"
24
#include "kvi_netutils.h"
25
#include "kvi_malloc.h"
26
#include "kvi_debug.h"
27
#include "kvi_memmove.h"
29
#include <sys/socket.h>
40
<a href="class_object.kvihelp">object</a>
42
!fn: $setDomain(<domain>)
43
Set the domain of the socket: valid values for <domain> are:<br>
44
inet and unix. (inet is the default for new socket objects)<br>
47
Returns the name of the domain of the socket.
49
!fn: $setType(<type>)
50
Set the type of the socket: valid values for <type> are:<br>
51
stream and raw. (stream is the default for new socket objects)<br>
54
Returns the type of the socket
56
!fn: $setProtocol(<protocol>)
57
Sets the protocol of the socket .<br>
58
The <protocol> argument is the protocol name to be matched
59
in the /etc/protocols file (see man getprotobyname :).<br>
60
(The default protocol is "ip" : that should work in most cases).<br>
63
Returns the protocol of the socket
66
Returns the last error string
68
!fn: $setMode(<data_mode>)
69
Sets the data mode of the socket.<br>
70
The valid modes are:<br>
71
- Ascii : for text based protocols (pop3 , irc...) , messages separated by combinations of CR and LF characters<br>
72
- Hex : hexadecimal endoded , for binary protocols. (see <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)
75
Returns the data mode of the socket.
77
!fn: $write(<string>)
78
Writes data to the socket and returns the length of the written buffer (that may also be zero!).<br>
79
If a serious error occures , -1 is returned and the OnDisconnect event is triggered.<br>
80
Please note that this function does NOT implicitly write a trailing CR or LF:
81
you must add it by yourself (All text based protocols require a messages terminator).<br>
82
Please note that this function can be called either in Ascii or Hex mode.<br>
84
!fn: $writeHex(<hexstring>)
85
Writes data (transformed to binary form) to the socket and returns the length of the written buffer (that may also be zero!).<br>
86
If an error occures , the OnDisconnect event is fired and -1 is returned.<br>
87
The hexstring is a string of pairs of hexadecimal digits that encode the bytes to be sent.<br>
88
This is useful when you want send null or unprintable characters.<br>
89
For example $writeHex(68656C6C6F00) writes a null terminated "hello" string to the socket.<br>
90
There is no other way to write the null character.<br>
91
(see also <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)<br>
92
Please note that this function can be called either in Ascii or Hex mode.<br>
95
Closes the connection. The OnDisconnect event is NOT fired.<br>
98
Returns the state of the socket : ready , connecting , listening or connected.<br>
100
!fn: $connect(<ipaddress>,<port>)
101
Attempts a connection to the specified host on a specified port.<br>
102
This function returns 1 if the creation of the socket and the "connect" system call
103
have been succesfull. Please note that this does NOT mean that you're connected:
104
the connection attempt has been only started.<br>
105
From that moment you can receive two events:<br>
106
OnConnectFailed , if the connection fails for some reason (for example the specified
107
host is unreachable) or OnConnect when the connection has been estabilished.<br>
108
If this function returns 0 you can obtain the error string by calling $lastError().<br>
110
!fn: $listen(<local_interface_address>,<port>)
111
Starts listening for incoming connectons on the specified port.<br>
112
<local_interface_address> can be a valid ip address of a local
113
network interface or the string "any" (or "default") that will cause to listen
114
on all available interfaces.<br>
115
This function returns 1 if the socket creation and the system "listen" call
116
have been succesfull. Please nothe that this does NOT mean that you're connected
117
to some host: the listening has just been started.<br>
118
When a connection arrives the "OnIncomingConnection" event will be triggered.<br>
119
If this function returns 0 you can obtain the error string by calling $lastError().<br>
120
Please note that not all ports are available to be "listened":
121
especially , if you're not the root user , you may have no access to ports below 1024.<br>
122
This function may fail also if some other "entity" is listening on the specified port.<br>
123
Passing 0 as the <port> argument causes the kernel to select the first available port for you,
124
(....at least on Linux).<br>
127
Returns the ip address of the currently connected remote host
130
Returns the remote end port of the current connection
132
!fn: $accept(<listening_socket_id>)
133
This function accepts an incoming connection that is pending on the specified listening socket.<br>
134
Returns 1 on success , 0 otherwise.<br>
135
Please note that usually the <listening_socket_id> is NOT *this* socket.<br>
136
Commonly , you will create a listening socket and override its OnIncomingConnection event.<br>
137
There you will create a new socket object to serve the incoming connection and call its
138
$accept member function passing the listening socket id.<br>
139
See the OnIncomingConnection example.<br>
140
The most common failure causes are:<br>
141
- The <listening_socket_id> is not an object id at all (the object can't be found)<br>
142
- The <listening_socket_id> object id does not refer to a class that inherits socket<br>
143
- The <listening_socket_id> socket has no pending incoming connections.<br>
144
You can retrieve the failure reason by calling $lastError().<br>
145
After a succesfull call of this function , the caller socket is ready to communicate:<br>
146
You can retrieve the remote host and port data with the $host and $port functions.<br>
147
The OnConnect event is not triggered (not useful).<br>
150
Returns the IP address of the local host.<br>
151
This identifier returns valid values only when the socket object is in connected state.<br>
154
!ev: OnConnect($1 = HostIp,$2 = Port)
155
Triggered just after a succesfull connection has been estabilished
156
The default implementation of this event echoes "Connected to $1 on port $2" to the active window
158
!ev: OnIncomingConnection()
159
Triggered by a listening socket when a connection request arrives.<br>
160
To accept the incoming connection you should create a new socket (or derived class)
161
object and call accept(<a href="s_this.kvihelp">$this</a>)<br>
163
event(OnIncomingConnection)
165
%sock = <a href="s_new.kvihelp">$new</a>(socket,<a href="s_this.kvihelp">$this</a>,incoming)
166
if(!%sock->$accept(<a href="s_this.kvihelp">$this</a>))
168
# Ops...something went really wrong
169
<a href="echo.kvihelp">echo</a> Accept failed : %sock->$lastError()
170
<a href="destroy.kvihelp">destroy</a> %sock;
174
If you do not accept in this way the incoming connection in this event,
175
it will be automatically discarded (the socket will be closed and the remote end will
176
see some error such as "Connection reset by peer").<br>
177
You can also accept the incoming connection with <a href="s_this.kvihelp">$this</a> socket
178
(the listening one) , but then you will obviously stop listening.<br>
180
event(OnIncomingConnection)
182
if(!<a href="s_this.kvihelp">$this</a>->$accept(<a href="s_this.kvihelp">$this</a>))
184
# Ops...something went really wrong
185
<a href="echo.kvihelp">echo</a> Accept failed : <a href="s_this.kvihelp">$this</a>->$lastError()
186
# Still listening
190
If $accept succeeds in this case , <a href="s_this.kvihelp">$this</a> socket stops listening
191
and starts the communication on the accepted connection.<br>
192
If $accept fails , <a href="s_this.kvihelp">$this</a> socket will be still listening.
193
The default implementation of this event does nothing
195
!ev: OnConnectFailed($1- = Error String)
196
Triggered when a connection attempt fails
197
The default implementation of this event echoes "Connect failed : $this->$lastError()" to the active window
199
!ev: OnDisconnect($1- = Error String)
200
Triggered when the socket gets disconnected for some user-independent reason.<br>
201
It may be caused by a transmission error or the remote host may simply close the connection
202
after a timeout...<br>
203
The default implementation of this event echoes "Disconnected from $this->$host() : $this->$lastError()" to the active window
205
!ev: OnDataReceived($1 = Data length,$2- = Data String)
206
The data string is a "normal" text line if the socket is in the Ascii mode ,
207
otherwise it is a sequence of hexadecimal-encoded bytes.<br>
208
In Ascii mode you will be loosing the leading and trailing whitespace,
209
and the unprintable characters. Also , in Ascii mode this event will NOT fire until
210
a CR or LF has been readed from the socket (to signal the end of a line).<br>
211
The $1 argument is the length of the Data string argument (in characters).<br>
212
In Hex mode the $1 argument is the length of the Data string argument (in characters);
213
since each character is encoded by two hexadecimal digits , so <a href="s_calc.kvihelp">$calc</a>($1 / 2)
214
will be the effective length in bytes of the received buffer.<br>
215
In hex mode this event will be triggered whenever any data arrives.<br>
216
If you need to receive such data , use the socket in Hex mode.<br>
217
(see <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)<br>
218
This event has a default event handler (in case that you don't want to bother yourself with
219
the <a href="class.kvihelp">CLASS</a> command) that "echoes" the received data to the active window.<br>
220
The body of the event is: "echo -w=$activewindow SOCKET $this : Received data :$2-".<br>
221
Obviously you can override it, as usual, with <a href="obj_setevent.kvihelp">obj_setevent</a>
222
or in a subclass definition.<br>
225
A socket device object.<br>
226
Theoretically it can work in the Unix and Inet domain
227
but actually only the Inet (default) domain has been tested.<br>
228
Theoretically it can work as Stream or Raw socket but
229
actually only the Stream (default) socket implementation has been tested.<br>
230
Theoretically it can work over any valid protocol but by now
231
only "ip" (0 - default) has been tested.
234
Simple code to check for new messages in a FIXED mailbox.<br>
235
The connection is made in Ascii mode since unless messages are transmitted
236
the pop3 protocol is text based: we will loose no data.<br>
237
You can easily optimize this code and convert it to an alias
238
that may accept the username, password and the mailserver as parameters.<br>
239
This version also wants the real IP address of the mailserver , not the hostname.<br>
240
Adding the DNS extension is left to you as exercise :).<br>
242
# Create a socket object
243
%o=<a href="s_new.kvihelp">$new</a>(socket,<a href="s_root.kvihelp">$root</a>,mysock);
244
# OnConnect event : Once connected , send USER , PASS and LIST
245
# You will probably want to set a different username and password :)
246
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o,OnConnect)
248
<a href="echo.kvihelp">echo</a> CHECKMAIL : Connected to the mailserver...
249
<a href="s_this.kvihelp">$this</a>->$write("USER place_here_your_username<a href="s_cr.kvihelp">$cr</a><a href="s_lf.kvihelp">$lf</a>")
250
<a href="s_this.kvihelp">$this</a>->$write("PASS place_here_your_password<a href="s_cr.kvihelp">$cr</a><a href="s_lf.kvihelp">$lf</a>")
251
<a href="s_this.kvihelp">$this</a>->$write("LIST<a href="s_cr.kvihelp">$cr</a><a href="s_lf.kvihelp">$lf</a>")
253
# When we get disconnected for some reason kill the object
254
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o,OnDisconnect)
256
<a href="echo.kvihelp">echo</a> CHECKMAIL : Disconnected ($1-)
257
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
259
# Data received ...here's the clue
260
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o,OnDataReceived)
262
<a href="switch.kvihelp">switch</a>($2-)
264
<a href="switch.kvihelp">match</a>("+OK * messages*")
265
{
266
<a href="echo.kvihelp">echo</a> CHECKMAIL : The mailbox contains <a href="s_strrightfromfirst.kvihelp">$strRightFromFirst</a>("+OK ",$1-)
267
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
268
}
269
<a href="switch.kvihelp">match</a>("-ERR *")
270
{
271
<a href="echo.kvihelp">echo</a> CHECKMAIL : Received error : $2-
272
<a href="echo.kvihelp">echo</a> CHECKMAIL : Aborting
273
<a href="destroye.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
274
}
277
# If the connection fails for some reason , kill the object
278
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o,OnConnectFailed)
280
<a href="echo.kvihelp">echo</a> CHECKMAIL : Connect failed : ($1-)
281
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
283
# Ok...ready to go... connect to our mailserver
284
# You will probably want to change the ip address :)
285
<a href="if.kvihelp">if</a>(!%o->$connect(127.0.0.1,110)){ <a href="echo.kvihelp">echo</a> Mail : Connection failed : %o->$lastError(); <a href="destroy.kvihelp">destroy</a> %o; }
286
<a href="if.kvihelp">else</a> <a href="echo.kvihelp">echo</a> Checking mail on 127.0.0.1....
287
# And now just wait and watch...
290
<a href="syntax_objects.kvihelp">Objects documentation</a>
293
KviScriptSocket::KviScriptSocket(KviScriptObjectController * cntrl,KviScriptObject * p,const char *name,KviScriptObjectClassDefinition * pDef)
294
: KviScriptObject(cntrl,p,name,pDef)
298
m_type = SOCK_STREAM;
300
m_error = KVI_ERROR_Success;
307
m_incomingConnectionSock = -1;
308
m_szLocalHostIp = "127.0.0.1";
311
KviScriptSocket::~KviScriptSocket()
316
void KviScriptSocket::initializeClassDefinition(KviScriptObjectClassDefinition *d)
318
d->addBuiltinFunction("mode",(scriptObjectFunction)&KviScriptSocket::builtinFunction_MODE);
319
d->addBuiltinFunction("type",(scriptObjectFunction)&KviScriptSocket::builtinFunction_TYPE);
320
d->addBuiltinFunction("setDomain",(scriptObjectFunction)&KviScriptSocket::builtinFunction_SETDOMAIN);
321
d->addBuiltinFunction("setType",(scriptObjectFunction)&KviScriptSocket::builtinFunction_SETTYPE);
322
d->addBuiltinFunction("setMode",(scriptObjectFunction)&KviScriptSocket::builtinFunction_SETMODE);
323
d->addBuiltinFunction("setProtocol",(scriptObjectFunction)&KviScriptSocket::builtinFunction_SETPROTOCOL);
324
d->addBuiltinFunction("write",(scriptObjectFunction)&KviScriptSocket::builtinFunction_WRITE);
325
d->addBuiltinFunction("writeHex",(scriptObjectFunction)&KviScriptSocket::builtinFunction_WRITEHEX);
326
d->addBuiltinFunction("close",(scriptObjectFunction)&KviScriptSocket::builtinFunction_CLOSE);
327
d->addBuiltinFunction("protocol",(scriptObjectFunction)&KviScriptSocket::builtinFunction_PROTOCOL);
328
d->addBuiltinFunction("host",(scriptObjectFunction)&KviScriptSocket::builtinFunction_HOST);
329
d->addBuiltinFunction("port",(scriptObjectFunction)&KviScriptSocket::builtinFunction_PORT);
330
d->addBuiltinFunction("state",(scriptObjectFunction)&KviScriptSocket::builtinFunction_STATE);
331
d->addBuiltinFunction("domain",(scriptObjectFunction)&KviScriptSocket::builtinFunction_DOMAIN);
332
d->addBuiltinFunction("connect",(scriptObjectFunction)&KviScriptSocket::builtinFunction_CONNECT);
333
d->addBuiltinFunction("listen",(scriptObjectFunction)&KviScriptSocket::builtinFunction_LISTEN);
334
d->addBuiltinFunction("lastError",(scriptObjectFunction)&KviScriptSocket::builtinFunction_LASTERROR);
335
d->addBuiltinFunction("accept",(scriptObjectFunction)&KviScriptSocket::builtinFunction_ACCEPT);
336
d->addBuiltinFunction("localhost",(scriptObjectFunction)&KviScriptSocket::builtinFunction_LOCALHOST);
338
KviScriptEventStruct * s = new KviScriptEventStruct;
339
s->szName = "OnDataReceived";
340
s->szBuffer = "echo -w=$activewindow SOCKET $this : Received data :$2-";
341
d->addDefaultEvent(s);
342
s = new KviScriptEventStruct;
343
s->szName = "OnDisconnect";
344
s->szBuffer = "echo -w=$activewindow SOCKET $this : Disconnected from $this->$host() : $this->$lastError()";
345
d->addDefaultEvent(s);
346
s = new KviScriptEventStruct;
347
s->szName = "OnConnect";
348
s->szBuffer = "echo -w=$activewindow SOCKET $this : Connected to $1 on port $2";
349
d->addDefaultEvent(s);
350
s = new KviScriptEventStruct;
351
s->szName = "OnConnectFailed";
352
s->szBuffer = "echo -w=$activewindow SOCKET $this : Connect failed : $this->$lastError()";
353
d->addDefaultEvent(s);
356
int KviScriptSocket::builtinFunction_MODE(QList<KviStr> * params,KviStr &buffer)
358
if(m_mode == Ascii)buffer.append("ascii");
359
else buffer.append("hex");
360
return KVI_ERROR_Success;
363
int KviScriptSocket::builtinFunction_LOCALHOST(QList<KviStr> * params,KviStr &buffer)
365
buffer.append(m_szLocalHostIp);
366
return KVI_ERROR_Success;
369
int KviScriptSocket::builtinFunction_SETDOMAIN(QList<KviStr> * params,KviStr &buffer)
373
KviStr *pDomain = params->first();
376
if(kvi_strEqualCI(pDomain->ptr(),"inet"))m_domain = PF_INET;
377
else if(kvi_strEqualCI(pDomain->ptr(),"unix"))m_domain = PF_UNIX;
378
else return KVI_ERROR_InvalidParameter;
379
return KVI_ERROR_Success;
382
return KVI_ERROR_MissingParameter;
385
int KviScriptSocket::builtinFunction_SETTYPE(QList<KviStr> * params,KviStr &buffer)
389
KviStr *pType = params->first();
392
if(kvi_strEqualCI(pType->ptr(),"stream"))m_type = SOCK_STREAM;
393
else if(kvi_strEqualCI(pType->ptr(),"raw"))m_type = SOCK_RAW;
394
else return KVI_ERROR_InvalidParameter;
395
return KVI_ERROR_Success;
398
return KVI_ERROR_MissingParameter;
401
int KviScriptSocket::builtinFunction_SETMODE(QList<KviStr> * params,KviStr &buffer)
405
KviStr *pType = params->first();
408
if(kvi_strEqualCI(pType->ptr(),"ascii"))
414
if(m_pReadBuffer)kvi_free(m_pReadBuffer);
417
} else if(kvi_strEqualCI(pType->ptr(),"hex"))
423
if(m_pReadBuffer)kvi_free(m_pReadBuffer);
427
else return KVI_ERROR_InvalidParameter;
428
return KVI_ERROR_Success;
431
return KVI_ERROR_MissingParameter;
434
int KviScriptSocket::builtinFunction_SETPROTOCOL(QList<KviStr> * params,KviStr &buffer)
438
KviStr *pProto = params->first();
441
struct protoent * p = getprotobyname(pProto->ptr());
442
if(p)m_protocol = p->p_proto;
443
else return KVI_ERROR_InvalidParameter;
444
return KVI_ERROR_Success;
447
return KVI_ERROR_MissingParameter;
450
int KviScriptSocket::builtinFunction_WRITE(QList<KviStr> * params,KviStr &buffer)
454
KviStr *pData = params->first();
457
KviStr tmp(KviStr::Format,"%d",writeData(pData->ptr(),pData->len()));
458
buffer.append(tmp.ptr());
459
return KVI_ERROR_Success;
462
return KVI_ERROR_MissingParameter;
466
int KviScriptSocket::builtinFunction_WRITEHEX(QList<KviStr> * params,KviStr &buffer)
470
KviStr *pData = params->first();
474
int len = pData->hexToBuffer(&buf);
475
KviStr tmp(KviStr::Format,"%d",writeData(buf,len));
476
if(buf)kvi_free(buf);
477
buffer.append(tmp.ptr());
478
return KVI_ERROR_Success;
481
return KVI_ERROR_MissingParameter;
484
int KviScriptSocket::builtinFunction_CLOSE(QList<KviStr> * params,KviStr &buffer)
486
if(m_sock == -1)buffer.append('0');
491
return KVI_ERROR_Success;
494
int KviScriptSocket::builtinFunction_PROTOCOL(QList<KviStr> * params,KviStr &buffer)
496
struct protoent * p = getprotobynumber(m_protocol);
497
if(p)buffer.append(p->p_name);
500
num.setNum(m_protocol);
501
buffer.append(num.ptr());
503
return KVI_ERROR_Success;
506
int KviScriptSocket::builtinFunction_TYPE(QList<KviStr> * params,KviStr &buffer)
508
if(m_type == SOCK_STREAM)buffer.append("stream");
509
else buffer.append("raw");
510
return KVI_ERROR_Success;
513
int KviScriptSocket::builtinFunction_HOST(QList<KviStr> * params,KviStr &buffer)
515
if(m_state == Connected)buffer.append(m_szHostIp);
516
return KVI_ERROR_Success;
519
int KviScriptSocket::builtinFunction_PORT(QList<KviStr> * params,KviStr &buffer)
521
if(m_state == Connected)buffer.append(m_szPort);
522
return KVI_ERROR_Success;
525
int KviScriptSocket::builtinFunction_STATE(QList<KviStr> * params,KviStr &buffer)
527
if(m_state == Connected)buffer.append("connected");
528
else if(m_state == Connecting)buffer.append("connecting");
529
else if(m_state == Listening)buffer.append("listening");
530
else buffer.append("ready");
531
return KVI_ERROR_Success;
534
int KviScriptSocket::builtinFunction_DOMAIN(QList<KviStr> * params,KviStr &buffer)
536
if(m_domain == PF_INET)buffer.append("inet");
537
else buffer.append("unix");
538
return KVI_ERROR_Success;
542
int KviScriptSocket::builtinFunction_CONNECT(QList<KviStr> * params,KviStr &buffer)
546
KviStr *pHost = params->first();
549
KviStr *pPort = params->next();
552
buffer.append(doConnect(pHost,pPort) ? '1' : '0');
553
return KVI_ERROR_Success;
557
return KVI_ERROR_MissingParameter;
560
int KviScriptSocket::builtinFunction_LISTEN(QList<KviStr> * params,KviStr &buffer)
564
KviStr *pHost = params->first();
567
KviStr *pPort = params->next();
570
buffer.append(doListen(pHost,pPort) ? '1' : '0');
571
return KVI_ERROR_Success;
575
return KVI_ERROR_MissingParameter;
578
int KviScriptSocket::builtinFunction_LASTERROR(QList<KviStr> * params,KviStr &buffer)
580
buffer.append(kvi_getErrorString(m_error));
581
return KVI_ERROR_Success;
584
int KviScriptSocket::builtinFunction_ACCEPT(QList<KviStr> * params,KviStr &buffer)
588
KviStr *pObId = params->first();
591
buffer.append(doAccept(pObId->ptr()) ? '1' : '0');
592
return KVI_ERROR_Success;
595
return KVI_ERROR_MissingParameter;
599
bool KviScriptSocket::doListen(KviStr *szLocalAddr,KviStr *szPort)
601
struct in_addr inAddress;
602
struct sockaddr_in hostSockAddr;
604
if(m_sock != -1){ m_error = KVI_ERROR_AnotherConnectionInProgress; return false; }
606
if(!kvi_stringIpToBinaryIp(szLocalAddr->ptr(),&inAddress))
608
if(kvi_strEqualCI("default",szLocalAddr->ptr()) || kvi_strEqualCI("any",szLocalAddr->ptr()))hostSockAddr.sin_addr.s_addr = INADDR_ANY;
609
else { m_error = KVI_ERROR_InvalidIpAddress; return false; }
610
} else hostSockAddr.sin_addr = inAddress;
613
unsigned short int port = szPort->toUInt(&bOk);
614
if(!bOk){ m_error = KVI_ERROR_InvalidPort; return false; }
616
hostSockAddr.sin_family = m_domain; //AF_INET == PF_INET && AF_UNIX == PF_UNIX
617
hostSockAddr.sin_port = htons(port);
619
m_sock = ::socket(m_domain,m_type,m_protocol);
621
if(m_sock < 0){ m_error = KVI_ERROR_SocketCreationFailed; return false; }
623
if(fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0){ m_error = KVI_ERROR_AsyncSocketFailed; return false; }
626
if((theError = bind(m_sock,(struct sockaddr *)&hostSockAddr,sizeof(hostSockAddr))) < 0)
627
{ setErrorFromSystemError(theError); reset(); return false; }
628
if((theError = listen(m_sock,1)) < 0)
629
{ setErrorFromSystemError(theError); reset(); return false; }
631
// and setup the READ notifier...
632
m_pTmpSn = new QSocketNotifier(m_sock,QSocketNotifier::Read);
634
QObject::connect(m_pTmpSn,SIGNAL(activated(int)),this,SLOT(listenNotifierFired(int)));
636
m_pTmpSn->setEnabled(true);
640
m_error = KVI_ERROR_Success;
646
void KviScriptSocket::listenNotifierFired(int)
648
struct sockaddr_in connectedAddr;
650
_this_should_be_socklen_t iSize = sizeof(connectedAddr);
651
m_incomingConnectionSock = accept(m_sock,(struct sockaddr*)&connectedAddr,&iSize);
652
if(m_incomingConnectionSock == -1)debug("warning : accept returns -1...waiting for the next accept call");
655
m_szIncomingConnectionPort.setNum(ntohs(connectedAddr.sin_port));
656
kvi_binaryIpToString(connectedAddr.sin_addr,m_szIncomingConnectionHostIp);
659
triggerEvent("OnIncomingConnection",parms);
661
if(m_incomingConnectionSock != -1)
664
close(m_incomingConnectionSock);
665
m_incomingConnectionSock = -1;
666
m_szIncomingConnectionPort = "";
667
m_szIncomingConnectionHostIp = "";
672
bool KviScriptSocket::doAccept(const char *sockObjectId)
674
KviScriptObject * o = (KviScriptObject *)controller()->findObjectById(sockObjectId);
676
if(!o){ m_error = KVI_ERROR_ObjectNotFound; return false; }
677
if(!o->inherits("KviScriptSocket")){ m_error = KVI_ERROR_ObjectIsNotASocket; return false; }
679
KviScriptSocket * s = (KviScriptSocket *)o;
681
if(!s>hasPendingConnection()){ m_error = KVI_ERROR_NoConnectionToAccept; return false; }
685
reset(); // if o == this it will work!...reset() will not clear the incomingConnectionSock
688
m_sock = s->m_incomingConnectionSock;
689
s->m_incomingConnectionSock = -1;
690
m_szHostIp = s->m_szIncomingConnectionHostIp;
691
s->m_szIncomingConnectionHostIp = "";
692
m_szPort = s->m_szIncomingConnectionPort;
693
s->m_szIncomingConnectionPort = "";
695
m_error = KVI_ERROR_Success;
697
m_pRsn = new QSocketNotifier(m_sock,QSocketNotifier::Read);
698
QObject::connect(m_pRsn,SIGNAL(activated(int)),this,SLOT(receivedData(int)));
699
m_pRsn->setEnabled(true);
706
void KviScriptSocket::setLocalHostIp()
708
if(m_state == Connected)
710
struct sockaddr_in name;
711
_this_should_be_socklen_t len = sizeof(name);
712
if(getsockname(m_sock, (struct sockaddr *)&name,&len) >= 0)
714
if(kvi_binaryIpToString(name.sin_addr,m_szLocalHostIp))return;
717
m_szLocalHostIp = "127.0.0.1";
720
bool KviScriptSocket::doConnect(KviStr * szHost,KviStr *szPort)
722
struct in_addr inAddress;
723
struct sockaddr_in hostSockAddr;
725
if(m_sock != -1){ m_error = KVI_ERROR_AnotherConnectionInProgress; return false; }
727
if(!kvi_stringIpToBinaryIp(szHost->ptr(),&inAddress)){ m_error = KVI_ERROR_InvalidIpAddress; return false; }
730
unsigned short int port = szPort->toUInt(&bOk);
731
if(!bOk){ m_error = KVI_ERROR_InvalidPort; return false; }
733
hostSockAddr.sin_family = m_domain; //AF_INET == PF_INET && AF_UNIX == PF_UNIX
734
hostSockAddr.sin_port = htons(port);
735
hostSockAddr.sin_addr = inAddress;
738
m_sock = ::socket(m_domain,m_type,m_protocol);
740
if(m_sock < 0){ m_error = KVI_ERROR_SocketCreationFailed; return false; }
742
if(fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0){ m_error = KVI_ERROR_AsyncSocketFailed; return false; }
744
if(::connect(m_sock,(struct sockaddr*)(&hostSockAddr),sizeof(hostSockAddr))<0){
745
if(errno != EINPROGRESS){
748
_this_should_be_socklen_t iSize=sizeof(int);
749
if(getsockopt(m_sock,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize)<0)sockError=0;
751
if(sockError > 0)setErrorFromSystemError(sockError);
752
else m_error = KVI_ERROR_UnknownError; //Error 0 ?
757
// and setup the WRITE notifier...
758
m_pTmpSn = new QSocketNotifier(m_sock,QSocketNotifier::Write);
760
QObject::connect(m_pTmpSn,SIGNAL(activated(int)),this,SLOT(writeNotifierFired(int)));
762
m_pTmpSn->setEnabled(true);
764
m_szHostIp = szHost->ptr();
765
m_szPort = szPort->ptr();
766
m_error = KVI_ERROR_Success;
767
m_state = Connecting;
771
void KviScriptSocket::writeNotifierFired(int)
774
_this_should_be_socklen_t iSize=sizeof(int);
775
if(getsockopt(m_sock,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize)<0)sockError = -1;
778
if(sockError > 0)setErrorFromSystemError(sockError);
779
else m_error = KVI_ERROR_UnknownError; //Error 0 ?
780
KviStr parms = kvi_getErrorString(m_error);
781
triggerEvent("OnConnectFailed",parms);
784
//Succesfully connected...
785
m_pRsn = new QSocketNotifier(m_sock,QSocketNotifier::Read);
786
// normal irc connection
787
KviStr parms(KviStr::Format,"%s %s",m_szHostIp.ptr(),m_szPort.ptr());
788
triggerEvent("OnConnect",parms);
789
if(m_state != Connecting)return; //Killed ?
791
QObject::connect(m_pRsn,SIGNAL(activated(int)),this,SLOT(receivedData(int)));
792
m_pRsn->setEnabled(true);
801
void KviScriptSocket::reset()
812
kvi_free(m_pReadBuffer);
829
void KviScriptSocket::setErrorFromSystemError(int errorNum)
832
case EBADF: m_error = KVI_ERROR_BadFileDescriptor; break;
833
case EFAULT: m_error = KVI_ERROR_OutOfAddressSpace; break;
834
case ECONNREFUSED: m_error = KVI_ERROR_ConnectionRefused; break;
835
case ENOTSOCK: m_error = KVI_ERROR_KernelNetworkingPanic; break;
836
case ETIMEDOUT: m_error = KVI_ERROR_ConnectionTimedOut; break;
837
case ENETUNREACH: m_error = KVI_ERROR_NetworkUnreachable; break;
838
case EPIPE: m_error = KVI_ERROR_BrokenPipe; break;
839
// Unhandled error...pass errno to the strerror function
840
default: m_error = -errorNum; break;
844
void KviScriptSocket::handleInvalidSocketRead(int readedLength)
847
m_error = KVI_ERROR_RemoteEndClosedConnection;
848
KviStr parms = kvi_getErrorString(m_error);
849
triggerEvent("OnDisconnect",parms);
852
//check for transmission errors
853
if((errno != EAGAIN) && (errno != EINTR)){
854
if(errno > 0)setErrorFromSystemError(errno);
855
else m_error = KVI_ERROR_RemoteEndClosedConnection;
856
KviStr parms = kvi_getErrorString(m_error);
857
triggerEvent("OnDisconnect",parms);
859
} //else transient error...wait again...
863
void KviScriptSocket::receivedData(int fd)
868
int readLength = read(m_sock,buffer,1024);
871
handleInvalidSocketRead(readLength);
878
KviStr tmp(KviStr::Format,"%d ",readLength * 2);
879
parms.bufferToHex(buffer,readLength);
880
parms.prepend(tmp.ptr());
881
m_pRsn->setEnabled(false);
882
triggerEvent("OnDataReceived",parms);
883
// check if we were closed() by the meantins
884
if(m_state != Connected)return; //closed
885
m_pRsn->setEnabled(true);
887
//terminate our buffer
888
(*(buffer+readLength))='\0';
890
register char *p=buffer;
891
char *beginOfCurData = buffer;
893
char *messageBuffer = (char *)kvi_malloc(1);
894
//Shut up the socket notifier
895
//in case that we enter in a local loop somewhere
896
//while processing data...
898
m_pRsn->setEnabled(false);
901
if((*p == '\r' )||(*p == '\n')){
902
//found a CR or LF...
903
//prepare a message buffer
904
bufLen = p - beginOfCurData;
905
//check for previous unterminated data
908
if(m_iReadBufLen > 0){
910
__range_valid(m_pReadBuffer);
911
messageBuffer = (char *)kvi_realloc(messageBuffer,bufLen + m_iReadBufLen + 1);
912
kvi_memmove(messageBuffer,m_pReadBuffer,m_iReadBufLen);
913
kvi_memmove((void *)(messageBuffer + m_iReadBufLen),beginOfCurData,bufLen);
914
*(messageBuffer + bufLen + m_iReadBufLen) = '\0';
915
kvi_free(m_pReadBuffer);
918
parms.sprintf("%d %s",bufLen + m_iReadBufLen,messageBuffer);
922
__range_invalid(m_pReadBuffer);
923
messageBuffer = (char *)kvi_realloc(messageBuffer,bufLen + 1);
924
kvi_memmove(messageBuffer,beginOfCurData,bufLen);
925
*(messageBuffer + bufLen) = '\0';
926
parms.sprintf("%d %s",bufLen,messageBuffer);
929
triggerEvent("OnDataReceived",parms);
930
// check if we were closed() by the meantins
931
if(m_state != Connected)
933
kvi_free(messageBuffer);
937
while(*p && ((*p=='\r')||(*p=='\n')) )p++;
941
if(inDelayedDestroy())
943
// already a zombie! (destroyed while processing data!)
944
kvi_free(messageBuffer);
949
//beginOfCurData points to '\0' if we have
950
//no more stuff to parse , or points to something
951
//different than '\r' or '\n'...
953
//Have remaining data...in the local buffer
954
bufLen = p - beginOfCurData;
955
if(m_iReadBufLen > 0){
956
//and there was more stuff saved... (really slow connection)
957
__range_valid(m_pReadBuffer);
958
m_pReadBuffer =(char *)kvi_realloc(m_pReadBuffer,m_iReadBufLen + bufLen);
959
kvi_memmove((void *)(m_pReadBuffer+m_iReadBufLen),beginOfCurData,bufLen);
960
m_iReadBufLen += bufLen;
963
__range_invalid(m_pReadBuffer);
964
m_iReadBufLen = bufLen;
965
m_pReadBuffer =(char *)kvi_malloc(m_iReadBufLen);
966
kvi_memmove(m_pReadBuffer,beginOfCurData,m_iReadBufLen);
970
kvi_free(messageBuffer);
972
m_pRsn->setEnabled(true);
977
int KviScriptSocket::writeData(char * buffer,int len)
981
m_error = KVI_ERROR_NotConnected;
985
int result = write(m_sock,buffer,len);
986
if(result >= len)return len;
989
if((errno == EAGAIN)||(errno == EINTR))return 0;
991
// Disconnected... :(
992
setErrorFromSystemError(errno);
993
KviStr parms = kvi_getErrorString(m_error);
994
triggerEvent("OnDisconnect",parms);
1000
#include "m_kvi_scriptsocket.moc"