~ubuntu-branches/ubuntu/hoary/kvirc/hoary

« back to all changes in this revision

Viewing changes to src/kvirc/script/kvi_script_socket.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Robin Verduijn
  • Date: 2004-12-14 15:32:19 UTC
  • mfrom: (0.2.1 upstream) (1.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20041214153219-fdink3gyp2s20b6g
Tags: 2:2.1.3.1-2
* Change Recommends on xmms to a Suggests.
* Rebuild against KDE 3.3.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// =============================================================================
 
2
//
 
3
//   This file is part of the KVIrc IRC client distribution
 
4
//   Copyright (C) 1999-2000 Szymon Stefanek (stefanek@tin.it)
 
5
//
 
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.
 
10
//
 
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.
 
15
//
 
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.
 
19
//
 
20
// =============================================================================
 
21
 
 
22
#define _KVI_DEBUG_CHECK_RANGE_
 
23
#define _KVI_DEBUG_CLASS_NAME_ "KviScriptSocket"
 
24
 
 
25
#include <sys/socket.h>
 
26
#include <sys/types.h>
 
27
#include <errno.h>
 
28
#include <fcntl.h>
 
29
#include <netdb.h>
 
30
#include <unistd.h>
 
31
 
 
32
#include <qsocketnotifier.h>
 
33
 
 
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"
 
42
 
 
43
/*
 
44
        @class: socket
 
45
        @short:
 
46
                A socket device.
 
47
        @inherits:
 
48
                <a href="class_object.kvihelp">object</a>
 
49
        @functions:
 
50
                !fn: $setDomain(&lt;domain&gt;)
 
51
                Set the domain of the socket: valid values for &lt;domain&gt; are:<br>
 
52
                inet and unix. (inet is the default for new socket objects)<br>
 
53
 
 
54
                !fn: $domain()
 
55
                Returns the name of the domain of the socket.
 
56
 
 
57
                !fn: $setType(&lt;type&gt;)
 
58
                Set the type of the socket: valid values for &lt;type&gt; are:<br>
 
59
                stream and raw. (stream is the default for new socket objects)<br>
 
60
 
 
61
                !fn: $type()
 
62
                Returns the type of the socket
 
63
 
 
64
                !fn: $setProtocol(&lt;protocol&gt;)
 
65
                Sets the protocol of the socket .<br>
 
66
                The &lt;protocol&gt; 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>
 
69
 
 
70
                !fn: $protocol()
 
71
                Returns the protocol of the socket
 
72
 
 
73
                !fn: $lastError()
 
74
                Returns the last error string
 
75
 
 
76
                !fn: $setMode(&lt;data_mode&gt;)
 
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>)
 
81
 
 
82
                !fn: $mode()
 
83
                Returns the data mode of the socket.
 
84
 
 
85
                !fn: $write(&lt;string&gt;)
 
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>
 
91
 
 
92
                !fn: $writeHex(&lt;hexstring&gt;)
 
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>
 
101
 
 
102
                !fn: $close()
 
103
                Closes the connection. The OnDisconnect event is NOT fired.<br>
 
104
 
 
105
                !fn: $state()
 
106
                Returns the state of the socket : ready, connecting, listening or connected.<br>
 
107
 
 
108
                !fn: $connect(&lt;ipaddress&gt;, &lt;port&gt;)
 
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>
 
117
 
 
118
                !fn: $listen(&lt;local_interface_address&gt;, &lt;port&gt;)
 
119
                Starts listening for incoming connectons on the specified port.<br>
 
120
                &lt;local_interface_address&gt; 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 &lt;port&gt; argument causes the kernel to select the first available port for you,
 
132
                (... at least on Linux).<br>
 
133
 
 
134
                !fn: $host()
 
135
                Returns the IP address of the currently connected remote host
 
136
 
 
137
                !fn: $port()
 
138
                Returns the remote end port of the current connection
 
139
 
 
140
                !fn: $accept(&lt;listening_socket_id&gt;)
 
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 &lt;listening_socket_id&gt; 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 &lt;listening_socket_id&gt; is not an object id at all (the object cannot be found)<br>
 
150
                - The &lt;listening_socket_id&gt; object id does not refer to a class that inherits socket<br>
 
151
                - The &lt;listening_socket_id&gt; 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>
 
156
 
 
157
                !fn: $localhost()
 
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>
 
160
 
 
161
        @events:
 
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
 
165
 
 
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>
 
170
                <example>
 
171
                        event(OnIncomingConnection)
 
172
                        {
 
173
                        &nbsp;&nbsp;%sock = <a href="s_new.kvihelp">$new</a>(socket, <a href="s_this.kvihelp">$this</a>, incoming)
 
174
                        &nbsp;&nbsp;if(!%sock->$accept(<a href="s_this.kvihelp">$this</a>))
 
175
                        &nbsp;&nbsp;{
 
176
                        &nbsp;&nbsp;&nbsp;&nbsp;# Oops! Something went really wrong
 
177
                        &nbsp;&nbsp;&nbsp;&nbsp;<a href="echo.kvihelp">echo</a> Accept failed : %sock->$lastError()
 
178
                        &nbsp;&nbsp;&nbsp;&nbsp;<a href="destroy.kvihelp">destroy</a> %sock;
 
179
                        &nbsp;&nbsp;}
 
180
                        }
 
181
                </example>
 
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>
 
187
                <example>
 
188
                        event(OnIncomingConnection)
 
189
                        {
 
190
                        &nbsp;&nbsp;if(!<a href="s_this.kvihelp">$this</a>->$accept(<a href="s_this.kvihelp">$this</a>))
 
191
                        &nbsp;&nbsp;{
 
192
                        &nbsp;&nbsp;&nbsp;&nbsp;# Oops! Something went really wrong
 
193
                        &nbsp;&nbsp;&nbsp;&nbsp;<a href="echo.kvihelp">echo</a> Accept failed : <a href="s_this.kvihelp">$this</a>->$lastError()
 
194
                        &nbsp;&nbsp;&nbsp;&nbsp;# Still listening
 
195
                        &nbsp;&nbsp;}
 
196
                        }
 
197
                </example>
 
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
 
202
 
 
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
 
206
 
 
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
 
212
 
 
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>
 
231
 
 
232
        @description:
 
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.
 
240
 
 
241
        @examples:
 
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>
 
249
                <example>
 
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)
 
255
                {
 
256
                &nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : connected to the mailserver...
 
257
                &nbsp;&nbsp;<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
                &nbsp;&nbsp;<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
                &nbsp;&nbsp;<a href="s_this.kvihelp">$this</a>->$write("LIST<a href="s_cr.kvihelp">$cr</a><a href="s_lf.kvihelp">$lf</a>")
 
260
                }
 
261
                # When we get disconnected for some reason remove the object
 
262
                <a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnDisconnect)
 
263
                {
 
264
                &nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : disconnected ($1-)
 
265
                &nbsp;&nbsp;<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
 
266
                }
 
267
                # Data received... here is the clue
 
268
                <a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnDataReceived)
 
269
                {
 
270
                &nbsp;&nbsp;<a href="switch.kvihelp">switch</a>($2-)
 
271
                &nbsp;&nbsp;{
 
272
                &nbsp;&nbsp;&nbsp;&nbsp;<a href="switch.kvihelp">match</a>("+OK * messages*")
 
273
                &nbsp;&nbsp;&nbsp;&nbsp;{
 
274
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : the mailbox contains <a href="s_strrightfromfirst.kvihelp">$strRightFromFirst</a>("+OK ", $1-)
 
275
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
 
276
                &nbsp;&nbsp;&nbsp;&nbsp;}
 
277
                &nbsp;&nbsp;&nbsp;&nbsp;<a href="switch.kvihelp">match</a>("-ERR *")
 
278
                &nbsp;&nbsp;&nbsp;&nbsp;{
 
279
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : received error : $2-
 
280
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : aborting
 
281
                &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="destroye.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
 
282
                &nbsp;&nbsp;&nbsp;&nbsp;}
 
283
                &nbsp;&nbsp;}
 
284
                }
 
285
                # If the connection fails for some reason, remove the object
 
286
                <a href="obj_setevent.kvihelp">obj_setevent</a>(%o, OnConnectFailed)
 
287
                {
 
288
                &nbsp;&nbsp;<a href="echo.kvihelp">echo</a> CHECKMAIL : connect failed : ($1-)
 
289
                &nbsp;&nbsp;<a href="destroy.kvihelp">destroy</a> <a href="s_this.kvihelp">$this</a>
 
290
                }
 
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...
 
296
                </example>
 
297
        @seealso:
 
298
                <a href="syntax_objects.kvihelp">Objects documentation</a>
 
299
*/
 
300
KviScriptSocket::KviScriptSocket(
 
301
        KviScriptObjectController *cntrl, KviScriptObject *p, const char *name, KviScriptObjectClassDefinition *pDef)
 
302
        : KviScriptObject(cntrl, p, name, pDef)
 
303
{
 
304
        m_sock                   = -1;
 
305
        m_domain                 = PF_INET;
 
306
        m_type                   = SOCK_STREAM;
 
307
        m_protocol               = 0;
 
308
        m_error                  = KVI_ERROR_Success;
 
309
        m_mode                   = Ascii;
 
310
        m_iReadBufLen            = 0;
 
311
        m_pReadBuffer            = 0;
 
312
        m_pRsn                   = 0;
 
313
        m_pTmpSn                 = 0;
 
314
        m_state                  = Ready;
 
315
        m_incomingConnectionSock = -1;
 
316
        m_szLocalHostIp          = "127.0.0.1";
 
317
}
 
318
 
 
319
KviScriptSocket::~KviScriptSocket()
 
320
{
 
321
        reset();
 
322
}
 
323
 
 
324
void KviScriptSocket::initializeClassDefinition(KviScriptObjectClassDefinition *d)
 
325
{
 
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);
 
345
 
 
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);
 
