1
//=============================================================================
3
// File : KvsObject_socket.cpp
4
// Creation date : Sun Nov 11 03:13:45 2001 GMT by Szymon Stefanek
6
// This file is part of the KVIrc irc client distribution
7
// Copyright (C) 2001-2010 Szymon Stefanek (pragma at kvirc dot net)
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//=============================================================================
25
#include "KvsObject_socket.h"
26
#include "KvsObject_memoryBuffer.h"
27
#include "KvsObject_file.h"
31
#include "kvi_settings.h"
32
#include "KviQString.h"
34
#include "kvi_debug.h"
37
#include "KviFileUtils.h"
38
#include "KviLocale.h"
40
#include "KviNetUtils.h"
41
#include "KviDnsResolver.h"
43
#include "kvi_socket.h"
44
#include "KviMemory.h"
45
#include "KviDataBuffer.h"
50
#include <QAbstractSocket>
51
#include <QHostAddress>
54
const char * const sockstate_tbl[] = {
64
const char * const sockerrors_tbl[] = {
74
"SocketAddressNotAvailable",
75
"UnsupportedSocketOperation",
76
"ProxyAuthenticationRequired",
78
"UnfinishedSocketOperation",
79
"ProxyConnectionRefused",
80
"ProxyConnectionClosed",
81
"ProxyConnectionTimeout",
95
A Ipv4/Ipv6 TCP socket
99
This class provides a standard TCP/IP socket functionality.[br]
100
It can be used either for connecting to a remote host or to listening for incoming connections.[br]
101
If the KVIrc executable has been compiled with the IPV6 protocol support, this socket also supports it.[br]
103
!fn: <integer or string> $status(<asString:boolean>)
104
Returns the status of the socket :[br]
113
!fn: $connect(<host>,<port>)
114
Attempts a connection to <host> on port <port>.[br]
115
<host> can be a numeric internet address (either Ipv4 or Ipv6 (if supported)) or a hostname.[br]
116
If a hostname is used, a DNS lookup is performed (the socket enters the "dns call" state).[br]
117
This function returns 1 if the connect attempt can be successfully initiated,
118
0 otherwise.[br] In fact, this function returns 0 only if the supplied <port> parameter
119
is invalid or the socket is in an incoherent state (already connected or listening):
120
for a newly created socket and with a valid <port> number you can safely ignore
121
the return value.[br]
122
Please note that the connection is asynchronous: when this function returns
123
the socket is NOT connected: it has just initiated the connect attempt
124
and you will be notified of the attempt result by an asynchronous event call:
125
in case of failure, $connectFailedEvent() will be called, in case of
126
succes, $connectEvent() will be called.
128
!fn: $listen([<port>[,<interface>[,<force_ipv6>]]])
129
Attempts to listen on the specified <port> and <interface>.[br]
130
If <port> is not passed it is assumed to be 0, if <interface> is not passed, it is assumed to be
131
"any interface" (INADDR_ANY).[br] Port 0 means that the kernel should choose a "random" port to listen on.[br]
132
If the <interface> is recognized as IPV6 address, and IPV6 is supported, the socket listens
133
in IPV6 mode. If <interface> is an empty string and <force_ipv6> is 1 the socket listens
134
on "any ipv6 interface".[br]
135
This function returns '1' in case of success and '0' in case of failure.[br]
136
On some systems listening in the IPV6 namespace allows to accept also IPV4 connections (this includes
137
linux but not windows afaik).[br]
138
When an incoming connection will arrive, $incomingConnectionEvent() will be called.
140
!fn: $connectedEvent()
141
This function is called when a connection attempt has been successfully completed.
142
The socket is actually connected to [classfnc:socket]$remoteIp[/classfnc]() on
143
[classfnc:socket]$remotePort[/classfnc](). You can start
144
writing data and you may expect [classfnc:socket]$dataAvailableEvent[/classfnc]() to be
147
!fn: $incomingConnectionEvent(<socket:h_object>)
148
This function is called when an incoming connection arrives over a socket in listening state.[br]
149
You must return 1 if you to terminad this incoming connectioncall [classfnc:socket]$accept[/classfnc]() passing a newly created socket object
150
to accept and handle the connection.[br] If you don't call [classfnc:socket]$accept[/classfnc]()
151
the incoming connection will be automatically terminated.
153
!fn: $connectFailedEvent(<reason>)
154
This function is called when a connection attempt fails for some reason. <reason> contains
155
the error string.[br]
156
This function may be called only between a call to [classfnc:socket]$connect[/classfnc]() and
157
the [classfnc:socket]$connectEvent[/classfnc]().
159
!fn: $disconnectEvent([error])
160
This function is called when a connection is terminated either cleanly or because of an error.[br]
161
[error] is an empty string in case of a "clean" termination (connection closed by the remote host)
162
or is a message describing the socket error that caused the connection to be interrupted.
164
!fn: $dataAvailableEvent(<data_length>)
165
This function is called when some data is available to be read: the <data_length> parameter specifies
166
the length of the available data in bytes.[br]
167
You can use one of the $read* functions to obtain the data
169
!fn: $read(<length>[,<hobject>])
170
Reads at most <length> bytes of data from the socket. If <length> is anything "outside" the
171
available data range (<length> < 0 or <length> > available_data_length), this function
172
returns all the available data.[br]
173
By default this function can deal ascii data only: NULL characters are transformed to
174
ASCII characters 255. You can pass a [class]memorybuffer[/class] object to read binary data.
176
!fn: $write(<data, array,files or hobject>[,length])
177
Writes <data> to the socket.[br]
178
This function can deal with binary data passing a [class]memorybuffer[/class] object[br]
179
Please note that when this function finishes it does not mean that the data has reached the remote end.[br]
180
Basically it does not even mean that the data has been sent to the remote host.[br]
181
The data is enqueued for sending and will be sent as soon as possible.[br]
182
Using an array you can pass bytes or data string like this: @$write($array($(0xff),$(0xff),$(0xff),$(0xff),"This is an example"));
183
If you're going to [cmd]delete[/cmd] this object just after the $write call, you should
184
call [classfnc:socket]$close[/classfnc]() just before [cmd]delete[/cmd] to ensure the data delivery.
187
Resets this socket state: kills any pending or active connection. After a close() call
188
the socket may be used for a new connection.[br]
189
If there is an active connection, there is a last attempt to flush the pending outgoing data.[br]
190
You don't need to call $close() if you [cmd]delete[/cmd] the socket: KVIrc will
191
reset the socket state automatically and free the memory. But if you want to ensure data delivery
192
after a $write call sequece and just before a [cmd]delete[/cmd], $close() is the only chance to do it.
195
Returns the IP address of the remote end of this socket.[br]
196
The return value is meaningful only if the socket is in connected or connecting state.
198
!fn: $setProtocol(<protocol>)
199
Let KVIrc use TCP or UDP protocol
202
Returns the port of the remote end of this socket.[br]
203
The return value is meaningful only if the socket is in connected or connecting state.
206
Returns the IP address of the local end of this socket.[br]
207
The return value is meaningful only if the socket is in connected, listening or connecting state.
210
Returns the port of the local end of this socket.[br]
211
The return value is meaningful only if the socket is in connected, listening or connecting state.
215
// Server socket: listen 8080 port and answer to requests (multi-threaded)
216
class("webserver","socket")
218
function incomingConnectionEvent()
220
// incoming connection socket passed by the framework
222
debug "Webserver incoming Conection from: %socket->$remoteIp : %socket->$remotePort"
223
%socket->$write("HTTP/1.0 200 OK\n\n<html><head></head><body><h1>KVIrc Webserver</h1></body></html>\n")
224
// tells KVIrc no need this socket anymore
227
function constructor()
229
debug listen @$listen(8080, "127.0.0.1")
232
// finally start webserver
233
%WebS = $new(webserver)
236
// Client socket - go to google and grab request header[br]
237
class("httprequest","socket")
239
function errorEvent()
241
// the connection to the server failed
242
debug "Connection failed: "$0
245
function disconnectedEvent()
247
// connection has been closed
248
debug "Connection is closed"
251
function destructor()
253
// if the socket is still open close it
254
if(@$status() == "Connected") @$close()
256
function stateChangedEvent()
258
debug socket state $0
260
function dataAvailableEvent()
262
// reading the received data
263
debug reading $0 bytes
264
%newdata = @$read($0)
266
// close and delete the socket
270
function constructor()
272
// connect to the server
273
@$connect("www.google.com",80)
275
function connectedEvent()
277
// connection is complete
278
// send a request to receive the headers only from http://www.google.com/
280
debug written bytes @$write("HEAD / HTTP/1.1\r\nHost: www.google.de\r\nConnection: Close\r\nUser-Agent: KVIrc socket\r\n\r\n") on socket;
283
%Temp = $new(httprequest)
287
KVSO_BEGIN_REGISTERCLASS(KvsObject_socket,"socket","object")
289
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,status)
290
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,remotePort)
291
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,remoteIp)
292
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,localIp)
293
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,localPort)
294
KVSO_REGISTER_HANDLER(KvsObject_socket,"connect",functionConnect)
295
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,close)
296
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,read)
297
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,write)
298
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,bytesAvailable)
300
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,setProtocol)
301
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,listen)
302
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,dataAvailableEvent)
303
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,incomingConnectionEvent)
304
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,connectedEvent)
305
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,disconnectedEvent)
306
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,errorEvent)
307
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,hostFoundEvent)
308
KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_socket,stateChangedEvent)
310
KVSO_END_REGISTERCLASS(KvsObject_socket)
312
bool KvsObject_socket::init(KviKvsRunTimeContext *c,KviKvsVariantList *)
314
m_pSocket = new QTcpSocket();
322
KVSO_BEGIN_CONSTRUCTOR(KvsObject_socket,KviKvsObject)
323
KVSO_END_CONSTRUCTOR(KvsObject_socket)
325
KVSO_BEGIN_DESTRUCTOR(KvsObject_socket)
326
if (m_pSocket)delete m_pSocket;
328
if (m_pServer) delete m_pServer;
330
KVSO_END_DESTRUCTOR(KvsObject_socket)
331
//----------------------
334
KVSO_CLASS_FUNCTION(socket,status)
337
c->returnValue()->setString(getStateString(m_pSocket->state()));
341
KVSO_CLASS_FUNCTION(socket,close)
344
m_pSocket->disconnectFromHost();
348
KVSO_CLASS_FUNCTION(socket,remotePort)
350
c->returnValue()->setInteger(m_pSocket->peerPort());
354
KVSO_CLASS_FUNCTION(socket,remoteIp)
356
c->returnValue()->setString(m_pSocket->peerAddress().toString());
360
KVSO_CLASS_FUNCTION(socket,localPort)
362
c->returnValue()->setInteger(m_pSocket->localPort());
365
KVSO_CLASS_FUNCTION(socket,bytesAvailable)
367
c->returnValue()->setInteger(m_pSocket->bytesAvailable());
370
KVSO_CLASS_FUNCTION(socket,localIp)
372
c->returnValue()->setString(m_pSocket->localAddress().toString());
377
KVSO_CLASS_FUNCTION(socket,read)
380
KviKvsObject * pObject;
381
kvs_hobject_t hObject;
382
KVSO_PARAMETERS_BEGIN(c)
383
KVSO_PARAMETER("length",KVS_PT_INTEGER,KVS_PF_OPTIONAL,iLen)
384
KVSO_PARAMETER("hobject",KVS_PT_HOBJECT,KVS_PF_OPTIONAL,hObject)
385
KVSO_PARAMETERS_END(c)
386
if (iLen>m_pSocket->bytesAvailable() || !iLen) iLen=m_pSocket->bytesAvailable();
389
pObject=KviKvsKernel::instance()->objectController()->lookupObject(hObject);
392
c->warning(__tr2qs_ctx("Buffer parameter is not an object","objects"));
395
if(pObject->inheritsClass("memorybuffer"))
397
QByteArray *pBuffer=((KvsObject_memoryBuffer *)pObject)->pBuffer();
398
int oldsize=pBuffer->size();
399
pBuffer->resize(oldsize+iLen);
400
m_pSocket->read(pBuffer->data()+oldsize,iLen);
402
else if(pObject->inheritsClass("file"))
404
KviFile *pFile=((KvsObject_file *)pObject)->file();
405
if (!pFile->isOpen())
407
c->warning(__tr2qs_ctx("File is not open!","objects"));
410
pFile->write(m_pSocket->read(iLen));
414
c->warning(__tr2qs_ctx("Buffer parameter is not a memorybuffer object","objects"));
421
// convert NULLS to char 255
422
char * buffer = (char*) KviMemory::allocate(iLen);
423
m_pSocket->read(buffer,iLen);
424
for(unsigned int i = 0;i < iLen;i++)
426
if(!buffer[i]) buffer[i] = (char)(255);
428
QString tmpBuffer = QString::fromUtf8(buffer,iLen);
429
c->returnValue()->setString(tmpBuffer);
430
KviMemory::free(buffer);
437
KVSO_CLASS_FUNCTION(socket,write)
440
KviKvsObject * pObject;
441
KviKvsVariant * pVariantData;
442
kvs_hobject_t hObject;
443
KVSO_PARAMETERS_BEGIN(c)
444
KVSO_PARAMETER("data_or_file_or_memorybuffer",KVS_PT_VARIANT,0,pVariantData)
445
KVSO_PARAMETER("length",KVS_PT_UNSIGNEDINTEGER,KVS_PF_OPTIONAL,uLen)
446
KVSO_PARAMETERS_END(c)
447
if (pVariantData->isHObject())
449
pVariantData->asHObject(hObject);
450
pObject = KviKvsKernel::instance()->objectController()->lookupObject(hObject);
453
c->warning(__tr2qs_ctx("Buffer parameter is not an object","objects"));
456
if(pObject->inheritsClass("memorybuffer"))
458
QByteArray *p=((KvsObject_memoryBuffer *)pObject)->pBuffer();
459
m_pSocket->write(*p);
461
else if(pObject->inheritsClass("file"))
463
KviFile * pFile = ((KvsObject_file *)pObject)->file();
466
c->warning(__tr2qs_ctx("File is not open!","objects"));
469
if(!uLen) uLen = pFile->size();
470
kvs_int_t size = pFile->size();
472
m_pSocket->write((const char*)pFile->read(uLen).data(),uLen);
473
c->returnValue()->setBoolean((size-pFile->pos()==0));
475
c->warning(__tr2qs_ctx("Buffer parameter is not a memorybuffer or file object","objects"));
479
if(pVariantData->isArray())
481
KviKvsArray * pArray = pVariantData->array();
482
for(unsigned int i=0; i < pArray->size(); i++)
484
KviKvsVariant * pVar = pArray->at(i);
486
if(pVar->asInteger(iValue))
488
if(iValue < 256 && iValue >= 0)
490
m_pSocket->putChar(iValue);
493
c->warning(__tr2qs_ctx("Only values in the range of 0-255 are allowed: integer %d is out of range","objects"),iValue);
500
pVar->asString(szData);
501
QByteArray szData8 = szData.toUtf8();
502
m_pSocket->write((const char*)szData8.data(),szData8.length());
504
c->warning(__tr2qs_ctx("Datatype not supported","objects"));
512
pVariantData->asString(szData);
513
if(!KviFileUtils::fileExists(szData))
515
QByteArray szData8 = szData.toUtf8();
516
if(szData8.length() > 0)
518
qDebug("write on socket %s",szData8.data());
519
kvs_int_t bytes=m_pSocket->write((const char*)szData8.data(),szData8.length());
520
c->returnValue()->setInteger(bytes);
524
f.open(QIODevice::ReadOnly);
525
QByteArray ar = f.readAll();
526
m_pSocket->write((const char*)ar.data(),ar.size());
532
KVSO_CLASS_FUNCTION(socket,setProtocol)
535
KVSO_PARAMETERS_BEGIN(c)
536
KVSO_PARAMETER("protocol",KVS_PT_STRING,0,szProto)
537
KVSO_PARAMETERS_END(c)
538
if (m_pSocket) delete m_pSocket;
539
if(KviQString::equalCI(szProto,"udp")) m_pSocket = new QUdpSocket();
540
else m_pSocket = new QTcpSocket();
544
KVSO_CLASS_FUNCTION(socket,functionConnect)
546
kvs_uint_t uRemotePort;
548
KVSO_PARAMETERS_BEGIN(c)
549
KVSO_PARAMETER("remote_ip",KVS_PT_STRING,0,szRemoteIp)
550
KVSO_PARAMETER("remote_port",KVS_PT_UNSIGNEDINTEGER,0,uRemotePort)
551
KVSO_PARAMETERS_END(c)
552
if (uRemotePort>65535)
554
c->warning(__tr2qs_ctx("Value %d for port is out of range (values allowed are from 0 to 65535)","objects"),uRemotePort);
557
m_pSocket->connectToHost(szRemoteIp,uRemotePort);
558
c->returnValue()->setBoolean(true);
562
KVSO_CLASS_FUNCTION(socket,listen)
564
kvs_uint_t uLocalPort;
566
KVSO_PARAMETERS_BEGIN(c)
567
KVSO_PARAMETER("local_port",KVS_PT_UNSIGNEDINTEGER,0,uLocalPort)
568
KVSO_PARAMETER("interface",KVS_PT_STRING,KVS_PF_OPTIONAL,m_szLocalIp)
569
KVSO_PARAMETERS_END(c)
570
if (uLocalPort>65535)
572
c->warning(__tr2qs_ctx("Value %d for port is out of range (values allowed are from 0 to 65535): switch to random value","objects"),uLocalPort);
575
if(m_pServer) delete m_pServer;
576
m_pServer = new QTcpServer();
577
QHostAddress address(m_szLocalIp);
578
bool bOk=m_pServer->listen(address,uLocalPort);
579
connect(m_pServer,SIGNAL(newConnection()),this,SLOT(slotNewConnection()));
580
c->returnValue()->setBoolean(bOk);
583
void KvsObject_socket::makeConnections()
585
connect(m_pSocket,SIGNAL(connected()),this,SLOT(slotConnected()));
586
connect(m_pSocket,SIGNAL(readyRead()),this,SLOT(slotReadyRead()));
587
connect(m_pSocket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
588
connect(m_pSocket,SIGNAL(error( QAbstractSocket::SocketError)),this,SLOT(slotError(QAbstractSocket::SocketError)));
589
connect(m_pSocket,SIGNAL(hostFound()),this,SLOT(slotHostFound()));
590
connect(m_pSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(slotStateChanged(QAbstractSocket::SocketState)));
591
//proxyAuthenticationRequired ( const QNetworkProxy & proxy, QAuthenticator * authenticator )
593
const char * KvsObject_socket::getStateString(QAbstractSocket::SocketState state)
598
case QAbstractSocket::UnconnectedState:
603
case QAbstractSocket::HostLookupState:
608
case QAbstractSocket::ConnectingState:
613
case QAbstractSocket::ConnectedState:
618
case QAbstractSocket::BoundState:
623
case QAbstractSocket::ListeningState:
628
case QAbstractSocket::ClosingState:
638
return sockstate_tbl[idx];
642
void KvsObject_socket::slotReadyRead()
644
KviKvsVariantList lParams;
645
lParams.append(new KviKvsVariant((kvs_int_t)m_pSocket->bytesAvailable()));
646
callFunction(this,"dataAvailableEvent",&lParams);
648
void KvsObject_socket::slotConnected()
650
KviKvsVariantList *lParams=0;
651
callFunction(this,"connectedEvent",lParams);
654
void KvsObject_socket::slotDisconnected()
656
KviKvsVariantList *lParams=0;
657
callFunction(this,"disconnectedEvent",lParams);
660
void KvsObject_socket::slotError( QAbstractSocket::SocketError socketError )
662
KviKvsVariantList lParams;
663
QString szError = sockerrors_tbl[socketError];
664
lParams.append(new KviKvsVariant(szError));
665
callFunction(this,"errorEvent",&lParams);
668
void KvsObject_socket::slotHostFound()
670
KviKvsVariantList *lParams=0;
671
callFunction(this,"hostFoundEvent",lParams);
674
void KvsObject_socket::slotStateChanged( QAbstractSocket::SocketState socketState )
676
KviKvsVariantList lParams;
677
QString szState = getStateString(socketState);
678
lParams.append(new KviKvsVariant(szState));
679
callFunction(this,"stateChangedEvent",&lParams);
682
void KvsObject_socket::slotNewConnection()
684
qDebug ("New connection");
685
QTcpSocket *pSocket = m_pServer->nextPendingConnection();
686
KviKvsObjectClass * pClass = KviKvsKernel::instance()->objectController()->lookupClass("socket");
687
KviKvsVariantList lParams;
688
KviKvsObject * pObject = pClass->allocateInstance(0,"internalsocket",m_pContext,&lParams);
689
((KvsObject_socket *)pObject)->setInternalSocket(pSocket);
690
kvs_hobject_t hobj=pObject->handle();
691
KviKvsVariantList params(new KviKvsVariant(hobj));
693
KviKvsVariant *retv=new KviKvsVariant(ret);
694
callFunction(this,"incomingConnectionEvent",retv,¶ms);
696
pObject=KviKvsKernel::instance()->objectController()->lookupObject(hobj);
697
if (pObject) pObject->dieNow();
700
KVSO_CLASS_FUNCTION(socket,incomingConnectionEvent)
702
emitSignal("incomingConnection",c,c->params());
705
KVSO_CLASS_FUNCTION(socket,dataAvailableEvent)
707
emitSignal("dataAvailable",c,c->params());
711
KVSO_CLASS_FUNCTION(socket,connectedEvent)
713
emitSignal("connected",c,c->params());
717
KVSO_CLASS_FUNCTION(socket,disconnectedEvent)
719
emitSignal("disconnected",c,c->params());
723
KVSO_CLASS_FUNCTION(socket,errorEvent)
725
emitSignal("error",c,c->params());
729
KVSO_CLASS_FUNCTION(socket,hostFoundEvent)
731
emitSignal("hostFound",c,c->params());
735
KVSO_CLASS_FUNCTION(socket,stateChangedEvent)
737
emitSignal("stateChanged",c,c->params());
741
#ifndef COMPILE_USE_STANDALONE_MOC_SOURCES
742
#include "m_KvsObject_socket.moc"
743
#endif //!COMPILE_USE_STANDALONE_MOC_SOURCES