1
// =============================================================================
3
// This file is part of the KVIrc IRC client distribution
4
// Copyright (C) 1999-2000 Szymon Stefanek (stefanek@tin.it)
6
// This program is FREE software. You can redistribute it and/or
7
// modify it under the terms of the GNU General Public License
8
// as published by the Free Software Foundation; either version 2
9
// of the License, or (at your opinion) any later version.
11
// This program is distributed in the HOPE that it will be USEFUL,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU General Public License for more details.
16
// You should have received a copy of the GNU General Public License
17
// along with this program. If not, write to the Free Software Foundation,
18
// Inc, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
// =============================================================================
22
#define _KVI_DEBUG_CHECK_RANGE_
23
#define _KVI_DEBUG_CLASS_NAME_ "KviScriptSocket"
25
#include <sys/socket.h>
26
#include <sys/types.h>
32
#include <qsocketnotifier.h>
34
#include "kvi_debug.h"
35
#include "kvi_error.h"
36
#include "kvi_malloc.h"
37
#include "kvi_memmove.h"
38
#include "kvi_netutils.h"
39
#include "kvi_script_objectclassdefinition.h"
40
#include "kvi_script_objectcontroller.h"
41
#include "kvi_script_socket.h"
48
<a href="class_object.kvihelp">object</a>
50
!fn: $setDomain(<domain>)
51
Set the domain of the socket: valid values for <domain> are:<br>
52
inet and unix. (inet is the default for new socket objects)<br>
55
Returns the name of the domain of the socket.
57
!fn: $setType(<type>)
58
Set the type of the socket: valid values for <type> are:<br>
59
stream and raw. (stream is the default for new socket objects)<br>
62
Returns the type of the socket
64
!fn: $setProtocol(<protocol>)
65
Sets the protocol of the socket .<br>
66
The <protocol> argument is the protocol name to be matched
67
in the /etc/protocols file (see man getprotobyname :).<br>
68
(The default protocol is "ip" : that should work in most cases).<br>
71
Returns the protocol of the socket
74
Returns the last error string
76
!fn: $setMode(<data_mode>)
77
Sets the data mode of the socket.<br>
78
The valid modes are:<br>
79
- Ascii : for text based protocols (pop3, irc...), messages separated by combinations of CR and LF characters<br>
80
- Hex : hexadecimal endoded, for binary protocols. (see <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)
83
Returns the data mode of the socket.
85
!fn: $write(<string>)
86
Writes data to the socket and returns the length of the written buffer (that may also be zero!).<br>
87
If a serious error occurs, -1 is returned and the OnDisconnect event is triggered.<br>
88
Please note that this function does NOT implicitly write a trailing CR or LF:
89
you must add it by yourself (All text based protocols require a messages terminator).<br>
90
Please note that this function can be called either in Ascii or Hex mode.<br>
92
!fn: $writeHex(<hexstring>)
93
Writes data (transformed to binary form) to the socket and returns the length of the written buffer (that may also be zero!).<br>
94
If an error occurs, the OnDisconnect event is fired and -1 is returned.<br>
95
The hexstring is a string of pairs of hexadecimal digits that encode the bytes to be sent.<br>
96
This is useful when you want send null or unprintable characters.<br>
97
For example $writeHex(68656C6C6F00) writes a null terminated "hello" string to the socket.<br>
98
There is no other way to write the null character.<br>
99
(see also <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)<br>
100
Please note that this function can be called either in Ascii or Hex mode.<br>
103
Closes the connection. The OnDisconnect event is NOT fired.<br>
106
Returns the state of the socket : ready, connecting, listening or connected.<br>
108
!fn: $connect(<ipaddress>, <port>)
109
Attempts a connection to the specified host on a specified port.<br>
110
This function returns 1 if the creation of the socket and the "connect" system call
111
have been successful. Please note that this does NOT mean that you are connected:
112
the connection attempt has been only started.<br>
113
From that moment you can receive two events:<br>
114
OnConnectFailed, if the connection fails for some reason (for example the specified
115
host is unreachable) or OnConnect when the connection has been estabilished.<br>
116
If this function returns 0 you can obtain the error string by calling $lastError().<br>
118
!fn: $listen(<local_interface_address>, <port>)
119
Starts listening for incoming connectons on the specified port.<br>
120
<local_interface_address> can be a valid IP address of a local
121
network interface or the string "any" (or "default") that will cause to listen
122
on all available interfaces.<br>
123
This function returns 1 if the socket creation and the system "listen" call
124
have been successful. Please nothe that this does NOT mean that you are connected
125
to some host: the listening has just been started.<br>
126
When a connection arrives the "OnIncomingConnection" event will be triggered.<br>
127
If this function returns 0 you can obtain the error string by calling $lastError().<br>
128
Please note that not all ports are available to be "listened":
129
especially, if you are not the root user, you may have no access to ports below 1024.<br>
130
This function may fail also if some other "entity" is listening on the specified port.<br>
131
Passing 0 as the <port> argument causes the kernel to select the first available port for you,
132
(... at least on Linux).<br>
135
Returns the IP address of the currently connected remote host
138
Returns the remote end port of the current connection
140
!fn: $accept(<listening_socket_id>)
141
This function accepts an incoming connection that is pending on the specified listening socket.<br>
142
Returns 1 on success, 0 otherwise.<br>
143
Please note that usually the <listening_socket_id> is NOT *this* socket.<br>
144
Commonly, you will create a listening socket and override its OnIncomingConnection event.<br>
145
There you will create a new socket object to serve the incoming connection and call its
146
$accept member function passing the listening socket id.<br>
147
See the OnIncomingConnection example.<br>
148
The most common failure causes are:<br>
149
- The <listening_socket_id> is not an object id at all (the object cannot be found)<br>
150
- The <listening_socket_id> object id does not refer to a class that inherits socket<br>
151
- The <listening_socket_id> socket has no pending incoming connections.<br>
152
You can retrieve the failure reason by calling $lastError().<br>
153
After a successful call of this function, the caller socket is ready to communicate:<br>
154
You can retrieve the remote host and port data with the $host and $port functions.<br>
155
The OnConnect event is not triggered (not useful).<br>
158
Returns the IP address of the local host.<br>
159
This identifier returns valid values only when the socket object is in connected state.<br>
162
!ev: OnConnect($1 = HostIp, $2 = Port)
163
Triggered just after a successful connection has been estabilished
164
The default implementation of this event echoes "Connected to $1 on port $2" to the active window
166
!ev: OnIncomingConnection()
167
Triggered by a listening socket when a connection request arrives.<br>
168
To accept the incoming connection you should create a new socket (or derived class)
169
object and call accept(<a href="s_this.kvihelp">$this</a>)<br>
171
event(OnIncomingConnection)
173
%sock = <a href="s_new.kvihelp">$new</a>(socket, <a href="s_this.kvihelp">$this</a>, incoming)
174
if(!%sock->$accept(<a href="s_this.kvihelp">$this</a>))
176
# Oops! Something went really wrong
177
<a href="echo.kvihelp">echo</a> Accept failed : %sock->$lastError()
178
<a href="destroy.kvihelp">destroy</a> %sock;
182
If you do not accept in this way the incoming connection in this event,
183
it will be automatically discarded (the socket will be closed and the remote end will
184
see some error such as "Connection reset by peer").<br>
185
You can also accept the incoming connection with <a href="s_this.kvihelp">$this</a> socket
186
(the listening one), but then you will obviously stop listening.<br>
188
event(OnIncomingConnection)
190
if(!<a href="s_this.kvihelp">$this</a>->$accept(<a href="s_this.kvihelp">$this</a>))
192
# Oops! Something went really wrong
193
<a href="echo.kvihelp">echo</a> Accept failed : <a href="s_this.kvihelp">$this</a>->$lastError()
194
# Still listening
198
If $accept succeeds in this case, <a href="s_this.kvihelp">$this</a> socket stops listening
199
and starts the communication on the accepted connection.<br>
200
If $accept fails, <a href="s_this.kvihelp">$this</a> socket will be still listening.
201
The default implementation of this event does nothing
203
!ev: OnConnectFailed($1- = Error String)
204
Triggered when a connection attempt fails
205
The default implementation of this event echoes "Connect failed : $this->$lastError()" to the active window
207
!ev: OnDisconnect($1- = Error String)
208
Triggered when the socket gets disconnected for some user-independent reason.<br>
209
It may be caused by a transmission error or the remote host may simply close the connection
210
after a timeout...<br>
211
The default implementation of this event echoes "Disconnected from $this->$host() : $this->$lastError()" to the active window
213
!ev: OnDataReceived($1 = Data length, $2- = Data String)
214
The data string is a "normal" text line if the socket is in the Ascii mode,
215
otherwise it is a sequence of hexadecimal-encoded bytes.<br>
216
In Ascii mode you will be loosing the leading and trailing whitespace,
217
and the unprintable characters. Also, in Ascii mode this event will NOT fire until
218
a CR or LF has been read from the socket (to signal the end of a line).<br>
219
The $1 argument is the length of the Data string argument (in characters).<br>
220
In Hex mode the $1 argument is the length of the Data string argument (in characters);
221
since each character is encoded by two hexadecimal digits, so <a href="s_calc.kvihelp">$calc</a>($1 / 2)
222
will be the effective length in bytes of the received buffer.<br>
223
In hex mode this event will be triggered whenever any data arrives.<br>
224
If you need to receive such data, use the socket in Hex mode.<br>
225
(see <a href="s_hextostr.kvihelp">$HexToStr()</a> and <a href="s_strtohex.kvihelp">$StrToHex()</a>)<br>
226
This event has a default event handler (in case you do not want to bother yourself with
227
the <a href="class.kvihelp">CLASS</a> command) that "echoes" the received data to the active window.<br>
228
The body of the event is: "echo -w=$activewindow SOCKET $this : received data :$2-".<br>
229
Obviously you can override it, as usual, with <a href="obj_setevent.kvihelp">obj_setevent</a>
230
or in a subclass definition.<br>
233
A socket device object.<br>
234
Theoretically it can work in the Unix and Inet domain
235
but actually only the Inet (default) domain has been tested.<br>
236
Theoretically it can work as Stream or Raw socket but
237
actually only the Stream (default) socket implementation has been tested.<br>
238
Theoretically it can work over any valid protocol but by now
239
only "ip" (0 - default) has been tested.
242
Simple code to check for new messages in a FIXED mailbox.<br>
243
The connection is made in Ascii mode since unless messages are transmitted
244
the pop3 protocol is text based: we will loose no data.<br>
245
You can easily optimize this code and convert it to an alias
246
that may accept the username, password and the mailserver as parameters.<br>
247
This version also wants the real IP address of the mailserver, not the hostname.<br>
248
Adding the DNS extension is left to you as exercise :).<br>
250
# Create a socket object
251
%o=<a href="s_new.kvihelp">$new</a>(socket, <a href="s_root.kvihelp">$root</a>, mysock);
252
# OnConnect event : Once connected, send USER, PASS and LIST
253
# You will probably want to set a different username and password :)
254
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnConnect)
256
<a href="echo.kvihelp">echo</a> CHECKMAIL : connected to the mailserver...
257
<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>")
258
<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>")
259
<a href="s_this.kvihelp">$this</a>->$write("LIST<a href="s_cr.kvihelp">$cr</a><a href="s_lf.kvihelp">$lf</a>")
261
# When we get disconnected for some reason remove the object
262
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnDisconnect)
264
<a href="echo.kvihelp">echo</a> CHECKMAIL : disconnected ($1-)
265
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
267
# Data received... here is the clue
268
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnDataReceived)
270
<a href="switch.kvihelp">switch</a>($2-)
272
<a href="switch.kvihelp">match</a>("+OK * messages*")
273
{
274
<a href="echo.kvihelp">echo</a> CHECKMAIL : the mailbox contains <a href="s_strrightfromfirst.kvihelp">$strRightFromFirst</a>("+OK ", $1-)
275
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
276
}
277
<a href="switch.kvihelp">match</a>("-ERR *")
278
{
279
<a href="echo.kvihelp">echo</a> CHECKMAIL : received error : $2-
280
<a href="echo.kvihelp">echo</a> CHECKMAIL : aborting
281
<a href="destroye.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
282
}
285
# If the connection fails for some reason, remove the object
286
<a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnConnectFailed)
288
<a href="echo.kvihelp">echo</a> CHECKMAIL : connect failed : ($1-)
289
<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
291
# OK, ready to go. Connect to our mailserver
292
# You will probably want to change the IP address :)
293
<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; }
294
<a href="if.kvihelp">else</a> <a href="echo.kvihelp">echo</a> Checking mail on 127.0.0.1...
295
# And now just wait and watch...
298
<a href="syntax_objects.kvihelp">Objects documentation</a>
300
KviScriptSocket::KviScriptSocket(
301
KviScriptObjectController *cntrl, KviScriptObject *p, const char *name, KviScriptObjectClassDefinition *pDef)
302
: KviScriptObject(cntrl, p, name, pDef)
306
m_type = SOCK_STREAM;
308
m_error = KVI_ERROR_Success;
315
m_incomingConnectionSock = -1;
316
m_szLocalHostIp = "127.0.0.1";
319
KviScriptSocket::~KviScriptSocket()
324
void KviScriptSocket::initializeClassDefinition(KviScriptObjectClassDefinition *d)
326
d->addBuiltinFunction("mode", (scriptObjectFunction) &KviScriptSocket::builtinFunction_MODE);
327
d->addBuiltinFunction("type", (scriptObjectFunction) &KviScriptSocket::builtinFunction_TYPE);
328
d->addBuiltinFunction("setDomain", (scriptObjectFunction) &KviScriptSocket::builtinFunction_SETDOMAIN);
329
d->addBuiltinFunction("setType", (scriptObjectFunction) &KviScriptSocket::builtinFunction_SETTYPE);
330
d->addBuiltinFunction("setMode", (scriptObjectFunction) &KviScriptSocket::builtinFunction_SETMODE);
331
d->addBuiltinFunction("setProtocol", (scriptObjectFunction) &KviScriptSocket::builtinFunction_SETPROTOCOL);
332
d->addBuiltinFunction("write", (scriptObjectFunction) &KviScriptSocket::builtinFunction_WRITE);
333
d->addBuiltinFunction("writeHex", (scriptObjectFunction) &KviScriptSocket::builtinFunction_WRITEHEX);
334
d->addBuiltinFunction("close", (scriptObjectFunction) &KviScriptSocket::builtinFunction_CLOSE);
335
d->addBuiltinFunction("protocol", (scriptObjectFunction) &KviScriptSocket::builtinFunction_PROTOCOL);
336
d->addBuiltinFunction("host", (scriptObjectFunction) &KviScriptSocket::builtinFunction_HOST);
337
d->addBuiltinFunction("port", (scriptObjectFunction) &KviScriptSocket::builtinFunction_PORT);
338
d->addBuiltinFunction("state", (scriptObjectFunction) &KviScriptSocket::builtinFunction_STATE);
339
d->addBuiltinFunction("domain", (scriptObjectFunction) &KviScriptSocket::builtinFunction_DOMAIN);
340
d->addBuiltinFunction("connect", (scriptObjectFunction) &KviScriptSocket::builtinFunction_CONNECT);
341
d->addBuiltinFunction("listen", (scriptObjectFunction) &KviScriptSocket::builtinFunction_LISTEN);
342
d->addBuiltinFunction("lastError", (scriptObjectFunction) &KviScriptSocket::builtinFunction_LASTERROR);
343
d->addBuiltinFunction("accept", (scriptObjectFunction) &KviScriptSocket::builtinFunction_ACCEPT);
344
d->addBuiltinFunction("localhost", (scriptObjectFunction) &KviScriptSocket::builtinFunction_LOCALHOST);
346
KviScriptEventStruct *s = new KviScriptEventStruct();
347
s->szName = "OnDataReceived";
348
s->szBuffer = "echo -w=$activewindow SOCKET $this : received data :$2-";
349
d->addDefaultEvent(s);
350
s = new KviScriptEventStruct();
351
s->szName = "OnDisconnect";
352
s->szBuffer = "echo -w=$activewindow SOCKET $this : disconnected from $this->$host() : $this->$lastError()";
353
d->addDefaultEvent(s);
354
s = new KviScriptEventStruct();
355
s->szName = "OnConnect";
356
s->szBuffer = "echo -w=$activewindow SOCKET $this : connected to $1 on port $2";
357
d->addDefaultEvent(s);
358
s = new KviScriptEventStruct();
359
s->szName = "OnConnectFailed";
360
s->szBuffer = "echo -w=$activewindow SOCKET $this : connect failed: $this->$lastError()";
361
d->addDefaultEvent(s);
364
int KviScriptSocket::builtinFunction_MODE(QPtrList<KviStr> *params, KviStr &buffer)
366
if( m_mode == Ascii )
367
buffer.append("ascii");
369
buffer.append("hex");
370
return KVI_ERROR_Success;
373
int KviScriptSocket::builtinFunction_LOCALHOST(QPtrList<KviStr> *params, KviStr &buffer)
375
buffer.append(m_szLocalHostIp);
376
return KVI_ERROR_Success;
379
int KviScriptSocket::builtinFunction_SETDOMAIN(QPtrList<KviStr> *params, KviStr &buffer)
382
KviStr *pDomain = params->first();
384
if( kvi_strEqualCI(pDomain->ptr(), "inet") )
386
else if( kvi_strEqualCI(pDomain->ptr(), "unix") )
389
return KVI_ERROR_InvalidParameter;
390
return KVI_ERROR_Success;
393
return KVI_ERROR_MissingParameter;
396
int KviScriptSocket::builtinFunction_SETTYPE(QPtrList<KviStr> *params, KviStr &buffer)
399
KviStr *pType = params->first();
401
if( kvi_strEqualCI(pType->ptr(), "stream") )
402
m_type = SOCK_STREAM;
403
else if( kvi_strEqualCI(pType->ptr(), "raw") )
406
return KVI_ERROR_InvalidParameter;
407
return KVI_ERROR_Success;
410
return KVI_ERROR_MissingParameter;
413
int KviScriptSocket::builtinFunction_SETMODE(QPtrList<KviStr> *params, KviStr &buffer)
416
KviStr *pType = params->first();
418
if( kvi_strEqualCI(pType->ptr(), "ascii") ) {
419
if( m_mode != Ascii ) {
423
kvi_free(m_pReadBuffer);
426
} else if( kvi_strEqualCI(pType->ptr(), "hex") ) {
427
if( m_mode != Hex ) {
431
kvi_free(m_pReadBuffer);
434
} else return KVI_ERROR_InvalidParameter;
435
return KVI_ERROR_Success;
438
return KVI_ERROR_MissingParameter;
441
int KviScriptSocket::builtinFunction_SETPROTOCOL(QPtrList<KviStr> *params, KviStr &buffer)
444
KviStr *pProto = params->first();
446
struct protoent *p = getprotobyname(pProto->ptr());
448
m_protocol = p->p_proto;
450
return KVI_ERROR_InvalidParameter;
451
return KVI_ERROR_Success;
454
return KVI_ERROR_MissingParameter;
457
int KviScriptSocket::builtinFunction_WRITE(QPtrList<KviStr> *params, KviStr &buffer)
460
KviStr *pData = params->first();
462
KviStr tmp(KviStr::Format, "%d", writeData(pData->ptr(), pData->len()));
463
buffer.append(tmp.ptr());
464
return KVI_ERROR_Success;
467
return KVI_ERROR_MissingParameter;
470
int KviScriptSocket::builtinFunction_WRITEHEX(QPtrList<KviStr> *params, KviStr &buffer)
473
KviStr *pData = params->first();
476
int len = pData->hexToBuffer(&buf);
477
KviStr tmp(KviStr::Format, "%d", writeData(buf, len));
480
buffer.append(tmp.ptr());
481
return KVI_ERROR_Success;
484
return KVI_ERROR_MissingParameter;
487
int KviScriptSocket::builtinFunction_CLOSE(QPtrList<KviStr> *params, KviStr &buffer)
495
return KVI_ERROR_Success;
498
int KviScriptSocket::builtinFunction_PROTOCOL(QPtrList<KviStr> *params, KviStr &buffer)
500
struct protoent *p = getprotobynumber(m_protocol);
502
buffer.append(p->p_name);
505
num.setNum(m_protocol);
506
buffer.append(num.ptr());
508
return KVI_ERROR_Success;
511
int KviScriptSocket::builtinFunction_TYPE(QPtrList<KviStr> *params, KviStr &buffer)
513
if( m_type == SOCK_STREAM )
514
buffer.append("stream");
516
buffer.append("raw");
517
return KVI_ERROR_Success;
520
int KviScriptSocket::builtinFunction_HOST(QPtrList<KviStr> *params, KviStr &buffer)
522
if( m_state == Connected )
523
buffer.append(m_szHostIp);
524
return KVI_ERROR_Success;
527
int KviScriptSocket::builtinFunction_PORT(QPtrList<KviStr> *params, KviStr &buffer)
529
if( m_state == Connected )
530
buffer.append(m_szPort);
531
return KVI_ERROR_Success;
534
int KviScriptSocket::builtinFunction_STATE(QPtrList<KviStr> *params, KviStr &buffer)
536
if( m_state == Connected ) buffer.append("connected");
537
else if( m_state == Connecting ) buffer.append("connecting");
538
else if( m_state == Listening ) buffer.append("listening");
539
else buffer.append("ready");
540
return KVI_ERROR_Success;
543
int KviScriptSocket::builtinFunction_DOMAIN(QPtrList<KviStr> *params, KviStr &buffer)
545
if( m_domain == PF_INET )
546
buffer.append("inet");
548
buffer.append("unix");
549
return KVI_ERROR_Success;
552
int KviScriptSocket::builtinFunction_CONNECT(QPtrList<KviStr> *params, KviStr &buffer)
555
KviStr *pHost = params->first();
557
KviStr *pPort = params->next();
559
buffer.append(doConnect(pHost, pPort) ? '1' : '0');
560
return KVI_ERROR_Success;
564
return KVI_ERROR_MissingParameter;
567
int KviScriptSocket::builtinFunction_LISTEN(QPtrList<KviStr> *params, KviStr &buffer)
570
KviStr *pHost = params->first();
572
KviStr *pPort = params->next();
574
buffer.append(doListen(pHost, pPort) ? '1' : '0');
575
return KVI_ERROR_Success;
579
return KVI_ERROR_MissingParameter;
582
int KviScriptSocket::builtinFunction_LASTERROR(QPtrList<KviStr> *params, KviStr &buffer)
584
buffer.append(kvi_getErrorString(m_error));
585
return KVI_ERROR_Success;
588
int KviScriptSocket::builtinFunction_ACCEPT(QPtrList<KviStr> *params, KviStr &buffer)
591
KviStr *pObId = params->first();
593
buffer.append(doAccept(pObId->ptr()) ? '1' : '0');
594
return KVI_ERROR_Success;
597
return KVI_ERROR_MissingParameter;
600
bool KviScriptSocket::doListen(KviStr *szLocalAddr, KviStr *szPort)
602
struct in_addr inAddress;
603
struct sockaddr_in hostSockAddr;
606
m_error = KVI_ERROR_AnotherConnectionInProgress;
610
if( !kvi_stringIpToBinaryIp(szLocalAddr->ptr(), &inAddress) ) {
611
if( kvi_strEqualCI("default", szLocalAddr->ptr()) || kvi_strEqualCI("any", szLocalAddr->ptr()) )
612
hostSockAddr.sin_addr.s_addr = INADDR_ANY;
614
m_error = KVI_ERROR_InvalidIpAddress;
617
} else hostSockAddr.sin_addr = inAddress;
620
unsigned short int port = szPort->toUInt(&bOk);
622
m_error = KVI_ERROR_InvalidPort;
626
hostSockAddr.sin_family = m_domain; // AF_INET == PF_INET and AF_UNIX == PF_UNIX
627
hostSockAddr.sin_port = htons(port);
629
m_sock = socket(m_domain, m_type, m_protocol);
631
m_error = KVI_ERROR_SocketCreationFailed;
634
if( fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0 ) {
635
m_error = KVI_ERROR_AsyncSocketFailed;
640
if( (theError = bind(m_sock, (struct sockaddr *) &hostSockAddr, sizeof(hostSockAddr))) < 0 ) {
641
setErrorFromSystemError(theError);
645
if( (theError = listen(m_sock, 1)) < 0 ) {
646
setErrorFromSystemError(theError);
651
// Set up the READ notifier
652
m_pTmpSn = new QSocketNotifier(m_sock, QSocketNotifier::Read);
653
QObject::connect(m_pTmpSn, SIGNAL(activated(int)), this, SLOT(listenNotifierFired(int)));
655
m_pTmpSn->setEnabled(true);
659
m_error = KVI_ERROR_Success;
665
void KviScriptSocket::listenNotifierFired(int)
667
struct sockaddr_in connectedAddr;
669
socklen_t iSize = sizeof(connectedAddr);
670
m_incomingConnectionSock = accept(m_sock, (struct sockaddr *) &connectedAddr, &iSize);
671
if( m_incomingConnectionSock == -1 )
672
debug("Warning: accept() returned -1: waiting for the next accept() call");
675
m_szIncomingConnectionPort.setNum(ntohs(connectedAddr.sin_port));
676
kvi_binaryIpToString(connectedAddr.sin_addr, m_szIncomingConnectionHostIp);
679
triggerEvent("OnIncomingConnection", parms);
681
if( m_incomingConnectionSock != -1 ) {
683
close(m_incomingConnectionSock);
684
m_incomingConnectionSock = -1;
685
m_szIncomingConnectionPort = "";
686
m_szIncomingConnectionHostIp = "";
691
bool KviScriptSocket::doAccept(const char *sockObjectId)
693
KviScriptObject *o = (KviScriptObject *) controller()->findObjectById(sockObjectId);
696
m_error = KVI_ERROR_ObjectNotFound;
699
if( !o->inherits("KviScriptSocket") ) {
700
m_error = KVI_ERROR_ObjectIsNotASocket;
703
KviScriptSocket *s = (KviScriptSocket *) o;
705
if( !s>hasPendingConnection() ) {
706
m_error = KVI_ERROR_NoConnectionToAccept;
710
if( m_state != Ready ) {
711
reset(); // If o == "this" it will work; reset() will not clear the incomingConnectionSock
714
m_sock = s->m_incomingConnectionSock;
715
s->m_incomingConnectionSock = -1;
716
m_szHostIp = s->m_szIncomingConnectionHostIp;
717
s->m_szIncomingConnectionHostIp = "";
718
m_szPort = s->m_szIncomingConnectionPort;
719
s->m_szIncomingConnectionPort = "";
721
m_error = KVI_ERROR_Success;
723
m_pRsn = new QSocketNotifier(m_sock, QSocketNotifier::Read);
724
QObject::connect(m_pRsn, SIGNAL(activated(int)), this, SLOT(receivedData(int)));
725
m_pRsn->setEnabled(true);
732
bool KviScriptSocket::hasPendingConnection()
734
return (m_incomingConnectionSock != -1);
737
void KviScriptSocket::setLocalHostIp()
739
if( m_state == Connected ) {
740
struct sockaddr_in name;
741
socklen_t len = sizeof(name);
742
if( getsockname(m_sock, (struct sockaddr *) &name, &len) >= 0 ) {
743
if( kvi_binaryIpToString(name.sin_addr, m_szLocalHostIp) )
747
m_szLocalHostIp = "127.0.0.1";
750
bool KviScriptSocket::doConnect(KviStr *szHost, KviStr *szPort)
752
struct in_addr inAddress;
753
struct sockaddr_in hostSockAddr;
756
m_error = KVI_ERROR_AnotherConnectionInProgress;
760
if( !kvi_stringIpToBinaryIp(szHost->ptr(), &inAddress) ) {
761
m_error = KVI_ERROR_InvalidIpAddress;
766
unsigned short int port = szPort->toUInt(&bOk);
768
m_error = KVI_ERROR_InvalidPort;
772
hostSockAddr.sin_family = m_domain; // AF_INET == PF_INET and AF_UNIX == PF_UNIX
773
hostSockAddr.sin_port = htons(port);
774
hostSockAddr.sin_addr = inAddress;
777
m_sock = socket(m_domain, m_type, m_protocol);
779
m_error = KVI_ERROR_SocketCreationFailed;
783
if( fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0 ) {
784
m_error = KVI_ERROR_AsyncSocketFailed;
788
if( ::connect(m_sock, (struct sockaddr *) (&hostSockAddr), sizeof(hostSockAddr)) < 0 ) {
789
if( errno != EINPROGRESS ) {
790
int sockError = errno;
791
if( sockError == 0 ) {
792
socklen_t iSize = sizeof(int);
793
if( getsockopt(m_sock, SOL_SOCKET, SO_ERROR, (void *) &sockError, &iSize) < 0 )
797
setErrorFromSystemError(sockError);
799
m_error = KVI_ERROR_UnknownError; // Error 0?
804
// Set up the WRITE notifier
805
m_pTmpSn = new QSocketNotifier(m_sock, QSocketNotifier::Write);
806
QObject::connect(m_pTmpSn, SIGNAL(activated(int)), this, SLOT(writeNotifierFired(int)));
808
m_pTmpSn->setEnabled(true);
810
m_szHostIp = szHost->ptr();
811
m_szPort = szPort->ptr();
812
m_error = KVI_ERROR_Success;
813
m_state = Connecting;
817
void KviScriptSocket::writeNotifierFired(int)
820
socklen_t iSize = sizeof(int);
821
if( getsockopt(m_sock, SOL_SOCKET, SO_ERROR, (void *) &sockError, &iSize) < 0 )
823
if( sockError != 0 ) {
826
setErrorFromSystemError(sockError);
828
m_error = KVI_ERROR_UnknownError; // Error 0?
829
KviStr parms = kvi_getErrorString(m_error);
830
triggerEvent("OnConnectFailed", parms);
833
// Successfully connected
834
m_pRsn = new QSocketNotifier(m_sock, QSocketNotifier::Read);
835
// Normal IRC connection
836
KviStr parms(KviStr::Format, "%s %s", m_szHostIp.ptr(), m_szPort.ptr());
837
triggerEvent("OnConnect", parms);
838
if( m_state != Connecting )
841
QObject::connect(m_pRsn, SIGNAL(activated(int)), this, SLOT(receivedData(int)));
842
m_pRsn->setEnabled(true);
850
void KviScriptSocket::reset()
858
if( m_pReadBuffer ) {
859
kvi_free(m_pReadBuffer);
874
void KviScriptSocket::setErrorFromSystemError(int errorNum)
877
case EBADF: m_error = KVI_ERROR_BadFileDescriptor; break;
878
case EFAULT: m_error = KVI_ERROR_OutOfAddressSpace; break;
879
case ECONNREFUSED: m_error = KVI_ERROR_ConnectionRefused; break;
880
case ENOTSOCK: m_error = KVI_ERROR_KernelNetworkingPanic; break;
881
case ETIMEDOUT: m_error = KVI_ERROR_ConnectionTimedOut; break;
882
case ENETUNREACH: m_error = KVI_ERROR_NetworkUnreachable; break;
883
case EPIPE: m_error = KVI_ERROR_BrokenPipe; break;
884
// Unhandled error; pass errno to the strerror function
885
default: m_error = -errorNum; break;
889
void KviScriptSocket::handleInvalidSocketRead(int readLength)
891
if( readLength == 0 ) {
892
m_error = KVI_ERROR_RemoteEndClosedConnection;
893
KviStr parms = kvi_getErrorString(m_error);
894
triggerEvent("OnDisconnect", parms);
897
// Check for transmission errors
898
if( (errno != EAGAIN) && (errno != EINTR) ) {
900
setErrorFromSystemError(errno);
902
m_error = KVI_ERROR_RemoteEndClosedConnection;
903
KviStr parms = kvi_getErrorString(m_error);
904
triggerEvent("OnDisconnect", parms);
906
} // else: transient error; wait again
910
void KviScriptSocket::receivedData(int fd)
914
int readLength = read(m_sock, buffer, 1024);
916
if( readLength <= 0 ) {
917
handleInvalidSocketRead(readLength);
921
if( m_mode == Hex ) {
923
KviStr tmp(KviStr::Format, "%d ", readLength << 1);
924
parms.bufferToHex(buffer, readLength);
925
parms.prepend(tmp.ptr());
926
m_pRsn->setEnabled(false);
927
triggerEvent("OnDataReceived", parms);
928
// Check if we were closed in the mean time
929
if( m_state != Connected )
931
m_pRsn->setEnabled(true);
933
// Terminate our buffer
934
(*(buffer + readLength)) = '\0';
936
register char *p = buffer;
937
char *beginOfCurData = buffer;
939
char *messageBuffer = (char *) kvi_malloc(1);
941
// Shut up the socket notifier in case we enter
942
// a local loop somewhere while processing data
943
m_pRsn->setEnabled(false);
946
if( (*p == '\r' ) || (*p == '\n') ) {
948
// Prepare a message buffer
949
bufLen = p - beginOfCurData;
950
// Check for previous unterminated data
953
if( m_iReadBufLen > 0 ) {
954
__range_valid(m_pReadBuffer);
955
messageBuffer = (char *) kvi_realloc(messageBuffer, bufLen + m_iReadBufLen + 1);
956
kvi_memmove(messageBuffer, m_pReadBuffer, m_iReadBufLen);
957
kvi_memmove((void *) (messageBuffer + m_iReadBufLen), beginOfCurData, bufLen);
958
*(messageBuffer + bufLen + m_iReadBufLen) = '\0';
959
kvi_free(m_pReadBuffer);
962
parms.sprintf("%d %s", bufLen + m_iReadBufLen, messageBuffer);
965
__range_invalid(m_pReadBuffer);
966
messageBuffer = (char *) kvi_realloc(messageBuffer, bufLen + 1);
967
kvi_memmove(messageBuffer, beginOfCurData, bufLen);
968
*(messageBuffer + bufLen) = '\0';
969
parms.sprintf("%d %s", bufLen, messageBuffer);
972
triggerEvent("OnDataReceived", parms);
973
// Check if we were closed in the mean time
974
if( m_state != Connected ) {
975
kvi_free(messageBuffer);
979
while( *p && ((*p == '\r') || (*p == '\n')) )
984
if( inDelayedDestroy() ) {
985
// Already being destroyed -- destroyed while processing data!
986
kvi_free(messageBuffer);
991
// beginOfCurData points to '\0' if we have no more stuff to
992
// parse, or points to something other than '\r' or '\n'.
993
if( *beginOfCurData ) {
994
// Have remaining data in the local buffer
995
bufLen = p - beginOfCurData;
996
if( m_iReadBufLen > 0 ) {
997
// And there was more stuff saved. Really slow connection
998
__range_valid(m_pReadBuffer);
999
m_pReadBuffer = (char *) kvi_realloc(m_pReadBuffer, m_iReadBufLen + bufLen);
1000
kvi_memmove((void *) (m_pReadBuffer + m_iReadBufLen), beginOfCurData, bufLen);
1001
m_iReadBufLen += bufLen;
1003
__range_invalid(m_pReadBuffer);
1004
m_iReadBufLen = bufLen;
1005
m_pReadBuffer = (char *) kvi_malloc(m_iReadBufLen);
1006
kvi_memmove(m_pReadBuffer, beginOfCurData, m_iReadBufLen);
1010
kvi_free(messageBuffer);
1012
m_pRsn->setEnabled(true);
1016
int KviScriptSocket::writeData(char *buffer, int len)
1018
if( m_sock == -1 ) {
1019
m_error = KVI_ERROR_NotConnected;
1023
int result = write(m_sock, buffer, len);
1028
if( (errno == EAGAIN) || (errno == EINTR) )
1032
setErrorFromSystemError(errno);
1033
KviStr parms = kvi_getErrorString(m_error);
1034
triggerEvent("OnDisconnect", parms);
1041
#include "m_kvi_script_socket.moc"