362
}
 
363
 
 
364
int KviScriptSocket::builtinFunction_MODE(QPtrList<KviStr> *params, KviStr &buffer)
 
365
{
 
366
        if( m_mode == Ascii )
 
367
                buffer.append("ascii");
 
368
        else
 
369
                buffer.append("hex");
 
370
        return KVI_ERROR_Success;
 
371
}
 
372
 
 
373
int KviScriptSocket::builtinFunction_LOCALHOST(QPtrList<KviStr> *params, KviStr &buffer)
 
374
{
 
375
        buffer.append(m_szLocalHostIp);
 
376
        return KVI_ERROR_Success;
 
377
}
 
378
 
 
379
int KviScriptSocket::builtinFunction_SETDOMAIN(QPtrList<KviStr> *params, KviStr &buffer)
 
380
{
 
381
        if( params ) {
 
382
                KviStr *pDomain = params->first();
 
383
                if( pDomain ) {
 
384
                        if( kvi_strEqualCI(pDomain->ptr(), "inet") )
 
385
                                m_domain = PF_INET;
 
386
                        else if( kvi_strEqualCI(pDomain->ptr(), "unix") )
 
387
                                m_domain = PF_UNIX;
 
388
                        else
 
389
                                return KVI_ERROR_InvalidParameter;
 
390
                        return KVI_ERROR_Success;
 
391
                }
 
392
        }
 
393
        return KVI_ERROR_MissingParameter;
 
394
}
 
395
 
 
396
int KviScriptSocket::builtinFunction_SETTYPE(QPtrList<KviStr> *params, KviStr &buffer)
 
397
{
 
398
        if( params ) {
 
399
                KviStr *pType = params->first();
 
400
                if( pType ) {
 
401
                        if( kvi_strEqualCI(pType->ptr(), "stream") )
 
402
                                m_type = SOCK_STREAM;
 
403
                        else if( kvi_strEqualCI(pType->ptr(), "raw") )
 
404
                                m_type = SOCK_RAW;
 
405
                        else
 
406
                                return KVI_ERROR_InvalidParameter;
 
407
                        return KVI_ERROR_Success;
 
408
                }
 
409
        }
 
410
        return KVI_ERROR_MissingParameter;
 
411
}
 
412
 
 
413
int KviScriptSocket::builtinFunction_SETMODE(QPtrList<KviStr> *params, KviStr &buffer)
 
414
{
 
415
        if( params ) {
 
416
                KviStr *pType = params->first();
 
417
                if( pType ) {
 
418
                        if( kvi_strEqualCI(pType->ptr(), "ascii") ) {
 
419
                                if( m_mode != Ascii ) {
 
420
                                        m_mode        = Ascii;
 
421
                                        m_iReadBufLen = 0;
 
422
                                        if( m_pReadBuffer )
 
423
                                                kvi_free(m_pReadBuffer);
 
424
                                        m_pReadBuffer = 0;
 
425
                                }
 
426
                        } else if( kvi_strEqualCI(pType->ptr(), "hex") ) {
 
427
                                if( m_mode != Hex ) {
 
428
                                        m_mode        = Hex;
 
429
                                        m_iReadBufLen = 0;
 
430
                                        if( m_pReadBuffer )
 
431
                                                kvi_free(m_pReadBuffer);
 
432
                                        m_pReadBuffer = 0;
 
433
                                }
 
434
                        } else return KVI_ERROR_InvalidParameter;
 
435
                        return KVI_ERROR_Success;
 
436
                }
 
437
        }
 
438
        return KVI_ERROR_MissingParameter;
 
439
}
 
440
 
 
441
int KviScriptSocket::builtinFunction_SETPROTOCOL(QPtrList<KviStr> *params, KviStr &buffer)
 
442
{
 
443
        if( params ) {
 
444
                KviStr *pProto = params->first();
 
445
                if( pProto ) {
 
446
                        struct protoent *p = getprotobyname(pProto->ptr());
 
447
                        if( p )
 
448
                                m_protocol = p->p_proto;
 
449
                        else
 
450
                                return KVI_ERROR_InvalidParameter;
 
451
                        return KVI_ERROR_Success;
 
452
                }
 
453
        }
 
454
        return KVI_ERROR_MissingParameter;
 
455
}
 
456
 
 
457
int KviScriptSocket::builtinFunction_WRITE(QPtrList<KviStr> *params, KviStr &buffer)
 
458
{
 
459
        if( params ) {
 
460
                KviStr *pData = params->first();
 
461
                if( pData ) {
 
462
                        KviStr tmp(KviStr::Format, "%d", writeData(pData->ptr(), pData->len()));
 
463
                        buffer.append(tmp.ptr());
 
464
                        return KVI_ERROR_Success;
 
465
                }
 
466
        }
 
467
        return KVI_ERROR_MissingParameter;
 
468
}
 
469
 
 
470
int KviScriptSocket::builtinFunction_WRITEHEX(QPtrList<KviStr> *params, KviStr &buffer)
 
471
{
 
472
        if( params ) {
 
473
                KviStr *pData = params->first();
 
474
                if( pData ) {
 
475
                        char *buf = 0;
 
476
                        int len = pData->hexToBuffer(&buf);
 
477
                        KviStr tmp(KviStr::Format, "%d", writeData(buf, len));
 
478
                        if( buf )
 
479
                                kvi_free(buf);
 
480
                        buffer.append(tmp.ptr());
 
481
                        return KVI_ERROR_Success;
 
482
                }
 
483
        }
 
484
        return KVI_ERROR_MissingParameter;
 
485
}
 
486
 
 
487
int KviScriptSocket::builtinFunction_CLOSE(QPtrList<KviStr> *params, KviStr &buffer)
 
488
{
 
489
        if( m_sock == -1 )
 
490
                buffer.append('0');
 
491
        else {
 
492
                reset();
 
493
                buffer.append('1');
 
494
        }
 
495
        return KVI_ERROR_Success;
 
496
}
 
497
 
 
498
int KviScriptSocket::builtinFunction_PROTOCOL(QPtrList<KviStr> *params, KviStr &buffer)
 
499
{
 
500
        struct protoent *p = getprotobynumber(m_protocol);
 
501
        if( p )
 
502
                buffer.append(p->p_name);
 
503
        else {
 
504
                KviStr num;
 
505
                num.setNum(m_protocol);
 
506
                buffer.append(num.ptr());
 
507
        }
 
508
        return KVI_ERROR_Success;
 
509
}
 
510
 
 
511
int KviScriptSocket::builtinFunction_TYPE(QPtrList<KviStr> *params, KviStr &buffer)
 
512
{
 
513
        if( m_type == SOCK_STREAM )
 
514
                buffer.append("stream");
 
515
        else
 
516
                buffer.append("raw");
 
517
        return KVI_ERROR_Success;
 
518
}
 
519
 
 
520
int KviScriptSocket::builtinFunction_HOST(QPtrList<KviStr> *params, KviStr &buffer)
 
521
{
 
522
        if( m_state == Connected )
 
523
                buffer.append(m_szHostIp);
 
524
        return KVI_ERROR_Success;
 
525
}
 
526
 
 
527
int KviScriptSocket::builtinFunction_PORT(QPtrList<KviStr> *params, KviStr &buffer)
 
528
{
 
529
        if( m_state == Connected )
 
530
                buffer.append(m_szPort);
 
531
        return KVI_ERROR_Success;
 
532
}
 
533
 
 
534
int KviScriptSocket::builtinFunction_STATE(QPtrList<KviStr> *params, KviStr &buffer)
 
535
{
 
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;
 
541
}
 
542
 
 
543
int KviScriptSocket::builtinFunction_DOMAIN(QPtrList<KviStr> *params, KviStr &buffer)
 
544
{
 
545
        if( m_domain == PF_INET )
 
546
                buffer.append("inet");
 
547
        else
 
548
                buffer.append("unix");
 
549
        return KVI_ERROR_Success;
 
550
}
 
551
 
 
552
int KviScriptSocket::builtinFunction_CONNECT(QPtrList<KviStr> *params, KviStr &buffer)
 
553
{
 
554
        if( params ) {
 
555
                KviStr *pHost = params->first();
 
556
                if( pHost ) {
 
557
                        KviStr *pPort = params->next();
 
558
                        if( pPort ) {
 
559
                                buffer.append(doConnect(pHost, pPort) ? '1' : '0');
 
560
                                return KVI_ERROR_Success;
 
561
                        }
 
562
                }
 
563
        }
 
564
        return KVI_ERROR_MissingParameter;
 
565
}
 
566
 
 
567
int KviScriptSocket::builtinFunction_LISTEN(QPtrList<KviStr> *params, KviStr &buffer)
 
568
{
 
569
        if( params ) {
 
570
                KviStr *pHost = params->first();
 
571
                if( pHost ) {
 
572
                        KviStr *pPort = params->next();
 
573
                        if( pPort ) {
 
574
                                buffer.append(doListen(pHost, pPort) ? '1' : '0');
 
575
                                return KVI_ERROR_Success;
 
576
                        }
 
577
                }
 
578
        }
 
579
        return KVI_ERROR_MissingParameter;
 
580
}
 
581
 
 
582
int KviScriptSocket::builtinFunction_LASTERROR(QPtrList<KviStr> *params, KviStr &buffer)
 
583
{
 
584
        buffer.append(kvi_getErrorString(m_error));
 
585
        return KVI_ERROR_Success;
 
586
}
 
587
 
 
588
int KviScriptSocket::builtinFunction_ACCEPT(QPtrList<KviStr> *params, KviStr &buffer)
 
589
{
 
590
        if( params ) {
 
591
                KviStr *pObId = params->first();
 
592
                if( pObId ) {
 
593
                        buffer.append(doAccept(pObId->ptr()) ? '1' : '0');
 
594
                        return KVI_ERROR_Success;
 
595
                }
 
596
        }
 
597
        return KVI_ERROR_MissingParameter;
 
598
}
 
599
 
 
600
bool KviScriptSocket::doListen(KviStr *szLocalAddr, KviStr *szPort)
 
601
{
 
602
        struct in_addr     inAddress;
 
603
        struct sockaddr_in hostSockAddr;
 
604
 
 
605
        if( m_sock != -1 ) {
 
606
                m_error = KVI_ERROR_AnotherConnectionInProgress;
 
607
                return false;
 
608
        }
 
609
 
 
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;
 
613
                else {
 
614
                        m_error = KVI_ERROR_InvalidIpAddress;
 
615
                        return false;
 
616
                }
 
617
        } else hostSockAddr.sin_addr = inAddress;
 
618
 
 
619
        bool bOk = false;
 
620
        unsigned short int port = szPort->toUInt(&bOk);
 
621
        if( !bOk ) {
 
622
                m_error = KVI_ERROR_InvalidPort;
 
623
                return false;
 
624
        }
 
625
 
 
626
        hostSockAddr.sin_family = m_domain; // AF_INET == PF_INET and AF_UNIX == PF_UNIX
 
627
        hostSockAddr.sin_port   = htons(port);
 
628
 
 
629
        m_sock = socket(m_domain, m_type, m_protocol);
 
630
        if( m_sock < 0 ) {
 
631
                m_error = KVI_ERROR_SocketCreationFailed;
 
632
                return false;
 
633
        }
 
634
        if( fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0 ) {
 
635
                m_error = KVI_ERROR_AsyncSocketFailed;
 
636
                return false;
 
637
        }
 
638
 
 
639
        int theError;
 
640
        if( (theError = bind(m_sock, (struct sockaddr *) &hostSockAddr, sizeof(hostSockAddr))) < 0 ) {
 
641
                setErrorFromSystemError(theError);
 
642
                reset();
 
643
                return false;
 
644
        }
 
645
        if( (theError = listen(m_sock, 1)) < 0 ) {
 
646
                setErrorFromSystemError(theError);
 
647
                reset();
 
648
                return false;
 
649
        }
 
650
 
 
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)));
 
654
 
 
655
        m_pTmpSn->setEnabled(true);
 
656
 
 
657
        m_szHostIp = "";
 
658
        m_szPort   = "";
 
659
        m_error    = KVI_ERROR_Success;
 
660
        m_state    = Listening;
 
661
 
 
662
        return true;
 
663
}
 
664
 
 
665
void KviScriptSocket::listenNotifierFired(int)
 
666
{
 
667
        struct sockaddr_in connectedAddr;
 
668
 
 
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");
 
673
        else {
 
674
                // Connected
 
675
                m_szIncomingConnectionPort.setNum(ntohs(connectedAddr.sin_port));
 
676
                kvi_binaryIpToString(connectedAddr.sin_addr, m_szIncomingConnectionHostIp);
 
677
 
 
678
                KviStr parms;
 
679
                triggerEvent("OnIncomingConnection", parms);
 
680
 
 
681
                if( m_incomingConnectionSock != -1 ) {
 
682
                        // Not accepted
 
683
                        close(m_incomingConnectionSock);
 
684
                        m_incomingConnectionSock     = -1;
 
685
                        m_szIncomingConnectionPort   = "";
 
686
                        m_szIncomingConnectionHostIp = "";
 
687
                }
 
688
        }
 
689
}
 
690
 
 
691
bool KviScriptSocket::doAccept(const char *sockObjectId)
 
692
{
 
693
        KviScriptObject *o = (KviScriptObject *) controller()->findObjectById(sockObjectId);
 
694
 
 
695
        if( !o ) {
 
696
                m_error = KVI_ERROR_ObjectNotFound;
 
697
                return false;
 
698
        }
 
699
        if( !o->inherits("KviScriptSocket") ) {
 
700
                m_error = KVI_ERROR_ObjectIsNotASocket;
 
701
                return false;
 
702
        }
 
703
        KviScriptSocket *s = (KviScriptSocket *) o;
 
704
 
 
705
        if( !s>hasPendingConnection() ) {
 
706
                m_error = KVI_ERROR_NoConnectionToAccept;
 
707
                return false;
 
708
        }
 
709
 
 
710
        if( m_state != Ready ) {
 
711
                reset(); // If o == "this" it will work; reset() will not clear the incomingConnectionSock
 
712
        }
 
713
 
 
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   = "";
 
720
        m_state                         = Connected;
 
721
        m_error                         = KVI_ERROR_Success;
 
722
 
 
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);
 
726
 
 
727
        setLocalHostIp();
 
728
 
 
729
        return true;
 
730
}
 
731
 
 
732
bool KviScriptSocket::hasPendingConnection()
 
733
{
 
734
        return (m_incomingConnectionSock != -1);
 
735
}
 
736
 
 
737
void KviScriptSocket::setLocalHostIp()
 
738
{
 
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) )
 
744
                                return;
 
745
                }
 
746
        }
 
747
        m_szLocalHostIp = "127.0.0.1";
 
748
}
 
749
 
 
750
bool KviScriptSocket::doConnect(KviStr *szHost, KviStr *szPort)
 
751
{
 
752
        struct in_addr     inAddress;
 
753
        struct sockaddr_in hostSockAddr;
 
754
 
 
755
        if( m_sock != -1 ) {
 
756
                m_error = KVI_ERROR_AnotherConnectionInProgress;
 
757
                return false;
 
758
        }
 
759
 
 
760
        if( !kvi_stringIpToBinaryIp(szHost->ptr(), &inAddress) ) {
 
761
                m_error = KVI_ERROR_InvalidIpAddress;
 
762
                return false;
 
763
        }
 
764
 
 
765
        bool bOk = false;
 
766
        unsigned short int port = szPort->toUInt(&bOk);
 
767
        if( !bOk ) {
 
768
                m_error = KVI_ERROR_InvalidPort;
 
769
                return false;
 
770
        }
 
771
 
 
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;
 
775
 
 
776
        // Let's go
 
777
        m_sock = socket(m_domain, m_type, m_protocol);
 
778
        if( m_sock < 0 ) {
 
779
                m_error = KVI_ERROR_SocketCreationFailed;
 
780
                return false;
 
781
        }
 
782
 
 
783
        if( fcntl(m_sock, F_SETFL, O_NONBLOCK) < 0 ) {
 
784
                m_error = KVI_ERROR_AsyncSocketFailed;
 
785
                return false;
 
786
        }
 
787
 
 
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 )
 
794
                                        sockError = 0;
 
795
                        }
 
796
                        if( sockError > 0 )
 
797
                                setErrorFromSystemError(sockError);
 
798
                        else
 
799
                                m_error = KVI_ERROR_UnknownError; // Error 0?
 
800
                        return false;
 
801
                }
 
802
        }
 
803
 
 
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)));
 
807
 
 
808
        m_pTmpSn->setEnabled(true);
 
809
        // Set the timer
 
810
        m_szHostIp = szHost->ptr();
 
811
        m_szPort   = szPort->ptr();
 
812
        m_error    = KVI_ERROR_Success;
 
813
        m_state    = Connecting;
 
814
        return true;
 
815
}
 
816
 
 
817
void KviScriptSocket::writeNotifierFired(int)
 
818
{
 
819
        int sockError;
 
820
        socklen_t iSize = sizeof(int);
 
821
        if( getsockopt(m_sock, SOL_SOCKET, SO_ERROR, (void *) &sockError, &iSize) < 0 )
 
822
                sockError = -1;
 
823
        if( sockError != 0 ) {
 
824
                // Failed
 
825
                if( sockError > 0 )
 
826
                        setErrorFromSystemError(sockError);
 
827
                else
 
828
                        m_error = KVI_ERROR_UnknownError; // Error 0?
 
829
                KviStr parms = kvi_getErrorString(m_error);
 
830
                triggerEvent("OnConnectFailed", parms);
 
831
                reset();
 
832
        } else {
 
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 )
 
839
                        return; // Killed?
 
840
 
 
841
                QObject::connect(m_pRsn, SIGNAL(activated(int)), this, SLOT(receivedData(int)));
 
842
                m_pRsn->setEnabled(true);
 
843
                delete m_pTmpSn;
 
844
                m_pTmpSn = 0;
 
845
                m_state  = Connected;
 
846
                setLocalHostIp();
 
847
        }
 
848
}
 
849
 
 
850
void KviScriptSocket::reset()
 
851
{
 
852
        if( m_sock != -1 ) {
 
853
                close(m_sock);
 
854
                m_sock = -1;
 
855
        }
 
856
        m_szHostIp = "";
 
857
        m_szPort   = "";
 
858
        if( m_pReadBuffer ) {
 
859
                kvi_free(m_pReadBuffer);
 
860
                m_pReadBuffer = 0;
 
861
        }
 
862
        m_iReadBufLen = 0;
 
863
        if( m_pRsn ) {
 
864
                delete m_pRsn;
 
865
                m_pRsn = 0;
 
866
        }
 
867
        if( m_pTmpSn ) {
 
868
                delete m_pTmpSn;
 
869
                m_pTmpSn = 0;
 
870
        }
 
871
        m_state = Ready;
 
872
}
 
873
 
 
874
void KviScriptSocket::setErrorFromSystemError(int errorNum)
 
875
{
 
876
        switch( 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;
 
886
        }
 
887
}
 
888
 
 
889
void KviScriptSocket::handleInvalidSocketRead(int readLength)
 
890
{
 
891
        if( readLength == 0 ) {
 
892
                m_error = KVI_ERROR_RemoteEndClosedConnection;
 
893
                KviStr parms = kvi_getErrorString(m_error);
 
894
                triggerEvent("OnDisconnect", parms);
 
895
                reset();
 
896
        } else {
 
897
                // Check for transmission errors
 
898
                if( (errno != EAGAIN) && (errno != EINTR) ) {
 
899
                        if( errno > 0 )
 
900
                                setErrorFromSystemError(errno);
 
901
                        else
 
902
                                m_error = KVI_ERROR_RemoteEndClosedConnection;
 
903
                        KviStr parms = kvi_getErrorString(m_error);
 
904
                        triggerEvent("OnDisconnect", parms);
 
905
                        reset();
 
906
                } // else: transient error; wait again
 
907
        }
 
908
}
 
909
 
 
910
void KviScriptSocket::receivedData(int fd)
 
911
{
 
912
        // Read data
 
913
        char buffer[1025];
 
914
        int readLength = read(m_sock, buffer, 1024);
 
915
 
 
916
        if( readLength <= 0 ) {
 
917
                handleInvalidSocketRead(readLength);
 
918
                return;
 
919
        }
 
920
 
 
921
        if( m_mode == Hex ) {
 
922
                KviStr parms;
 
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 )
 
930
                        return; // Closed
 
931
                m_pRsn->setEnabled(true);
 
932
        } else {
 
933
                // Terminate our buffer
 
934
                (*(buffer + readLength)) = '\0';
 
935
 
 
936
                register char *p     = buffer;
 
937
                char *beginOfCurData = buffer;
 
938
                int   bufLen         = 0;
 
939
                char *messageBuffer  = (char *) kvi_malloc(1);
 
940
 
 
941
                // Shut up the socket notifier in case we enter
 
942
                // a local loop somewhere while processing data
 
943
                m_pRsn->setEnabled(false);
 
944
 
 
945
                while( *p ) {
 
946
                        if( (*p == '\r' ) || (*p == '\n') ) {
 
947
                                // Found a CR or LF.
 
948
                                // Prepare a message buffer
 
949
                                bufLen = p - beginOfCurData;
 
950
                                // Check for previous unterminated data
 
951
                                KviStr parms;
 
952
 
 
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);
 
960
                                        m_pReadBuffer = 0;
 
961
 
 
962
                                        parms.sprintf("%d %s", bufLen + m_iReadBufLen, messageBuffer);
 
963
                                        m_iReadBufLen = 0;
 
964
                                } else {
 
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);
 
970
                                }
 
971
 
 
972
                                triggerEvent("OnDataReceived", parms);
 
973
                                // Check if we were closed in the mean time
 
974
                                if( m_state != Connected ) {
 
975
                                        kvi_free(messageBuffer);
 
976
                                        return;
 
977
                                }
 
978
 
 
979
                                while( *p && ((*p == '\r') || (*p == '\n')) )
 
980
                                        p++;
 
981
                                beginOfCurData = p;
 
982
                        } else p++;
 
983
 
 
984
                        if( inDelayedDestroy() ) {
 
985
                                // Already being destroyed -- destroyed while processing data!
 
986
                                kvi_free(messageBuffer);
 
987
                                return;
 
988
                        }
 
989
                }
 
990
                // Now *p == '\0'
 
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;
 
1002
                        } else {
 
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);
 
1007
                        }
 
1008
                }
 
1009
 
 
1010
                kvi_free(messageBuffer);
 
1011
                // Ready to receive
 
1012
                m_pRsn->setEnabled(true);
 
1013
        }
 
1014
}
 
1015
 
 
1016
int KviScriptSocket::writeData(char *buffer, int len)
 
1017
{
 
1018
        if( m_sock == -1 ) {
 
1019
                m_error = KVI_ERROR_NotConnected;
 
1020
                return -1;
 
1021
        }
 
1022
 
 
1023
        int result = write(m_sock, buffer, len);
 
1024
        if( result >= len )
 
1025
                return len;
 
1026
        else {
 
1027
                // Oops. Error?
 
1028
                if( (errno == EAGAIN) || (errno == EINTR) )
 
1029
                        return 0;
 
1030
                {
 
1031
                        // Disconnected
 
1032
                        setErrorFromSystemError(errno);
 
1033
                        KviStr parms = kvi_getErrorString(m_error);
 
1034
                        triggerEvent("OnDisconnect", parms);
 
1035
                        reset();
 
1036
                        return -1;
 
1037
                }
 
1038
        }
 
1039
}
 
1040
 
 
1041
#include "m_kvi_script_socket.moc"