summaryrefslogtreecommitdiffstats
path: root/src/modules/dcc
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/dcc')
-rw-r--r--src/modules/dcc/Makefile.am66
-rw-r--r--src/modules/dcc/adpcmcodec.cpp294
-rw-r--r--src/modules/dcc/adpcmcodec.h59
-rw-r--r--src/modules/dcc/broker.cpp898
-rw-r--r--src/modules/dcc/broker.h124
-rw-r--r--src/modules/dcc/canvas.cpp301
-rw-r--r--src/modules/dcc/canvas.h74
-rw-r--r--src/modules/dcc/canvaswidget.cpp1601
-rw-r--r--src/modules/dcc/canvaswidget.h322
-rw-r--r--src/modules/dcc/chat.cpp842
-rw-r--r--src/modules/dcc/chat.h101
-rw-r--r--src/modules/dcc/codec.cpp88
-rw-r--r--src/modules/dcc/codec.h57
-rw-r--r--src/modules/dcc/descriptor.cpp224
-rw-r--r--src/modules/dcc/descriptor.h163
-rw-r--r--src/modules/dcc/dialogs.cpp206
-rw-r--r--src/modules/dcc/dialogs.h82
-rw-r--r--src/modules/dcc/gsmcodec.cpp149
-rw-r--r--src/modules/dcc/gsmcodec.h54
-rw-r--r--src/modules/dcc/kvi_dccfiletransfericons.pngbin0 -> 25192 bytes
-rw-r--r--src/modules/dcc/libkvidcc.cpp2766
-rw-r--r--src/modules/dcc/marshal.cpp647
-rw-r--r--src/modules/dcc/marshal.h112
-rw-r--r--src/modules/dcc/requests.cpp1154
-rw-r--r--src/modules/dcc/send.cpp1898
-rw-r--r--src/modules/dcc/send.h260
-rw-r--r--src/modules/dcc/thread.cpp111
-rw-r--r--src/modules/dcc/thread.h79
-rw-r--r--src/modules/dcc/utils.cpp169
-rw-r--r--src/modules/dcc/utils.h34
-rw-r--r--src/modules/dcc/voice.cpp1041
-rw-r--r--src/modules/dcc/voice.h440
-rw-r--r--src/modules/dcc/window.cpp53
-rw-r--r--src/modules/dcc/window.h49
34 files changed, 14518 insertions, 0 deletions
diff --git a/src/modules/dcc/Makefile.am b/src/modules/dcc/Makefile.am
new file mode 100644
index 00000000..8a63b888
--- /dev/null
+++ b/src/modules/dcc/Makefile.am
@@ -0,0 +1,66 @@
+###############################################################################
+# KVirc IRC client Makefile - 10.03.2000 Szymon Stefanek <stefanek@tin.it>
+###############################################################################
+
+tmpdir=$(picsdir)
+
+tmp_DATA=kvi_dccfiletransfericons.png
+
+EXTRA_DIST=kvi_dccfiletransfericons.png
+
+
+AM_CPPFLAGS = -I$(SS_TOPSRCDIR)/src/kvilib/include/ -I$(SS_TOPSRCDIR)/src/kvirc/include/ \
+$(SS_INCDIRS) $(SS_CPPFLAGS) -DGLOBAL_KVIRC_DIR=\"$(globalkvircdir)\"
+
+pluglib_LTLIBRARIES = libkvidcc.la
+
+libkvidcc_la_LDFLAGS = -module -avoid-version $(SS_LDFLAGS) $(SS_LIBDIRS)
+
+libkvidcc_la_SOURCES = adpcmcodec.cpp \
+ broker.cpp \
+ canvas.cpp \
+ canvaswidget.cpp \
+ chat.cpp \
+ codec.cpp \
+ descriptor.cpp \
+ dialogs.cpp \
+ gsmcodec.cpp \
+ libkvidcc.cpp \
+ marshal.cpp \
+ requests.cpp \
+ send.cpp \
+ thread.cpp \
+ utils.cpp \
+ voice.cpp \
+ window.cpp
+
+libkvidcc_la_LIBADD = $(SS_LIBLINK) ../../kvilib/build/libkvilib.la
+
+noinst_HEADERS= adpcmcodec.h \
+ broker.h \
+ canvas.h \
+ canvaswidget.h \
+ chat.h \
+ codec.h \
+ descriptor.h \
+ dialogs.h \
+ gsmcodec.h \
+ marshal.h \
+ send.h \
+ thread.h \
+ utils.h \
+ voice.h \
+ window.h
+
+m_%.moc: %.h
+ $(SS_QT_MOC) $< -o $@
+
+broker.cpp: m_broker.moc
+canvas.cpp: m_canvas.moc
+canvaswidget.cpp: m_canvaswidget.moc
+chat.cpp: m_chat.moc
+dialogs.cpp: m_dialogs.moc
+marshal.cpp: m_marshal.moc
+send.cpp: m_send.moc
+voice.cpp: m_voice.moc
+window.cpp: m_window.moc
diff --git a/src/modules/dcc/adpcmcodec.cpp b/src/modules/dcc/adpcmcodec.cpp
new file mode 100644
index 00000000..65f12a00
--- /dev/null
+++ b/src/modules/dcc/adpcmcodec.cpp
@@ -0,0 +1,294 @@
+//
+// File : adpcmcodec.cpp
+// Creation date : Wed Aug 22 19:12:50 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+//
+// Code derived from adpcm.c : Intel ADPCM coder/decoder
+// Adapted for the KVirc distribution by Szymon Stefanek (pragma at kvirc dot net)
+// Last revision : 20 Sep 1999
+//
+// Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.
+// All Rights Reserved
+//
+// Permission to use, copy, modify, and distribute this software and its
+// documentation for any purpose and without fee is hereby granted,
+// provided that the above copyright notice appear in all copies and that
+// both that copyright notice and this permission notice appear in
+// supporting documentation, and that the names of Stichting Mathematisch
+// Centrum or CWI not be used in advertising or publicity pertaining to
+// distribution of the software without specific, written prior permission.
+//
+// STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+// THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+// FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+// FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+//
+// Intel/DVI ADPCM coder/decoder.
+//
+// The algorithm for this coder was taken from the IMA Compatability Project
+// proceedings, Vol 2, Number 2; May 1992.
+//
+// Version 1.2, 18-Dec-92.
+//
+
+#define _ADPCMCODEC_CPP_
+#include "adpcmcodec.h"
+
+#include <stdio.h> /*DBG*/
+
+#ifndef __STDC__
+ #define signed
+#endif
+
+
+
+#define ADPCM_PACKED_FRAME_SIZE_IN_BYTES 512
+#define ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES 2048
+#define ADPCM_UNPACKED_FRAME_SIZE_IN_SHORTS 1024
+
+// Intel ADPCM step variation table */
+static int indexTable[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8,
+};
+
+static int stepsizeTable[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+};
+
+
+void ADPCM_compress(short indata[],char outdata[],int len,ADPCM_state *state)
+{
+ short *lpIn; /* Input buffer pointer */
+ signed char *lpOut; /* output buffer pointer */
+ int val; /* Current input sample value */
+ int sign; /* Current adpcm sign bit */
+ int delta; /* Current adpcm output value */
+ int diff; /* Difference between val and valprev */
+ int step; /* Stepsize */
+ int valpred; /* Predicted output value */
+ int vpdiff; /* Current change to valpred */
+ int index; /* Current step change index */
+ int outputbuffer = 0; /* place to keep previous 4-bit value */
+ int bufferstep; /* toggle between outputbuffer/output */
+
+ lpOut = (signed char *)outdata;
+ lpIn = indata;
+
+ valpred = state->valprev;
+ index = state->index;
+ step = stepsizeTable[index];
+
+ bufferstep = 1;
+
+ for ( ;len > 0;len-- ) {
+ val = *lpIn++;
+ // Step 1 - compute difference with previous value
+ diff = val - valpred;
+ sign = (diff < 0) ? 8 : 0;
+ if(sign)diff=(-diff);
+ // Step 2 - Divide and clamp
+ // Note:
+ // This code *approximately* computes:
+ // delta = diff*4/step;
+ // vpdiff = (delta+0.5)*step/4;
+ // but in shift step bits are dropped. The net result of this is
+ // that even if you have fast mul/div hardware you cannot put it to
+ // good use since the fixup would be too expensive.
+ //
+ delta = 0;
+ vpdiff = (step >> 3);
+ if (diff >=step){
+ delta = 4;
+ diff -= step;
+ vpdiff += step;
+ }
+ step >>= 1;
+ if (diff >= step) {
+ delta |= 2;
+ diff -= step;
+ vpdiff += step;
+ }
+ step >>= 1;
+ if ( diff >= step ) {
+ delta |= 1;
+ vpdiff += step;
+ }
+ // Step 3 - Update previous value
+ if(sign)valpred -= vpdiff;
+ else valpred += vpdiff;
+ // Step 4 - Clamp previous value to 16 bits
+ if ( valpred > 32767 )valpred = 32767;
+ else if ( valpred < -32768 )valpred = -32768;
+ // Step 5 - Assemble value, update index and step values
+ delta |= sign;
+ index += indexTable[delta];
+ if ( index < 0 ) index = 0;
+ if ( index > 88 ) index = 88;
+ step = stepsizeTable[index];
+ // Step 6 - Output value
+ if ( bufferstep )outputbuffer = (delta << 4) & 0xf0;
+ else *lpOut++ = (delta & 0x0f) | outputbuffer;
+ bufferstep = !bufferstep;
+ }
+ // Output last step, if needed
+ if (!bufferstep)*lpOut++ = outputbuffer;
+ state->valprev = valpred;
+ state->index = index;
+}
+
+void ADPCM_uncompress(char indata[],short outdata[],int len,ADPCM_state *state)
+{
+ signed char *inp; /* Input buffer pointer */
+ short *outp; /* output buffer pointer */
+ int sign; /* Current adpcm sign bit */
+ int delta; /* Current adpcm output value */
+ int step; /* Stepsize */
+ int valpred; /* Predicted value */
+ int vpdiff; /* Current change to valpred */
+ int index; /* Current step change index */
+ int inputbuffer=0; /* place to keep next 4-bit value */
+ int bufferstep; /* toggle between inputbuffer/input */
+
+ outp = outdata;
+ inp = (signed char *)indata;
+
+ valpred = state->valprev;
+ index = state->index;
+ step = stepsizeTable[index];
+
+ bufferstep = 0;
+
+ for ( ; len > 0 ; len-- ) {
+
+ /* Step 1 - get the delta value */
+ if ( bufferstep )delta = inputbuffer & 0xf;
+ else {
+ inputbuffer = *inp++;
+ delta = (inputbuffer >> 4) & 0xf;
+ }
+ bufferstep = !bufferstep;
+
+ /* Step 2 - Find new index value (for later) */
+ index += indexTable[delta];
+ if ( index < 0 ) index = 0;
+ if ( index > 88 ) index = 88;
+
+ /* Step 3 - Separate sign and magnitude */
+ sign = delta & 8;
+ delta = delta & 7;
+
+ /* Step 4 - Compute difference and new predicted value */
+ /*
+ ** Computes 'vpdiff = (delta+0.5)*step/4', but see comment
+ ** in adpcm_coder.
+ */
+ vpdiff = step >> 3;
+ if( delta & 4 )vpdiff += step;
+ if( delta & 2 )vpdiff += step>>1;
+ if( delta & 1 )vpdiff += step>>2;
+
+ if(sign)valpred -= vpdiff;
+ else valpred += vpdiff;
+
+ /* Step 5 - clamp output value */
+ if(valpred > 32767)valpred = 32767;
+ else if(valpred < -32768)valpred = -32768;
+
+ /* Step 6 - Update step value */
+ step = stepsizeTable[index];
+
+ /* Step 7 - Output value */
+ *outp++ = valpred;
+ }
+
+ state->valprev = valpred;
+ state->index = index;
+}
+
+
+KviDccVoiceAdpcmCodec::KviDccVoiceAdpcmCodec()
+: KviDccVoiceCodec()
+{
+ m_pEncodeState = new ADPCM_state;
+ m_pEncodeState->index = 0;
+ m_pEncodeState->valprev = 0;
+ m_pDecodeState = new ADPCM_state;
+ m_pDecodeState->index = 0;
+ m_pDecodeState->valprev = 0;
+ m_szName = "adpcm (compression 1:4)";
+}
+
+KviDccVoiceAdpcmCodec::~KviDccVoiceAdpcmCodec()
+{
+ delete m_pEncodeState;
+ delete m_pDecodeState;
+}
+
+void KviDccVoiceAdpcmCodec::encode(KviDataBuffer * signal,KviDataBuffer * stream)
+{
+ if(signal->size() < ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES)return; // nothing to encode
+
+ char * ptr = (char *)signal->data();
+
+ int uFrames = signal->size() / ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ int uTotalDataCompressed = uFrames * ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ int uFrameOffset = stream->size();
+ char * endPtr = ptr + uTotalDataCompressed;
+
+ stream->addSize(ADPCM_PACKED_FRAME_SIZE_IN_BYTES * uFrames);
+
+ while(ptr != endPtr)
+ {
+ ADPCM_compress((short *)ptr,(char *)(stream->data() + uFrameOffset),ADPCM_UNPACKED_FRAME_SIZE_IN_SHORTS,m_pEncodeState);
+ ptr += ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ uFrameOffset += ADPCM_PACKED_FRAME_SIZE_IN_BYTES;
+ }
+ signal->remove(uTotalDataCompressed);
+}
+
+void KviDccVoiceAdpcmCodec::decode(KviDataBuffer * stream,KviDataBuffer * signal)
+{
+ if(stream->size() < ADPCM_PACKED_FRAME_SIZE_IN_BYTES)return; // nothing to decode
+
+ char * ptr = (char *)stream->data();
+
+ // Adpcm codec
+ int uFrames = stream->size() / ADPCM_PACKED_FRAME_SIZE_IN_BYTES;
+ int uTotalDataDecompressed = uFrames * ADPCM_PACKED_FRAME_SIZE_IN_BYTES;
+ int uSignalOffset = signal->size();
+ char * endPtr = ptr + (uTotalDataDecompressed);
+
+ signal->addSize(ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES * uFrames);
+
+ while(ptr != endPtr)
+ {
+ ADPCM_uncompress((char *)ptr,(short *)(signal->data() + uSignalOffset),ADPCM_UNPACKED_FRAME_SIZE_IN_SHORTS,m_pDecodeState);
+ ptr += ADPCM_PACKED_FRAME_SIZE_IN_BYTES;
+ uSignalOffset += ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ }
+ stream->remove(uTotalDataDecompressed);
+}
+
+int KviDccVoiceAdpcmCodec::encodedFrameSize()
+{
+ return ADPCM_PACKED_FRAME_SIZE_IN_BYTES;
+}
+
+int KviDccVoiceAdpcmCodec::decodedFrameSize()
+{
+ return ADPCM_UNPACKED_FRAME_SIZE_IN_BYTES;
+}
diff --git a/src/modules/dcc/adpcmcodec.h b/src/modules/dcc/adpcmcodec.h
new file mode 100644
index 00000000..63343df0
--- /dev/null
+++ b/src/modules/dcc/adpcmcodec.h
@@ -0,0 +1,59 @@
+#ifndef _ADPCMCODEC_H_
+#define _ADPCMCODEC_H_
+//
+// File : adpcmcodec.h
+// Creation date : Wed Aug 22 19:12:46 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Code derived from adpcm.c : Intel ADPCM coder/decoder
+// Adapted for the KVirc distribution by Szymon Stefanek (pragma at kvirc dot net)
+// Last revision : 22 Aug 2001
+// See kvi_adpcm.cpp for the complete copyright notice.
+//
+//
+// adpcm.h - include file for adpcm coder.
+//
+// Version 1.0, 7-Jul-92.
+//
+//
+// Average compression speed experiment: P166MMX 32MEG
+//
+// INPUT | OUTPUT | COMPRESSION TIME
+// ------------------------------------------------------------------------
+// shorts bytes | bytes | min msec. avrg. high peak (1 time)
+// ------------------------------------------------------------------------
+// 512 1024 | 256 | ~4 4 34
+// 1024 2048 | 512 | ~7 8 75
+// 2048 4096 | 1024 | ~13 15 175 (!!!)
+// 4096 8192 | 2048 | ~26 33 91
+// 8192 16384 | 4096 | ~57 80 220 (~1 sec of audio)
+// 16384 32768 | 8192 | ~110 250 290
+//
+
+#include <qstring.h> // fix for qtextstream.h
+#include "codec.h"
+
+typedef struct adpcm_state
+{
+ short valprev; /* Previous output value */
+ char index; /* Index into stepsize table */
+} ADPCM_state;
+
+
+class KviDccVoiceAdpcmCodec : public KviDccVoiceCodec
+{
+public:
+ KviDccVoiceAdpcmCodec();
+ virtual ~KviDccVoiceAdpcmCodec();
+private:
+ ADPCM_state * m_pEncodeState;
+ ADPCM_state * m_pDecodeState;
+public:
+ virtual void encode(KviDataBuffer * signal,KviDataBuffer * stream);
+ virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal);
+ virtual int encodedFrameSize();
+ virtual int decodedFrameSize();
+};
+
+
+#endif //_ADPCMCODEC_H_
diff --git a/src/modules/dcc/broker.cpp b/src/modules/dcc/broker.cpp
new file mode 100644
index 00000000..b6548e0c
--- /dev/null
+++ b/src/modules/dcc/broker.cpp
@@ -0,0 +1,898 @@
+//
+// File : broker.cpp
+// Creation date : Tue Sep 19 09 2000 10:21:54 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "broker.h"
+#include "dialogs.h"
+#include "chat.h"
+#include "send.h"
+#ifdef COMPILE_DCC_CANVAS
+#include "canvas.h"
+#endif
+#include "voice.h"
+
+#include "kvi_app.h"
+#include "kvi_frame.h"
+#include "kvi_locale.h"
+#include "kvi_options.h"
+#include "kvi_console.h"
+#include "kvi_fileutils.h"
+#include "kvi_out.h"
+#include "kvi_mediatype.h"
+#include "kvi_ircconnection.h"
+#include "kvi_sharedfiles.h"
+
+// kvi_app.cpp
+extern KVIRC_API KviMediaManager * g_pMediaManager;
+extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager;
+
+#include <qfileinfo.h>
+#include <qstring.h>
+
+//#warning "The broker might lookup the remote host name"
+
+KviDccBroker::KviDccBroker()
+: QObject(0,"dcc_broker")
+{
+ KviDccFileTransfer::init();
+
+ m_pBoxList = new KviPointerList<KviDccBox>;
+ m_pBoxList->setAutoDelete(false);
+
+ m_pDccWindowList = new KviPointerList<KviWindow>;
+ m_pDccWindowList->setAutoDelete(false);
+
+ m_pZeroPortTags = new KviPointerHashTable<QString,KviDccZeroPortTag>(17);
+ m_pZeroPortTags->setAutoDelete(true);
+}
+
+KviDccBroker::~KviDccBroker()
+{
+ delete m_pZeroPortTags;
+ while(m_pBoxList->first())delete m_pBoxList->first();
+ delete m_pBoxList;
+ m_pBoxList = 0;
+ while(m_pDccWindowList->first())delete m_pDccWindowList->first();
+ delete m_pDccWindowList;
+ KviDccFileTransfer::done();
+}
+
+
+KviDccZeroPortTag * KviDccBroker::addZeroPortTag()
+{
+ static unsigned int g_uNextZeroPortTag = 0;
+ g_uNextZeroPortTag++;
+ KviDccZeroPortTag * t = new KviDccZeroPortTag;
+ t->m_tTimestamp = QDateTime::currentDateTime();
+ t->m_szTag.setNum(g_uNextZeroPortTag);
+ //t->m_szTag.prepend("mIrc-zero-port-");
+ t->m_uResumePosition = 0;
+ // FIXME: we should clear this dict if it grows too high....
+ m_pZeroPortTags->insert(t->m_szTag,t);
+ return t;
+}
+
+KviDccZeroPortTag * KviDccBroker::findZeroPortTag(const QString &szTag)
+{
+ KviDccZeroPortTag * t = m_pZeroPortTags->find(szTag);
+ if(!t)return 0;
+ if(t->m_tTimestamp.secsTo(QDateTime::currentDateTime()) > 180)
+ {
+ // too late man...
+ m_pZeroPortTags->remove(szTag);
+ return 0;
+ }
+ return t;
+}
+
+void KviDccBroker::removeZeroPortTag(const QString &szTag)
+{
+ m_pZeroPortTags->remove(szTag);
+}
+
+unsigned int KviDccBroker::dccBoxCount()
+{
+ return m_pBoxList->count();
+}
+
+void KviDccBroker::unregisterDccWindow(KviWindow *wnd)
+{
+ m_pDccWindowList->removeRef(wnd);
+}
+
+void KviDccBroker::unregisterDccBox(KviDccBox * box)
+{
+ //debug("Forgetting box %d",box);
+ m_pBoxList->removeRef(box);
+}
+
+
+void KviDccBroker::cancelDcc(KviDccDescriptor * dcc)
+{
+ delete dcc;
+ dcc = 0;
+}
+
+void KviDccBroker::cancelDcc(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+ delete dcc;
+ dcc = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RSEND
+///////////////////////////////////////////////////////////////////////////////
+
+void KviDccBroker::rsendManage(KviDccDescriptor * dcc)
+{
+ // We need the filename...
+ QFileInfo fi(dcc->szLocalFileName);
+ if(fi.exists())rsendExecute(0,dcc);
+ else rsendAskForFileName(dcc);
+}
+
+void KviDccBroker::rsendAskForFileName(KviDccDescriptor * dcc)
+{
+ QStringList filenames;
+ if(
+ KviFileDialog::askForOpenFileNames(filenames,
+ __tr2qs_ctx("Choose Files to Send - KVIrc","dcc"),"")
+ ) {
+ if(filenames.count() > 0)
+ {
+ KviDccDescriptor * d;
+ KviDccDescriptor * templ = dcc;
+ QStringList::Iterator it=filenames.begin();
+ while(it != filenames.end())
+ {
+ d = new KviDccDescriptor(*dcc);
+ d->szLocalFileName = *(it);
+ d->szLocalFileName.stripWhiteSpace();
+ ++it;
+ if(d->szLocalFileName.isEmpty())
+ cancelDcc(d);
+ else
+ rsendExecute(d);
+ }
+ delete dcc;
+ }
+ } else {
+ cancelDcc(dcc);
+ }
+}
+
+void KviDccBroker::rsendExecute(KviDccDescriptor * dcc)
+{
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // No way...we NEED the right IRC context...
+ g_pApp->activeConsole()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("Can't send DCC %Q request to %Q: IRC connection has been terminated","dcc"),
+ &(dcc->szType),&(dcc->szNick));
+ delete dcc;
+ return;
+ }
+
+ // Ok...we need the file to exist
+ QFileInfo fi(dcc->szLocalFileName);
+ if(!(fi.exists() && fi.isReadable() && (fi.isFile()) && (fi.size() > 0)))
+ {
+ dcc->console()->output(KVI_OUT_DCCERROR,__tr2qs_ctx("Can't open file %Q for reading","dcc"),
+ &(dcc->szLocalFileName));
+ delete dcc;
+ return;
+ }
+
+ dcc->szFileName = dcc->szLocalFileName;
+ dcc->szFileName = QFileInfo(dcc->szFileName).fileName();
+
+ QString fName = dcc->szFileName;
+ fName.replace(' ',"\\040"); // be cool :)
+
+ QString szTag;
+ if(dcc->isZeroPortRequest())
+ {
+ // actually we tagged it as "nonempty" in /dcc.rsend --zero-port
+ // retag it with something more reasonable
+ KviDccZeroPortTag * t = addZeroPortTag();
+ t->m_uFileSize = fi.size();
+ dcc->setZeroPortRequestTag(t->m_szTag.latin1()); // latin1() should be ok here
+ szTag = t->m_szTag;
+
+ // DCC [ST]SEND <filename> <fakeipaddress> <zero-port> <filesize> <sessionid>
+ dcc->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s 127.0.0.1 0 %u %s%c",
+ dcc->console()->connection()->encodeText(dcc->szNick).data(),
+ 0x01,
+ dcc->console()->connection()->encodeText(dcc->szType).data(),
+ dcc->console()->connection()->encodeText(fName).data(),
+ fi.size(),
+ dcc->console()->connection()->encodeText(szTag).data(),
+ 0x01);
+ } else {
+ dcc->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %u%c",
+ dcc->console()->connection()->encodeText(dcc->szNick).data(),
+ 0x01,
+ dcc->console()->connection()->encodeText(dcc->szType).data(),
+ dcc->console()->connection()->encodeText(fName).data(),
+ fi.size(),0x01);
+ szTag = dcc->szFileName;
+ }
+
+ // now add a file offer , so he we will accept it automatically
+ // 120 secs is a reasonable timeout
+ QString szMask = dcc->szNick;
+ szMask += "!*@*";
+
+ g_pSharedFilesManager->addSharedFile(szTag,dcc->szLocalFileName,szMask,120);
+
+ delete dcc;
+}
+
+void KviDccBroker::rsendExecute(KviDccBox * box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+ rsendExecute(dcc);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DCC CHAT
+///////////////////////////////////////////////////////////////////////////////
+
+void KviDccBroker::handleChatRequest(KviDccDescriptor * dcc)
+{
+
+ if(!dcc->bAutoAccept)
+ {
+ // FIXME: better message ? Secure Direct Client Connection...eventually
+ // need confirmation
+ QString tmp = __tr2qs_ctx( \
+ "<b>%1 [%2@%3]</b> requests a " \
+ "<b>Direct Client Connection</b> in <b>%4</b> mode.<br>", \
+ "dcc").arg(dcc->szNick).arg(dcc->szUser).arg(dcc->szHost).arg(dcc->szType);
+
+#ifdef COMPILE_SSL_SUPPORT
+ if(dcc->bIsSSL)tmp += __tr2qs_ctx("The connection will be secured using SSL.<br>","dcc");
+#endif
+
+ if(dcc->isZeroPortRequest())
+ {
+ tmp += __tr2qs_ctx( \
+ "You will be the passive side of the connection.<br>" \
+ ,"dcc");
+ } else {
+ tmp += __tr2qs_ctx( \
+ "The connection target will be host <b>%1</b> on port <b>%2</b><br>" \
+ ,"dcc").arg(dcc->szIp).arg(dcc->szPort);
+ }
+
+
+ QString caption = __tr2qs_ctx("DCC %1 Request - KVIrc","dcc").arg(dcc->szType);
+
+ KviDccAcceptBox * box = new KviDccAcceptBox(this,dcc,tmp,caption);
+
+ m_pBoxList->append(box);
+ connect(box,SIGNAL(accepted(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(executeChat(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(rejected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(cancelDcc(KviDccBox *,KviDccDescriptor *)));
+ box->show();
+ } else {
+ // auto accept
+ executeChat(0,dcc);
+ }
+}
+
+void KviDccBroker::executeChat(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // rebind to the first available console....
+ dcc->setConsole(g_pApp->activeConsole());
+ }
+
+ KviStr szSubProto = dcc->szType;
+ szSubProto.toLower();
+
+ QString tmp = QString("dcc: %1 %2@%3:%4").arg(szSubProto.ptr()).arg(dcc->szNick).arg(dcc->szIp).arg(dcc->szPort);
+ KviDccChat * chat = new KviDccChat(dcc->console()->frame(),dcc,tmp.utf8().data());
+
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : \
+ (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat) || \
+ (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChatWhenAutoAccepted)));
+
+ dcc->console()->frame()->addWindow(chat,!bMinimized);
+ if(bMinimized)chat->minimize();
+ m_pDccWindowList->append(chat);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// ACTIVE VOICE
+///////////////////////////////////////////////////////////////////////////////
+
+void KviDccBroker::activeVoiceManage(KviDccDescriptor * dcc)
+{
+ if(!dcc->bAutoAccept)
+ {
+ // need confirmation
+ QString tmp = __tr2qs_ctx(
+ "<b>%1 [%2@%3]</b> requests a<br>" \
+ "<b>Direct Client Connection</b> in <b>VOICE</b> mode.<br>" \
+ "The connection target will be host <b>%4</b> on port <b>%5</b><br>" \
+ ,"dcc" \
+ ).arg(dcc->szNick).arg(dcc->szUser).arg(dcc->szHost).arg(dcc->szIp).arg(dcc->szPort);
+
+ KviDccAcceptBox * box = new KviDccAcceptBox(this,dcc,tmp,__tr2qs_ctx("DCC VOICE request","dcc"));
+ m_pBoxList->append(box);
+ connect(box,SIGNAL(accepted(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(activeVoiceExecute(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(rejected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(cancelDcc(KviDccBox *,KviDccDescriptor *)));
+ box->show();
+ } else {
+ // auto accept
+ activeVoiceExecute(0,dcc);
+ }
+}
+
+void KviDccBroker::activeVoiceExecute(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // rebind to the first available console....
+ dcc->setConsole(g_pApp->activeConsole());
+ }
+
+ KviStr tmp(KviStr::Format,"dcc: voice %s@%s:%s",dcc->szNick.utf8().data(),dcc->szIp.utf8().data(),dcc->szPort.utf8().data());
+ KviDccVoice * v = new KviDccVoice(dcc->console()->frame(),dcc,tmp.ptr());
+
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : \
+ (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVoice) || \
+ (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVoiceWhenAutoAccepted)));
+
+ dcc->console()->frame()->addWindow(v,!bMinimized);
+ if(bMinimized)v->minimize();
+
+ m_pDccWindowList->append(v);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// PASSIVE VOICE
+///////////////////////////////////////////////////////////////////////////////
+
+void KviDccBroker::passiveVoiceExecute(KviDccDescriptor * dcc)
+{
+ KviStr tmp(KviStr::Format,"dcc: voice %s@%s:%s",dcc->szNick.utf8().data(),dcc->szIp.utf8().data(),dcc->szPort.utf8().data());
+ KviDccVoice * v = new KviDccVoice(dcc->console()->frame(),dcc,tmp.ptr());
+//#warning "Create minimized dcc voice ?... or maybe it's too much ? :)"
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat);
+ dcc->console()->frame()->addWindow(v,!bMinimized);
+ if(bMinimized)v->minimize();
+ m_pDccWindowList->append(v);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ACTIVE CANVAS
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef COMPILE_DCC_CANVAS
+
+void KviDccBroker::activeCanvasManage(KviDccDescriptor * dcc)
+{
+ if(!dcc->bAutoAccept)
+ {
+ // need confirmation
+ QString tmp = __tr2qs_ctx( \
+ "<b>%1 [%2@%3]</b> requests a<br>" \
+ "<b>Direct Client Connection</b> in <b>CANVAS</b> mode.<br>" \
+ "The connection target will be host <b>%4</b> on port <b>%5</b><br>" \
+ ,"dcc" \
+ ).arg(dcc->szNick).arg(dcc->szUser).arg(dcc->szHost).arg(dcc->szIp).arg(dcc->szPort);
+
+ KviDccAcceptBox * box = new KviDccAcceptBox(this,dcc,tmp,__tr2qs_ctx("DCC CANVAS request","dcc"));
+ m_pBoxList->append(box);
+ connect(box,SIGNAL(accepted(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(activeCanvasExecute(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(rejected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(cancelDcc(KviDccBox *,KviDccDescriptor *)));
+ box->show();
+ } else {
+ // auto accept
+ activeCanvasExecute(0,dcc);
+ }
+}
+
+#endif
+
+void KviDccBroker::activeCanvasExecute(KviDccBox *box,KviDccDescriptor * dcc)
+{
+#ifdef COMPILE_DCC_CANVAS
+ if(box)box->forgetDescriptor();
+
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // rebind to the first available console....
+ dcc->setConsole(g_pApp->activeConsole());
+ }
+
+ KviStr tmp(KviStr::Format,"dcc: canvas %s@%s:%s",dcc->szNick.utf8().data(),dcc->szIp.utf8().data(),dcc->szPort.utf8().data());
+ KviDccCanvas * cnv = new KviDccCanvas(dcc->console()->frame(),dcc,tmp.ptr());
+
+//#warning "This option should be dedicated to Dcc Canvas!....for now we are using the DccChat options"
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : \
+ (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat) || \
+ (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChatWhenAutoAccepted)));
+
+ dcc->console()->frame()->addWindow(cnv,!bMinimized);
+ if(bMinimized)cnv->minimize();
+
+ m_pDccWindowList->append(cnv);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PASSIVE CANVAS
+///////////////////////////////////////////////////////////////////////////////
+#ifdef COMPILE_DCC_CANVAS
+void KviDccBroker::passiveCanvasExecute(KviDccDescriptor * dcc)
+{
+ KviStr tmp(KviStr::Format,"dcc: canvas %s@%s:%s",dcc->szNick.utf8().data(),dcc->szIp.utf8().data(),dcc->szPort.utf8().data());
+ KviDccCanvas * cnv = new KviDccCanvas(dcc->console()->frame(),dcc,tmp.ptr());
+//#warning "This option should be dedicated to Dcc Canvas!....for now we are using the DccChat options"
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat);
+ dcc->console()->frame()->addWindow(cnv,!bMinimized);
+ if(bMinimized)cnv->minimize();
+ m_pDccWindowList->append(cnv);
+}
+
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// SEND
+///////////////////////////////////////////////////////////////////////////////
+
+void KviDccBroker::recvFileManage(KviDccDescriptor * dcc)
+{
+ if(dcc->bIsIncomingAvatar)
+ {
+ bool bOk;
+ uint size = dcc->szFileSize.toUInt(&bOk);
+ if(bOk) {
+ if(size>=KVI_OPTION_UINT(KviOption_uintMaximumRequestedAvatarSize)) {
+ cancelDcc(0,dcc);
+ return;
+ }
+ }
+ }
+
+ if(!dcc->bAutoAccept)
+ {
+ // need confirmation
+ QString tmp;
+
+ if(dcc->bActive)
+ {
+ // Normal active send: we will be connecting
+ tmp = __tr2qs_ctx( \
+ "<b>%1 [%2@%3]</b> " \
+ "wants to send you the file " \
+ "'<b>%4</b>', " \
+ "<b>%5</b> large.<br>" \
+ "The connection target will be host <b>%6</b> on port <b>%7</b><br>" \
+ ,"dcc" \
+ ).arg(dcc->szNick).arg(dcc->szUser).arg(dcc->szHost).arg(
+ dcc->szFileName).arg(KviQString::makeSizeReadable(dcc->szFileSize.toInt())).arg(
+ dcc->szIp).arg(dcc->szPort);
+
+ } else {
+ // passive: we will be listening!
+ tmp = __tr2qs_ctx( \
+ "<b>%1 [%2@%3]</b> "
+ "wants to send you the file " \
+ "'<b>%4</b>', " \
+ "<b>%5</b> large.<br>" \
+ "You will be the passive side of the connection.<br>" \
+ ,"dcc" \
+ ).arg(dcc->szNick).arg(dcc->szUser).arg(dcc->szHost).arg(
+ dcc->szFileName).arg(KviQString::makeSizeReadable(dcc->szFileSize.toInt()));
+ }
+
+ if(dcc->bIsIncomingAvatar)
+ {
+ tmp += __tr2qs_ctx( \
+ "<center><b>Note:</b></center>" \
+ "The file appears to be an avatar that you have requested. " \
+ "You should not change its filename. " \
+ "Save it in a location where KVIrc can find it, such as " \
+ "the 'avatars', 'incoming', or 'pics' directories, " \
+ "your home directory, or the save directory for the incoming file type. " \
+ "The default save path will probably work. " \
+ "You can instruct KVIrc to accept incoming avatars automatically " \
+ "by setting the option <tt>boolAutoAcceptIncomingAvatars</tt> to true.<br>" \
+ ,"dcc" \
+ );
+ }
+
+//#warning "Maybe remove the pending avatar if rejected ?"
+
+ QString title = __tr2qs_ctx("DCC %1 Request - KVIrc","dcc").arg(dcc->szType);
+
+ KviDccAcceptBox * box = new KviDccAcceptBox(this,dcc,tmp,title);
+ m_pBoxList->append(box);
+ connect(box,SIGNAL(accepted(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(chooseSaveFileName(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(rejected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(cancelDcc(KviDccBox *,KviDccDescriptor *)));
+ box->show();
+ } else {
+ // auto accept
+
+ if(_OUTPUT_VERBOSE)
+ {
+ dcc->console()->output(KVI_OUT_DCCMSG,__tr2qs_ctx("Auto-accepting DCC %Q request from %Q!%Q@%Q for file %Q","dcc"),
+ &(dcc->szType),&(dcc->szNick),&(dcc->szUser),
+ &(dcc->szHost),&(dcc->szFileName));
+ }
+ chooseSaveFileName(0,dcc);
+ }
+}
+
+void KviDccBroker::chooseSaveFileName(KviDccBox *box,KviDccDescriptor *dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ // Lookup the suggested save directory
+
+ dcc->szLocalFileName = "";
+
+ if(dcc->bIsIncomingAvatar)g_pApp->getLocalKvircDirectory(dcc->szLocalFileName,KviApp::Avatars);
+ else {
+
+ if(KVI_OPTION_BOOL(KviOption_boolUseIncomingDccMediaTypeSavePath))
+ {
+ g_pMediaManager->lock();
+ if(KviMediaType * mt = g_pMediaManager->findMediaType(dcc->szFileName.utf8().data(),false))
+ {
+ if(mt->szSavePath.hasData())
+ {
+ if(KviFileUtils::directoryExists(mt->szSavePath.ptr()))dcc->szLocalFileName = mt->szSavePath;
+ else {
+ if(KviFileUtils::makeDir(mt->szSavePath.ptr()))dcc->szLocalFileName = mt->szSavePath;
+ }
+ if(KVI_OPTION_BOOL(KviOption_boolSortReceivedByDccFilesByNicks))
+ {
+ KviQString::ensureLastCharIs(dcc->szLocalFileName,KVI_PATH_SEPARATOR_CHAR);
+ dcc->szLocalFileName.append(dcc->szNick);
+ KviFileUtils::adjustFilePath(dcc->szLocalFileName);
+ }
+ KviFileUtils::makeDir(dcc->szLocalFileName);
+ }
+ }
+ g_pMediaManager->unlock();
+ }
+
+ if(dcc->szLocalFileName.isEmpty())
+ {
+ g_pApp->getLocalKvircDirectory(dcc->szLocalFileName,KviApp::Incoming);
+ if(KVI_OPTION_BOOL(KviOption_boolSortReceivedByDccFilesByNicks))
+ {
+ KviQString::ensureLastCharIs(dcc->szLocalFileName,KVI_PATH_SEPARATOR_CHAR);
+ dcc->szLocalFileName.append(dcc->szNick);
+ KviFileUtils::adjustFilePath(dcc->szLocalFileName);
+ KviFileUtils::makeDir(dcc->szLocalFileName);
+ }
+ }
+ }
+ KviFileUtils::adjustFilePath(dcc->szLocalFileName);
+ KviQString::ensureLastCharIs(dcc->szLocalFileName,KVI_PATH_SEPARATOR_CHAR);
+
+ if(!(dcc->bAutoAccept))
+ {
+ dcc->szLocalFileName+=dcc->szFileName;
+ if(KviFileDialog::askForSaveFileName(dcc->szLocalFileName,
+ __tr2qs_ctx("Choose Files to Save - KVIrc","dcc"),dcc->szLocalFileName))
+ {
+ renameOverwriteResume(0,dcc);
+ } else {
+ cancelDcc(dcc);
+ }
+ } else {
+ // auto accept
+ // WE choose the filename
+ dcc->szLocalFileName.append(dcc->szFileName);
+
+ if(_OUTPUT_VERBOSE)
+ {
+ dcc->console()->output(KVI_OUT_DCCMSG,__tr2qs_ctx("Auto-saving DCC %Q file %Q as \r![!dbl]play $0\r%Q\r","dcc"),
+ &(dcc->szType),&(dcc->szFileName),&(dcc->szLocalFileName));
+ }
+
+ renameOverwriteResume(0,dcc);
+ }
+}
+
+void KviDccBroker::renameOverwriteResume(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ // Check if file exists
+ QFileInfo fi(dcc->szLocalFileName);
+ if(fi.exists() && (fi.size() > 0)) // 0 byte files are senseless for us
+ {
+ dcc->szLocalFileSize.setNum(fi.size());
+
+ bool bOk;
+ int iRemoteSize = dcc->szFileSize.toInt(&bOk);
+ if(!bOk)iRemoteSize = -1;
+
+ // FIXME: Files downloaded succesfully shouldn't be resumed
+ // we should keep a db of downloaded files!
+
+ if(!dcc->bAutoAccept)
+ {
+ QString tmp;
+ bool bDisableResume = false;
+
+ if((iRemoteSize > -1) || // remote size is unknown
+ (iRemoteSize > ((int)(fi.size())))) // or it is larger than the actual size on disk
+ {
+ tmp = __tr2qs_ctx( \
+ "The file '<b>%1</b>' already exists " \
+ "and is <b>%2</b> large.<br>" \
+ "Do you wish to<br>" \
+ "<b>overwrite</b> the existing file,<br> " \
+ "<b>auto-rename</b> the new file, or<br>" \
+ "<b>resume</b> an incomplete download?" \
+ ,"dcc" \
+ ).arg(dcc->szLocalFileName).arg(KviQString::makeSizeReadable(fi.size()));
+ } else {
+ bDisableResume = true;
+ // the file on disk is larger or equal to the remote one
+ tmp = __tr2qs_ctx( \
+ "The file '<b>%1</b>' already exists" \
+ "and is larger than the offered one.<br>" \
+ "Do you wish to<br>" \
+ "<b>overwrite</b> the existing file, or<br> " \
+ "<b>auto-rename</b> the new file ?" \
+ ,"dcc" \
+ ).arg(dcc->szLocalFileName);
+ }
+
+ KviDccRenameBox * box = new KviDccRenameBox(this,dcc,tmp,bDisableResume);
+ m_pBoxList->append(box);
+ connect(box,SIGNAL(renameSelected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(renameDccSendFile(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(overwriteSelected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(recvFileExecute(KviDccBox *,KviDccDescriptor *)));
+ connect(box,SIGNAL(cancelSelected(KviDccBox *,KviDccDescriptor *)),
+ this,SLOT(cancelDcc(KviDccBox *,KviDccDescriptor *)));
+ box->show();
+ return;
+ } else {
+ // auto resume ?
+ if(KVI_OPTION_BOOL(KviOption_boolAutoResumeDccSendWhenAutoAccepted) &&
+ (iRemoteSize > -1) && // only if the remote size is really known
+ (iRemoteSize > ((int)(fi.size()))) && // only if the remote size is larger than the local size
+ (!KviDccFileTransfer::nonFailedTransferWithLocalFileName(dcc->szLocalFileName.utf8().data()))) // only if there is no transfer with this local file name yet
+ {
+ // yep, auto resume...
+ dcc->bResume = true;
+ recvFileExecute(0,dcc);
+ } else {
+ // otherwise auto rename
+ renameDccSendFile(0,dcc);
+ }
+ return;
+ }
+ } else dcc->szLocalFileSize = "0";
+
+ // everything OK
+ recvFileExecute(0,dcc);
+}
+
+void KviDccBroker::renameDccSendFile(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+
+
+ if(QFileInfo(dcc->szLocalFileName).exists())
+ {
+ KviStr szOrig = dcc->szLocalFileName;
+ int i = 1;
+ do {
+ KviStr szNum;
+ szNum.setNum(i);
+ int idx = szOrig.findLastIdx('.');
+ if(idx != -1)
+ {
+ dcc->szLocalFileName = szOrig.left(idx);
+ dcc->szLocalFileName += ".";
+ dcc->szLocalFileName += szNum;
+ dcc->szLocalFileName += szOrig.right(szOrig.len() - idx);
+ } else {
+ dcc->szLocalFileName = szOrig;
+ dcc->szLocalFileName += ".";
+ dcc->szLocalFileName += szNum;
+ }
+ i++;
+ } while(QFileInfo(dcc->szLocalFileName).exists());
+
+ if(_OUTPUT_VERBOSE)
+ {
+ dcc->console()->output(KVI_OUT_DCCMSG,__tr2qs_ctx("File %s exists, auto-renaming to %Q","dcc"),
+ szOrig.ptr(),&(dcc->szLocalFileName));
+ }
+ }
+
+ dcc->szLocalFileSize = "0"; // 0 for sure
+
+ recvFileExecute(0,dcc);
+}
+
+void KviDccBroker::recvFileExecute(KviDccBox *box,KviDccDescriptor * dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // rebind to the first available console....
+ dcc->setConsole(g_pApp->activeConsole());
+ }
+
+ //KviDccSend * send = new KviDccSend(dcc->console()->frame(),dcc,tmp.ptr());
+ KviDccFileTransfer * send = new KviDccFileTransfer(dcc);
+
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : \
+ (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSend) || \
+ (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSendWhenAutoAccepted)));
+
+ send->invokeTransferWindow(dcc->console(),bMinimized,bMinimized);
+}
+
+
+void KviDccBroker::sendFileManage(KviDccDescriptor * dcc)
+{
+ QStringList filenames;
+ if(
+ KviFileDialog::askForOpenFileNames(filenames,
+ __tr2qs_ctx("Choose Files to Send - KVIrc","dcc"),"")
+ ) {
+ if(filenames.count() > 0)
+ {
+ KviDccDescriptor * d;
+ KviDccDescriptor * templ = dcc;
+ QStringList::Iterator it=filenames.begin();
+ while(it != filenames.end())
+ {
+ d = new KviDccDescriptor(*dcc);
+ d->szLocalFileName = *(it);
+ d->szLocalFileName.stripWhiteSpace();
+ ++it;
+ if(d->szLocalFileName.isEmpty())
+ cancelDcc(d);
+ else
+ sendFileExecute(0,d);
+ }
+ delete dcc;
+ }
+ } else {
+ cancelDcc(dcc);
+ }
+}
+
+void KviDccBroker::sendFileExecute(KviDccBox * box,KviDccDescriptor *dcc)
+{
+ if(box)box->forgetDescriptor();
+
+ if(!g_pApp->windowExists(dcc->console()))
+ {
+ // rebind to the first available console....
+ dcc->setConsole(g_pApp->activeConsole());
+ }
+
+ QFileInfo fi(dcc->szLocalFileName);
+ if(!(fi.exists() && fi.isReadable() && (fi.isFile()) && (fi.size() > 0)))
+ {
+ dcc->console()->output(KVI_OUT_DCCERROR,__tr2qs_ctx("Can't open file %Q for reading","dcc"),
+ &(dcc->szLocalFileName));
+ delete dcc;
+ return;
+ }
+
+ dcc->szFileName = dcc->szLocalFileName;
+ dcc->szFileName = QFileInfo(dcc->szFileName).fileName();
+
+ dcc->szLocalFileSize.setNum(fi.size());
+
+ KviDccFileTransfer * send = new KviDccFileTransfer(dcc);
+
+ bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSend);
+
+ send->invokeTransferWindow(dcc->console(),bMinimized,bMinimized);
+}
+
+bool KviDccBroker::canUnload()
+{
+ if(m_pBoxList)
+ {
+ if((m_pBoxList->count() != 0) ||
+ (m_pDccWindowList->count() != 0) ||
+ (KviDccFileTransfer::transferCount() != 0))return false;
+ } // else in the destructor anyway (going to die)
+ return true;
+}
+
+bool KviDccBroker::handleResumeAccepted(const char * filename,const char * port,const char * szZeroPortTag)
+{
+ return KviDccFileTransfer::handleResumeAccepted(filename,port,szZeroPortTag);
+}
+
+bool KviDccBroker::handleResumeRequest(KviDccRequest * dcc,const char * filename,const char * port,unsigned int filePos,const char * szZeroPortTag)
+{
+ //debug("HANDLE %s %s %u %s",filename,port,filePos,szZeroPortTag);
+ // the zeroPOrtTag is nonempty here only if port == 0
+ if(kvi_strEqualCI("0",port) && szZeroPortTag)
+ {
+ // zero port resume request (we have sent out a DCC SEND <filename> <fakeip> 0 <tag>
+ KviDccZeroPortTag * t = findZeroPortTag(QString(szZeroPortTag));
+ if(t)
+ {
+ //debug("FOUND");
+ // valid zero port resume request
+ if(filePos < t->m_uFileSize)
+ {
+ //debug("VALID");
+ // ok!
+ t->m_uResumePosition = filePos;
+
+ KviStr szBuffy;
+ KviServerParser::encodeCtcpParameter(filename,szBuffy);
+
+ dcc->ctcpMsg->msg->console()->connection()->sendFmtData(
+ "PRIVMSG %s :%cDCC ACCEPT %s %s %u %s%c",
+ dcc->ctcpMsg->msg->console()->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),
+ 0x01,
+ szBuffy.ptr(),
+ port,
+ filePos,
+ szZeroPortTag,
+ 0x01);
+
+ return true;
+ } else {
+ return false; // invalid resume size
+ }
+ }
+ }
+ //debug("NOT A ZeRO PORT");
+
+ return KviDccFileTransfer::handleResumeRequest(filename,port,filePos);
+}
+
+
+#include "m_broker.moc"
diff --git a/src/modules/dcc/broker.h b/src/modules/dcc/broker.h
new file mode 100644
index 00000000..80fa9768
--- /dev/null
+++ b/src/modules/dcc/broker.h
@@ -0,0 +1,124 @@
+#ifndef _BROKER_H_
+#define _BROKER_H_
+//=======================================================================================
+//
+// File : broker.h
+// Creation date : Tue Sep 19 09 2000 10:20:01 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=======================================================================================
+
+#include "kvi_settings.h"
+
+#ifdef COMPILE_USE_QT4
+ // #define COMPILE_DCC_CANVAS
+#else
+ #define COMPILE_DCC_CANVAS
+#endif
+
+#include "kvi_string.h"
+#include "kvi_pointerlist.h"
+#include "kvi_sparser.h"
+
+#include "kvi_pointerhashtable.h"
+#include <qdatetime.h>
+#include <qobject.h>
+
+class KviConsole;
+class KviDccBroker;
+class KviWindow;
+class KviDccBox;
+
+#include "descriptor.h"
+
+class KviDccZeroPortTag
+{
+public:
+ QDateTime m_tTimestamp;
+ QString m_szTag;
+ unsigned int m_uFileSize; // outgoing file size, valid only for file transfers obviously
+ unsigned int m_uResumePosition; // if 0 = no resume, valid only for file transfers obviously
+};
+
+class KviDccBroker : public QObject
+{
+ Q_OBJECT
+public:
+ KviDccBroker();
+ ~KviDccBroker();
+protected:
+ KviPointerList<KviDccBox> * m_pBoxList;
+ KviPointerList<KviWindow> * m_pDccWindowList;
+ KviPointerHashTable<QString,KviDccZeroPortTag> * m_pZeroPortTags;
+public:
+ KviDccZeroPortTag * addZeroPortTag();
+ KviDccZeroPortTag * findZeroPortTag(const QString &szTag);
+ void removeZeroPortTag(const QString &szTag);
+
+ unsigned int dccWindowsCount(){ return m_pDccWindowList->count(); };
+ unsigned int dccBoxCount();
+
+ void unregisterDccBox(KviDccBox * box);
+ void unregisterDccWindow(KviWindow *dcc);
+
+ void rsendManage(KviDccDescriptor * dcc);
+ void rsendAskForFileName(KviDccDescriptor * dcc);
+
+ void handleChatRequest(KviDccDescriptor * dcc);
+
+#ifdef COMPILE_DCC_CANVAS
+ void activeCanvasManage(KviDccDescriptor * dcc);
+ void passiveCanvasExecute(KviDccDescriptor * dcc);
+#endif
+
+ void activeVoiceManage(KviDccDescriptor * dcc);
+ void passiveVoiceExecute(KviDccDescriptor * dcc);
+
+ void recvFileManage(KviDccDescriptor * dcc);
+ void sendFileManage(KviDccDescriptor * dcc);
+
+ bool handleResumeAccepted(const char * filename,const char * port,const char * szZeroPortTag);
+ bool handleResumeRequest(KviDccRequest * dcc,const char * filename,const char * port,unsigned int filePos,const char * szZeroPortTag);
+
+public slots:
+ void rsendExecute(KviDccBox * box,KviDccDescriptor * dcc);
+ void rsendExecute(KviDccDescriptor * dcc);
+
+// void activeChatExecute(KviDccBox * box,KviDccDescriptor * dcc);
+ void executeChat(KviDccBox * box,KviDccDescriptor * dcc);
+
+ void activeCanvasExecute(KviDccBox * box,KviDccDescriptor * dcc);
+ void activeVoiceExecute(KviDccBox * box,KviDccDescriptor * dcc);
+
+ void sendFileExecute(KviDccBox * box,KviDccDescriptor * dcc);
+ void recvFileExecute(KviDccBox * box,KviDccDescriptor * dcc);
+
+
+ void chooseSaveFileName(KviDccBox *box,KviDccDescriptor * dcc);
+ void renameOverwriteResume(KviDccBox *box,KviDccDescriptor * dcc);
+ void renameDccSendFile(KviDccBox *box,KviDccDescriptor * dcc);
+
+ void cancelDcc(KviDccBox *box,KviDccDescriptor * dcc);
+ void cancelDcc(KviDccDescriptor * dcc);
+
+public:
+ bool canUnload();
+};
+
+#endif
diff --git a/src/modules/dcc/canvas.cpp b/src/modules/dcc/canvas.cpp
new file mode 100644
index 00000000..e80272ae
--- /dev/null
+++ b/src/modules/dcc/canvas.cpp
@@ -0,0 +1,301 @@
+//
+// File : canvas.cpp
+// Creation date : Sun Jul 29 07 2001 20:23:13 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#define QT_MODULE_CANVAS
+
+#include "canvas.h"
+
+#ifdef COMPILE_DCC_CANVAS
+
+#include "dialogs.h"
+#include "marshal.h"
+#include "canvaswidget.h"
+
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_options.h"
+#include "kvi_input.h"
+#include "kvi_ircview.h"
+#include "kvi_iconmanager.h"
+#include "kvi_locale.h"
+#include "kvi_error.h"
+#include "kvi_out.h"
+#include "kvi_netutils.h"
+#include "kvi_console.h"
+#include "kvi_frame.h"
+#include "kvi_malloc.h"
+#include "kvi_memmove.h"
+#include "kvi_thread.h"
+#include "kvi_ircsocket.h"
+#include "kvi_settings.h"
+#include "kvi_themedlabel.h"
+#include "kvi_ircconnection.h"
+
+#include <qsplitter.h>
+
+extern KviDccBroker * g_pDccBroker;
+
+
+KviDccCanvas::KviDccCanvas(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name)
+: KviDccWindow(KVI_WINDOW_TYPE_DCCCANVAS,pFrm,name,dcc)
+{
+ m_pSplitter = new QSplitter(QSplitter::Vertical,this,"splitter");
+
+ m_pCanvas = new KviCanvasWidget(m_pSplitter);
+
+ m_pIrcView = new KviIrcView(m_pSplitter,pFrm,this);
+ m_pInput = new KviInput(this);
+
+// setFocusHandler(m_pInput,this);
+
+ m_pMarshal = new KviDccMarshal(this);
+ connect(m_pMarshal,SIGNAL(error(int)),this,SLOT(handleMarshalError(int)));
+ connect(m_pMarshal,SIGNAL(connected()),this,SLOT(connected()));
+
+
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting a passive DCC CANVAS connection","dcc"));
+ int ret = m_pMarshal->dccListen(dcc->szListenIp,dcc->szListenPort,m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ else {
+
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Listening on interface %Q port %Q","dcc"),
+ &(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+
+ if(dcc->bSendRequest)
+ {
+ QString ip = !dcc->szFakeIp.isEmpty() ? dcc->szFakeIp : dcc->szListenIp;
+ QString port = !dcc->szFakePort.isEmpty() ? dcc->szFakePort.utf8().data() : m_pMarshal->localPort();
+//#warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned number)"
+ struct in_addr a;
+ if(kvi_stringIpToBinaryIp(ip.utf8().data(),&a))ip.setNum(htonl(a.s_addr));
+ dcc->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC CANVAS chat %Q %Q%c",
+ dcc->console()->connection()->encodeText( dcc->szNick.utf8().data() ).data(),
+ 0x01,&ip,
+ &port,
+ 0x01);
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Sent DCC CANVAS request to %Q, waiting for the remote client to connect...","dcc"),
+ &(dcc->szNick));
+ } else outputNoFmt(KVI_OUT_DCCMSG,__tr2qs_ctx("DCC CANVAS request not sent: awaiting manual connections","dcc"));
+ }
+ } else {
+ // ACTIVE CONNECTION
+ outputNoFmt(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting an active DCC CANVAS connection","dcc"));
+ int ret = m_pMarshal->dccConnect(dcc->szIp.utf8().data(),dcc->szPort.utf8().data(),m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ else output(KVI_OUT_DCCMSG,__tr2qs_ctx("Contacting host %Q on port %Q","dcc"),&(dcc->szIp),&(dcc->szPort));
+ }
+
+// m_pSlaveThread = 0;
+}
+
+KviDccCanvas::~KviDccCanvas()
+{
+ g_pDccBroker->unregisterDccWindow(this);
+// if(m_pSlaveThread)
+// {
+// m_pSlaveThread->terminate();
+// delete m_pSlaveThread;
+// m_pSlaveThread = 0;
+// }
+ KviThreadManager::killPendingEvents(this);
+// delete m_pDescriptor;
+// delete m_pMarshal;
+}
+
+const QString & KviDccCanvas::target()
+{
+ // This may change on the fly...
+ m_szTarget.sprintf("%s@%s:%s",
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data());
+ return m_szTarget;
+}
+
+void KviDccCanvas::fillCaptionBuffers()
+{
+ KviStr tmp(KviStr::Format,"DCC Canvas %s@%s:%s",
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data());
+
+ m_szPlainTextCaption = tmp;
+
+ m_szHtmlActiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextActive).name().ascii(),tmp.ptr());
+ m_szHtmlInactiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextInactive).name().ascii(),tmp.ptr());
+}
+
+QPixmap * KviDccCanvas::myIconPtr()
+{
+ return g_pIconManager->getSmallIcon(KVI_SMALLICON_CANVAS);
+}
+
+
+void KviDccCanvas::getBaseLogFileName(KviStr &buffer)
+{
+ buffer.sprintf("%s_%s_%s",m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data());
+}
+
+void KviDccCanvas::ownMessage(const char * text)
+{
+ KviStr buf(KviStr::Format,"%s\r\n",text);
+// m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG,
+ m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(),
+ m_pDescriptor->szLocalHost.utf8().data(),text);
+}
+
+void KviDccCanvas::ownAction(const char * text)
+{
+ KviStr buf(KviStr::Format,"%cACTION %s%c\r\n",text);
+// m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ output(KVI_OUT_ACTION,"%Q %s",&(m_pDescriptor->szLocalNick),text);
+}
+
+bool KviDccCanvas::event(QEvent *e)
+{
+// if(e->type() == KVI_THREAD_EVENT)
+// {
+// switch(((KviThreadEvent *)e)->id())
+// {
+// case KVI_DCC_THREAD_EVENT_ERROR:
+// {
+// int * err = ((KviThreadDataEvent<int> *)e)->getData();
+// output(KVI_OUT_DCCERROR,__tr("ERROR: %s"),kvi_getErrorString(*err));
+// delete err;
+// return true;
+// }
+// break;
+// case KVI_DCC_THREAD_EVENT_DATA:
+// {
+// KviStr * d = ((KviThreadDataEvent<KviStr> *)e)->getData();
+// if(d->firstCharIs(0x01))
+// {
+// d->cutLeft(1);
+// if(d->lastCharIs(0x01))d->cutRight(1);
+// if(kvi_strEqualCIN("ACTION",d->ptr(),6))d->cutLeft(6);
+// d->stripLeftWhiteSpace();
+// output(KVI_OUT_ACTION,"%s %s",m_pDescriptor->szNick.ptr(),d->ptr());
+// } else {
+//
+//#ifdef COMPILE_CRYPT_SUPPORT
+// if(KviCryptSessionInfo * cinf = cryptSessionInfo())
+// {
+// if(cinf->bDoDecrypt)
+// {
+// if(cinf->pEngine->isCryptographicEngine() && (*(d->ptr()) == KVI_TEXT_CRYPT))
+// {
+// KviStr decryptedStuff;
+// if(cinf->pEngine->decrypt(d->ptr() + 1,decryptedStuff))
+// {
+// m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSGCRYPTED,
+// m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),
+// m_pDescriptor->szHost.ptr(),decryptedStuff.ptr());
+// } else {
+// output(KVI_OUT_SYSTEMERROR,
+// __tr("The following message looks like an encrypted one, but the crypting engine failed to decode it: %s"),
+// cinf->pEngine->lastError());
+// m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG,
+// m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),
+// m_pDescriptor->szHost.ptr(),d->ptr() + 1);
+// }
+// delete d;
+// return true;
+// } else {
+// if(!(cinf->pEngine->isCryptographicEngine()))
+// {
+// KviStr decryptedStuff;
+// if(cinf->pEngine->decrypt(d->ptr(),decryptedStuff))
+// {
+// m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG,
+// m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),
+// m_pDescriptor->szHost.ptr(),decryptedStuff.ptr());
+// delete d;
+// return true;
+// } else {
+// output(KVI_OUT_SYSTEMERROR,
+// __tr("The following message looks like an encrypted one, but the crypting engine failed to decode it: %s"),
+// cinf->pEngine->lastError());
+// }
+// }
+// }
+// }
+// }
+//#endif
+// m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG,
+// m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),
+// m_pDescriptor->szHost.ptr(),d->ptr());
+// }
+// delete d;
+// return true;
+// }
+// break;
+// }
+// }
+ return KviWindow::event(e);
+}
+
+void KviDccCanvas::resizeEvent(QResizeEvent *e)
+{
+ int hght = m_pInput->heightHint();
+// int hght2 = m_pTopSplitter->sizeHint().height();
+// m_pTopSplitter->setGeometry(0,0,width(),hght2);
+ m_pSplitter->setGeometry(0,0,width(),height() - hght);
+ m_pInput->setGeometry(0,height() - hght,width(),hght);
+}
+
+QSize KviDccCanvas::sizeHint() const
+{
+ QSize ret(m_pIrcView->sizeHint().width(),
+ m_pIrcView->sizeHint().height() + m_pInput->heightHint());
+ return ret;
+}
+
+void KviDccCanvas::handleMarshalError(int err)
+{
+ QString sss = KviError::getDescription(err);
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("DCC Failed: %Q","dcc"),&sss);
+}
+
+void KviDccCanvas::connected()
+{
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Connected to %Q:%Q","dcc"),
+ &(m_pMarshal->remoteIp()),&(m_pMarshal->remotePort()));
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Local end is %Q:%Q","dcc"),
+ &(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION...Find out the remote end
+ m_pDescriptor->szIp = m_pMarshal->remoteIp();
+ m_pDescriptor->szPort = m_pMarshal->remotePort();
+ m_pDescriptor->szHost = m_pMarshal->remoteIp();
+ }
+ updateCaption();
+// m_pSlaveThread = new KviDccCanvasThread(this,m_pMarshal->releaseSocket());
+// m_pSlaveThread->start();
+}
+
+
+#include "m_canvas.moc"
+
+#endif
diff --git a/src/modules/dcc/canvas.h b/src/modules/dcc/canvas.h
new file mode 100644
index 00000000..8e6b1fb3
--- /dev/null
+++ b/src/modules/dcc/canvas.h
@@ -0,0 +1,74 @@
+#ifndef _CANVAS_H_
+#define _CANVAS_H_
+//
+// File : canvas.h
+// Creation date : Sun Jul 29 07 2001 20:17:12 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "broker.h"
+
+#ifdef COMPILE_DCC_CANVAS
+
+#include "kvi_window.h"
+#include "kvi_string.h"
+
+#include "descriptor.h"
+#include "window.h"
+#include "thread.h"
+
+#include "kvi_pointerlist.h"
+
+
+
+class KviDccMarshal;
+class KviCanvasWidget;
+
+class QSplitter;
+
+
+class KviDccCanvas : public KviDccWindow
+{
+ Q_OBJECT
+public:
+ KviDccCanvas(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name);
+ ~KviDccCanvas();
+protected:
+// KviDccCanvasThread * m_pSlaveThread;
+ KviCanvasWidget * m_pCanvas;
+// QSplitter * m_pTopSplitter;
+ QString m_szTarget;
+protected:
+ virtual const QString &target();
+ virtual void fillCaptionBuffers();
+ virtual void getBaseLogFileName(KviStr &buffer);
+ virtual QPixmap * myIconPtr();
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual QSize sizeHint() const;
+ virtual bool event(QEvent *e);
+ virtual void ownMessage(const char *text);
+ virtual void ownAction(const char *text);
+protected slots:
+ void handleMarshalError(int err);
+ void connected();
+};
+
+#endif
+
+#endif // _CANVAS_H_
diff --git a/src/modules/dcc/canvaswidget.cpp b/src/modules/dcc/canvaswidget.cpp
new file mode 100644
index 00000000..203e0b36
--- /dev/null
+++ b/src/modules/dcc/canvaswidget.cpp
@@ -0,0 +1,1601 @@
+//
+// File : canvaswidget.cpp
+// Creation date : Mon Jul 30 07 2001 04:50:50 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "canvaswidget.h"
+
+#ifdef COMPILE_DCC_CANVAS
+
+
+#include <qcursor.h>
+#include <qpainter.h>
+#include <qsimplerichtext.h>
+#include <qlineedit.h>
+#include <qcombobox.h>
+#include <qvalidator.h>
+#include <stdlib.h>
+
+#include "kvi_string.h"
+
+#include "kvi_locale.h"
+#include "kvi_tal_popupmenu.h"
+
+#include <math.h>
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasRectangleItem
+//
+
+KviCanvasRectangleItem::KviCanvasRectangleItem(QCanvas * c,int x,int y,int w,int h)
+: QCanvasRectangle(x,y,w,h,c)
+{
+}
+
+KviCanvasRectangleItem::~KviCanvasRectangleItem()
+{
+}
+
+void KviCanvasRectangleItem::drawSelection(QPainter &p)
+{
+ p.setRasterOp(NotROP);
+ p.fillRect((int)x() + 1,(int)y() + 1,width() - 2,height() - 2,QBrush(Dense6Pattern));
+ p.setPen(QPen(DotLine));
+ p.drawRect((int)x(),(int)y(),width(),height());
+ p.setRasterOp(CopyROP);
+}
+
+
+void KviCanvasRectangleItem::setProperty(const QString &property,const QVariant &val)
+{
+ if(m_properties[property].isValid())
+ {
+ m_properties.replace(property,val);
+ hide();
+ show();
+ }
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasPolygon
+//
+
+
+KviCanvasPolygon::KviCanvasPolygon(QCanvas * c,int x,int y,QPointArray &pnts,double dScaleFactor)
+: QCanvasPolygon(c)
+{
+ m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
+ m_properties.insert("uLineWidth",QVariant((unsigned int)0));
+
+ m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
+ m_properties.insert("bHasBackground",QVariant(false,1));
+
+ m_dScaleFactor = dScaleFactor;
+ m_points = pnts;
+
+ resetPoints();
+ move(x,y);
+}
+
+
+KviCanvasPolygon::~KviCanvasPolygon()
+{
+}
+
+void KviCanvasPolygon::setScaleFactor(double dScaleFactor)
+{
+ m_dScaleFactor = dScaleFactor;
+ resetPoints();
+}
+
+void KviCanvasPolygon::setInternalPoints(const QPointArray &pnts)
+{
+ m_points = pnts;
+ resetPoints();
+
+}
+
+void KviCanvasPolygon::resetPoints()
+{
+ QPointArray scaled(m_points.size());
+ for(unsigned int i=0;i<m_points.size();i++)
+ {
+ int px;
+ int py;
+ m_points.point(i,&px,&py);
+ px = (int)(px * m_dScaleFactor);
+ py = (int)(py * m_dScaleFactor);
+ scaled.setPoint(i,px,py);
+ }
+ setPoints(scaled);
+}
+
+int KviCanvasPolygon::rtti() const
+{
+ return KVI_CANVAS_RTTI_POLYGON;
+}
+
+void KviCanvasPolygon::setProperty(const QString &property,const QVariant &val)
+{
+ if(m_properties[property].isValid())
+ {
+ m_properties.replace(property,val);
+ if((property == "clrForeground") || (property == "uLineWidth"))
+ {
+ setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
+ } else if((property == "clrBackground") || (property == "bHasBackground"))
+ {
+ if(m_properties["bHasBackground"].asBool())
+ setBrush(QBrush(m_properties["clrBackground"].asColor()));
+ else
+ setBrush(QBrush());
+ } else {
+ hide(); show();
+ }
+ }
+}
+
+void KviCanvasPolygon::draw(QPainter &p)
+{
+#if QT_VERSION >= 300
+ if(isEnabled())
+#else
+ if(enabled())
+#endif
+ {
+ p.setBrush(brush());
+ p.setPen(pen());
+ p.drawPolygon(areaPoints());
+ }
+
+#if QT_VERSION >=300
+ if(isSelected())
+#else
+ if(selected())
+#endif
+ {
+ p.setRasterOp(NotROP);
+ p.setPen(QPen(DotLine));
+ p.drawPolygon(areaPoints());
+ p.setBrush(QBrush());
+ double dVal = 10;
+ p.drawEllipse((int)(x() - dVal),(int)(y() - dVal),(int)(dVal * 2),(int)(dVal * 2));
+ p.drawLine((int)(x() - (dVal * 2)),(int)y(),(int)(x() + (dVal * 2)),(int)y());
+ p.drawLine((int)x(),(int)(y() - (dVal * 2)),(int)x(),(int)(y() + (dVal * 2)));
+ p.setRasterOp(CopyROP);
+ canvas()->setChanged(QRect((int)(x() - dVal),(int)(y() - dVal),(int)(dVal * 4),(int)(dVal * 4)));
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasEllipticItem
+//
+
+KviCanvasEllipticItem::KviCanvasEllipticItem(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasRectangleItem(c,x,y,w,h)
+{
+ m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
+ m_properties.insert("uLineWidth",QVariant((unsigned int)0));
+
+ m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
+ m_properties.insert("bHasBackground",QVariant(false,1));
+
+// m_properties.insert("iStartAngle",QVariant(0));
+// m_properties.insert("iEndAngle",QVariant(360));
+}
+
+KviCanvasEllipticItem::~KviCanvasEllipticItem()
+{
+}
+
+void KviCanvasEllipticItem::draw(QPainter &p)
+{
+#if QT_VERSION >= 300
+ if(isEnabled())
+#else
+ if(enabled())
+#endif
+ {
+ QBrush b = p.brush();
+ if(m_properties["bHasBackground"].asBool())p.setBrush(m_properties["clrBackground"].asColor());
+ else p.setBrush(QBrush());
+ p.setPen(pen());
+ drawContent(p);
+ p.setBrush(b);
+ }
+
+#if QT_VERSION >= 300
+ if(isSelected())drawSelection(p);
+#else
+ if(selected())drawSelection(p);
+#endif
+}
+
+void KviCanvasEllipticItem::drawContent(QPainter &p)
+{
+}
+
+
+void KviCanvasEllipticItem::setProperty(const QString & property,const QVariant &val)
+{
+ if(m_properties[property].isValid())
+ {
+ m_properties.replace(property,val);
+ if((property == "clrForeground") || (property == "uLineWidth"))
+ {
+ setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
+ } else {
+ hide(); show();
+ }
+ }
+}
+
+int KviCanvasEllipticItem::rtti() const
+{
+ return KVI_CANVAS_RTTI_ELLIPSE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasEllipse
+//
+
+
+KviCanvasEllipse::KviCanvasEllipse(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasEllipticItem(c,x,y,w,h)
+{
+}
+
+KviCanvasEllipse::~KviCanvasEllipse()
+{
+}
+
+int KviCanvasEllipse::rtti() const
+{
+ return KVI_CANVAS_RTTI_ELLIPSE;
+}
+
+void KviCanvasEllipse::drawContent(QPainter &p)
+{
+ p.drawEllipse((int)x(),(int)y(),width(),height());
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasPie
+//
+
+
+KviCanvasPie::KviCanvasPie(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasEllipticItem(c,x,y,w,h)
+{
+ m_properties.insert("iStartAngle",QVariant((int)0));
+ m_properties.insert("iExtensionAngle",QVariant((int)360));
+}
+
+KviCanvasPie::~KviCanvasPie()
+{
+}
+
+int KviCanvasPie::rtti() const
+{
+ return KVI_CANVAS_RTTI_PIE;
+}
+
+void KviCanvasPie::drawContent(QPainter &p)
+{
+ int iStartAngle = m_properties["iStartAngle"].asInt() * 16;
+ int iEndAngle = m_properties["iExtensionAngle"].asInt() * 16;
+ p.drawPie((int)x(),(int)y(),width(),height(),iStartAngle,iEndAngle);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasChord
+//
+
+
+KviCanvasChord::KviCanvasChord(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasEllipticItem(c,x,y,w,h)
+{
+ m_properties.insert("iStartAngle",QVariant((int)0));
+ m_properties.insert("iExtensionAngle",QVariant((int)360));
+}
+
+KviCanvasChord::~KviCanvasChord()
+{
+}
+
+int KviCanvasChord::rtti() const
+{
+ return KVI_CANVAS_RTTI_CHORD;
+}
+
+void KviCanvasChord::drawContent(QPainter &p)
+{
+ int iStartAngle = m_properties["iStartAngle"].asInt() * 16;
+ int iEndAngle = m_properties["iExtensionAngle"].asInt() * 16;
+ p.drawChord((int)x(),(int)y(),width(),height(),iStartAngle,iEndAngle);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasRectangle
+//
+
+KviCanvasRectangle::KviCanvasRectangle(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasRectangleItem(c,x,y,w,h)
+{
+ m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
+ m_properties.insert("uLineWidth",QVariant((unsigned int)0));
+
+ m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
+ m_properties.insert("bHasBackground",QVariant(false,1));
+}
+
+KviCanvasRectangle::~KviCanvasRectangle()
+{
+}
+
+int KviCanvasRectangle::rtti() const
+{
+ return KVI_CANVAS_RTTI_RECTANGLE;
+}
+
+
+void KviCanvasRectangle::setProperty(const QString &property,const QVariant &val)
+{
+ if(m_properties[property].isValid())
+ {
+ m_properties.replace(property,val);
+ if((property == "clrForeground") || (property == "uLineWidth"))
+ {
+ setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
+ } else {
+ hide(); show();
+ }
+ }
+}
+
+void KviCanvasRectangle::draw(QPainter & p)
+{
+#if QT_VERSION >= 300
+ if(isEnabled())
+#else
+ if(enabled())
+#endif
+ {
+ if(m_properties["bHasBackground"].asBool())
+ {
+ p.fillRect((int)x() + 1,(int)y() + 1,width() - 2,height() - 2,m_properties["clrBackground"].asColor());
+ }
+ p.setPen(pen());
+ p.drawRect((int)x(),(int)y(),width(),height());
+ }
+#if QT_VERSION >= 300
+ if(isSelected())drawSelection(p);
+#else
+ if(selected())drawSelection(p);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasRichText
+//
+
+KviCanvasRichText::KviCanvasRichText(QCanvas * c,int x,int y,int w,int h)
+: KviCanvasRectangleItem(c,x,y,w,h)
+{
+ QFont f = QFont();
+ f.setStyleHint(QFont::SansSerif);
+ f.setPointSize(12);
+ m_properties.insert("szText",QVariant(QString("<center>Insert here your <font color=\"#FF0000\"><b>RICH TEXT</b></font></center>")));
+ m_properties.insert("fntDefault",QVariant(f));
+}
+
+KviCanvasRichText::~KviCanvasRichText()
+{
+}
+
+int KviCanvasRichText::rtti() const
+{
+ return KVI_CANVAS_RTTI_RICHTEXT;
+}
+
+void KviCanvasRichText::draw(QPainter & p)
+{
+#if QT_VERSION >= 300
+ if(isEnabled())
+#else
+ if(enabled())
+#endif
+ {
+ QString szText = m_properties["szText"].asString();
+ QSimpleRichText text(szText,m_properties["fntDefault"].asFont());
+ text.setWidth(width());
+#if QT_VERSION >= 300
+ text.draw(&p,(int)x() + 1,(int)y() + 1,QRegion(QRect((int)x() + 1,(int)y() + 1,width(),height())),QColorGroup());
+#else
+ text.draw(&p,(int)x() + 1,(int)y() + 1,QRegion(QRect((int)x() + 1,(int)y() + 1,width(),height())),QPalette());
+#endif
+ }
+#if QT_VERSION >= 300
+ if(isSelected())drawSelection(p);
+#else
+ if(selected())drawSelection(p);
+#endif
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasLine
+//
+
+
+KviCanvasLine::KviCanvasLine(QCanvas * c,int x1,int y1,int x2,int y2)
+: QCanvasLine(c)
+{
+ setPoints(x1,y1,x2,y2);
+ m_properties.insert("uLineWidth",QVariant((unsigned int)0));
+ m_properties.insert("clrForeground",QVariant(QColor()));
+}
+
+KviCanvasLine::~KviCanvasLine()
+{
+}
+
+void KviCanvasLine::setProperty(const QString &property,const QVariant &val)
+{
+ m_properties.replace(property,val);
+ if((property == "uLineWidth") || (property == "clrForeground"))
+ {
+ setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
+ }
+}
+
+int KviCanvasLine::rtti() const
+{
+ return KVI_CANVAS_RTTI_LINE;
+}
+
+void KviCanvasLine::draw(QPainter &p)
+{
+#if QT_VERSION >= 300
+ if(isEnabled())
+#else
+ if(enabled())
+#endif
+ {
+ p.setPen(pen());
+ p.drawLine(startPoint(),endPoint());
+ }
+
+#if QT_VERSION >= 300
+ if(isSelected())
+#else
+ if(selected())
+#endif
+ {
+ p.setRasterOp(NotROP);
+ p.setPen(QPen(DotLine));
+ p.drawLine(startPoint(),endPoint());
+ p.setRasterOp(CopyROP);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasView
+//
+
+KviCanvasView::KviCanvasView(QCanvas * c,KviCanvasWidget * cw,QWidget * par)
+: QCanvasView(c,par)
+{
+ m_pCanvasWidget = cw;
+ m_state = Idle;
+ m_dragMode = None;
+ m_pSelectedItem = 0;
+ viewport()->setMouseTracking(true);
+}
+
+
+KviCanvasView::~KviCanvasView()
+{
+}
+
+
+void KviCanvasView::insertRectangle()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = Rectangle;
+}
+
+void KviCanvasView::insertRichText()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = RichText;
+}
+
+void KviCanvasView::insertLine()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = Line;
+}
+
+void KviCanvasView::insertEllipse()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = Ellipse;
+}
+
+void KviCanvasView::insertPie()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = Pie;
+}
+
+void KviCanvasView::insertChord()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = Chord;
+}
+
+
+void KviCanvasView::insertPolygonTriangle()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = PolygonTriangle;
+}
+
+
+void KviCanvasView::insertPolygonRectangle()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = PolygonRectangle;
+}
+
+
+void KviCanvasView::insertPolygonPentagon()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = PolygonPentagon;
+}
+
+void KviCanvasView::insertPolygonHexagon()
+{
+ m_state = SelectOrigin;
+ setCursor(crossCursor);
+ m_objectToInsert = PolygonPentagon;
+}
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+static void calcPolygonPoints(QPointArray &pnts,unsigned int nVertices)
+{
+ double dDelta = (2 * M_PI) / nVertices;
+ for(unsigned int i=0;i<nVertices;i++)
+ {
+ double dAng = dDelta * i;
+ double theX = 300 * sin(dAng);
+ double theY = 300 * cos(dAng);
+ pnts.setPoint(i,(int)theX,(int)theY);
+ }
+}
+
+void KviCanvasView::insertObjectAt(const QPoint & pnt,ObjectType o)
+{
+ QCanvasItem * r = 0;
+
+ switch(o)
+ {
+ case Rectangle:
+ r = new KviCanvasRectangle(canvas(),pnt.x(),pnt.y(),0,0);
+ break;
+ case RichText:
+ r = new KviCanvasRichText(canvas(),pnt.x(),pnt.y(),0,0);
+ break;
+ case Line:
+ r = new KviCanvasLine(canvas(),pnt.x(),pnt.y(),pnt.x(),pnt.y());
+ break;
+ case Ellipse:
+ r = new KviCanvasEllipse(canvas(),pnt.x(),pnt.y(),0,0);
+ break;
+ case Pie:
+ r = new KviCanvasPie(canvas(),pnt.x(),pnt.y(),0,0);
+ break;
+ case Chord:
+ r = new KviCanvasChord(canvas(),pnt.x(),pnt.y(),0,0);
+ break;
+ case PolygonTriangle:
+ {
+ QPointArray pa(3);
+ pa.setPoint(0,0,-500);
+ pa.setPoint(1,-450,220);
+ pa.setPoint(2,450,220);
+ r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
+ }
+ break;
+ case PolygonRectangle:
+ {
+ QPointArray pa(4);
+ pa.setPoint(0,-350,-350);
+ pa.setPoint(1,350,-350);
+ pa.setPoint(2,350,350);
+ pa.setPoint(3,-350,350);
+ r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
+ }
+ break;
+ case PolygonPentagon:
+ {
+ QPointArray pa(5);
+ calcPolygonPoints(pa,5);
+ r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
+ }
+ break;
+ case PolygonHexagon:
+ {
+ QPointArray pa(6);
+ calcPolygonPoints(pa,6);
+ r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
+ }
+ break;
+ }
+
+ if(r)
+ {
+ setItemSelected(r);
+ r->setEnabled(true);
+ r->show();
+ }
+
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(r))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ beginDragRectangle((KviCanvasRectangleItem *)r,pnt,true);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ beginDragLine((KviCanvasLine *)r,pnt,true);
+ break;
+// case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+// beginDragPolygon((KviCanvasPolygon *)r,pnt,true);
+// break;
+ }
+
+// canvas()->update();
+}
+
+void KviCanvasView::setItemSelected(QCanvasItem * it)
+{
+ clearSelection();
+ it->setSelected(true);
+ m_pSelectedItem = it;
+ m_pCanvasWidget->m_pPropertiesWidget->editItem(it);
+
+}
+
+void KviCanvasView::clearSelection()
+{
+ if(!m_pSelectedItem)return;
+ m_pSelectedItem->setSelected(false);
+ m_pSelectedItem = 0;
+ m_pCanvasWidget->m_pPropertiesWidget->editItem(0);
+}
+
+
+
+void KviCanvasView::beginDragLine(KviCanvasLine * it,const QPoint &p,bool bInitial)
+{
+ QPoint sp = it->startPoint();
+
+ m_dragBegin = p - sp;
+
+ if(bInitial)
+ {
+ m_dragMode = Bottom;
+ setCursor(sizeAllCursor);
+ return;
+ }
+
+ if((abs(p.x() - sp.x()) < 3) && (abs(p.y() - sp.y()) < 3))
+ {
+ m_dragMode = Top;
+ setCursor(sizeAllCursor);
+ return;
+ }
+
+ sp = it->endPoint();
+ if((abs(p.x() - sp.x()) < 3) && (abs(p.y() - sp.y()) < 3))
+ {
+ m_dragMode = Bottom;
+ setCursor(sizeAllCursor);
+ return;
+ }
+
+ m_dragMode = All;
+ setCursor(pointingHandCursor);
+}
+
+void KviCanvasView::dragLine(KviCanvasLine * it,const QPoint &p)
+{
+ switch(m_dragMode)
+ {
+ case Bottom:
+ {
+ QPoint sp = it->startPoint();
+ it->setPoints(sp.x(),sp.y(),p.x(),p.y());
+ }
+ break;
+ case Top:
+ {
+ QPoint ep = it->endPoint();
+ it->setPoints(p.x(),p.y(),ep.x(),ep.y());
+ }
+ break;
+ case All:
+ {
+ QPoint sp = p - m_dragBegin;
+ QPoint ep = sp + (it->endPoint() - it->startPoint());
+ it->setPoints(sp.x(),sp.y(),ep.x(),ep.y());
+ }
+ break;
+ default: /* make gcc happy */
+ break;
+ }
+ canvas()->update();
+}
+
+
+static double ssm_2d_rotationAngleFromXAxis(double dx,double dy)
+{
+ //
+ // v1 . v2 dx * 1 + dy * 0 dx
+ //acos(---------) = acos(-----------------) = acos(---------)
+ // |v1||v2| |(dx,dy)| * 1 |(dx,dy)|
+ //
+
+ //double dVal = hypot(dx,dy);
+ double dVal = sqrt((dx * dx) + (dy * dy));
+
+ if(dVal == 0.0)return 0; // ???
+
+ dVal = acos(dx / dVal);
+
+ return (dy > 0.0) ? dVal : -dVal;
+}
+
+static double ssm_2d_rotationAngle(double drefx,double drefy,double drotatedx,double drotatedy)
+{
+ double dRefAngle = ssm_2d_rotationAngleFromXAxis(drefx,drefy);
+ double dRotAngle = ssm_2d_rotationAngleFromXAxis(drotatedx,drotatedy);
+ return dRotAngle - dRefAngle;
+}
+
+static void ssm_2d_rotate(double &dx,double &dy,double dAngle)
+{
+ // Rotation matrix:
+ //
+ // | cos(x) sin(x) |
+ // | |
+ // | -sin(x) cos(x) |
+
+ double s = sin(dAngle);
+ double c = cos(dAngle);
+
+ double tmpX = (dx * c) - (dy * s);
+ double tmpY = (dx * s) + (dy * c);
+
+ dx = tmpX;
+ dy = tmpY;
+}
+
+static double ssm_hypot(double a,double b)
+{
+ return sqrt((a * a) + (b * b));
+}
+
+void KviCanvasView::beginDragPolygon(KviCanvasPolygon * it,const QPoint &p,bool bShift,bool bCtrl)
+{
+ m_dragBegin = QPoint((int)(p.x() - it->x()),(int)(p.y() - it->y()));
+
+ QPointArray pa = it->areaPoints();
+
+ for(unsigned int i=0;i<pa.size();i++)
+ {
+ QPoint pnt = pa.point(i);
+ double dX = pnt.x() - p.x();
+ double dY = pnt.y() - p.y();
+ double dHypot = sqrt((dX * dX) + (dY * dY));
+ if(dHypot < 3.0)
+ {
+ // We're dragging a point
+ m_dragMode = SinglePoint;
+ m_dragPointIndex = i;
+ setCursor(crossCursor);
+ return;
+ }
+ }
+
+ if(bShift)
+ {
+ m_dragMode = Scale;
+ m_dragScaleFactor = it->scaleFactor();
+ setCursor(sizeAllCursor);
+ return;
+ }
+
+ if(bCtrl)
+ {
+ m_dragMode = Rotate;
+ m_dragPointArray = it->internalPoints();
+// debug("Here");
+ setCursor(sizeHorCursor);
+ return;
+ }
+
+ m_dragMode = All;
+ setCursor(pointingHandCursor);
+}
+
+void KviCanvasView::dragPolygon(KviCanvasPolygon * it,const QPoint &p)
+{
+ switch(m_dragMode)
+ {
+ case All:
+ it->move(p.x() - m_dragBegin.x(),p.y() - m_dragBegin.y());
+ break;
+ case SinglePoint:
+ {
+ QPointArray pnt = it->internalPoints();
+ pnt.setPoint(m_dragPointIndex,(int)((p.x() - it->x()) / it->scaleFactor()),(int)((p.y() - it->y()) / it->scaleFactor()));
+ it->setInternalPoints(pnt);
+ }
+ break;
+ case Scale:
+ {
+ double dDistance = ssm_hypot(p.x() - it->x(),p.y() - it->y());
+ double dOriginal = ssm_hypot(m_dragBegin.x(),m_dragBegin.y());
+ if(dOriginal < 1)dOriginal = 1;
+ if(dDistance < 0.1)dDistance = 0.1;
+ it->setScaleFactor(m_dragScaleFactor * dDistance / dOriginal);
+ }
+ break;
+ case Rotate:
+ {
+ QPoint act((int)(p.x() - it->x()),(int)(p.y() - it->y()));
+ double dAngle = ssm_2d_rotationAngle(m_dragBegin.x(),m_dragBegin.y(),act.x(),act.y());
+// debug("%d,%d %d,%d %f",m_dragBegin.x(),m_dragBegin.y(),act.x(),act.y(),dAngle);
+ QPointArray thePoints = m_dragPointArray.copy();
+ for(unsigned int i=0;i<thePoints.size();i++)
+ {
+ QPoint tmp = thePoints.point(i);
+ double dx = tmp.x();
+ double dy = tmp.y();
+ ssm_2d_rotate(dx,dy,dAngle);
+ thePoints.setPoint(i,(int)dx,(int)dy);
+ }
+ it->setInternalPoints(thePoints);
+ }
+ break;
+ default:
+ break;
+ }
+ canvas()->update();
+}
+
+void KviCanvasView::beginDragRectangle(KviCanvasRectangleItem * it,const QPoint & p,bool bInitial)
+{
+ m_dragBegin = QPoint((int)(p.x() - it->x()),(int)(p.y() - it->y()));
+
+ if(bInitial)
+ {
+ // Right bottom
+ m_dragMode = RightBottom;
+ setCursor(sizeFDiagCursor);
+ return;
+ }
+
+ if(p.x() < (((int)it->x()) + 2))
+ {
+ // Left edge
+ if(p.y() < (((int)it->y()) + 2))
+ {
+ // Left top
+ m_dragMode = LeftTop;
+ setCursor(sizeFDiagCursor);
+ return;
+ }
+ if(p.y() > ( it->bottom() - 2))
+ {
+ // Left bottom
+ m_dragMode = LeftBottom;
+ setCursor(sizeBDiagCursor);
+ return;
+ }
+ m_dragMode = Left;
+ setCursor(sizeHorCursor);
+ return;
+ }
+
+ if(p.x() > (it->right() - 2))
+ {
+ // Right edge
+ if(p.y() < (((int)it->y()) + 2))
+ {
+ // Right top
+ m_dragMode = RightTop;
+ setCursor(sizeBDiagCursor);
+ return;
+ }
+ if(p.y() > ( it->bottom() - 2))
+ {
+ // Right bottom
+ m_dragMode = RightBottom;
+ setCursor(sizeFDiagCursor);
+ return;
+ }
+ m_dragMode = Right;
+ setCursor(sizeHorCursor);
+ return;
+ }
+
+ // Somewhere in the middle
+ if(p.y() < (((int)it->y()) + 2))
+ {
+ // Top
+ m_dragMode = Top;
+ setCursor(sizeVerCursor);
+ return;
+ }
+ if(p.y() > ( it->bottom() - 2))
+ {
+ // Bottom
+ m_dragMode = Bottom;
+ setCursor(sizeVerCursor);
+ return;
+ }
+
+ m_dragMode = All;
+ setCursor(pointingHandCursor);
+}
+
+void KviCanvasView::dragRectangle(KviCanvasRectangleItem * it,const QPoint & p)
+{
+
+ int aux1,aux2,aux3,aux4;
+
+ switch(m_dragMode)
+ {
+ case All:
+ it->move(p.x() - m_dragBegin.x(),p.y() - m_dragBegin.y());
+ break;
+ case Left:
+ aux1 = it->width() + (int)(it->x() - p.x());
+ aux2 = p.x();
+ if(aux1 < 1)
+ {
+ aux2 += (aux1 - 1);
+ aux1 = 1;
+ }
+ it->move(aux2,it->y());
+ it->setSize(aux1,it->height());
+ break;
+ case Right:
+ aux1 = it->width() + (p.x() - it->right());
+ if(aux1 < 1)aux1 = 1;
+ it->setSize(aux1,it->height());
+ break;
+ case Top:
+ aux1 = it->height() + (int)(it->y() - p.y());
+ aux2 = p.y();
+ if(aux1 < 1)
+ {
+ aux2 += (aux1 - 1);
+ aux1 = 1;
+ }
+ it->move(it->x(),aux2);
+ it->setSize(it->width(),aux1);
+ break;
+ case Bottom:
+ aux1 = (int)it->height() + (p.y() - it->bottom());
+ if(aux1 < 1)aux1 = 1;
+ it->setSize(it->width(),aux1);
+ break;
+ case LeftTop:
+ aux1 = it->width() + (int)(it->x() - p.x());
+ aux3 = p.x();
+ if(aux1 < 1)
+ {
+ aux3 += (aux1 - 1);
+ aux1 = 1;
+ }
+ aux2 = it->height() + (int)(it->y() - p.y());
+ aux4 = p.y();
+ if(aux2 < 1)
+ {
+ aux4 += (aux2 - 1);
+ aux2 = 1;
+ }
+ it->setSize(aux1,aux2);
+ it->move(aux3,aux4);
+ break;
+ case RightTop:
+ aux1 = it->width() + (int)(p.x() - it->right());
+ if(aux1 < 1)aux1 = 1;
+ aux2 = it->height() + (int)(it->y() - p.y());
+ aux4 = p.y();
+ if(aux2 < 1)
+ {
+ aux4 += (aux2 - 1);
+ aux2 = 1;
+ }
+ it->setSize(aux1,aux2);
+ it->move(it->x(),aux4);
+ break;
+ case LeftBottom:
+ aux1 = it->width() + (int)(it->x() - p.x());
+ aux3 = p.x();
+ if(aux1 < 1)
+ {
+ aux3 += (aux1 - 1);
+ aux1 = 1;
+ }
+ aux2 = it->height() + (int)(p.y() - it->bottom());
+ if(aux2 < 1)aux2 = 1;
+ it->setSize(aux1,aux2);
+ it->move(aux3,it->y());
+ break;
+ case RightBottom:
+ aux1 = it->width() + (int)(p.x() - it->right());
+ if(aux1 < 1)aux1 = 1;
+ aux2 = it->height() + (int)(p.y() - it->bottom());
+ if(aux2 < 1)aux2 = 1;
+ it->setSize(aux1,aux2);
+ break;
+ default:
+ break;
+ }
+
+ canvas()->update();
+}
+
+void KviCanvasView::contentsMouseMoveEvent(QMouseEvent *e)
+{
+// QPoint p = inverseWorldMatrix().map(e->pos());
+ QPoint p = e->pos();
+ if(e->state() & Qt::LeftButton)
+ {
+ if((m_dragMode != None) && (m_pSelectedItem))
+ {
+#if QT_VERSION >= 300
+ if(m_pSelectedItem->isEnabled())m_pSelectedItem->setEnabled(false);
+#else
+ if(m_pSelectedItem->enabled())m_pSelectedItem->setEnabled(false);
+#endif
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ dragRectangle((KviCanvasRectangleItem *)m_pSelectedItem,p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ dragLine((KviCanvasLine *)m_pSelectedItem,p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+ dragPolygon((KviCanvasPolygon *)m_pSelectedItem,p);
+ break;
+ }
+ }
+ } else {
+ // Without buttons
+ if(m_state == Idle)
+ {
+ QCanvasItemList l = canvas()->collisions(p);
+ QCanvasItemList::Iterator it = l.begin();
+
+ if(it != l.end())
+ {
+ // Got an item
+ QCanvasItem * hit = (QCanvasItem *)*it;
+ // Now find the point on that we have clicked it
+ if(hit == m_pSelectedItem)
+ {
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ beginDragRectangle((KviCanvasRectangleItem *)m_pSelectedItem,p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ beginDragLine((KviCanvasLine *)m_pSelectedItem,p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+ beginDragPolygon((KviCanvasPolygon *)m_pSelectedItem,p);
+ break;
+ }
+ }
+ else if(m_dragMode != None)setCursor(arrowCursor);
+ } else {
+ if(m_dragMode != None)setCursor(arrowCursor);
+ }
+ }
+ }
+}
+
+void KviCanvasView::contentsMouseReleaseEvent(QMouseEvent *e)
+{
+ if(m_dragMode != None)
+ {
+ // Was just dragging a rectangle
+ m_dragMode = None;
+ setCursor(arrowCursor);
+ if(m_pSelectedItem)
+ {
+ m_pSelectedItem->setEnabled(true);
+ canvas()->update();
+ }
+ }
+}
+
+void KviCanvasView::contentsMousePressEvent(QMouseEvent *e)
+{
+ if(e->button() & Qt::LeftButton)
+ {
+// QPoint p = inverseWorldMatrix().map(e->pos());
+ QPoint p = e->pos();
+
+ switch(m_state)
+ {
+ case SelectOrigin:
+ clearSelection();
+ setCursor(arrowCursor);
+ m_state = Idle;
+ insertObjectAt(p,m_objectToInsert);
+ canvas()->update();
+ break;
+
+ case Idle:
+ {
+ QCanvasItemList l = canvas()->collisions(p);
+ QCanvasItemList::Iterator it = l.begin();
+
+ if(it != l.end())
+ {
+ // Got an item
+ QCanvasItem * hit = *it;
+ if(hit != m_pSelectedItem)
+ {
+ // Was not selected yet
+ setItemSelected(hit);
+ canvas()->update();
+ }
+ // Now find the point on that we have clicked it
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(hit))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ beginDragRectangle(((KviCanvasRectangleItem *)hit),p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ beginDragLine(((KviCanvasLine *)hit),p);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+ beginDragPolygon(((KviCanvasPolygon *)hit),p,e->state() & Qt::ShiftButton,e->state() & Qt::ControlButton);
+ break;
+ }
+ } else {
+ // No item
+ clearSelection();
+ canvas()->update();
+ }
+ }
+ break;
+ }
+ }
+}
+
+
+void KviCanvasView::propertyChanged(const QString &s,const QVariant &v)
+{
+ if(!m_pSelectedItem)return;
+
+
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ ((KviCanvasRectangleItem *)m_pSelectedItem)->setProperty(s,v);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ ((KviCanvasLine *)m_pSelectedItem)->setProperty(s,v);
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+ ((KviCanvasPolygon *)m_pSelectedItem)->setProperty(s,v);
+ break;
+ }
+
+ canvas()->update();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviVariantTableItem
+//
+
+
+
+KviVariantTableItem::KviVariantTableItem(QTable * t,const QVariant & property)
+: QTableItem(t,QTableItem::WhenCurrent,QString::null)
+{
+ m_property = property;
+}
+
+KviVariantTableItem::~KviVariantTableItem()
+{
+}
+
+
+QWidget * KviVariantTableItem::createEditor() const
+{
+ switch(m_property.type())
+ {
+ case QVariant::String:
+ {
+ QLineEdit * e = new QLineEdit(table()->viewport());
+ e->setText(m_property.toString());
+ return e;
+ }
+ break;
+ case QVariant::Int:
+ {
+ QLineEdit * e = new QLineEdit(table()->viewport());
+ QString sz;
+ sz.setNum(m_property.toInt());
+ e->setText(sz);
+ e->setValidator(new QIntValidator(e));
+ return e;
+ }
+ break;
+ case QVariant::UInt:
+ {
+ QLineEdit * e = new QLineEdit(table()->viewport());
+ QString sz;
+ sz.setNum(m_property.toInt());
+ e->setText(sz);
+ QIntValidator * i = new QIntValidator(e);
+ i->setBottom(0);
+ e->setValidator(i);
+ return e;
+ }
+ break;
+ case QVariant::Bool:
+ {
+ QComboBox * b = new QComboBox(false,table()->viewport());
+ b->insertItem("FALSE");
+ b->insertItem("TRUE");
+ b->setCurrentItem(m_property.toBool() ? 1 : 0);
+ return b;
+ }
+ break;
+ case QVariant::Color:
+ {
+ QLineEdit * e = new QLineEdit(table()->viewport());
+ e->setText(m_property.toColor().name());
+ return e;
+ }
+ break;
+ case QVariant::Font:
+ {
+ QComboBox * b = new QComboBox(true,table()->viewport());
+
+ QString tmp;
+ QString tmpDefault;
+ QFont f = QFont();
+ f.setStyleHint(QFont::SansSerif);
+ tmpDefault = f.family();
+ f.setStyleHint(QFont::TypeWriter);
+ tmp.setNum(m_property.toFont().pointSize());
+ tmp.prepend(", ");
+ tmp.prepend(m_property.toFont().family());
+ b->insertItem(tmp);
+ b->insertItem(tmpDefault + ", 8");
+ b->insertItem(tmpDefault + ", 10");
+ b->insertItem(tmpDefault + ", 12");
+ b->insertItem(tmpDefault + ", 14");
+ b->insertItem(tmpDefault + ", 16");
+ b->insertItem(tmpDefault + ", 18");
+ b->insertItem(tmpDefault + ", 20");
+ b->insertItem(tmpDefault + ", 24");
+ b->insertItem(tmpDefault + ", 28");
+ b->insertItem(tmpDefault + ", 32");
+ b->insertItem(tmpDefault + ", 40");
+ b->insertItem(tmpDefault + ", 48");
+ b->insertItem(f.family() + ", 12");
+ b->setCurrentItem(0);
+
+ b->setCurrentItem(m_property.toBool() ? 1 : 0);
+ return b;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void KviVariantTableItem::setContentFromEditor(QWidget * w)
+{
+ switch(m_property.type())
+ {
+ case QVariant::String:
+ m_property = QVariant(((QLineEdit *)w)->text());
+ break;
+ case QVariant::Int:
+ m_property = QVariant(((QLineEdit *)w)->text().toInt());
+ break;
+ case QVariant::UInt:
+ m_property = QVariant(((QLineEdit *)w)->text().toUInt());
+ break;
+ case QVariant::Bool:
+ m_property = QVariant(((QComboBox *)w)->currentItem(),1);
+ break;
+ case QVariant::Color:
+ m_property.asColor().setNamedColor(((QLineEdit *)w)->text());
+ break;
+ case QVariant::Font:
+ {
+ KviStr txt = ((QComboBox *)w)->currentText();
+ if(txt.hasData())
+ {
+ KviStr fam = txt;
+ fam.cutFromFirst(',',true);
+ fam.stripWhiteSpace();
+ KviStr psz = txt;
+ psz.cutToFirst(',',true);
+ psz.stripWhiteSpace();
+ bool bOk;
+ unsigned int uSize = psz.toUInt(&bOk);
+ if(!bOk)uSize = 12;
+ m_property = QVariant(QFont(fam.ptr(),uSize));
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+
+void KviVariantTableItem::paint(QPainter *p,const QColorGroup &cg,const QRect &cr,bool)
+{
+ p->fillRect(0,0,cr.width(),cr.height(),cg.base());
+
+ if(m_property.type() == QVariant::Color)
+ {
+ p->fillRect(3,3,cr.width() - 6,cr.height() - 6,m_property.asColor());
+ } else {
+ QString sz;
+ switch(m_property.type())
+ {
+ case QVariant::String:
+ sz = m_property.toString();
+ break;
+ case QVariant::Bool:
+ sz = m_property.toBool() ? "TRUE" : "FALSE";
+ break;
+ case QVariant::Font:
+ sz.setNum(m_property.toFont().pointSize());
+ sz.prepend(", ");
+ sz.prepend(m_property.toFont().family());
+ break;
+ case QVariant::Int:
+ sz.setNum(m_property.toInt());
+ break;
+ case QVariant::UInt:
+ sz.setNum(m_property.toUInt());
+ break;
+ default:
+ break;
+ }
+ p->setPen(cg.text());
+ p->drawText(0,0,cr.width(),cr.height(),Qt::AlignLeft | Qt::AlignTop,sz);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasItemPropertiesWidget
+//
+
+
+KviCanvasItemPropertiesWidget::KviCanvasItemPropertiesWidget(QWidget * par)
+: QTable(par)
+{
+ setSelectionMode(QTable::NoSelection);
+ setColumnMovingEnabled(false);
+ setRowMovingEnabled(false);
+ setShowGrid(true);
+ setNumCols(2);
+ setSorting(false);
+ setLeftMargin(0);
+ verticalHeader()->hide();
+ connect(this,SIGNAL(valueChanged(int,int)),this,SLOT(cellEdited(int,int)));
+}
+
+KviCanvasItemPropertiesWidget::~KviCanvasItemPropertiesWidget()
+{
+}
+
+void KviCanvasItemPropertiesWidget::cellEdited(int row,int)
+{
+ QTableItem * it = item(row,0);
+ if(!it)return;
+ QString szName = it->text();
+ it = item(row,1);
+ if(!it)return;
+ emit propertyChanged(szName,((KviVariantTableItem *)it)->property());
+}
+
+void KviCanvasItemPropertiesWidget::editItem(QCanvasItem * it)
+{
+ if(!it)
+ {
+ for(int i=0;i<numRows();i++)
+ {
+ clearCell(i,0);
+ clearCell(i,1);
+ clearCellWidget(i,1);
+ }
+ setNumRows(0);
+ return;
+ }
+
+ QMap<QString,QVariant> * m = 0;
+
+ switch(KVI_CANVAS_RTTI_CONTROL_TYPE(it))
+ {
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
+ m = ((KviCanvasRectangleItem *)it)->properties();
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
+ m = ((KviCanvasLine *)it)->properties();
+ break;
+ case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
+ m = ((KviCanvasPolygon *)it)->properties();
+ break;
+ }
+
+ if(!m)
+ {
+ editItem(0);
+ return;
+ }
+
+ for(int i=0;i<numRows();i++)
+ {
+ clearCell(i,0);
+ clearCell(i,1);
+ clearCellWidget(i,1);
+ }
+
+ setNumRows(m->count());
+
+ QTableItem * item;
+
+ int idx = 0;
+
+ for(QMap<QString,QVariant>::ConstIterator iter = m->begin();iter != m->end();++iter)
+ {
+ item = new QTableItem(this,QTableItem::Never,iter.key().utf8().data());
+ setItem(idx,0,item);
+ item = new KviVariantTableItem(this,iter.data());
+ setItem(idx,1,item);
+ idx++;
+ }
+
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// KviCanvasWidget
+//
+
+KviCanvasWidget::KviCanvasWidget(QWidget * par)
+: QWidget(par,"canvas_widget")
+{
+ m_pCanvas = new QCanvas(this);
+//#warning "Make this size as parameter of Dcc ?"
+ m_pCanvas->resize(648,480);
+ m_pMenuBar = new QMenuBar(this);
+ m_pSplitter = new QSplitter(QSplitter::Horizontal,this);
+ m_pCanvasView = new KviCanvasView(m_pCanvas,this,m_pSplitter);
+ m_pStatusLabel = new QLabel(this);
+ m_pPropertiesWidget = new KviCanvasItemPropertiesWidget(m_pSplitter);
+ QValueList<int> l;
+ l.append(80);
+ l.append(20);
+ m_pSplitter->setSizes(l);
+
+ connect(m_pPropertiesWidget,SIGNAL(propertyChanged(const QString &,const QVariant &)),m_pCanvasView,SLOT(propertyChanged(const QString &,const QVariant &)));
+
+ KviTalPopupMenu * add = new KviTalPopupMenu(m_pMenuBar);
+ KviTalPopupMenu * shapes = new KviTalPopupMenu(add);
+ KviTalPopupMenu * polygons = new KviTalPopupMenu(add);
+ KviTalPopupMenu * items = new KviTalPopupMenu(add);
+ shapes->insertItem(__tr2qs_ctx("&Line","dcc"),m_pCanvasView,SLOT(insertLine()));
+ shapes->insertItem(__tr2qs_ctx("&Rectangle","dcc"),m_pCanvasView,SLOT(insertRectangle()));
+ shapes->insertItem(__tr2qs_ctx("&Ellipse","dcc"),m_pCanvasView,SLOT(insertEllipse()));
+ shapes->insertItem(__tr2qs_ctx("&Pie","dcc"),m_pCanvasView,SLOT(insertPie()));
+ shapes->insertItem(__tr2qs_ctx("&Chord","dcc"),m_pCanvasView,SLOT(insertChord()));
+
+ items->insertItem(__tr2qs_ctx("&Rich text (html)","dcc"),m_pCanvasView,SLOT(insertRichText()));
+
+ polygons->insertItem(__tr2qs_ctx("&Triangle","dcc"),m_pCanvasView,SLOT(insertPolygonTriangle()));
+ polygons->insertItem(__tr2qs_ctx("&Rectangle","dcc"),m_pCanvasView,SLOT(insertPolygonRectangle()));
+ polygons->insertItem(__tr2qs_ctx("&Pentagon","dcc"),m_pCanvasView,SLOT(insertPolygonPentagon()));
+ polygons->insertItem(__tr2qs_ctx("&Hexagon","dcc"),m_pCanvasView,SLOT(insertPolygonHexagon()));
+
+ add->insertItem(__tr2qs_ctx("&Shape","dcc"),shapes);
+ add->insertItem(__tr2qs_ctx("&Item","dcc"),items);
+ add->insertItem(__tr2qs_ctx("&Polygons","dcc"),polygons);
+
+ m_pMenuBar->insertItem(__tr2qs_ctx("&Insert","dcc"),add);
+}
+
+KviCanvasWidget::~KviCanvasWidget()
+{
+}
+
+
+
+void KviCanvasWidget::resizeEvent(QResizeEvent *)
+{
+ int h = m_pMenuBar->sizeHint().height();
+ m_pMenuBar->setGeometry(0,0,width(),h);
+ int h2 = m_pStatusLabel->sizeHint().height();
+ m_pStatusLabel->setGeometry(0,height() - h2,width(),h2);
+ m_pSplitter->setGeometry(0,h,width(),height() - (h + h2));
+}
+
+
+#include "m_canvaswidget.moc"
+
+#endif
diff --git a/src/modules/dcc/canvaswidget.h b/src/modules/dcc/canvaswidget.h
new file mode 100644
index 00000000..41d451e4
--- /dev/null
+++ b/src/modules/dcc/canvaswidget.h
@@ -0,0 +1,322 @@
+#ifndef _CANVAS_WIDGET_H_
+#define _CANVAS_WIDGET_H_
+//
+// File : canvaswidget.h
+// Creation date : Mon Jul 30 07 2001 04:49:49 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "broker.h"
+
+#ifdef COMPILE_DCC_CANVAS
+
+
+#include <qcanvas.h>
+
+//#ifdef QT_NO_CANVAS
+// #warning "HEre is not"
+//#endif
+
+
+#include <qmenubar.h>
+#include <qlabel.h>
+
+#include <qmap.h>
+#include <qvariant.h>
+#include <qtable.h>
+#include <qsplitter.h>
+
+
+
+class KviCanvasWidget;
+
+
+
+#define KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE 1
+#define KVI_CANVAS_RTTI_CONTROL_TYPE_LINE 2
+#define KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON 4
+
+#define KVI_CANVAS_RTTI_CONTROL_TYPE_MASK 255
+
+#define KVI_CANVAS_RTTI_RECTANGLE (KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE | (1 << 8))
+#define KVI_CANVAS_RTTI_RICHTEXT (KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE | (2 << 8))
+#define KVI_CANVAS_RTTI_LINE (KVI_CANVAS_RTTI_CONTROL_TYPE_LINE | (3 << 8))
+#define KVI_CANVAS_RTTI_ELLIPSE (KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE | (4 << 8))
+#define KVI_CANVAS_RTTI_CHORD (KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE | (5 << 8))
+#define KVI_CANVAS_RTTI_PIE (KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE | (6 << 8))
+#define KVI_CANVAS_RTTI_POLYGON (KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON | (7 << 8))
+
+#define KVI_CANVAS_RTTI_CONTROL_TYPE(__item) (__item->rtti() & KVI_CANVAS_RTTI_CONTROL_TYPE_MASK)
+
+
+
+class KviCanvasPolygon : public QCanvasPolygon
+{
+public:
+ KviCanvasPolygon(QCanvas * c,int x,int y,QPointArray &pnts,double dScaleFactor);
+ virtual ~KviCanvasPolygon();
+protected:
+ QMap<QString,QVariant> m_properties;
+ double m_dScaleFactor;
+ QPointArray m_points;
+public:
+ virtual void draw(QPainter & p);
+ double scaleFactor(){ return m_dScaleFactor; };
+ void setScaleFactor(double dScaleFactor);
+ void resetPoints();
+ const QPointArray & internalPoints(){ return m_points; };
+ void setInternalPoints(const QPointArray &pnts);
+ QMap<QString,QVariant> * properties(){ return &m_properties; };
+ virtual void setProperty(const QString &property,const QVariant &val);
+ virtual int rtti() const;
+};
+
+
+
+
+class KviCanvasRectangleItem : public QCanvasRectangle
+{
+ friend class KviCanvasRectangle;
+ friend class KviCanvasRichText;
+ friend class KviCanvasEllipticItem;
+protected:
+ KviCanvasRectangleItem(QCanvas * c,int x,int y,int w,int h);
+public:
+ virtual ~KviCanvasRectangleItem();
+protected:
+ QMap<QString,QVariant> m_properties;
+public:
+ int right(){ return ((int)x()) + width(); };
+ int bottom(){ return ((int)y()) + height(); };
+ QMap<QString,QVariant> * properties(){ return &m_properties; };
+ virtual void setProperty(const QString &property,const QVariant &val);
+// QVariant property(const QString &name){ return m_properties[name]; };
+protected:
+ void drawSelection(QPainter &p);
+};
+
+
+class KviCanvasEllipticItem : public KviCanvasRectangleItem
+{
+ friend class KviCanvasEllipse;
+ friend class KviCanvasChord;
+ friend class KviCanvasPie;
+protected:
+ KviCanvasEllipticItem(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasEllipticItem();
+public:
+ virtual void setProperty(const QString & property,const QVariant &val);
+ virtual void draw(QPainter & p);
+ virtual void drawContent(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasEllipse : public KviCanvasEllipticItem
+{
+public:
+ KviCanvasEllipse(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasEllipse();
+public:
+ virtual void drawContent(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasPie : public KviCanvasEllipticItem
+{
+public:
+ KviCanvasPie(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasPie();
+public:
+ virtual void drawContent(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasChord : public KviCanvasEllipticItem
+{
+public:
+ KviCanvasChord(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasChord();
+public:
+ virtual void drawContent(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasLine : public QCanvasLine
+{
+public:
+ KviCanvasLine(QCanvas * c,int x1,int y1,int x2,int y2);
+ virtual ~KviCanvasLine();
+protected:
+ QMap<QString,QVariant> m_properties;
+public:
+ QMap<QString,QVariant> * properties(){ return &m_properties; };
+ virtual void setProperty(const QString &property,const QVariant &val);
+// QVariant property(const QString &name){ return m_properties[name]; };
+ virtual void draw(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+
+class KviCanvasRectangle : public KviCanvasRectangleItem
+{
+public:
+ KviCanvasRectangle(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasRectangle();
+public:
+ virtual void draw(QPainter & p);
+ virtual void setProperty(const QString &property,const QVariant &val);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasRichText : public KviCanvasRectangleItem
+{
+public:
+ KviCanvasRichText(QCanvas * c,int x,int y,int w,int h);
+ ~KviCanvasRichText();
+public:
+ virtual void draw(QPainter & p);
+ virtual int rtti() const;
+};
+
+
+class KviCanvasView : public QCanvasView
+{
+ Q_OBJECT
+public:
+ KviCanvasView(QCanvas * c,KviCanvasWidget * cw,QWidget * par);
+ ~KviCanvasView();
+public:
+ enum State { Idle , SelectOrigin };
+ enum ObjectType {
+ Rectangle , RichText , Line , Ellipse , Chord , Pie ,
+ PolygonTriangle , PolygonRectangle , PolygonPentagon ,
+ PolygonHexagon
+ };
+ enum DragMode {
+ None , All , Left , Right , Top , Bottom , LeftTop , RightTop ,
+ LeftBottom , RightBottom , Scale , SinglePoint , Rotate
+ };
+protected:
+ KviCanvasWidget * m_pCanvasWidget;
+
+ // Insertion of objects
+ State m_state;
+ ObjectType m_objectToInsert;
+
+ // Selected item
+ QCanvasItem * m_pSelectedItem;
+
+ DragMode m_dragMode;
+ QPoint m_dragBegin;
+ double m_dragScaleFactor;
+ unsigned int m_dragPointIndex;
+ QPointArray m_dragPointArray;
+protected:
+ void beginDragRectangle(KviCanvasRectangleItem * it,const QPoint &p,bool bInitial = false);
+ void dragRectangle(KviCanvasRectangleItem * it,const QPoint & p);
+
+ void beginDragLine(KviCanvasLine * it,const QPoint &p,bool bInitial = false);
+ void dragLine(KviCanvasLine * it,const QPoint &p);
+
+ void beginDragPolygon(KviCanvasPolygon * it,const QPoint &p,bool bShift = false,bool bCtrl = false);
+ void dragPolygon(KviCanvasPolygon * it,const QPoint &p);
+
+
+ void setItemSelected(QCanvasItem * it);
+ void clearSelection();
+ void insertObjectAt(const QPoint & pnt,ObjectType o);
+ virtual void contentsMousePressEvent(QMouseEvent *e);
+ virtual void contentsMouseMoveEvent(QMouseEvent *e);
+ virtual void contentsMouseReleaseEvent(QMouseEvent *e);
+public slots:
+ void insertRectangle();
+ void insertRichText();
+ void insertLine();
+ void insertPie();
+ void insertChord();
+ void insertEllipse();
+ void insertPolygonTriangle();
+ void insertPolygonRectangle();
+ void insertPolygonPentagon();
+ void insertPolygonHexagon();
+ void propertyChanged(const QString &s,const QVariant &v);
+};
+
+// For Qt3.0 this might need to be changed
+
+class KviVariantTableItem : public QTableItem
+{
+public:
+ KviVariantTableItem(QTable * t,const QVariant & property);
+ ~KviVariantTableItem();
+private:
+ QVariant m_property;
+public:
+ virtual QWidget * createEditor() const;
+ virtual void setContentFromEditor(QWidget *w);
+ QVariant & property(){ return m_property; };
+ virtual void paint(QPainter *p,const QColorGroup &cg,const QRect &cr,bool selected);
+};
+
+
+
+class KviCanvasItemPropertiesWidget : public QTable
+{
+ Q_OBJECT
+public:
+ KviCanvasItemPropertiesWidget(QWidget * par);
+ ~KviCanvasItemPropertiesWidget();
+public:
+ void editItem(QCanvasItem * it);
+protected slots:
+ void cellEdited(int row,int col);
+signals:
+ void propertyChanged(const QString &s,const QVariant &v);
+};
+
+
+
+class KviCanvasWidget : public QWidget
+{
+ friend class KviCanvasView;
+ Q_OBJECT
+public:
+ KviCanvasWidget(QWidget * par);
+ ~KviCanvasWidget();
+protected:
+ QSplitter * m_pSplitter;
+ QCanvas * m_pCanvas;
+ QMenuBar * m_pMenuBar;
+ KviCanvasView * m_pCanvasView;
+ QLabel * m_pStatusLabel;
+ KviCanvasItemPropertiesWidget * m_pPropertiesWidget;
+protected:
+ virtual void resizeEvent(QResizeEvent *);
+};
+
+
+#endif
+
+#endif //_CANVAS_WIDGET_H_
diff --git a/src/modules/dcc/chat.cpp b/src/modules/dcc/chat.cpp
new file mode 100644
index 00000000..715d17b9
--- /dev/null
+++ b/src/modules/dcc/chat.cpp
@@ -0,0 +1,842 @@
+//=======================================================================================
+//
+// File : chat.cpp
+// Creation date : Tue Sep 20 09 2000 15:13:13 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=======================================================================================
+
+#include "chat.h"
+#include "marshal.h"
+#include "broker.h"
+
+#ifdef COMPILE_ON_WINDOWS
+ // Ugly Windoze compiler...
+ #include "dialogs.h"
+#endif
+
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_options.h"
+#include "kvi_input.h"
+#include "kvi_ircview.h"
+#include "kvi_iconmanager.h"
+#include "kvi_locale.h"
+#include "kvi_error.h"
+#include "kvi_out.h"
+#include "kvi_netutils.h"
+#include "kvi_console.h"
+#include "kvi_frame.h"
+#include "kvi_malloc.h"
+#include "kvi_memmove.h"
+#include "kvi_thread.h"
+#include "kvi_ircsocket.h"
+#include "kvi_settings.h"
+#include "kvi_themedlabel.h"
+#include "kvi_socket.h"
+#include "kvi_app.h"
+#include "kvi_parameterlist.h"
+#include "kvi_ircconnection.h"
+#include "kvi_ircconnectionuserinfo.h"
+#include "kvi_kvs_eventtriggers.h"
+#include "kvi_qcstring.h"
+
+#ifdef COMPILE_CRYPT_SUPPORT
+ #include "kvi_crypt.h"
+ #include "kvi_cryptcontroller.h"
+ #include "kvi_mirccntrl.h"
+#endif
+
+#include <qsplitter.h>
+#include <qevent.h>
+#include "kvi_tal_vbox.h"
+
+
+#ifdef COMPILE_SSL_SUPPORT
+ #include "kvi_sslmaster.h"
+#endif
+
+
+
+extern KviDccBroker * g_pDccBroker;
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////
+////// WINDOW
+//////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+KviDccChat::KviDccChat(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name)
+: KviDccWindow(KVI_WINDOW_TYPE_DCCCHAT,pFrm,name,dcc)
+{
+ m_pTopSplitter = new QSplitter(Qt::Horizontal,this,"top_splitter");
+ KviThemedLabel * dummy;
+ dummy = new KviThemedLabel(m_pTopSplitter,"dummy_label");
+ KviTalVBox * box = new KviTalVBox(m_pTopSplitter);
+
+#ifdef COMPILE_CRYPT_SUPPORT
+ createCryptControllerButton(box);
+#endif
+
+ m_pSplitter = new QSplitter(Qt::Horizontal,this,"splitter");
+ m_pIrcView = new KviIrcView(m_pSplitter,pFrm,this);
+ connect(m_pIrcView,SIGNAL(rightClicked()),this,SLOT(textViewRightClicked()));
+ m_pInput = new KviInput(this);
+
+ //setFocusHandler(m_pInput,this);
+
+ m_pSlaveThread = 0;
+
+ if(KVI_OPTION_BOOL(KviOption_boolAutoLogDccChat))m_pIrcView->startLogging();
+
+ m_pMarshal = new KviDccMarshal(this);
+ connect(m_pMarshal,SIGNAL(error(int)),this,SLOT(handleMarshalError(int)));
+ connect(m_pMarshal,SIGNAL(connected()),this,SLOT(connected()));
+ connect(m_pMarshal,SIGNAL(inProgress()),this,SLOT(connectionInProgress()));
+#ifdef COMPILE_SSL_SUPPORT
+ connect(m_pMarshal,SIGNAL(startingSSLHandshake()),this,SLOT(startingSSLHandshake()));
+ connect(m_pMarshal,SIGNAL(sslError(const char *)),this,SLOT(sslError(const char *)));
+#endif
+
+ m_pSlaveThread = 0;
+
+ startConnection();
+}
+
+KviDccChat::~KviDccChat()
+{
+ g_pDccBroker->unregisterDccWindow(this);
+ if(m_pSlaveThread)
+ {
+ m_pSlaveThread->terminate();
+ delete m_pSlaveThread;
+ m_pSlaveThread = 0;
+ }
+ KviThreadManager::killPendingEvents(this);
+}
+
+void KviDccChat::textViewRightClicked()
+{
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatPopupRequest,this,m_pDescriptor->idString());
+}
+
+void KviDccChat::triggerCreationEvents()
+{
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowCreated,this,m_pDescriptor->idString());
+}
+
+void KviDccChat::triggerDestructionEvents()
+{
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatWindowClosing,this,m_pDescriptor->idString());
+}
+
+void KviDccChat::startConnection()
+{
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting a passive DCC %s connection","dcc"),m_pDescriptor->szType.utf8().data());
+#ifdef COMPILE_SSL_SUPPORT
+ int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout,m_pDescriptor->bIsSSL);
+#else
+ int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout);
+#endif
+ if(ret != KviError_success)handleMarshalError(ret);
+
+ } else {
+ // ACTIVE CONNECTION
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting an active DCC %s connection","dcc"),m_pDescriptor->szType.utf8().data());
+#ifdef COMPILE_SSL_SUPPORT
+ int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout,m_pDescriptor->bIsSSL);
+#else
+ int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout);
+#endif
+ if(ret != KviError_success)handleMarshalError(ret);
+ }
+}
+
+void KviDccChat::connectionInProgress()
+{
+ if(m_pDescriptor->bActive)
+ {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Contacting host %Q on port %Q","dcc"),&(m_pDescriptor->szIp),&(m_pDescriptor->szPort));
+ } else {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Listening on interface %Q port %Q","dcc"),&(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+
+ if(m_pDescriptor->bSendRequest)
+ {
+
+ KviStr ip;
+ if(!m_pDescriptor->szFakeIp.isEmpty())
+ {
+ ip = m_pDescriptor->szFakeIp;
+ } else {
+ ip = m_pDescriptor->szListenIp;
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccGuessIpFromServerWhenLocalIsUnroutable))
+ {
+ if(!kvi_isRoutableIpString(ip.ptr()))
+ {
+ // try to get the IP that the IRC server can see
+ if(m_pDescriptor->console())
+ {
+ KviStr tmp = m_pDescriptor->console()->connection() ? m_pDescriptor->console()->connection()->userInfo()->hostIp().utf8().data() : "";
+ if(tmp.hasData())
+ {
+ ip = tmp;
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("The local IP address is private, determining from IRC server: %s","dcc"),ip.ptr());
+ } else {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("The local IP address is private, but unable to determine it from the IRC server","dcc"));
+ }
+ } else {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("The local IP address is private, but have no IRC server to determine it from","dcc"));
+ }
+ }
+ }
+ }
+
+ QString port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : QString(m_pMarshal->localPort());
+
+ //FIXME: #warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned number)"
+ struct in_addr a;
+ if(kvi_stringIpToBinaryIp(ip.ptr(),&a))ip.setNum(htonl(a.s_addr));
+
+ QString szReq = QString("PRIVMSG %1 :%2DCC %3 chat %4 %5").arg(m_pDescriptor->szNick).arg((char)0x01).arg(m_pDescriptor->szType).arg(ip.ptr()).arg(port);
+
+ if(m_pDescriptor->isZeroPortRequest())
+ {
+ szReq.append(" ");
+ szReq+=m_pDescriptor->zeroPortRequestTag();
+ }
+ szReq.append((char)(0x01));
+
+ m_pDescriptor->console()->connection()->sendData(m_pDescriptor->console()->connection()->encodeText(szReq).data());
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Sent DCC %Q request to %Q, waiting for the remote client to connect...","dcc"),
+ &(m_pDescriptor->szType),&(m_pDescriptor->szNick));
+ //qDebug(m_pDescriptor->szNick);
+ } else output(KVI_OUT_DCCMSG,__tr2qs_ctx("DCC %Q request not sent, awaiting manual connection","dcc"),&(m_pDescriptor->szType));
+ }
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatConnectionInProgress,this,m_pDescriptor->idString());
+}
+
+void KviDccChat::startingSSLHandshake()
+{
+#ifdef COMPILE_SSL_SUPPORT
+ outputNoFmt(KVI_OUT_SSL,__tr2qs_ctx("Low-level transport connection established","dcc"));
+ outputNoFmt(KVI_OUT_SSL,__tr2qs_ctx("Starting Secure Socket Layer handshake","dcc"));
+#endif
+}
+
+void KviDccChat::sslError(const char * msg)
+{
+#ifdef COMPILE_SSL_SUPPORT
+ if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError,this,QString(msg),m_pDescriptor->idString()))
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("[SSL ERROR]: %s","dcc"),msg);
+#endif
+}
+
+const QString & KviDccChat::target()
+{
+ // This may change on the fly...
+ m_szTarget = m_pDescriptor->szNick;
+ m_szTarget += "@";
+ m_szTarget += m_pDescriptor->szIp;
+ m_szTarget += ":";
+ m_szTarget += m_pDescriptor->szPort;
+ return m_szTarget;
+}
+
+void KviDccChat::fillCaptionBuffers()
+{
+ QString tmp = QString("DCC %1 %2@%3:%4").arg(
+#ifdef COMPILE_SSL_SUPPORT
+ m_pDescriptor->bIsSSL ? "SChat" : "Chat").arg(
+#else
+ "Chat").arg(
+#endif
+ m_pDescriptor->szNick).arg(m_pDescriptor->szIp).arg(m_pDescriptor->szPort);
+
+ m_szPlainTextCaption = tmp;
+
+ m_szHtmlActiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextActive).name().ascii(),tmp.utf8().data());
+ m_szHtmlInactiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextInactive).name().ascii(),tmp.utf8().data());
+}
+
+QPixmap * KviDccChat::myIconPtr()
+{
+ return g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCMSG);
+}
+
+
+void KviDccChat::getBaseLogFileName(KviStr &buffer)
+{
+ buffer.sprintf("%s_%s_%s",m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data());
+}
+
+void KviDccChat::ownMessage(const QString &text)
+{
+ if(!m_pSlaveThread)
+ {
+ output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc"));
+ return;
+ }
+
+ KviQCString szData = encodeText(text);
+ const char * d = szData.data();
+ if(!d)return;
+
+#ifdef COMPILE_CRYPT_SUPPORT
+ if(cryptSessionInfo())
+ {
+ if(cryptSessionInfo()->bDoEncrypt)
+ {
+ if(*d != KVI_TEXT_CRYPTESCAPE)
+ {
+ KviStr encrypted;
+ cryptSessionInfo()->pEngine->setMaxEncryptLen(-1);
+ switch(cryptSessionInfo()->pEngine->encrypt(d,encrypted))
+ {
+ case KviCryptEngine::Encrypted:
+ {
+ KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr());
+ m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSGCRYPTED,
+ m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(),
+ m_pDescriptor->szLocalHost.utf8().data(),text,KviConsole::NoNotifications);
+ }
+ break;
+ case KviCryptEngine::Encoded:
+ {
+ KviStr buf(KviStr::Format,"%s\r\n",encrypted.ptr());
+ m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ QString encr = decodeText(encrypted.ptr());
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG,
+ m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(),
+ m_pDescriptor->szLocalHost.utf8().data(),encr,KviConsole::NoNotifications);
+ }
+ break;
+ default: // also case KviCryptEngine::EncryptError
+ {
+ QString szErr = cryptSessionInfo()->pEngine->lastError();
+ output(KVI_OUT_SYSTEMERROR,
+ __tr2qs_ctx("The crypto engine was not able to encrypt the current message (%Q): %Q, no data was sent to the remote end","dcc"),
+ &text,&szErr);
+ }
+ break;
+ }
+ return;
+ } else {
+ d++; //eat the escape code
+ KviStr buf(KviStr::Format,"%s\r\n",d);
+ QString tmp = text.right(text.length() - 1);
+ m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG,
+ m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(),
+ m_pDescriptor->szLocalHost.utf8().data(),tmp,KviConsole::NoNotifications);
+ return;
+ }
+ }
+ }
+#endif
+ KviStr buf(KviStr::Format,"%s\r\n",d);
+ m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_OWNPRIVMSG,
+ m_pDescriptor->szLocalNick.utf8().data(),m_pDescriptor->szLocalUser.utf8().data(),
+ m_pDescriptor->szLocalHost.utf8().data(),text,KviConsole::NoNotifications);
+}
+
+const QString & KviDccChat::localNick()
+{
+ // FIXME: This is just a complete HACK
+ m_szLocalNick = m_pDescriptor->szLocalNick;
+ return m_szLocalNick;
+}
+
+void KviDccChat::ownAction(const QString &text)
+{
+ if(m_pSlaveThread)
+ {
+ KviQCString szData = encodeText(text);
+ const char * d = szData.data();
+ if(!d)return;
+ KviStr buf(KviStr::Format,"%cACTION %s%c\r\n",0x01,d,0x01);
+ m_pSlaveThread->sendRawData(buf.ptr(),buf.len());
+ output(KVI_OUT_ACTION,"%Q %Q",&(m_pDescriptor->szLocalNick),&text);
+ } else {
+ output(KVI_OUT_SYSTEMWARNING,__tr2qs_ctx("Cannot send data: No active connection","dcc"));
+ }
+}
+
+bool KviDccChat::event(QEvent *e)
+{
+ if(e->type() == KVI_THREAD_EVENT)
+ {
+ switch(((KviThreadEvent *)e)->id())
+ {
+ case KVI_DCC_THREAD_EVENT_ERROR:
+ {
+ int * err = ((KviThreadDataEvent<int> *)e)->getData();
+ QString szErr = KviError::getDescription(*err);
+ if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError,this,szErr,m_pDescriptor->idString()))
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("ERROR: %Q","dcc"),&szErr);
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCChatDisconnected,this,m_pDescriptor->idString());
+ delete err;
+ return true;
+ }
+ break;
+ case KVI_DCC_THREAD_EVENT_DATA:
+ {
+ KviStr * encoded = ((KviThreadDataEvent<KviStr> *)e)->getData();
+ KviStr d=KviStr(decodeText(encoded->ptr()));
+ if(d.firstCharIs(0x01))
+ {
+ d.cutLeft(1);
+ if(d.lastCharIs(0x01))d.cutRight(1);
+ if(kvi_strEqualCIN("ACTION",d.ptr(),6))d.cutLeft(6);
+ d.stripLeftWhiteSpace();
+ output(KVI_OUT_ACTION,"%Q %s",&(m_pDescriptor->szNick),d.ptr());
+ } else {
+
+#ifdef COMPILE_CRYPT_SUPPORT
+ if(KviCryptSessionInfo * cinf = cryptSessionInfo())
+ {
+ if(cinf->bDoDecrypt)
+ {
+ KviStr decryptedStuff;
+ switch(cinf->pEngine->decrypt(d.ptr(),decryptedStuff))
+ {
+ case KviCryptEngine::DecryptOkWasEncrypted:
+ case KviCryptEngine::DecryptOkWasEncoded:
+ case KviCryptEngine::DecryptOkWasPlainText:
+ if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage,this,QString(decryptedStuff.ptr()),m_pDescriptor->idString()))
+ {
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG,
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szUser.utf8().data(),
+ m_pDescriptor->szHost.utf8().data(),decryptedStuff.ptr());
+ }
+ delete encoded;
+ return true;
+ break;
+
+ default: // also case KviCryptEngine::DecryptError
+ {
+ QString szErr = cinf->pEngine->lastError();
+ output(KVI_OUT_SYSTEMERROR,
+ __tr2qs_ctx("The following message appears to be encrypted, but the crypto engine failed to decode it: %Q","dcc"),
+ &szErr);
+ }
+ break;
+ }
+ }
+ } else {
+#endif
+ // FIXME!
+ if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatMessage,this,QString(d.ptr()),m_pDescriptor->idString()))
+ m_pFrm->firstConsole()->outputPrivmsg(this,KVI_OUT_DCCCHATMSG,
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szUser.utf8().data(),
+ m_pDescriptor->szHost.utf8().data(),d.ptr());
+#ifdef COMPILE_CRYPT_SUPPORT
+ }
+#endif
+ }
+ delete encoded;
+ return true;
+ }
+ break;
+ }
+ }
+ return KviWindow::event(e);
+}
+
+void KviDccChat::resizeEvent(QResizeEvent *e)
+{
+ int hght = m_pInput->heightHint();
+ int hght2 = m_pTopSplitter->sizeHint().height();
+ m_pTopSplitter->setGeometry(0,0,width(),hght2);
+ m_pSplitter->setGeometry(0,hght2,width(),height() - (hght + hght2));
+ m_pInput->setGeometry(0,height() - hght,width(),hght);
+}
+
+QSize KviDccChat::sizeHint() const
+{
+ QSize ret(m_pIrcView->sizeHint().width(),
+ m_pIrcView->sizeHint().height() + m_pInput->heightHint());
+ return ret;
+}
+
+void KviDccChat::handleMarshalError(int err)
+{
+ QString szErr = KviError::getDescription(err);
+ if(!KVS_TRIGGER_EVENT_2_HALTED(KviEvent_OnDCCChatError,this,szErr,m_pDescriptor->idString()))
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("DCC %Q failed: %Q","dcc"),&(m_pDescriptor->szType),&szErr);
+}
+
+void KviDccChat::connected()
+{
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION...Find out the remote end
+ m_pDescriptor->szIp = m_pMarshal->remoteIp();
+ m_pDescriptor->szPort = m_pMarshal->remotePort();
+ m_pDescriptor->szHost = m_pMarshal->remoteIp();
+ }
+
+ updateCaption();
+
+ m_pSlaveThread = new KviDccChatThread(this,m_pMarshal->releaseSocket());
+#ifdef COMPILE_SSL_SUPPORT
+ KviSSL * s = m_pMarshal->releaseSSL();
+ if(s)
+ {
+ KviSSLMaster::printSSLConnectionInfo(this,s);
+ m_pSlaveThread->setSSL(s);
+ }
+#endif
+ m_pSlaveThread->start();
+
+ if(!KVS_TRIGGER_EVENT_1_HALTED(KviEvent_OnDCCChatConnected,this,m_pDescriptor->idString()))
+ {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Connected to %Q:%Q","dcc"),
+ &(m_pMarshal->remoteIp()),&(m_pMarshal->remotePort()));
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Local end is %Q:%Q","dcc"),
+ &(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////
+////// THREAD
+//////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+KviDccChatThread::KviDccChatThread(KviWindow *wnd,kvi_socket_t fd)
+: KviDccThread(wnd,fd)
+{
+ m_pOutBuffers = new KviPointerList<KviDataBuffer>;
+ m_pOutBuffers->setAutoDelete(true);
+}
+
+
+KviDccChatThread::~KviDccChatThread()
+{
+ if(m_pOutBuffers)delete m_pOutBuffers;
+}
+
+
+void KviDccChatThread::run()
+{
+ KviDccThreadIncomingData data;
+ data.iLen = 0;
+ data.buffer = 0;
+
+ for(;;)
+ {
+ // Dequeue events
+ while(KviThreadEvent * e = dequeueEvent())
+ {
+ if(e->id() == KVI_THREAD_EVENT_TERMINATE)
+ {
+ delete e;
+ goto out_of_the_loop;
+ } else {
+ // Other events are senseless to us
+ delete e;
+ }
+ }
+
+ bool bCanRead;
+ bool bCanWrite;
+ if(kvi_select(m_fd,&bCanRead,&bCanWrite))
+ {
+ if(bCanWrite)
+ {
+ if(!tryFlushOutBuffers())goto out_of_the_loop;
+ }
+ if(bCanRead)
+ {
+ data.buffer = (char *) kvi_realloc(data.buffer,(data.iLen + 512) * sizeof(char));
+ int readLen;
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ readLen = m_pSSL->read(data.buffer + data.iLen,512);
+ } else {
+#endif
+ readLen = kvi_socket_recv(m_fd,data.buffer + data.iLen,512);
+#ifdef COMPILE_SSL_SUPPORT
+ }
+#endif
+ if(readLen > 0)
+ {
+ data.iLen += readLen;
+ data.buffer = (char *)kvi_realloc(data.buffer,data.iLen * sizeof(char));
+ if(!handleIncomingData(&data,false))break; // non critical...
+ } else {
+
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ // ssl error....?
+ switch(m_pSSL->getProtocolError(readLen))
+ {
+ case KviSSL::ZeroReturn:
+ readLen = 0;
+ break;
+ case KviSSL::WantRead:
+ case KviSSL::WantWrite:
+ // hmmm...
+ break;
+ case KviSSL::SyscallError:
+ {
+ int iE = m_pSSL->getLastError(true);
+ if(iE != 0)
+ {
+ raiseSSLError();
+ postErrorEvent(KviError_SSLError);
+ goto out_of_the_loop;
+ }
+ }
+ break;
+ case KviSSL::SSLError:
+ {
+ raiseSSLError();
+ postErrorEvent(KviError_SSLError);
+ goto out_of_the_loop;
+ }
+ break;
+ default:
+ // Raise unknown SSL ERROR
+ postErrorEvent(KviError_SSLError);
+ goto out_of_the_loop;
+ break;
+ }
+ }
+#endif
+ if(data.iLen > 0)
+ {
+ data.buffer = (char *)kvi_realloc(data.buffer,data.iLen * sizeof(char));
+ } else {
+ kvi_free(data.buffer);
+ data.buffer = 0;
+ }
+
+ if(!handleInvalidSocketRead(readLen))
+ {
+ if(data.iLen)handleIncomingData(&data,true); // critical
+ __range_invalid(data.iLen);
+ break; // error
+ }
+ }
+ }
+ msleep(100);
+ }
+ }
+
+out_of_the_loop:
+
+
+ if(data.iLen)kvi_free(data.buffer);
+
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ KviSSLMaster::freeSSL(m_pSSL);
+ m_pSSL = 0;
+ }
+#endif
+
+ if(m_fd != KVI_INVALID_SOCKET)::kvi_socket_close(m_fd);
+ m_fd = KVI_INVALID_SOCKET;
+}
+
+bool KviDccChatThread::handleIncomingData(KviDccThreadIncomingData * data,bool bCritical)
+{
+ __range_valid(data->iLen);
+ __range_valid(data->buffer);
+ char * aux = data->buffer;
+ char * end = data->buffer + data->iLen;
+ while(aux != end)
+ {
+ if((*aux == '\n') || (*aux == '\0'))
+ {
+ KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA);
+ // The left part is len chars long
+ int len = aux - data->buffer;
+// debug("LEN = %d, iLen = %d",len,data->iLen);
+//#warning "DO IT BETTER (the \r cutting)"
+ KviStr * s = new KviStr(data->buffer,len);
+ if(s->lastCharIs('\r'))s->cutRight(1);
+ e->setData(s);
+ // but we cut also \n (or \0)
+ ++aux;
+ // so len += 1; --> new data->iLen -= len;
+ data->iLen -= (len + 1);
+// debug("iLen now = %d",data->iLen);
+ __range_valid(data->iLen >= 0);
+ if(data->iLen > 0)
+ {
+ // memmove the remaining part to the beginning
+ // aux points after \n or \0
+ kvi_memmove(data->buffer,aux,data->iLen);
+ data->buffer = (char *)kvi_realloc(data->buffer,data->iLen);
+ end = data->buffer + data->iLen;
+ aux = data->buffer;
+ } else {
+ // no more data in the buffer
+ __range_valid(data->iLen == 0);
+ kvi_free(data->buffer);
+ data->buffer = end = aux = 0;
+ }
+ postEvent(parent(),e);
+ } else aux++;
+// debug("PASSING CHAR %c",*aux);
+ }
+ // now aux == end
+ if(bCritical)
+ {
+ // need to flush everything...
+ if(data->iLen > 0)
+ {
+ // in the last part there are no NULL and \n chars
+ KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_DATA);
+ KviStr * s = new KviStr(data->buffer,data->iLen);
+ if(s->lastCharIs('\r'))s->cutRight(1);
+ e->setData(s);
+ data->iLen = 0;
+ kvi_free(data->buffer);
+ data->buffer = 0;
+ postEvent(parent(),e);
+ }
+ }
+ return true;
+}
+
+void KviDccChatThread::sendRawData(const void * buffer,int len)
+{
+ m_pMutex->lock();
+ m_pOutBuffers->append(new KviDataBuffer((unsigned int)len,(const unsigned char *)buffer));
+ m_pMutex->unlock();
+}
+
+bool KviDccChatThread::tryFlushOutBuffers()
+{
+ bool bRet = true;
+ m_pMutex->lock();
+ while(KviDataBuffer * b = m_pOutBuffers->first())
+ {
+ int sentLen;
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ sentLen = m_pSSL->write((const char *)b->data(),b->size());
+ } else {
+#endif
+ sentLen = kvi_socket_send(m_fd,b->data(),b->size());
+#ifdef COMPILE_SSL_SUPPORT
+ }
+#endif
+ if(sentLen > 0)
+ {
+ if(sentLen == b->size())m_pOutBuffers->removeFirst();
+ else {
+ // just a part
+ b->remove((unsigned int)sentLen);
+ break;
+ }
+ } else {
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)
+ {
+ // ops...might be an SSL error
+ switch(m_pSSL->getProtocolError(sentLen))
+ {
+ case KviSSL::WantWrite:
+ case KviSSL::WantRead:
+ // Async continue...
+ goto handle_system_error;
+ break;
+ case KviSSL::SyscallError:
+ if(sentLen == 0)
+ {
+ raiseSSLError();
+ postErrorEvent(KviError_remoteEndClosedConnection);
+ bRet = false;
+ goto out_of_the_loop;
+ } else {
+ int iSSLErr = m_pSSL->getLastError(true);
+ if(iSSLErr != 0)
+ {
+ raiseSSLError();
+ postErrorEvent(KviError_SSLError);
+ bRet = false;
+ goto out_of_the_loop;
+ } else {
+ goto handle_system_error;
+ }
+ }
+ break;
+ case KviSSL::SSLError:
+ raiseSSLError();
+ postErrorEvent(KviError_SSLError);
+ bRet = false;
+ goto out_of_the_loop;
+ break;
+ default:
+ postErrorEvent(KviError_SSLError);
+ bRet = false;
+ goto out_of_the_loop;
+ break;
+ }
+ }
+#endif
+handle_system_error:
+ if(sentLen < 0)
+ {
+ int err = kvi_socket_error();
+#ifdef COMPILE_ON_WINDOWS
+ if((err != EAGAIN) || (err != EINTR) || (err != WSAEWOULDBLOCK))
+#else
+ if((err != EAGAIN)||(err != EINTR))
+#endif
+ {
+ postErrorEvent(KviError::translateSystemError(err));
+ bRet = false;
+ goto out_of_the_loop;
+ }
+ }
+ break; // send error
+ }
+ }
+out_of_the_loop:
+ m_pMutex->unlock();
+ return bRet;
+}
+
+#include "m_chat.moc"
diff --git a/src/modules/dcc/chat.h b/src/modules/dcc/chat.h
new file mode 100644
index 00000000..ae1d51b9
--- /dev/null
+++ b/src/modules/dcc/chat.h
@@ -0,0 +1,101 @@
+#ifndef _CHAT_H_
+#define _CHAT_H_
+//=============================================================================
+//
+// File : chat.h
+// Creation date : Tue Sep 20 09 2000 15:11:12 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2000-2004 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "kvi_window.h"
+#include "kvi_string.h"
+#include "kvi_databuffer.h"
+
+#include "window.h"
+#include "descriptor.h"
+#include "thread.h"
+
+#include "kvi_pointerlist.h"
+
+#ifdef COMPILE_SSL_SUPPORT
+ class KviSSL;
+#endif
+
+class KviDccChatThread : public KviDccThread
+{
+public:
+ KviDccChatThread(KviWindow * wnd,kvi_socket_t fd);
+ ~KviDccChatThread();
+protected:
+ KviPointerList<KviDataBuffer> * m_pOutBuffers;
+protected:
+ virtual void run();
+ bool tryFlushOutBuffers();
+ // This should handle the incoming data buffer
+ // must "eat" some data from data.buffer, memmove the remaining part
+ // to the beginning , kvi_realloc data.buffer and update data.iLen
+ // If bCritical is true , it should handle the whole data buffer
+ // since the thread is going to die
+ // It should return true if the handing was succesfull
+ // or false if the thread should be stopped
+ virtual bool handleIncomingData(KviDccThreadIncomingData *data,bool bCritical);
+public:
+ virtual void sendRawData(const void * buffer,int len); // mutex (m_pOutBuffers usage)
+};
+
+class KviDccMarshal;
+class QSplitter;
+
+class KviDccChat : public KviDccWindow
+{
+ Q_OBJECT
+public:
+ KviDccChat(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name);
+ ~KviDccChat();
+protected:
+ KviDccChatThread * m_pSlaveThread;
+ QSplitter * m_pTopSplitter;
+ QString m_szTarget;
+ QString m_szLocalNick;
+protected:
+ virtual const QString & target();
+ virtual void fillCaptionBuffers();
+ virtual void getBaseLogFileName(KviStr &buffer);
+ virtual QPixmap * myIconPtr();
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual QSize sizeHint() const;
+ virtual const QString & localNick();
+ virtual bool event(QEvent *e);
+ virtual void ownMessage(const QString &text);
+ virtual void ownAction(const QString &text);
+ virtual void triggerCreationEvents();
+ virtual void triggerDestructionEvents();
+ void startConnection();
+protected slots:
+ void handleMarshalError(int err);
+ void connected();
+ void sslError(const char * msg);
+ void connectionInProgress();
+ void startingSSLHandshake();
+ void textViewRightClicked();
+};
+
+
+#endif //_CHAT_H_
diff --git a/src/modules/dcc/codec.cpp b/src/modules/dcc/codec.cpp
new file mode 100644
index 00000000..833796ac
--- /dev/null
+++ b/src/modules/dcc/codec.cpp
@@ -0,0 +1,88 @@
+//
+// File : codec.cpp
+// Creation date : Sun Aug 26 04:19:36 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "codec.h"
+
+KviDccVoiceCodec::KviDccVoiceCodec()
+{
+}
+
+KviDccVoiceCodec::~KviDccVoiceCodec()
+{
+}
+
+void KviDccVoiceCodec::encode(KviDataBuffer *,KviDataBuffer *)
+{
+}
+
+void KviDccVoiceCodec::decode(KviDataBuffer *,KviDataBuffer *)
+{
+}
+
+int KviDccVoiceCodec::encodedFrameSize()
+{
+ return 0;
+}
+
+int KviDccVoiceCodec::decodedFrameSize()
+{
+ return 0;
+}
+
+const char * KviDccVoiceCodec::name()
+{
+ return m_szName.ptr();
+}
+
+KviDccVoiceNullCodec::KviDccVoiceNullCodec()
+: KviDccVoiceCodec()
+{
+ m_szName = "null (no compression)";
+}
+
+KviDccVoiceNullCodec::~KviDccVoiceNullCodec()
+{
+}
+
+void KviDccVoiceNullCodec::encode(KviDataBuffer * signal,KviDataBuffer * stream)
+{
+ if(signal->size() < 1)return;
+ stream->append(signal->data(),signal->size());
+ signal->resize(0);
+}
+
+void KviDccVoiceNullCodec::decode(KviDataBuffer * stream,KviDataBuffer * signal)
+{
+ if(stream->size() < 1)return;
+ signal->append(stream->data(),stream->size());
+ stream->resize(0);
+}
+
+int KviDccVoiceNullCodec::encodedFrameSize()
+{
+ return 1024;
+}
+
+int KviDccVoiceNullCodec::decodedFrameSize()
+{
+ return 1024;
+}
diff --git a/src/modules/dcc/codec.h b/src/modules/dcc/codec.h
new file mode 100644
index 00000000..72fa6023
--- /dev/null
+++ b/src/modules/dcc/codec.h
@@ -0,0 +1,57 @@
+#ifndef _CODEC_H_
+#define _CODEC_H_
+//
+// File : codec.h
+// Creation date : Sun Aug 26 04:19:34 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_string.h"
+#include "kvi_databuffer.h"
+
+class KviDccVoiceCodec
+{
+public:
+ KviDccVoiceCodec();
+ virtual ~KviDccVoiceCodec();
+protected:
+ KviStr m_szName;
+public:
+ const char * name();
+ virtual void encode(KviDataBuffer * signal,KviDataBuffer * stream);
+ virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal);
+ virtual int encodedFrameSize();
+ virtual int decodedFrameSize();
+};
+
+class KviDccVoiceNullCodec : public KviDccVoiceCodec
+{
+public:
+ KviDccVoiceNullCodec();
+ virtual ~KviDccVoiceNullCodec();
+public:
+ virtual void encode(KviDataBuffer * signal,KviDataBuffer * stream);
+ virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal);
+ virtual int encodedFrameSize();
+ virtual int decodedFrameSize();
+};
+
+
+
+#endif //_CODEC_H_
diff --git a/src/modules/dcc/descriptor.cpp b/src/modules/dcc/descriptor.cpp
new file mode 100644
index 00000000..b21b0561
--- /dev/null
+++ b/src/modules/dcc/descriptor.cpp
@@ -0,0 +1,224 @@
+//=============================================================================
+//
+// File : src/modules/dcc/descriptor.cpp
+// Creation date : Tue Jul 23 01:11:54 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "descriptor.h"
+
+#include "kvi_locale.h"
+#include "kvi_kvs_eventtriggers.h"
+
+#include "kvi_window.h"
+#include "kvi_app.h"
+
+#include "send.h"
+#include "window.h"
+
+
+static unsigned int g_uNextDescriptorId = 1; // we use 0 as an invalid descriptor id
+static KviPointerHashTable<int,KviDccDescriptor> * g_pDescriptorDict = 0;
+
+KviPointerHashTable<int,KviDccDescriptor> * KviDccDescriptor::descriptorDict()
+{
+ return g_pDescriptorDict;
+}
+
+/*
+KviDccDescriptor::KviDccDescriptor(const KviDccDescriptor & src)
+{
+ copyFrom(src);
+}
+*/
+
+KviDccDescriptor::KviDccDescriptor(KviConsole * pConsole)
+{
+ m_pConsole = pConsole;
+ m_pDccWindow = 0;
+ m_pDccTransfer = 0;
+
+ m_uId = g_uNextDescriptorId;
+ g_uNextDescriptorId++;
+
+ m_szId.setNum(m_uId);
+
+ if(!g_pDescriptorDict)
+ {
+ g_pDescriptorDict = new KviPointerHashTable<int,KviDccDescriptor>;
+ g_pDescriptorDict->setAutoDelete(false);
+ }
+ g_pDescriptorDict->replace((long)m_uId,this);
+
+ szNick = __tr_ctx("unknown","dcc");
+ szUser = szNick;
+ szHost = szNick;
+
+ szLocalNick = szNick;
+ szLocalUser = szNick;
+ szLocalHost = szNick;
+
+ szIp = szNick;
+ szPort = szNick;
+
+
+ bSendRequest = true;
+ bDoTimeout = true;
+ bIsTdcc = false;
+ bOverrideMinimize = false;
+ bShowMinimized = false;
+ bAutoAccept = false;
+#ifdef COMPILE_SSL_SUPPORT
+ bIsSSL = false;
+#endif
+ bRecvFile = false;
+ bResume = false;
+ bNoAcks = false;
+ bIsIncomingAvatar = false;
+
+ iSampleRate = 0;
+
+
+ m_bCreationEventTriggered = false;
+}
+
+KviDccDescriptor::~KviDccDescriptor()
+{
+ if(m_bCreationEventTriggered)
+ {
+ KviWindow * pEventWindow = m_pConsole;
+ if(!pEventWindow)pEventWindow = g_pApp->activeConsole(); // any console
+ else {
+ if(!(g_pApp->windowExists(pEventWindow)))pEventWindow = g_pApp->activeConsole();
+ }
+
+ if(pEventWindow)
+ {
+ // recheck it again...
+ if(g_pApp->windowExists(pEventWindow))
+ {
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCSessionDestroyed,pEventWindow,m_szId);
+ }
+ }
+ }
+
+ if(g_pDescriptorDict)
+ {
+ g_pDescriptorDict->remove((long)m_uId);
+ if(g_pDescriptorDict->count() < 1)
+ {
+ delete g_pDescriptorDict;
+ g_pDescriptorDict = 0;
+ }
+ }
+
+}
+
+void KviDccDescriptor::triggerCreationEvent()
+{
+ if(m_bCreationEventTriggered)
+ {
+ debug("Ops.. trying to trigger OnDccSessionCreated twice");
+ return;
+ }
+ m_bCreationEventTriggered = true;
+ KviWindow * pEventWindow = m_pConsole;
+ if(!pEventWindow)pEventWindow = g_pApp->activeConsole(); // any console
+ if(pEventWindow)
+ {
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCSessionCreated,pEventWindow,m_szId);
+ }
+}
+
+
+KviDccDescriptor * KviDccDescriptor::find(unsigned int uId)
+{
+ if(!g_pDescriptorDict)return 0;
+ return g_pDescriptorDict->find((long)uId);
+}
+
+/*
+void KviDccDescriptor::copyFrom(const KviDccDescriptor &src)
+{
+ m_pDccWindow = src.m_pDccWindow;
+ m_pDccTransfer = src.m_pDccTransfer;
+ m_uId = src.m_uId;
+ szType = src.szType;
+ szNick = src.szNick;
+ szUser = src.szUser;
+ szHost = src.szHost;
+ szLocalNick = src.szLocalNick;
+ szLocalUser = src.szLocalUser;
+ szLocalHost = src.szLocalHost;
+ szIp = src.szIp;
+ szPort = src.szPort;
+ m_pConsole = src.console();
+ m_szZeroPortRequestTag= src.zeroPortRequestTag();
+ bActive = src.bActive;
+ szListenIp = src.szListenIp;
+ szListenPort = src.szListenPort;
+ szFakeIp = src.szFakeIp;
+ szFakePort = src.szFakePort;
+ bSendRequest = src.bSendRequest;
+ bDoTimeout = src.bDoTimeout;
+ szFileName = src.szFileName;
+ szFileSize = src.szFileSize;
+ bResume = src.bResume;
+ bRecvFile = src.bRecvFile;
+ bNoAcks = src.bNoAcks;
+ bIsTdcc = src.bIsTdcc;
+ bOverrideMinimize = src.bOverrideMinimize;
+ bShowMinimized = src.bShowMinimized;
+ bAutoAccept = src.bAutoAccept;
+ bIsIncomingAvatar = src.bIsIncomingAvatar;
+#ifdef COMPILE_SSL_SUPPORT
+ bIsSSL = src.bIsSSL;
+#endif
+}
+*/
+
+bool KviDccDescriptor::isFileUpload()
+{
+ if(szType.upper()=="SEND")return true;
+ if(szType.upper()=="TSEND")return true;
+#ifdef COMPILE_SSL_SUPPORT
+ if(szType.upper()=="SSEND")return true;
+#endif
+ return false;
+}
+
+bool KviDccDescriptor::isFileDownload()
+{
+ if(szType.upper()=="RECV")return true;
+ if(szType.upper()=="TRECV")return true;
+#ifdef COMPILE_SSL_SUPPORT
+ if(szType.upper()=="SRECV")return true;
+#endif
+ return false;
+}
+
+bool KviDccDescriptor::isDccChat()
+{
+ if(szType.upper()=="CHAT")return true;
+#ifdef COMPILE_SSL_SUPPORT
+ if(szType.upper()=="SCHAT")return true;
+#endif
+ return false;
+}
diff --git a/src/modules/dcc/descriptor.h b/src/modules/dcc/descriptor.h
new file mode 100644
index 00000000..f3f6c3f3
--- /dev/null
+++ b/src/modules/dcc/descriptor.h
@@ -0,0 +1,163 @@
+#ifndef _DESCRIPTOR_H_
+#define _DESCRIPTOR_H_
+//=============================================================================
+//
+// File : src/modules/dcc/descriptor.h
+// Creation date : Tue Jul 23 01:11:52 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002-2004 Szymon Stefanek (oragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "kvi_string.h"
+#include "kvi_console.h"
+
+#include "kvi_pointerhashtable.h"
+
+
+class KviDccWindow;
+class KviDccFileTransfer;
+
+class KviDccDescriptor
+{
+public:
+ KviDccDescriptor(KviConsole * pConsole);
+ //KviDccDescriptor(const KviDccDescriptor & src);
+ ~KviDccDescriptor();
+protected:
+ KviConsole * m_pConsole;
+
+ // mIrc zero port reverse send/chat extension
+ KviStr m_szZeroPortRequestTag;
+
+ unsigned int m_uId; // this dcc session ID
+ QString m_szId;
+
+ KviDccWindow * m_pDccWindow; // 0 if it has no window
+ KviDccFileTransfer * m_pDccTransfer; // 0 if it is not a transfer
+
+ bool m_bCreationEventTriggered;
+public:
+ // A console that this DCC is bound to (might be replaced while we wait for user acknowledge in dialogs)
+ KviConsole * console() const { return m_pConsole; };
+ void setConsole(KviConsole * c){ m_pConsole = c; };
+
+ KviDccWindow * window() const { return m_pDccWindow; };
+ void setWindow(KviDccWindow * w){ m_pDccWindow = w; };
+
+ KviDccFileTransfer * transfer() const { return m_pDccTransfer; };
+ void setTransfer(KviDccFileTransfer * t){ m_pDccTransfer = t; };
+
+ // mIrc zero port reverse send/chat extension
+ bool isZeroPortRequest() const { return m_szZeroPortRequestTag.hasData(); };
+ const char * zeroPortRequestTag() const { return m_szZeroPortRequestTag.ptr(); };
+ void setZeroPortRequestTag(const KviStr &szTag){ m_szZeroPortRequestTag = szTag; };
+
+ unsigned int id() const { return m_uId; };
+ const QString & idString() const { return m_szId; };
+ static KviDccDescriptor * find(unsigned int uId);
+ static KviPointerHashTable<int,KviDccDescriptor> * descriptorDict();
+
+ void triggerCreationEvent(); // this MUST be called by the creator of the descriptor!
+//private:
+// void copyFrom(const KviDccDescriptor &src);
+public:
+ // Generic parameters
+ QString szType; // DCC protocol : CHAT , SCHAT , SEND , TSSEND....
+
+ bool bActive; // active or passive connection ?
+
+ QString szNick; // remote user nickname
+ QString szUser; // remote user name (unknown for passive dcc)
+ QString szHost; // remote user host (unknown for passive dcc)
+
+ QString szLocalNick; // local user nickname (always from irc)
+ QString szLocalUser; // local user username (always from irc)
+ QString szLocalHost; // local user hostname (always from irc)
+
+ QString szIp; // remote user ip (active dcc only)
+ QString szPort; // remote user port (active dcc only)
+
+ QString szListenIp; // passive only : ip to listen on
+ QString szListenPort; // passive only : port to listen on
+
+ bool bSendRequest; // passive only : true if we have to send the CTCP request
+
+ QString szFakeIp; // passive only : fake ip to send in the CTCP
+ QString szFakePort; // passive only : fake port to send in the CTCP
+
+ bool bDoTimeout; // the marshall has to setup a timeout ?
+
+ bool bIsTdcc; // is this a TDCC ?
+
+ bool bOverrideMinimize; // Override the default minimize option ?
+ bool bShowMinimized; // Show minimized ? (valid if bOverrideMinimize is true)
+
+ bool bAutoAccept; // Auto accepted dcc send/chat ?
+#ifdef COMPILE_SSL_SUPPORT
+ bool bIsSSL; // do we have to use SSL ?
+#endif
+ // Specific parameters
+
+ // DCC SEND/RECV
+
+ QString szFileName; // RECVFILE: incoming file name, SENDFILE: filename sent to the remote end
+ QString szFileSize; // RECVFILE: incoming file size, SENDFILE: remote resume size
+
+ QString szLocalFileName; // RECVFILE: save file name selected, SENDFILE: file to send
+ QString szLocalFileSize; // RECVFILE: local file size (to resume), SENDFILE: file to send size
+
+ bool bRecvFile; // do we have to RECEIVE the file or SEND it ?
+
+ bool bResume; // do we want to resume ?
+ bool bNoAcks; // blind dcc send ? (do not receive nor send acknowledges)
+
+ bool bIsIncomingAvatar; // It is an Incoming Avatar DCC SEND ?
+
+ // DCC VOICE
+
+ KviStr szCodec; // codec name
+ int iSampleRate; // Sample rate
+public:
+ // new interface... but should be converted to QString...
+ QString protocol(){ return szType; };
+ bool isActive(){ return bActive; };
+ QString remoteNick(){ return szNick; };
+ QString remoteUser(){ return szUser; };
+ QString remoteHost(){ return szHost; };
+ QString remoteIp(){ return szIp; };
+ QString remotePort(){ return szPort; };
+ QString remoteFileName(){ return szFileName; };
+ QString remoteFileSize(){ return szFileSize; };
+ QString localNick(){ return szLocalNick; };
+ QString localUser(){ return szLocalUser; };
+ QString localHost(){ return szLocalHost; };
+ QString localIp(){ return szIp; };
+ QString localPort(){ return szPort; };
+ QString localFileName(){ return szLocalFileName; };
+ QString localFileSize(){ return szLocalFileSize; };
+ bool isFileUpload();
+ bool isFileDownload();
+ bool isDccChat();
+ bool isFileTransfer(){ return (isFileUpload() || isFileDownload()); };
+};
+
+
+
+
+#endif //_DESCRIPTOR_H_
diff --git a/src/modules/dcc/dialogs.cpp b/src/modules/dcc/dialogs.cpp
new file mode 100644
index 00000000..0ec0afba
--- /dev/null
+++ b/src/modules/dcc/dialogs.cpp
@@ -0,0 +1,206 @@
+//
+// File : dialogs.cpp
+// Creation date : Tue Sep 19 09 2000 15:23:12 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+#include "dialogs.h"
+
+#include "kvi_locale.h"
+#include "kvi_iconmanager.h"
+#include "kvi_app.h"
+
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qlabel.h>
+#include <qstringlist.h>
+#include <qevent.h>
+#include <qdesktopwidget.h>
+
+KviDccBox::KviDccBox(KviDccBroker * br,KviDccDescriptor * dcc)
+{
+ m_pDescriptor = dcc;
+ m_pBroker = br;
+}
+
+KviDccBox::~KviDccBox()
+{
+ if(m_pDescriptor)delete m_pDescriptor;
+ m_pDescriptor = 0;
+ m_pBroker->unregisterDccBox(this);
+}
+
+void KviDccBox::forgetDescriptor()
+{
+ m_pDescriptor = 0;
+}
+
+KviDccAcceptBox::KviDccAcceptBox(KviDccBroker * br,KviDccDescriptor * dcc,const QString &text,const QString &capt)
+: QWidget(0,"dcc_accept_box") , KviDccBox(br,dcc)
+{
+ QVBoxLayout * vb = new QVBoxLayout(this,4,4);
+ QLabel * l = new QLabel(text,this);
+#ifdef COMPILE_USE_QT4
+ l->setWordWrap(true);
+#endif
+ vb->addWidget(l);
+ QHBoxLayout *hb = new QHBoxLayout(4);
+ vb->addLayout(hb,Qt::AlignCenter);
+ QPushButton * btn = new QPushButton(__tr2qs_ctx("&Accept","dcc"),this);
+ btn->setDefault(true);
+ //btn->setFocus();
+ hb->addWidget(btn);
+ connect(btn,SIGNAL(clicked()),this,SLOT(acceptClicked()));
+ btn = new QPushButton(__tr2qs_ctx("&Reject","dcc"),this);
+ connect(btn,SIGNAL(clicked()),this,SLOT(rejectClicked()));
+ hb->addWidget(btn);
+
+ setIcon(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCMSG)));
+ setCaption(capt);
+
+ l->setActiveWindow();
+ l->setFocus();
+}
+
+KviDccAcceptBox::~KviDccAcceptBox()
+{
+}
+
+void KviDccAcceptBox::acceptClicked()
+{
+ hide();
+ emit accepted(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccAcceptBox::rejectClicked()
+{
+ hide();
+ emit rejected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccAcceptBox::closeEvent(QCloseEvent *e)
+{
+ hide();
+ e->ignore();
+ emit rejected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccAcceptBox::showEvent(QShowEvent *e)
+{
+ move((g_pApp->desktop()->width() - width()) >> 1,
+ (g_pApp->desktop()->height() - height()) >> 1);
+ QWidget::showEvent(e);
+}
+
+
+
+
+KviDccRenameBox::KviDccRenameBox(KviDccBroker * br,KviDccDescriptor * dcc,const QString &text,bool bDisableResume)
+: QWidget(0,"dcc_rename_box") , KviDccBox(br,dcc)
+{
+ QVBoxLayout * vb = new QVBoxLayout(this,4,4);
+ QLabel * l = new QLabel(text,this);
+#ifdef COMPILE_USE_QT4
+ l->setWordWrap(true);
+#endif
+ vb->addWidget(l);
+
+ QHBoxLayout *hb = new QHBoxLayout(4);
+ vb->addLayout(hb,Qt::AlignCenter);
+
+ QPushButton * btn = new QPushButton(__tr2qs_ctx("&Rename","dcc"),this);
+ hb->addWidget(btn);
+ connect(btn,SIGNAL(clicked()),this,SLOT(renameClicked()));
+
+ btn = new QPushButton(__tr2qs_ctx("Over&write","dcc"),this);
+ hb->addWidget(btn);
+ connect(btn,SIGNAL(clicked()),this,SLOT(overwriteClicked()));
+
+ btn = new QPushButton(__tr2qs_ctx("Re&sume","dcc"),this);
+ hb->addWidget(btn);
+ connect(btn,SIGNAL(clicked()),this,SLOT(resumeClicked()));
+ if(bDisableResume)btn->setEnabled(false);
+
+ btn = new QPushButton(__tr2qs_ctx("Cancel","dcc"),this);
+ hb->addWidget(btn);
+ connect(btn,SIGNAL(clicked()),this,SLOT(cancelClicked()));
+ btn->setDefault(true);
+ //btn->setFocus();
+
+ setIcon(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCMSG)));
+ setCaption(__tr2qs_ctx("File Already Exists - KVIrc","dcc"));
+}
+
+KviDccRenameBox::~KviDccRenameBox()
+{
+}
+
+void KviDccRenameBox::closeEvent(QCloseEvent *e)
+{
+ hide();
+ e->ignore();
+ if(m_pDescriptor)
+ {
+ emit cancelSelected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+ }
+}
+
+void KviDccRenameBox::showEvent(QShowEvent *e)
+{
+ move((g_pApp->desktop()->width() - width()) >> 1,
+ (g_pApp->desktop()->height() - height()) >> 1);
+ QWidget::showEvent(e);
+}
+
+void KviDccRenameBox::renameClicked()
+{
+ hide();
+ emit renameSelected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccRenameBox::overwriteClicked()
+{
+ hide();
+ emit overwriteSelected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccRenameBox::resumeClicked()
+{
+ hide();
+ m_pDescriptor->bResume = true;
+ emit overwriteSelected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+void KviDccRenameBox::cancelClicked()
+{
+ hide();
+ emit cancelSelected(this,m_pDescriptor);
+ g_pApp->collectGarbage(this);
+}
+
+
+
+
+#include "m_dialogs.moc"
diff --git a/src/modules/dcc/dialogs.h b/src/modules/dcc/dialogs.h
new file mode 100644
index 00000000..805c0147
--- /dev/null
+++ b/src/modules/dcc/dialogs.h
@@ -0,0 +1,82 @@
+#ifndef _DIALOGS_H_
+#define _DIALOGS_H_
+//
+// File : dialogs.h
+// Creation date : Tue Sep 19 09 2000 15:17:22 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+
+#include <qwidget.h>
+#include "kvi_filedialog.h"
+
+#include "broker.h"
+
+class KviDccBox
+{
+protected:
+ KviDccDescriptor * m_pDescriptor;
+ KviDccBroker * m_pBroker;
+public:
+ KviDccBox(KviDccBroker * br,KviDccDescriptor * dcc);
+ virtual ~KviDccBox();
+public:
+ virtual void forgetDescriptor();
+};
+
+
+
+class KviDccAcceptBox : public QWidget , public KviDccBox
+{
+ Q_OBJECT
+public:
+ KviDccAcceptBox(KviDccBroker * br,KviDccDescriptor * dcc,const QString &text,const QString &capt);
+ ~KviDccAcceptBox();
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+ virtual void showEvent(QShowEvent *e);
+private slots:
+ void acceptClicked();
+ void rejectClicked();
+signals:
+ void accepted(KviDccBox *,KviDccDescriptor *);
+ void rejected(KviDccBox *,KviDccDescriptor *);
+};
+
+class KviDccRenameBox : public QWidget , public KviDccBox
+{
+ Q_OBJECT
+public:
+ KviDccRenameBox(KviDccBroker * br,KviDccDescriptor * dcc,const QString &text,bool bDisableResume);
+ ~KviDccRenameBox();
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+ virtual void showEvent(QShowEvent *e);
+private slots:
+ void renameClicked();
+ void overwriteClicked();
+ void resumeClicked();
+ void cancelClicked();
+signals:
+ void overwriteSelected(KviDccBox *,KviDccDescriptor *);
+ void renameSelected(KviDccBox *,KviDccDescriptor *);
+ void cancelSelected(KviDccBox *,KviDccDescriptor *);
+};
+
+#endif
diff --git a/src/modules/dcc/gsmcodec.cpp b/src/modules/dcc/gsmcodec.cpp
new file mode 100644
index 00000000..58af1473
--- /dev/null
+++ b/src/modules/dcc/gsmcodec.cpp
@@ -0,0 +1,149 @@
+//
+// File : gsmcodec.cpp
+// Creation date : Wed Aug 22 19:12:55 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#define _GSMCODEC_CPP_
+
+#include "gsmcodec.h"
+
+#ifdef COMPILE_USE_GSM
+
+#include <dlfcn.h>
+
+#define GSM_PACKED_FRAME_SIZE_IN_BYTES 33
+#define GSM_UNPACKED_FRAME_SIZE_IN_BYTES 320
+#define GSM_UNPACKED_FRAME_SIZE_IN_SHORTS 160
+
+void * (*gsm_session_create)() = 0;
+void (*gsm_session_destroy)(void *) = 0;
+void (*gsm_session_encode)(void *,short *,unsigned char *) = 0;
+int (*gsm_session_decode)(void *,unsigned char *,short *) = 0;
+
+
+void * g_pGSMCodecLibraryHandle = 0;
+
+bool kvi_gsm_codec_init()
+{
+ if(g_pGSMCodecLibraryHandle)return true; // Already initialized
+
+ g_pGSMCodecLibraryHandle = dlopen("libgsm.so",RTLD_NOW | RTLD_GLOBAL);
+ if(!g_pGSMCodecLibraryHandle)return false; // no way to open it
+
+ gsm_session_create = (void * (*)()) dlsym(g_pGSMCodecLibraryHandle,"gsm_create");
+ gsm_session_destroy = (void (*)(void *)) dlsym(g_pGSMCodecLibraryHandle,"gsm_destroy");
+ gsm_session_encode = (void (*)(void *,short *,unsigned char *)) dlsym(g_pGSMCodecLibraryHandle,"gsm_encode");
+ gsm_session_decode = (int (*)(void *,unsigned char *,short *)) dlsym(g_pGSMCodecLibraryHandle,"gsm_decode");
+
+ if(! (gsm_session_create && gsm_session_destroy && gsm_session_encode && gsm_session_decode))
+ {
+ dlclose(g_pGSMCodecLibraryHandle);
+ g_pGSMCodecLibraryHandle = 0;
+ return false;
+ }
+ return true;
+}
+
+void kvi_gsm_codec_done()
+{
+ if(g_pGSMCodecLibraryHandle)
+ {
+ dlclose(g_pGSMCodecLibraryHandle);
+ g_pGSMCodecLibraryHandle = 0;
+ }
+}
+
+
+
+KviDccVoiceGsmCodec::KviDccVoiceGsmCodec()
+: KviDccVoiceCodec()
+{
+ m_pEncodeState = gsm_session_create();
+ m_pDecodeState = gsm_session_create();
+ m_szName = "gsm (compression 33:320)";
+}
+
+KviDccVoiceGsmCodec::~KviDccVoiceGsmCodec()
+{
+ gsm_session_destroy(m_pEncodeState);
+ gsm_session_destroy(m_pDecodeState);
+}
+
+void KviDccVoiceGsmCodec::encode(KviDataBuffer * signal,KviDataBuffer * stream)
+{
+ if(signal->size() < GSM_UNPACKED_FRAME_SIZE_IN_BYTES)return; // nothing to encode
+
+ unsigned char * ptr = signal->data();
+
+ int uFrames = signal->size() / GSM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ int uTotalDataCompressed = uFrames * GSM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ int uFrameOffset = stream->size();
+ unsigned char * endPtr = ptr + uTotalDataCompressed;
+
+ stream->addSize(GSM_PACKED_FRAME_SIZE_IN_BYTES * uFrames);
+
+ while(ptr != endPtr)
+ {
+ gsm_session_encode(m_pEncodeState,(short *)ptr,stream->data() + uFrameOffset);
+ ptr += GSM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ uFrameOffset += GSM_PACKED_FRAME_SIZE_IN_BYTES;
+ }
+ signal->remove(uTotalDataCompressed);
+}
+
+void KviDccVoiceGsmCodec::decode(KviDataBuffer * stream,KviDataBuffer * signal)
+{
+ if(stream->size() < GSM_PACKED_FRAME_SIZE_IN_BYTES)return; // nothing to decode
+
+ unsigned char * ptr = stream->data();
+
+ // Gsm codec
+ int uFrames = stream->size() / GSM_PACKED_FRAME_SIZE_IN_BYTES;
+ int uTotalDataDecompressed = uFrames * GSM_PACKED_FRAME_SIZE_IN_BYTES;
+ int uSignalOffset = signal->size();
+ unsigned char * endPtr = ptr + (uTotalDataDecompressed);
+
+ signal->addSize(GSM_UNPACKED_FRAME_SIZE_IN_BYTES * uFrames);
+
+ while(ptr != endPtr)
+ {
+ // We don't check the return value here
+ // Well..it is either an unrecoverable internal error
+ // or a broken frame...
+ // but if we receive broken frames over DCC...well....better
+ // check the hardware...or the remote codec as well...
+ gsm_session_decode(m_pDecodeState,ptr,(short *)(signal->data() + uSignalOffset));
+ ptr += GSM_PACKED_FRAME_SIZE_IN_BYTES;
+ uSignalOffset += GSM_UNPACKED_FRAME_SIZE_IN_BYTES;
+ }
+ stream->remove(uTotalDataDecompressed);
+}
+
+int KviDccVoiceGsmCodec::encodedFrameSize()
+{
+ return GSM_PACKED_FRAME_SIZE_IN_BYTES;
+}
+
+int KviDccVoiceGsmCodec::decodedFrameSize()
+{
+ return GSM_UNPACKED_FRAME_SIZE_IN_BYTES;
+}
+
+#endif // COMPILE_USE_GSM
diff --git a/src/modules/dcc/gsmcodec.h b/src/modules/dcc/gsmcodec.h
new file mode 100644
index 00000000..a1de407b
--- /dev/null
+++ b/src/modules/dcc/gsmcodec.h
@@ -0,0 +1,54 @@
+#ifndef _GSMCODEC_H_
+#define _GSMCODEC_H_
+//
+// File : gsmcodec.h
+// Creation date : Wed Aug 22 19:12:54 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_settings.h"
+
+
+#ifdef COMPILE_USE_GSM
+
+ #include "codec.h"
+
+ #ifndef _GSMCODEC_CPP_
+ extern bool kvi_gsm_codec_init();
+ extern void kvi_gsm_codec_done();
+ #endif //_GSMCODEC_CPP_
+
+ class KviDccVoiceGsmCodec : public KviDccVoiceCodec
+ {
+ public:
+ KviDccVoiceGsmCodec();
+ virtual ~KviDccVoiceGsmCodec();
+ private:
+ void * m_pEncodeState;
+ void * m_pDecodeState;
+ public:
+ virtual void encode(KviDataBuffer * signal,KviDataBuffer * stream);
+ virtual void decode(KviDataBuffer * stream,KviDataBuffer * signal);
+ virtual int encodedFrameSize();
+ virtual int decodedFrameSize();
+ };
+
+#endif //COMPILE_USE_GSM
+
+#endif //_GSMCODEC_H_
diff --git a/src/modules/dcc/kvi_dccfiletransfericons.png b/src/modules/dcc/kvi_dccfiletransfericons.png
new file mode 100644
index 00000000..11999a03
--- /dev/null
+++ b/src/modules/dcc/kvi_dccfiletransfericons.png
Binary files differ
diff --git a/src/modules/dcc/libkvidcc.cpp b/src/modules/dcc/libkvidcc.cpp
new file mode 100644
index 00000000..717f568d
--- /dev/null
+++ b/src/modules/dcc/libkvidcc.cpp
@@ -0,0 +1,2766 @@
+//=============================================================================
+//
+// File : libkviobjects.cpp
+// Creation date : Wed Sep 09 2000 20:59:01 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2000-2005 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "kvi_debug.h"
+#include "kvi_settings.h"
+#include "kvi_string.h"
+#include "kvi_module.h"
+#include "kvi_sparser.h"
+#include "kvi_locale.h"
+#include "kvi_out.h"
+#include "kvi_console.h"
+#include "kvi_netutils.h"
+#include "kvi_frame.h"
+#include "kvi_console.h"
+#include "kvi_error.h"
+#include "kvi_options.h"
+#include "kvi_defaults.h"
+#include "kvi_mirccntrl.h"
+#include "kvi_app.h"
+#include "kvi_ircconnection.h"
+#include "kvi_ircconnectionuserinfo.h"
+
+#include "gsmcodec.h"
+#include "broker.h"
+#include "voice.h"
+#include "utils.h"
+#include "send.h"
+#include "window.h"
+
+#include <qfileinfo.h>
+
+#ifdef COMPILE_ON_WINDOWS
+ // Ugly Windoze compiler...
+ #include "dialogs.h"
+#endif
+
+//#warning "KviOption_boolIgnoreDccChat and other types too"
+
+//extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager;
+
+KviDccBroker * g_pDccBroker = 0;
+
+
+static void dcc_module_set_dcc_type(KviDccDescriptor * d,const char * szBaseType)
+{
+ d->szType = szBaseType;
+#ifdef COMPILE_SSL_SUPPORT
+ if(d->bIsSSL)d->szType.prepend('S');
+#endif
+ if(d->bIsTdcc)d->szType.prepend('T');
+}
+
+static bool dcc_kvs_parse_default_parameters(KviDccDescriptor * d,KviKvsModuleCommandCall *c)
+{
+ d->bIsTdcc = c->switches()->find('t',"tdcc");
+
+ KviKvsVariant * pSw = c->switches()->find('m',"minimize");
+
+ if(pSw != 0)
+ d->bOverrideMinimize = pSw->asBoolean();
+ else
+ d->bOverrideMinimize = false;
+
+ if(!d->console())
+ {
+ // We don't need a console with -c and -n , otherwise we need it
+ if(!(c->switches()->find('c',"connect") || c->switches()->find('n',"no-ctcp")))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("This window has no associated IRC context (an IRC context is required unless -c or -n are passed)","dcc"));
+ return false;
+ } else d->setConsole(c->window()->frame()->firstConsole());
+ }
+
+ __range_valid(d->console());
+
+ if(!d->console()->isConnected())
+ {
+ // We don't need a connection with -c and -n , otherwise we need it
+ if(!(c->switches()->find('c',"connect") || c->switches()->find('n',"no-ctcp")))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("You're not connected to a server (an active connection is required unless -c or -n are passed)","dcc"));
+ return false;
+ } else {
+ // -c or -n , grab a local nick from somewhere
+ d->szLocalNick = KVI_OPTION_STRING(KviOption_stringNickname1);
+ d->szLocalNick.stripWhiteSpace();
+ if(d->szLocalNick.isEmpty())d->szLocalNick = KVI_DEFAULT_NICKNAME1;
+ d->szLocalUser = __tr2qs_ctx("unknown","dcc"); // we can live without it
+ d->szLocalHost = d->szLocalUser; // we can live without it
+ }
+ } else {
+ // We know everything
+ d->szLocalNick = d->console()->connection()->userInfo()->nickName();
+ d->szLocalUser = d->console()->connection()->userInfo()->userName();
+ d->szLocalHost = d->console()->connection()->userInfo()->hostName();
+ }
+
+ if(pSw = c->switches()->find('i',"ip"))
+ {
+ pSw->asString(d->szListenIp);
+ if(!(d->szListenIp.contains('.') || d->szListenIp.contains(':')))
+ {
+ // This will magically work with the same buffer as source and dst!
+ if(!KviNetUtils::getInterfaceAddress(d->szListenIp,d->szListenIp))
+ {
+ c->error(__tr2qs_ctx("Unable to get address of interface %Q","dcc"),&(d->szListenIp));
+ delete d;
+ return false;
+ }
+ }
+ } else {
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(c,d->console(),tmp))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("No suitable interfaces to listen on, use -i","dcc"));
+ return false;
+ }
+ d->szListenIp=tmp;
+ }
+
+ if(pSw = c->switches()->find('p',"port"))
+ {
+ pSw->asString(d->szListenPort); // fixme!
+ }
+ else d->szListenPort = "0"; // any port is ok
+
+ if(pSw = c->switches()->find('a',"fake-address"))
+ {
+ pSw->asString(d->szFakeIp);
+ }
+ else {
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+ }
+
+ if(pSw = c->switches()->find('f',"fake-port"))
+ {
+ pSw->asString(d->szFakePort);
+ }
+
+ d->bDoTimeout = (!c->switches()->find('u',"unlimited"));
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = c->switches()->find('s',"ssl");
+#else
+ if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable was built without SSL support, -s switch ignored","dcc"));
+#endif
+
+ return true;
+}
+
+
+/*
+ @doc: dcc.chat
+ @type:
+ command
+ @title:
+ dcc.chat
+ @short:
+ Starts a DCC Chat connection
+ @syntax:
+ dcc.chat [-s] [-n] [-c] [-u] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname>
+ @switches:
+ !sw: -m[=<boolean>] | --minimize[=<boolean>]
+ If the -m switch is passed, the default boolCreateMinimizedDccChat option
+ is overridden with the <boolean> parameter passed. So actually
+ by passing -m=1 will create a minimized DCC send even
+ if the [fnc]$option[/fnc](boolCreateMinimizedDccChat) returns false.[br]
+ In the same way, by passing -m=0 you will create a non minimized DCC send.
+ If no <boolean> value is specified, it defaults to 1.[br]
+
+ !sw: -n | --no-ctcp
+ Do NOT send the CTCP request to the target user, you will have to do it manually,
+ or the remote user will have to connect manually (for example by using dcc.chat -c).[br]
+
+ !sw: -c | --connect
+ Attempt to CONNECT to the remote host specified as <interface> and <port>,
+ instead of listening (active connection instead of a passive one).
+ In this case the -i and -p switches are mandatory.[br]
+ The 'c' switch takes precedence over 'n' (In fact both should
+ be mutually exclusive).[br]
+ If the 'c' and 'n' switches are missing, this commands
+ needs to be executed in a window that is bound to a connected
+ IRC context (you need a third entity to accomplish the negotiation).[br]
+
+ !sw: -i=<interface> | --ip=<interface>
+ Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6).
+ If this switch is NOT specified, the socket is bound to the interface of
+ the current IRC connection (if any) or to "127.0.0.1".[br]
+ You can also specify a local interface name to get the address from (this works only for IPv4 interfaces
+ since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br]
+ Here go some examples:[br]
+ -i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br]
+ -i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br]
+ -i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br]
+ The -i switch parameter may serve also as a target address when the -c switch is used.[br]
+
+ !sw: -p=<port> | --port=<port>
+ Bind the local listening socket to the specified <port>.
+ If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br]
+
+ !sw: -a=<fake address> | --fake-address=<fake address>
+ Send the <fake address> as target for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real IP address of the listening
+ interface.[br]
+
+ !sw: -f=<fake port> | --fake-port=<fake port>
+ Send the <fake port> as target port for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real port of the listening socket.
+ [br][br]
+ All these switches are meant to allow maximum flexibility of the
+ DCC negotiation, earlier KVIrc releases had serious problems
+ with firewalled and/or masqueraded machines. With the -a and -f switches
+ you can work around it.[br]
+ [br]
+
+ !sw: -u | --unlimited
+ If the 'u' switch is given, the connection attempt will
+ never time out; this might be useful if you want to leave
+ a listening socket for a friend of yours while you are sleeping
+ and have the CTCP processing disabled. The 'u' switch works either
+ in active and passive mode.[br]
+
+ !sw: -s | --ssl
+ Use a Secure Socket Layer for the transfer; the whole communication will be encrypted
+ with a private key algorithm after a public key handshake.[br]
+ This option will work only if the KVIrc executable has been compiled with SSL support
+ and the remote end supports the SSL protocol too.[br]
+ Please note that this will may down the transfer somewhat.[br]
+ -s can be combined with -t.[br]
+ The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br]
+ When requesting a SSL based DCC send to someone you probably will need a
+ certificate. If you don't have one, create it (for example with CA.pl -newcert)
+ and set it in the options dialog.
+
+ !sw: -z | --zero-port
+ Use the 0 port method. This is a dirty hack that allows you to use the CHAT
+ protocol with mIrc receiving clients.
+ @description:
+ Attempts a DCC connection to <nickname>.[br]
+ The simplest case "dcc.chat <nickname>" will work just as in all
+ the other IRC clients, but this command is really more powerful...[br]
+ Before attempting to understand the possibilities of this command,
+ be sure to know how [doc:dcc_connection]DCC negotiation and connections[/doc] work.
+ If the 'i' switch is specified, the local listening socket
+ will be bound to the specified <interface> (which is an IP address, IPv4 or IPv6),
+ otherwise it will be bound to the interface of the
+ current IRC connection.[br]
+ You can also specify a local interface name to get the address from (this works only for IPv4 interfaces
+ since IPv6 ones seem to be unsupported by the system ioctl() calls at the moment (in Linux at least)).[br]
+ Here are some examples:[br]
+ -i=215.243.12.12: This will bind to the IPv4 interface with the specified address.[br]
+ -i=3ffe:1001::1: This will bind to the IPv6 interface with the specified address.[br]
+ -i=ppp0: This will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br]
+ The -i switch parameter may serve also as a target address when the -c switch is used.[br]
+ If the 'p' switch is specified, the local listening socket
+ will be bound to the <port>, otherwise it will be bound to
+ a random port choosen by the kernel.[br]
+ If the 'a' switch is specified, the requesting CTCP message
+ will contain <fake address> as target for the remote user,
+ otherwise the CTCP message will contain the real IP address
+ of the listening interface.
+ If the 'f' switch is specified, the requesting CTCP message
+ will contain <fake port> as target for the remote user,
+ otherwise the CTCP message will contain the real port of the
+ listening socket.
+ All these switches are meant to allow maximum flexibility of the
+ DCC negotiation, earlier KVIrc releases had serious problems
+ with firewalled and/or masqueraded machines. With the -a and -f switches
+ you can workaround it.
+ If the 'n' switch is specified, KVIrc will NOT send the CTCP request
+ to the target user; you will have to do it manually, or the remote user
+ will have to connect manually (for example by using dcc.chat -c).
+ If the 'c' switch is specified, KVIrc will attempt to connect
+ to the remote host specified as <interface> and <port>, instead
+ of listening (active connection instead of a passive one).
+ In this case the -i and -p switches are mandatory.[br]
+ The 'c' switch takes precedence over 'n' (In fact both should
+ be mutually exclusive).[br]
+ If the 'c' and 'n' switches are missing, this commands
+ needs to be executed in a window that is bound to a connected
+ IRC context (you need a third entity to accomplish the negotiation).[br]
+ If the 'u' switch is given, the connection attempt will
+ never time out; this might be useful if you want to leave
+ a listening socket for a friend of yours while you are sleeping
+ and have the CTCP processing disabled. The 'u' switch works either
+ in active and passive mode.[br]
+ If the -m switch is passed, the default boolCreateMinimizedDccChat option
+ is overridden with the <boolean> parameter passed. So actually
+ by passing -m=1 will create a minimized DCC chat even
+ if the [fnc]$option[/fnc](boolCreateMinimizedDccChat) returns false.[br]
+ In the same way, by passing -m=0 you will create a non minimized DCC chat.
+ If no <boolean> value is specified, it defaults to 1.[br]
+ -s will cause the DCC chat to be attempted in Secure Sockets Layer mode:
+ the connection will be encrypted with a private key algorithm after a
+ public key handshake. -s will work only if the KVIrc executable was compiled
+ with SSL support enabled and if the remote end supports it.
+ The eventual DCC request will contain the string SCHAT instead of CHAT.[br]
+ When requesting a SSL based DCC chat to someone you probably will need a
+ certificate. If you don't have one, create it (for example with CA.pl -newcert)
+ and set it in the options dialog.
+ @examples:
+ Simple examples:
+ [example]
+ # Simple DCC chat to Pragma
+ dcc.chat Pragma
+ # DCC chat to Pragma, listen on address 127.0.0.1
+ dcc.chat -i=127.0.0.1 Pragma
+ # DCC chat to Pragma, listen on address 168.0.0.1 and port 1025
+ dcc.chat -i=168.0.0.1 -p=1025 Pragma
+ [/example]
+ More tricky ones:
+ [example]
+ # DCC chat to Pragma, listen on address 127.0.0.1 and port 1080
+ # but tell him to connect to address 212.134.22.11 port 1080
+ dcc.chat -i=127.0.0.1 -p=1080 -a=212.134.22.11 Pragma
+ # DCC chat to Pragma, listen on address 127.0.0.1 and port 1080
+ # but tell him to connect to address 212.134.22.11 port 1090
+ dcc.chat -i=127.0.0.1 -p=1080 -a=212.134.22.11 -f=1090 Pragma
+ [/example]
+ Now run completely out of bounds. Use dcc.chat connections
+ to do various things:
+ [example]
+ # Tricky: simulate a HTTP server
+ dcc.chat -n -i=127.0.0.1 -p=80 Netscape
+ # Now open http://127.0.0.1 with netscape
+ # and type "<html><body>Hello!</body></html>" :)
+ #
+ # Tricky 2: simulate the ident daemon (if you have none)
+ dcc.chat -n -i=127.0.0.1 -p=113 Client
+ #
+ # Now a self-DCC connection without the IRC negotiation
+ # Src: Setup a listening socket awaiting the "Destination" user connection
+ dcc.chat -n -i=127.0.0.1 -p=1080 Dst
+ # Dst: Connect to the listening socket at addr 127.0.0.1 and port 1080
+ dcc.chat -c -i=127.0.0.1 -p=1080 Src
+ # The above example will mess you a bit...
+ # Try to guess why in both windows YOU have the same nickname
+ # that is probably not Dst nor Src... :)
+ [/example]
+ Using the shell ftp proggie is too easy:
+ we're [b]real hackers[/b] and want to do complicated things...
+ [example]
+ # Connect to the local ftp server and get the list of contents
+ /dcc.chat -c -i=127.0.0.1 -p=21 FtpServer
+ # Now login, type in the new window (the following lines are NOT commands):
+ USER youruser
+ PASS yourpass
+ # Now enter passive mode
+ PASV
+ # And watch the line that you have received...sth like
+ # 227 Entering passive mode (127,0,0,1,210,195)
+ /dcc.chat -c -i=127.0.0.1 -p=$((210 * 256) + 195) FtpList
+ # (Change the port numbers accordingly)
+ # And then type in the FtpServer window (this is NOT a KVIrc command):
+ LIST
+ # Then watch the ls output in the FtpList window. :)
+ # In this way you can also read ascii files by ftp:
+ # Assume that in the previous ls output you have seen
+ # a README file.
+ # In the FtpServer window type:
+ PASV
+ # Watch the message
+ # 227 Entering passive mode (127,0,0,1,22,227)
+ /dcc.chat -c -i=127.0.0.1 -p=$((22 * 256) + 227) README
+ # In the FtpServer window type:
+ RETR README
+ # And read the file in the README window :)
+ [/example]
+*/
+
+static bool dcc_kvs_cmd_chat(KviKvsModuleCommandCall * c)
+{
+ QString szTarget;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+
+ d->szNick = szTarget; // we always specify the nickname
+ d->szUser = __tr2qs_ctx("unknown","dcc"); // username is always unknown
+ d->szHost = d->szUser; // host is always unknown
+
+ if(!dcc_kvs_parse_default_parameters(d,c))return false;
+ dcc_module_set_dcc_type(d,"CHAT");
+
+ if(c->switches()->find('z',"zero-port"))
+ {
+ // we want to have it reversed... add a tag and send out the request
+ KviDccZeroPortTag * t = g_pDccBroker->addZeroPortTag();
+
+ d->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s chat 127.0.0.1 0 %s%c",
+ d->console()->connection()->encodeText(d->szNick).data(),
+ 0x01,
+ d->console()->connection()->encodeText(d->szType).data(),
+ d->console()->connection()->encodeText(t->m_szTag).data(),
+ 0x01);
+
+ return true;
+ }
+
+ if(c->switches()->find('c',"connect"))
+ {
+ if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port")))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("-c requires -i and -p","dcc"));
+ return false;
+ }
+ d->szIp = d->szListenIp;
+ d->szPort = d->szListenPort;
+ d->szListenIp = ""; // useless
+ d->szListenPort = ""; // useless
+ d->bActive = true;
+ } else {
+ d->szIp = __tr2qs_ctx("unknown","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = !c->switches()->find('n',"no-ctcp");
+ }
+
+ //c->window()->output(0,"%Q %Q %Q",&(d->szIp),&(d->szPort),&(d->szListenIp));
+ d->triggerCreationEvent();
+ g_pDccBroker->executeChat(0,d);
+
+ return true;
+}
+
+
+/*
+ @doc: dcc.send
+ @type:
+ command
+ @title:
+ dcc.send
+ @short:
+ Sends a file
+ @syntax:
+ dcc.send [-s] [-n] [-c] [-u] [-b] [-g[=<file size>]] [-t] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname> [filename]
+ @switches:
+ !sw: -g[=<file size>] | --get[=<file size>]
+ This switch is a dirty trick, you can use it to receive files from people
+ behind a firewall with masquerading enabled.[br]
+ It causes the transfer direction to be inverted; your client will receive
+ the file from the remote host instead of sending it.[br]
+ <file size> is the expected file size in bytes. This parameter can be omitted,
+ and in this case the DCC will "blindly" trust the remote end and assume
+ that the file has been transferred correctly when the remote end closes the connection.[br]
+ If you don't pass the -n option, the remote end will receive an informational DCC RECV request,
+ specifying the IP address and the port to connect to.[br]
+ -t can be used to prevent sending acknowledges to the remote end, and -u can be used
+ to avoid the listening socket to timeout.[br]
+ -a and -f can be used as well, but I see no real reason for that...[br]
+ Here is an example of usage of this option:[br]
+ spion can't accept connections (is behind a firewall with masquerading or some other reason...),
+ to his machine.[br]
+ spion wants to send the file important.jpg to Pragma.[br]
+ spion tells to Pragma that he wants to send him the file and Pragma sets up a connection in the following way:[br]
+ [b]dcc.send -g spion important.png[/b][br]
+ spion will see the informational DCC RECV request with the IP address and port that Pragma is listening on.
+ Assume that the address was 212.212.231.220 and the port 32344.[br]
+ spion will then use the following command:[br]
+ [b]dcc.send -c -i=212.212.231.220 -p=32344 Pragma /home/spion/important.jpg[/b][br]
+ Et voila!..the file is transferring.[br]
+ Pragma will see no file progress indication, since the file size is unknown on Pragma's side.[br]
+ If spion had specified the file size, Pragma could use -g=<file size> while setting up the connection,
+ to be able to see the progress indications.[br]
+ If Pragma used the the -n option, the DCC RECV indication wouldn't have been sent, in this case
+ Pragma would need to communicate the Ip address and the port "manually" to spion.[br]
+
+ !sw: -b | --blind
+ Assume that no acknowledges are sent.
+ Assume that the transfer was successful when the whole file has been sent,
+ then close the socket.[br]
+ This is called a "blind" DCC send.[br]
+
+ !sw: -t | -tdcc
+ Emulate the TDCC protocol: Use the TDCC CTCP message (DCC TSEND) for requesting the connection
+ and assume that no acknowledges are sent. Wait for the remote end to close the connection.[br]
+
+ !sw: -m[=<boolean>] | --minimize[=<boolean>]
+ If the -m switch is passed, the default boolCreateMinimizedDccSend option
+ is overridden with the <boolean> parameter passed. So actually
+ by passing -m=1 will create a minimized DCC send even
+ if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br]
+ In the same way, by passing -m=0 you will create a non minimized DCC send.
+ If no <boolean> value is specified, it defaults to 1.[br]
+
+ !sw: -n | --no-ctcp
+ Do NOT send the CTCP request to the target user, you will have to do it manually,
+ or the remote user will have to connect manually (for example by using dcc.recv -c).[br]
+
+ !sw: -c | --connect
+ Attempt to CONNECT to the remote host specified as <interface> and <port>,
+ instead of listening (active connection instead of a passive one).
+ In this case the -i and -p switches are mandatory.[br]
+ The 'c' switch takes precedence over 'n' (In fact both should
+ be mutually exclusive).[br]
+ If the 'c' and 'n' switches are missing, this commands
+ needs to be executed in a window that is bound to a connected
+ IRC context (you need a third entity to accomplish the negotiation).[br]
+
+ !sw: -i=<interface> | --ip=<interface>
+ Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6).
+ If this switch is NOT specified, the socket is bound to the interface of
+ the current IRC connection (if any) or to "127.0.0.1".[br]
+ You can also specify a local interface name to get the address from (this works only for IPv4 interfaces
+ since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br]
+ Here go some examples:[br]
+ -i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br]
+ -i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br]
+ -i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br]
+ The -i switch parameter may serve also as a target address when the -c switch is used.[br]
+
+ !sw: -p=<port> | --port=<port>
+ Bind the local listening socket to the specified <port>.
+ If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br]
+
+ !sw: -a=<fake address> | --fake-address=<fake address>
+ Send the <fake address> as target for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real IP address of the listening
+ interface.[br]
+
+ !sw: -f=<fake port> | --fake-port=<fake port>
+ Send the <fake port> as target port for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real port of the listening socket.
+ [br][br]
+ All these switches are meant to allow maximum flexibility of the
+ DCC negotiation, earlier KVIrc releases had serious problems
+ with firewalled and/or masqueraded machines. With the -a and -f switches
+ you can work around it.[br]
+ [br]
+
+ !sw: -u | --unlimited
+ If the 'u' switch is given, the connection attempt will
+ never time out; this might be useful if you want to leave
+ a listening socket for a friend of yours while you are sleeping
+ and have the CTCP processing disabled. The 'u' switch works either
+ in active and passive mode.[br]
+
+ !sw: -s | --ssl
+ Use a Secure Socket Layer for the transfer; the whole communication will be encrypted
+ with a private key algorithm after a public key handshake.[br]
+ This option will work only if the KVIrc executable has been compiled with SSL support
+ and the remote end supports the SSL protocol too.[br]
+ Please note that this will may down the transfer somewhat.[br]
+ -s can be combined with -t.[br]
+ The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br]
+ When requesting a SSL based DCC send to someone you probably will need a
+ certificate. If you don't have one, create it (for example with CA.pl -newcert)
+ and set it in the options dialog.
+
+ @description:
+ Attempts to send the file <filename> to <nickname>.[br]
+ If [filename] is specified it must be an absolute file path,
+ otherwise a file selection dialog is opened.[br]
+ The simplest case "dcc.send <nickname> <filename>" will work just as in all
+ the other IRC clients, but this command is really more powerful...[br]
+ Before attempting to understand the possibilities of this command,
+ be sure to know how a [doc:dcc_connection]DCC negotiation and connection[/doc] works.[br]
+ The file will be sent as a sequence of packets which must
+ be acknowledged one by one by the active client.[br]
+ There is a special option (see $option()) called "fast send" (also known
+ as "send ahead") that makes KVIrc avoid to wait for the acknowledge
+ of the last packet before sending the next one.[br]
+ Anyway, the connection is declared as successful only
+ when the whole file (all the packets) has been acknowledged.[br]
+ If you need to send a file but you're firewalled, you might take
+ a look at the [cmd]dcc.rsend[/cmd] command. It also handles
+ the mIrc zero port protocol extension.
+ @examples:
+
+*/
+
+//#warning "Example for -r"
+//#warning "OPTION FOR NO GUI ? (this is hard...)"
+//#warning "When in IPv6 mode, should be able to use the IPv4 binding!"
+
+static bool dcc_kvs_cmd_send(KviKvsModuleCommandCall * c)
+{
+ QString szTarget,szFileName;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETER("file name",KVS_PT_NONEMPTYSTRING,KVS_PF_OPTIONAL,szFileName)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+
+ d->szNick = szTarget; // we always specify the nickname
+
+ QString szTmp;
+ KviKvsVariant * pSw = 0;
+
+ if(pSw = c->switches()->find('g',"get"))
+ {
+ d->szFileName = QFileInfo(szFileName).fileName();
+
+ if(!pSw->isBoolean())
+ {
+ kvs_int_t iSize;
+ if(pSw->asInteger(iSize)) // is an integer
+ {
+ pSw->asString(szTmp);
+ // avoid sprintf as long as possibile
+ d->szFileSize = szTmp;
+ } else {
+ d->szFileSize = __tr_ctx("<unknown size>","dcc");
+ }
+ }
+ } else {
+ d->szFileName = szFileName;
+ d->szLocalFileName = szFileName;
+ }
+
+ d->szUser = __tr2qs_ctx("unknown","dcc"); // username is always unknown
+ d->szHost = d->szUser; // host is always unknown
+ d->bRecvFile = pSw != 0;
+ d->bNoAcks = c->switches()->find('b',"blind") || c->switches()->find('t',"tdcc");
+ d->bResume = false;
+ d->bAutoAccept = pSw != 0;
+ d->bIsIncomingAvatar = false;
+
+ if(!dcc_kvs_parse_default_parameters(d,c))return false;
+
+ if(c->switches()->find('c',"connect"))
+ {
+ if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port")))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("-c requires -i and -p","dcc"));
+ return false;
+ }
+ d->szIp = d->szListenIp;
+ d->szPort = d->szListenPort;
+ d->szListenIp = ""; // useless
+ d->szListenPort = ""; // useless
+ d->bActive = true;
+ } else {
+ d->szIp = __tr2qs_ctx("unknown","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = !c->switches()->find('n',"no-ctcp");
+ }
+
+ if(c->switches()->find('g',"get"))
+ {
+ dcc_module_set_dcc_type(d,"RECV");
+ d->triggerCreationEvent();
+ g_pDccBroker->recvFileManage(d);
+ } else {
+ dcc_module_set_dcc_type(d,"SEND");
+ d->triggerCreationEvent();
+ if(!d->szLocalFileName.isEmpty())
+ {
+ g_pDccBroker->sendFileExecute(0,d);
+ } else {
+ g_pDccBroker->sendFileManage(d);
+ }
+ }
+
+ return true;
+}
+
+/*
+ @doc: dcc.recv
+ @type:
+ command
+ @title:
+ dcc.recv
+ @short:
+ Sets up a file receiving connection
+ @syntax:
+ dcc.recv [-s] [-t] [-u] [-b] [-n] [-c] [-i=<interface>] [-p=<port>] [-m[=<boolean>]] <nickname> <filename> <remote file size>
+ @switches:
+ !sw: -b | --blind
+ Assume that no acknowledges are sent.
+ Assume that the transfer was successful when the whole file has been sent,
+ then close the socket.[br]
+ This is called a "blind" DCC send.[br]
+
+ !sw: -t | -tdcc
+ Emulate the TDCC protocol: Use the TDCC CTCP message (DCC TSEND) for requesting the connection
+ and assume that no acknowledges are sent. Wait for the remote end to close the connection.[br]
+
+ !sw: -m[=<boolean>] | --minimize[=<boolean>]
+ If the -m switch is passed, the default boolCreateMinimizedDccSend option
+ is overridden with the <boolean> parameter passed. So actually
+ by passing -m=1 will create a minimized DCC send even
+ if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br]
+ In the same way, by passing -m=0 you will create a non minimized DCC send.
+ If no <boolean> value is specified, it defaults to 1.[br]
+
+ !sw: -n | --no-ctcp
+ Do NOT send the CTCP request to the target user, you will have to do it manually,
+ or the remote user will have to connect manually (for example by using dcc.recv -c).[br]
+
+ !sw: -i=<interface> | --ip=<interface>
+ Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6).
+ If this switch is NOT specified, the socket is bound to the interface of
+ the current IRC connection (if any) or to "127.0.0.1".[br]
+ You can also specify a local interface name to get the address from (this works only for IPv4 interfaces
+ since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br]
+ Here go some examples:[br]
+ -i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br]
+ -i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br]
+ -i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br]
+ The -i switch parameter may serve also as a target address when the -c switch is used.[br]
+
+ !sw: -p=<port> | --port=<port>
+ Bind the local listening socket to the specified <port>.
+ If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br]
+
+ !sw: -a=<fake address> | --fake-address=<fake address>
+ Send the <fake address> as target for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real IP address of the listening
+ interface.[br]
+
+ !sw: -f=<fake port> | --fake-port=<fake port>
+ Send the <fake port> as target port for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real port of the listening socket.
+ [br][br]
+ All these switches are meant to allow maximum flexibility of the
+ DCC negotiation, earlier KVIrc releases had serious problems
+ with firewalled and/or masqueraded machines. With the -a and -f switches
+ you can work around it.[br]
+ [br]
+
+ !sw: -u | --unlimited
+ If the 'u' switch is given, the connection attempt will
+ never time out; this might be useful if you want to leave
+ a listening socket for a friend of yours while you are sleeping
+ and have the CTCP processing disabled. The 'u' switch works either
+ in active and passive mode.[br]
+
+ !sw: -s | --ssl
+ Use a Secure Socket Layer for the transfer; the whole communication will be encrypted
+ with a private key algorithm after a public key handshake.[br]
+ This option will work only if the KVIrc executable has been compiled with SSL support
+ and the remote end supports the SSL protocol too.[br]
+ Please note that this will may down the transfer somewhat.[br]
+ -s can be combined with -t.[br]
+ The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br]
+ When requesting a SSL based DCC send to someone you probably will need a
+ certificate. If you don't have one, create it (for example with CA.pl -newcert)
+ and set it in the options dialog.
+
+ !sw: -c | --connect
+ Accepted for compatibility: don't use it!
+ @description:
+ Sets up a connection ready to receive a file.[br]
+ In most 'common' cases you will not need this command, you might want to use [cmd]dcc.get[/cmd] instead.[br]
+ This works like dcc.send but has two main differences: The file is INCOMING, and the CTCP sent to the other side
+ is a CTCP RECV.[br]
+ This command is the counterpart of [cmd]dcc.send[/cmd] and its parameters are exactly the same, so please refer to that
+ help page for the full discussion. This help page contains only a brief resume of these parameters.[br]
+ The [doc:dcc_connection]dcc documentation[/doc] explains the DCC Recv subprotocol in detail.[br]
+ @examples:
+
+*/
+
+//#warning "ENCODE THE CTCP'S!!!!!!!"
+//#warning "DOCS FOR dcc.recv (examples!)"
+
+static bool dcc_kvs_cmd_recv(KviKvsModuleCommandCall * c)
+{
+ QString szTarget,szFileName;
+ kvs_uint_t uSize;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,0,szFileName)
+ KVSM_PARAMETER("size",KVS_PT_UINT,0,uSize)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+ d->szNick = szTarget;
+ d->szUser = __tr2qs_ctx("unknown","dcc");
+ d->szHost = d->szUser;
+ d->szIp = __tr2qs_ctx("unknown","dcc");
+ d->szPort = d->szIp;
+
+ // -c is senseless here...but we accept it for compatibility
+
+ if(!dcc_kvs_parse_default_parameters(d,c))return false;
+
+ d->szFileName = szFileName;
+ d->szFileSize.setNum(uSize);
+
+ d->bActive = false; // we have to listen!
+ d->bResume = false;
+ d->bRecvFile = true; // we have to receive the file!
+ d->bSendRequest = !c->switches()->find('n',"no-ctcp");
+ d->bNoAcks = d->bIsTdcc || c->switches()->find('b',"blind");
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend);
+ d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(d->console(),d->szNick,d->szFileName);
+
+ if(KVI_OPTION_BOOL(KviOption_boolAutoAcceptIncomingAvatars))d->bAutoAccept = d->bAutoAccept || d->bIsIncomingAvatar;
+ dcc_module_set_dcc_type(d,"RECV");
+ d->triggerCreationEvent();
+ g_pDccBroker->recvFileManage(d);
+
+ return true;
+}
+
+/*
+ @doc: dcc.rsend
+ @type:
+ command
+ @title:
+ dcc.rsend
+ @short:
+ Sends a file by using the Reverse DCC Send protocol
+ @syntax:
+ dcc.rsend [-s] [-t] <nickname> [filename]
+ @switches:
+ !sw: -t | -tdcc
+ Emulate the TDCC protocol.
+
+ !sw: -s | --ssl
+ Use a Secure Socket Layer for the transfer; the whole communication will be encrypted
+ with a private key algorithm after a public key handshake.[br]
+ This option will work only if the KVIrc executable has been compiled with SSL support
+ and the remote end supports the SSL protocol too.[br]
+ Please note that this will may down the transfer somewhat.[br]
+ -s can be combined with -t.[br]
+ The CTCP negotiation will use SSEND as parameter (or eventually TSSEND).[br]
+ When requesting a SSL based DCC send to someone you probably will need a
+ certificate. If you don't have one, create it (for example with CA.pl -newcert)
+ and set it in the options dialog.
+ !sw: -z | --zero-port
+ Use the 0 port method. This is a dirty hack that allows you to use the RSEND
+ protocol with mIrc receiving clients.
+ @description:
+ Sends a DCC RSEND request to <nickname> notifying him that you want to
+ send him the file [filename].[br]
+ The remote end may acknowledge the request by sending a DCC RECV request.
+ This command effects are similar to [cmd]dcc.send[/cmd], but will work also on machines
+ that can't accept incoming connections (firewalling or masquerading problems).[br]
+ A 120 seconds file offer is added for the specified file and mask "<nickname>!*@*".
+ @examples:
+
+*/
+
+//#warning "ENCODE THE CTCP'S!!!!!!!"
+//#warning "DOCS FOR dcc.rsend"
+
+static bool dcc_kvs_cmd_rsend(KviKvsModuleCommandCall * c)
+{
+ QString szTarget,szFileName;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,KVS_PF_OPTIONAL,szFileName)
+ KVSM_PARAMETERS_END(c)
+
+ KVSM_REQUIRE_CONNECTION(c)
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+ d->szNick = szTarget;
+ d->szLocalFileName = szFileName;
+ d->bIsTdcc = c->switches()->find('t',"tdcc");
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = c->switches()->find('s',"ssl");
+#else
+ if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable has been built without SSL support, -s switch ignored","dcc"));
+#endif //!COMPILE_SSL_SUPPORT
+
+ if(c->switches()->find('z',"zero-port"))
+ {
+ dcc_module_set_dcc_type(d,"SEND");
+ d->setZeroPortRequestTag("nonempty"); // just to tag it
+ } else
+ dcc_module_set_dcc_type(d,"RSEND");
+ d->triggerCreationEvent();
+ g_pDccBroker->rsendManage(d);
+
+ return true;
+}
+
+
+
+
+/*
+ @doc: dcc.get
+ @type:
+ command
+ @title:
+ dcc.get
+ @short:
+ Requests a file
+ @syntax:
+ dcc.get [-s] [-t] <nickname> <filename> [filesize]
+ @description:
+ Sends a CTCP DCC GET to <nickname> requesting the file <filename>.
+ The remote end should reply with a DCC SEND request CTCP.
+ <filename> must not contain any leading path.
+ If the -t switch is given, the message is a DCC TGET, expecting
+ a TSEND reply.[br]
+ If the -s switch is given, the message will be a DCC SGET, expecting
+ a SSEND reply.[br]
+ -t and -s can be combined together to obtain a "turbo"+"SSL" extension transfer.[br]
+ -s will work only if the KVIrc executable has been compiled with SSL support and
+ the remote client supports it.[br]
+ @examples:
+
+*/
+
+//#warning "ENCODE THE CTCP'S!!!!!!!"
+//#warning "DOCS FOR dcc.get"
+
+static bool dcc_kvs_cmd_get(KviKvsModuleCommandCall * c)
+{
+ QString szTarget,szFileName;
+ kvs_uint_t uSize;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETER("filename",KVS_PT_NONEMPTYSTRING,0,szFileName)
+ KVSM_PARAMETER("size",KVS_PT_UINT,KVS_PF_OPTIONAL,uSize)
+ KVSM_PARAMETERS_END(c)
+
+ KVSM_REQUIRE_CONNECTION(c)
+
+ KviQString::cutToLast(szFileName,'/');
+
+ if(szFileName.contains(' '))
+ {
+ szFileName.prepend('"');
+ szFileName.append('"');
+ }
+
+ KviStr szDCC("GET");
+#ifdef COMPILE_SSL_SUPPORT
+ if(c->switches()->find('s',"ssl"))szDCC.prepend('S');
+#else
+ if(c->switches()->find('s',"ssl"))c->warning(__tr2qs_ctx("This executable has no SSL support, -s switch ignored","dcc"));
+#endif
+ if(c->switches()->find('t',"tdcc"))szDCC.prepend('T');
+
+ if(uSize == 0)
+ {
+ c->window()->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s%c",
+ c->window()->console()->connection()->encodeText(szTarget).data(),
+ 0x01,
+ c->window()->console()->connection()->encodeText(szDCC.ptr()).data(),
+ c->window()->console()->connection()->encodeText(szFileName).data(),
+ 0x01);
+ } else {
+ c->window()->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %u%c",
+ c->window()->console()->connection()->encodeText(szTarget).data(),
+ 0x01,
+ c->window()->console()->connection()->encodeText(szDCC.ptr()).data(),
+ c->window()->console()->connection()->encodeText(szFileName).data(),
+ uSize,0x01);
+ }
+
+ return true;
+}
+
+
+
+
+// FIXME: SSL support for DCC VOICE ? (has a sense ?)
+
+/*
+ @doc: dcc.voice
+ @type:
+ command
+ @title:
+ dcc.voice
+ @short:
+ Starts a DCC Voice connection
+ @syntax:
+ dcc.voice [-g=<codec>] [-n] [-c] [-u] [-h=<sample_rate_in_hz>] [-m[=<boolean>]] [-i=<interface>] [-p=<port>] [-a=<fake address>] [-f=<fake port>] <nickname>
+ @switches:
+ !sw: -g=<codec> | --codec=<codec>
+ Use the codec specified as parameter.
+ Actually the supported codecs are "null","adpcm" and "gsm".
+
+ !sw: -h=<rate> | --sample-rate=<rate>
+ Use the sample rate specified by <rage>.
+ Valid sample rates are 8000, 11025, 22050 and 44100 Hz.
+
+ !sw: -m[=<boolean>] | --minimize[=<boolean>]
+ If the -m switch is passed, the default boolCreateMinimizedDccSend option
+ is overridden with the <boolean> parameter passed. So actually
+ by passing -m=1 will create a minimized DCC send even
+ if the [fnc]$option[/fnc](boolCreateMinimizedDccSend) returns false.[br]
+ In the same way, by passing -m=0 you will create a non minimized DCC send.
+ If no <boolean> value is specified, it defaults to 1.[br]
+
+ !sw: -n | --no-ctcp
+ Do NOT send the CTCP request to the target user, you will have to do it manually,
+ or the remote user will have to connect manually (for example by using dcc.recv -c).[br]
+
+ !sw: -c | --connect
+ Attempt to CONNECT to the remote host specified as <interface> and <port>,
+ instead of listening (active connection instead of a passive one).
+ In this case the -i and -p switches are mandatory.[br]
+ The 'c' switch takes precedence over 'n' (In fact both should
+ be mutually exclusive).[br]
+ If the 'c' and 'n' switches are missing, this commands
+ needs to be executed in a window that is bound to a connected
+ IRC context (you need a third entity to accomplish the negotiation).[br]
+
+ !sw: -i=<interface> | --ip=<interface>
+ Bind the local listening socket to the specified <interface> (which is an IP address, IPv4 or IPv6).
+ If this switch is NOT specified, the socket is bound to the interface of
+ the current IRC connection (if any) or to "127.0.0.1".[br]
+ You can also specify a local interface name to get the address from (this works only for IPv4 interfaces
+ since IPv6 ones seems to be unsupported by the system ioctl() calls at the moment (for linux at least)).[br]
+ Here go some examples:[br]
+ -i=215.243.12.12: this will bind to the IPv4 interface with the specified address.[br]
+ -i=3ffe:1001::1: this will bind to the IPv6 interface with the specified address.[br]
+ -i=ppp0: this will bind to the IPv4 address of the interface ppp0 (if supported by the underlying system).[br]
+ The -i switch parameter may serve also as a target address when the -c switch is used.[br]
+
+ !sw: -p=<port> | --port=<port>
+ Bind the local listening socket to the specified <port>.
+ If this switch is NOT specified, the port will be a "random" one choosen by the kernel.[br]
+
+ !sw: -a=<fake address> | --fake-address=<fake address>
+ Send the <fake address> as target for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real IP address of the listening
+ interface.[br]
+
+ !sw: -f=<fake port> | --fake-port=<fake port>
+ Send the <fake port> as target port for the remote client in the requesting CTCP message.
+ If this switch is not given, the CTCP will contain the real port of the listening socket.
+ [br][br]
+ All these switches are meant to allow maximum flexibility of the
+ DCC negotiation, earlier KVIrc releases had serious problems
+ with firewalled and/or masqueraded machines. With the -a and -f switches
+ you can work around it.[br]
+ [br]
+
+ !sw: -u | --unlimited
+ If the 'u' switch is given, the connection attempt will
+ never time out; this might be useful if you want to leave
+ a listening socket for a friend of yours while you are sleeping
+ and have the CTCP processing disabled. The 'u' switch works either
+ in active and passive mode.[br]
+
+ @description:
+ Attempts a DCC Voice connection to <nickname>.[br]
+ The -g option is used to select the GSM codec, available codecs are "gsm", "adpcm" and "null".[br]
+ The adpcm codec is the one that was used in previous KVIrc versions, it provides a 1:4 compression rate
+ and is designed for 8 KHz audio sampling rate (but will work also with other sampling rates).[br]
+ The gsm codec offers 1:10 compression at the cost of some quality and cpu time. If you have a good
+ cpu and plan to transmit voice only, use this codec.<br> The null codec
+ offers no compression and may be used to transfer plain audio data over a fast connection (usually loopback
+ connection or local networks). The null codec with 44100 sampling rate would provide CD quality audio
+ streaming, but it is practically not usable (at the time I'm writing) since requires a
+ monster bandwidth. If the -g switch is not present, the adpcm codec is used by default (for compatibility reasons).[br]
+ The -h switch is used to select the sampling rate, if not given the sampling rate defaults to 8000 Hz.
+ This switch accepts any value, but in fact the soundcards have limits on the values. Typically
+ the lowest limit is 5 KHz and the upper limit is 44.1 KHz (but some soundcards support 96 KHz).
+ It is also possible that the soundcard can't support a continous range of frequencies and
+ will select a discrete closest match instead.[br]
+ The "commonly used" sample rates are 8000, 11025, 22050 and 44100 Hz.[br]
+ The remaining parameters are equivalent to the ones used in [cmd]dcc.send[/cmd], so please refer to that
+ help page for the full discussion. This help page contains only a brief resume of these parameters.[br]
+ @examples:
+ [example]
+ [comment]# Setup a DCC VOICE connection with Pragma (IRC user)[/comment]
+ dcc.voice Pragma
+ [comment]# Setup a DCC VOICE connection with Pragma and use the gsm codec[/comment]
+ dcc.voice -g=gsm Pragma
+ [comment]# Setup a DCC VOICE connection with Pragma, use the gsm codec and 22050 Hz sampling rate[/comment]
+ dcc.voice -g=gsm -h=22050 Pragma
+ [comment]# Setup a listening socket for a DCC VOICE connection on interface 127.0.0.1 and port 8088[/comment]
+ dcc.voice -n -i=127.0.0.1 -p=8088 Pippo
+ [comment]# Connect to the socket above[/comment]
+ dcc.voice -c -i=127.0.0.1 -p=8088 Pluto
+ [comment]# Same as above but use the NULL codec with 11025 Hz sampling rate[/comment]
+ dcc.voice -g=null -h=11025 -n -i=127.0.0.1 -p=8088 Pippo
+ [comment]# Connect to the socket above[/comment]
+ dcc.voice -g=null -h=11025 -c -i=127.0.0.1 -p=8088 Pluto
+ [/example]
+*/
+
+static bool dcc_kvs_cmd_voice(KviKvsModuleCommandCall * c)
+{
+ QString szTarget;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("target",KVS_PT_NONEMPTYSTRING,0,szTarget)
+ KVSM_PARAMETERS_END(c)
+
+#ifdef COMPILE_DISABLE_DCC_VOICE
+ c->warning(__tr2qs_ctx("DCC VOICE support not enabled at compilation time","dcc"));
+ return true;
+#endif
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+
+ d->szNick = szTarget; // we always specify the nickname
+ d->szUser = __tr2qs_ctx("unknown","dcc"); // username is always unknown
+ d->szHost = d->szUser; // host is always unknown
+ d->iSampleRate = 8000;
+
+ if(!dcc_kvs_parse_default_parameters(d,c))return false;
+
+ if(KviKvsVariant * pSR = c->switches()->find('h',"sample-rate"))
+ {
+ kvs_int_t iSR;
+ if(!pSR->asInteger(iSR))
+ {
+ c->warning(__tr2qs_ctx("Invalid sample rate specified, defaulting to 8000","dcc"));
+ d->iSampleRate = 8000;
+ } else {
+ d->iSampleRate = iSR;
+ }
+ }
+
+ d->szCodec = "adpcm";
+
+ if(KviKvsVariant * pCodec = c->switches()->find('g',"codec"))
+ {
+ QString szCodec;
+ pCodec->asString(szCodec);
+
+ if(!kvi_dcc_voice_is_valid_codec(szCodec))
+ {
+ c->warning(__tr2qs_ctx("Invalid codec specified, defaulting to 'adpcm'","dcc"));
+ d->szCodec = "adpcm";
+ }
+ }
+
+ dcc_module_set_dcc_type(d,"VOICE");
+ if(c->switches()->find('c',"connect"))
+ {
+ if(!(c->switches()->find('i',"ip") && c->switches()->find('p',"port")))
+ {
+ delete d;
+ c->error(__tr2qs_ctx("-c requires -i and -p","dcc"));
+ return false;
+ }
+ d->szIp = d->szListenIp;
+ d->szPort = d->szListenPort;
+ d->szListenIp = ""; // useless
+ d->szListenPort = ""; // useless
+ d->bActive = true;
+
+ d->triggerCreationEvent();
+ g_pDccBroker->activeVoiceExecute(0,d);
+ } else {
+ d->szIp = __tr2qs_ctx("unknown","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = !(c->switches()->find('n',"no-ctcp"));
+
+ d->triggerCreationEvent();
+ g_pDccBroker->passiveVoiceExecute(d);
+ }
+
+ return true;
+}
+
+
+
+
+/*
+static bool dcc_module_cmd_canvas(KviModule *m,KviCommand *c)
+{
+ ENTER_STACK_FRAME(c,"dcc_module_cmd_canvas");
+
+ KviStr target;
+ if(!g_pUserParser->parseCmdFinalPart(c,target))return false;
+
+ if(target.isEmpty())return c->error(KviError_notEnoughParameters,"%s",__tr_ctx("Missing target nickname","dcc"));
+
+ KviDccDescriptor * d = new KviDccDescriptor(c->window()->console());
+
+ d->szNick = target.ptr(); // we always specify the nickname
+ d->szUser = __tr2qs_ctx("unknown","dcc"); // username is always unknown
+ d->szHost = d->szUser; // host is always unknown
+*/
+/*
+ d->bIsTdcc = c->hasSwitch('t');
+
+ d->bOverrideMinimize = c->hasSwitch('m');
+
+ if(d->bOverrideMinimize)
+ {
+ KviStr tmpVal;
+ if(!(c->getSwitchValue('m',tmpVal)))d->bShowMinimized = false;
+ else d->bShowMinimized = kvi_strEqualCI(tmpVal.ptr(),"1");
+ }
+
+
+ if(!d->console())
+ {
+ // We don't need a console with -c and -n , otherwise we need it
+ if(!(c->hasSwitch('c') || c->hasSwitch('n')))return c->noIrcContext();
+ else d->console() = c->window()->frame()->firstConsole();
+ }
+
+ __range_valid(d->console());
+
+ if(!d->console()->isConnected())
+ {
+ // We don't need a connection with -c and -n , otherwise we need it
+ if(!(c->hasSwitch('c') || c->hasSwitch('n')))return c->notConnectedToServer();
+ else {
+ // -c or -n , grab a local nick from somewhere
+ d->szLocalNick = KVI_OPTION_STRING(KviOption_stringNickname1);
+ d->szLocalNick.stripWhiteSpace();
+ if(d->szLocalNick.isEmpty())d->szLocalNick = KVI_DEFAULT_NICKNAME1;
+ d->szLocalUser = __tr("unknown"); // we can live without it
+ d->szLocalHost = d->szLocalUser; // we can live without it
+ }
+ } else {
+ // We know everything
+ d->szLocalNick = d->console()->currentNickName();
+ d->szLocalUser = d->console()->currentUserName();
+ d->szLocalHost = d->console()->currentLocalHostName();
+ }
+
+
+ if(c->hasSwitch('i'))
+ {
+ c->getSwitchValue('i',d->szListenIp);
+ if(!(d->szListenIp.contains('.') || d->szListenIp.contains(':')))
+ {
+ // This will magically work with the same buffer as source and dst!
+ if(!kvi_getInterfaceAddress(d->szListenIp.ptr(),d->szListenIp))
+ {
+ return c->error(KviError_invalidParameter,__tr("Can't get address of interface %s"),d->szListenIp.ptr());
+ }
+ }
+ } else {
+ if(d->console()->isConnected())
+ {
+ d->console()->socket()->getLocalHostIp(d->szListenIp,d->console()->isIpV6Connection());
+ } else d->szListenIp = "127.0.0.1"; // huh ? :)
+ }
+
+ if(c->hasSwitch('p'))c->getSwitchValue('p',d->szListenPort);
+ else d->szListenPort = "0"; // any port is ok
+
+ if(c->hasSwitch('a'))c->getSwitchValue('a',d->szFakeIp);
+
+ if(c->hasSwitch('f'))c->getSwitchValue('f',d->szFakePort);
+
+ d->bDoTimeout = (!c->hasSwitch('u'));
+
+*/
+/*
+ if(!dcc_module_parse_default_parameters(d,c))return false;
+ dcc_module_set_dcc_type(d,"VOICE");
+
+ if(c->hasSwitch('c'))
+ {
+ if(!(c->hasSwitch('i') && c->hasSwitch('p')))
+ {
+ delete d;
+ return c->error(KviError_notEnoughParameters,__tr_ctx("-c requires -i and -p","dcc"));
+ }
+ d->szIp = d->szListenIp;
+ d->szPort = d->szListenPort;
+ d->szListenIp = ""; // useless
+ d->szListenPort = ""; // useless
+ d->bActive = true;
+
+ d->triggerCreationEvent();
+ g_pDccBroker->activeCanvasExecute(0,d);
+ } else {
+ d->szIp = __tr2qs_ctx("unknown","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = !c->hasSwitch('n');
+
+ d->triggerCreationEvent();
+ g_pDccBroker->passiveCanvasExecute(d);
+ }
+
+ return c->leaveStackFrame();
+}
+*/
+
+
+
+
+
+/*
+ @doc: dcc_connection
+ @type:
+ generic
+ @title:
+ DCC negotiation and connection
+ @short:
+ Overview of the DCC internals
+ @keyterms:
+ DCC without IRC
+ @body:
+ [big]What is DCC?[/big][br]
+ 'DCC' stands for Direct Client Connection, it is used to exchange data
+ directly between two IRC clients (with no IRC server in the middle).[br]
+ DCC itself is not a well-defined protocol, but rather a set of
+ subprotocols with (more or less) standardized rules.[br]
+ Sub-protocols are also (historically) called [b]"DCC types"[/b]; this term often
+ leads to confusion and it will become clear later.[br]
+ Each subprotocol has two main parts: The [b]DCC negotiation[/b] and the [b]DCC transfer[/b].[br]
+ The [b]DCC negotiation[/b] part is used to request the [b]DCC transfer[/b] and define its necessary parameters,[br]
+ while the [b]DCC transfer[/b] part is the real data transfer between clients.[br]
+ The [b]DCC negotiation[/b] requires a third entity that routes the negotiation data between clients,
+ this is usually an IRC server.[br]
+ [br]
+ [big]DCC Negotiation[/big][br]
+ This part of the protocol is the most tricky and difficult one, and is different for almost every DCC subprotocol.[br]
+ The "constant" scenario of the negotiation is more or less the following:[br]
+ There are two IRC clients connected to the same IRC network and they want to exchange some data in
+ a direct client connection.[br]
+ Each client knows the other by nickname only (and eventually by the host displayed by the IRC server,
+ but this cannot be trusted for several reasons), and can send text messages to each other by using the
+ IRC network as data channel.[br]
+ To initiate a direct client connection, one of the clients must start listening on some port (this is called [b]passive client[/b])
+ and the other must connect to that port on the first client's machine (this is the [b]active client[/b]).[br]
+ Both clients must agree on who is the passive and who is the active client. The active client must also
+ know the passive client's IP address and port (in order to be able to contact it).[br]
+ Finally, both clients must agree on the transfer type that has to be initiated.[br]
+ The negotiation exchanges these informations between clients by using IRC as channel and CTCP messages
+ as encoding method.[br]
+ An example will make things clearer:[br]
+ DCC Chat is the simplest (and most widely implemented) DCC subprotocol:
+ it is used to exchange <cr><lf> separated text data between clients.[br]
+ Assume that you want to establish a DCC Chat
+ connection to 'Sarah' that is actually connected to your IRC network (so
+ she/he is an IRC user just like you).
+ All you have to do is type sth as "/dcc chat Sarah" in your IRC client.
+ The client will setup a listening socket on a random port choosen
+ usually by the kernel of your OS. In this case YOU are the [b]passive client[/b], and Sarah is the active one.[br]
+ Once the socket is ready to accept connections,
+ your client will send a [doc:ctcp_handling]CTCP message[/doc] to Sarah using the IRC connection (and protocol) as channel:[br]
+ PRIVMSG Sarah :<0x01>DCC CHAT chat <ip_address> <port><0x01>[br]
+ where <ip_address> is the address of the listening socket and <port>
+ is the port that it has been bound to (these informations are obtained
+ after the socket has been setup). Once Sarah has received the CTCP message,
+ and agreed to connect, her (active) client will attempt to connect to the
+ specified <ip_address> and <port> (eg. to your listening socket).[br]
+ Once the connection has been established, it continues using the
+ specific CHAT transfer protocol.[br]
+ Some IRC clients allow modifications of this procedure:[br]
+ First of all, the port to listen on can be specified by the user
+ and not by the kernel; this is useful when the passive client
+ is behind a firewall that "shades" some sets of ports.
+ The ip address for the listening socket
+ can be specified by the user as well (especially when the machine has more than one network interface).[br]
+ A more challenging trick is to listen on a specified ip address and port
+ and notify different ones to the remote user (eg, <ip_address> and <port>
+ parameters of the CTCP message are not the ones that the client is listening on).
+ This is especially useful with "transparent proxy" firewalls that
+ often are not transparent enough to allow the DCC connections.
+ (If you have one of these firewalls you know what I'm talking about,
+ otherwise just read on). KVIrc allows to avoid the usage of a third entity
+ for the protocol negotiation too.
+ You can setup a listening socket on a specified port and ip address
+ without notyfying anyone of this.
+ You can also manually connect to a specified port and ip address without
+ having been notified of a DCC request.[br][br][br]
+ Is everything clear ?...I don't think so... my English is really too bad...
+ [br]
+ [big]DCC Transfer[/big][br]
+ The DCC transfer part is different for every DCC subprotocol, but
+ it always happens over a direct client to client TCP connection.[br]
+ [br]
+ [big]DCC Subprotocols[/big][br]
+ There are two main standardized DCC subprotocols that are widely implemented in IRC clients:
+ [b]DCC Chat[/b] and [b]DCC Send[/b].[br]
+ DCC Chat is quite simple and the protocol is more or less completely defined.[br]
+ DCC Send is a *real mess*, the original definition was not very flexible
+ so many IRC clients tried to enchance both the negotiation and the transfer, leading
+ often to incompatible implementations. (I can remember the Turbo File Transfer implemented
+ by VIrc, the Send-Ahead enchancement implemented in many clients, the RESUME facility...)[br]
+ Many clients introduced new DCC subprotocols with non-standard implementations,
+ leading again to client incompatibility.[br]
+ Some of the notable subprotocols are DCC Voice, DCC Draw, DCC Whiteboard...[br]
+ [br]
+ [big]DCC Chat[/big][br]
+ This is the simplest and most standardized DCC subprotocol. Almost every IRC client implements it.[br]
+ It is used to exchange lines of text between the two clients.[br]
+ The negotiation is quite simple, we assume that Client A wants to establish a DCC Chat connection to Client B.
+ Client A sets up a listening socket and retrieves its adress (ip address and port).[br]
+ Once the socket is ready Client A sends a CTCP request to B, in the following form:[br]
+ [b]DCC CHAT chat <ipaddress> <port>[/b][br]
+ Where <ipaddress> is a string representing an positive integer that is the A socket's IP address
+ in network byte order, and where <port> is a string representing an positive integer that is the
+ A socket's port.[br]
+ The original purpose of the second "chat" string in the CTCP request is quite obscure, it was probably
+ introduced to have the <ipaddress> as second parameter, as in the DCC Send subprotocol.[br]
+ Client B receives the CTCP, parses it, eventually asks the user for permission and connects
+ to the specified ip address and port.
+ The transfer protocol is quite simple, both clients can send text lines separated by <cr><lf> pairs.[br]
+ Some clients use only <lf> as line terminator so the general idea is that one of <cr> <cr><lf> or <lf>
+ can be used as line terminator.[br]
+ As extension to the protocol, KVIrc allows <ipaddress> to be an IPv6 address in the standard hexadecimal
+ notation, the connection will be made over the IPv6 protocol in this case (obviously if both clients
+ support this feature).[br]
+ (It is not clear why the original DCC specification used the unsigned int format instead of a
+ standard string representation of the IP address... missing inet_aton() function on the target system?).[br]
+ KVIrc adds the Secure Sockets Layer to the DCC Chat protocol. In this case the negotiation string becomes:[br]
+ [b]DCC SCHAT chat <ipaddress> <port>[/b][br]
+ where "SCHAT" stands for Secure CHAT.[br] The external protocol is exactly the same but is built on top of a Secure Sockets Layer
+ implementation (specifically OpenSSL). The connection will be encrypted with a private key algorithm after
+ a public key handshake.[br]
+ [br]
+ [big]DCC Send[/big][br]
+ DCC Send is another standard subprotocol. Most clients implement this as well, many have tried
+ to enchance it.[br]
+ The basic DCC Send protocol allows transferring a file from the requesting client to the receiving client.[br]
+ The requesting client (the one that sends the file) is always passive and the receiving client is always active.[br]
+ This is a huge protocol limitation since firewalled clients are often unable to accept incoming connections.[br]
+ The negotiation protocol is more complex than DCC Chat; we assume that Client A wants to send the file F to Client B.[br]
+ Client A sets up a listening socket and retrieves its ip address and port.[br]
+ Client A sends a CTCP request to Client B in the following form:[br]
+ [b]DCC SEND <filename> <ipaddress> <port> <filesize>[/b][br]
+ <ipaddress> and <port> have the same semantics as in the DCC Chat subprotocol.[br]
+ <filename> is the name (without path!) of the file to be sent, and <filesize> is (yeah), the file size.[br]
+ Client B receives the CTCP, parses it, eventually asks the user for confirmation and connects to the
+ specified ip address and port; the transfer then begins.[br]
+ Client A sends blocks of data (usually 1-2 KB) and at every block awaits confirmation from the Client B,
+ that when receiving a block should reply 4 bytes containing an positive number specifying the total size
+ of the file received up to that moment.[br]
+ The transmission closes when the last acknowledge is received by Client A.[br]
+ The acknowledges were meant to include some sort of coherency check in the transmission, but in fact
+ no client can "recover" from an acknowledge error/desync, all of them just close the connection declaring the
+ transfer as failed (the situation is even worse in fact, often acknowledge errors aren't even detected!).[br]
+ Since the packet-acknowledge round trip eats a lot of time, many clients included
+ the "send-ahead" feature; the Client A does NOT wait for the acknowledge of the first packet before sending the second one.[br]
+ The acknowledges are still sent, but just a reverse independent stream.[br] This makes the DCC Send considerably faster.[br]
+ Since the acknowledge stream has non-zero bandwidth usage, no client can recover from an acknowledge error and
+ having them as an independant stream is more or less like having no acknowledges, the "Turbo" ( :) ) extension has been added:
+ Client B will send no acknowledges and will just close the connection when he has received all the expected data.[br]
+ This makes the DCC Send as fast as FTP transfers.[br]
+ The "Turbo" extension is specified during the negotiation phase, bu using TSEND as DCC message type (instead of SEND).[br]
+ The "Turbo" extension is not widely implemented.[br]
+ Later implementations have added the support for resuming interrupted DCC Send transfers:[br]
+ Client A sets up the socket and sends the CTCP request as before.[br]
+ If Client B discovers that the file has been partially received in a previous DCC Send session it sends
+ a resume request in the following form:[br]
+ [b]DCC RESUME <filename> <port> <resume position>[/b][br]
+ Where <port> is the <port> sent in the DCC SEND request and <resume position> is the position in the file
+ from where the transfer should start.[br]
+ Cilent A receives the request, parses it and eventually replies with:[br]
+ [b]DCC ACCEPT <filename> <port> <resume position>[/b][br]
+ Client B receives the ACCEPT message, connects to Client A and the transfer initiates as before.[br]
+ The "Send-ahead" and "Turbo" extensions can obviously be used also in this case (But 'T' is NOT prepended to the RESUME and ACCEPT messages).[br]
+ The IPv6 extension can be used also in this subprotocol, so <ipaddress> can be also an IPv6 address in hexadecimal notation.[br]
+ KVIrc introduces the SSL extension also to DCC Send. The protocol remains the same again but it is built on top of
+ a Secure Sockets Layer implementation just like DCC Chat.[br]
+ With SSL the negotiation string becomes:[br]
+ [b]DCC SSEND <filename> <ipaddress> <port> <filesize>[/b][br]
+ where "SSEND" stands for Secure SEND.[br]
+ The "turbo" extension can be combined with the SSL extension too. In this case the second parameter
+ of the negotiation string must be "TSSEND" or "STSEND".[br]
+ [br]
+ [big]DCC Recv[/big][br]
+ DCC Recv is the counterpart of DCC Send. This is a KVIrc extension and is not standard yet.[br]
+ The purpose of this subprotocol will not be immediately clear, but read on for an explanation.[br]
+ It is used to request a file from another client; we assume that Client A knows that Client B has
+ a specific file and is able/wants to send it.[br]
+ Client A sets up a listening socket, retrieves its address and port and then
+ sends a CTCP request to Client B in the following form:[br]
+ [b]DCC RECV <filename> <ipaddress> <port> <resume position>[/b][br]
+ where <filename> is the name of the requested file without path, <ipaddress> and <port> have the usual meaning and <resume position>
+ is the position from that the transfer should start from.[br]
+ <ipaddress> can be an IPv6 address as well.[br]
+ Client B receives the CTCP message, parses it, looks for the file to send (in some unspecified way)
+ and connects to the specified ip address and port. The transfer then begins just as in the DCC send, but in the inverse way:
+ Client B sends blocks of data to Client A and Client B sends back acknowledges.[br]
+ This subprotocol is useful in transferring data from clients that are behind a firewall and are not able to accept
+ incoming connections (this is not possible with a normal DCC Send). In this case the client that receives
+ the file is passive and the client that sends it is active (as opposite to DCC Send).[br]
+ The "Send ahead" extension can be used also in this case and the "Turbo" extension is activated by prepending a 'T' to the
+ DCC message, "TRECV" instead of "RECV". The SSL extension is activated by prepending an 'S' to the
+ DCC message, "SRECV", "STRECV" or "TSRECV".[br]
+ This subprotocol has an implicit resume capability and thus has no need for RESUME and ACCEPT messages.[br]
+ DCC Recv requires the initiating (passive) client to know that the file to be transferred is avaiable on the B's side
+ and probably also know the file size. This subprotocol does not specify how this information is obtained, but it
+ will become clear soon that it can be obtained either manually (User B can simply tell the info to User A),
+ or automatically (as in the DCC Rsend subprotocol (keep reading)).[br]
+ [br]
+ [big]DCC RSend[/big][br]
+ DCC RSend stands for Reverse Send. This is a KVIrc extension to the SEND protocol to allow firewalled clients
+ to send files.[br] In fact, this is a "half" subprotocol, since it defines only a part of the DCC negotiation;
+ the transfer is defined by another subprotocol (and specifically bu DCC Recv).[br]
+ The requesting client (the one that sends the file) is active and the receiving client is passive.[br]
+ Assume that Client A wants to send a file to Client B and that Client A cannot accept incoming connections.[br]
+ Client A sends a CTCP request to Client B in the following form:[br]
+ [b]DCC RSEND <filename> <filesize>[/b][br]
+ Client B receives the request, parses it, eventually asks the user for confirmation, sets up a listening socket, retrieves
+ its ip address and port and switches to the DCC Recv subprotocol by effectively sending the following CTCP message:[br]
+ [b]DCC RECV <filename> <ipaddress> <port> <resume position>[/b][br]
+ The rest of the transfer is defined by the DCC Recv subprotocol.[br]
+ The "Turbo" extension is again activated by prepending a 'T' to the RSEND string, so the initial CTCP will become:[br]
+ [b]DCC TRSEND <filename> <filesize>[/b][br]
+ The "SSL" extension is also activated by prepending an 'S' to the RSEND string. It can be again combined
+ with the "turbo" extension. The negotiation parameter becomes then "SRSEND","TSRSEND" or "STRSEND".[br]
+ Easy, no ? :)[br]
+ [br]
+ [big]DCC Get[/big][br]
+ This is again a "half" subprotocol in fact since it defines only a part of the negotiation for file transfers.[br]
+ It is also NON standard, since actually no client except KVIrc implements it (AFAIK).[br]
+ DCC Get is used to request a file from a remote client. Assume that Client A wants to request a file from Client B
+ (and assume that Client A knows that B has that file and wants to send it).[br]
+ Client A sends a CTCP message to Client B in the following form:[br]
+ [b]DCC GET <filename>[/b][br]
+ Where <filename> is a name of a file without path.[br]
+ Client B receives the message, parses it, looks for an association of the <filename> to a real filesystem file
+ and starts one of the two DCC File transfer subprotocols, DCC Send or DCC RSend.[br]
+ Client B should prefer the DCC Send method and choose DCC RSend only if it is not able to accept incoming connections.[br]
+ This subprotocol can be used by firewalled clients that can't accept connections but still want to request a file
+ from another client, this one can fail only if both clients are firewalled (in this case no DCC transfer is possible at all).[br]
+ This subprotocol also does not need to "magically" know the file size, the size definition
+ is found in the subprotocol that the remote client will choose.[br]
+ The association of <filename> with a real file on the B's machine is not explicitly defined by the subprotocol;
+ KVIrc uses an internal "file-offer" table with a list of files that are available for download.[br]
+ The "turbo" and "SSL" extensions are activated as usual, "TGET", "SGET", "TSGET" and "STGET" are supported.[br]
+ [br]
+ [big]DCC File Transfer[/big][br]
+ DCC Send: Send a file, sender is passive, receiver is active (not good for firewalled senders)[br]
+ DCC Recv: Receive a file, sender is active, receiver is passive (not good for firewalled receivers)[br]
+ DCC RSend: Send a file, sender is active, receiver is passive (not good for firewalled receivers)[br]
+ DCC Get: Receive a file, sender is passive if not firewalled, receiver active if sender not firewalled (will fail only if both are firewalled)[br]
+ The "turbo" extension disables the stream of acknowledges and is activated by prepending the 'T' character to the DCC subprotocol name[br]
+ The "SSL" extension causes a Secure Socket Layer to be used and is activated by prepending the 'S' character to the DCC subprotocol name[br]
+ [br]
+ [big]DCC Voice[/big][br]
+ DCC Voice is a KVIrc extension (there is a Windows client called VIrc that implements such
+ a protocol, but it is incompatible with KVIrc).[br]
+ DCC Voice allows audio level communication between two clients, the audio stream is compressed
+ with a specified codec.[br]
+ KVIrc currently supports the ADPCM (core support) and the GSM codec (if the libgsm is available on the target system).[br]
+ [b]TODO: Finish the DCC Voice doc :)[/b]
+ [big]More tricks[/big][br]
+ KVIrc supports another "hack" to the DCC negotiation, it recognizes "XDCC" as
+ a DCC negotiation CTCP parameter.[br]
+ This can be used to circumvent limitations of some IRC clients (read mIRC) that will not allow
+ you to send a /DCC GET since it is an unrecognized DCC type.[br]
+ "XDCC" has exactly the same meaning as "DCC" (at least in KVIrc).[br]
+*/
+
+static KviDccDescriptor * dcc_kvs_find_dcc_descriptor(const kvs_uint_t &uId,KviKvsModuleRunTimeCall * c,bool bWarn = true)
+{
+ KviDccDescriptor * dcc = 0;
+ if(uId == 0)
+ {
+ if(c->window()->inherits("KviDccWindow"))
+ {
+ dcc = ((KviDccWindow *)(c->window()))->descriptor();
+ }
+ if((!dcc) && bWarn)
+ c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc"));
+ return dcc;
+ }
+ dcc = KviDccDescriptor::find(uId);
+ if((!dcc) && bWarn)
+ c->warning(__tr2qs_ctx("The specified parameter is not a valid DCC identifier","dcc"));
+ return dcc;
+}
+
+
+/*
+ @doc: dcc.abort
+ @type:
+ command
+ @title:
+ dcc.abort
+ @short:
+ Aborts a dcc session
+ @syntax:
+ dcc.abort [-q] [dcc_id:uint]
+ @description:
+ Terminates the Direct Client Connection specified by <dcc_id>.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function doesn't abort anything and prints a warning unless the -q switch is used.[br]
+ If <dcc_id> refers to a file transfer then it the transfer is simply
+ terminated. If <dcc_id> refers to a dcc chat then the result
+ is equivalent to closing the related window.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+ @examples:
+*/
+
+static bool dcc_kvs_cmd_abort(KviKvsModuleCommandCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,!c->switches()->find('q',"quiet"));
+
+ if(dcc)
+ {
+ if(dcc->transfer())dcc->transfer()->abort();
+ else if(dcc->window())dcc->window()->close();
+ }
+
+ return true;
+}
+
+/*
+ @doc: dcc.setBandwidthLimit
+ @type:
+ command
+ @title:
+ dcc.setBandwidthLimit
+ @short:
+ Set the bandwidthlimit of a dcc.send session.
+ @syntax:
+ dcc.setBandwidthLimit [-q] [dcc_id:uint]
+ @description:
+ Terminates the Direct Client Connection specified by <dcc_id>.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning unless the -q switch is used.[br]
+ If <dcc_id> does not refers to a file transfer a warning will be printing unless the -q switch is used.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+ @examples:
+*/
+static bool dcc_kvs_cmd_setBandwidthLimit(KviKvsModuleCommandCall * c)
+{
+ kvs_uint_t uDccId,uVal;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("limit_value",KVS_PT_UINT,0,uVal)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,!c->switches()->find('q',"quiet"));
+ if(dcc)
+ {
+ if (dcc->transfer())dcc->transfer()->setBandwidthLimit(uVal);
+ else if (!c->switches()->find('q',"quiet"))c->warning(__tr2qs_ctx("This DCC session is not a DCC transfer session","dcc"));
+ }
+ return true;
+}
+
+/*
+ @doc: dcc.protocol
+ @type:
+ function
+ @title:
+ $dcc.protocol
+ @short:
+ Returns the protocol of the specified DCC
+ @syntax:
+ <string> $dcc.protocol
+ <string> $dcc.protocol(<dcc_id:uint>)
+ @description:
+ Returns the string describing the protocol of the
+ Direct Client Connection specified by <dcc_id>.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_protocol(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->protocol());
+ return true;
+}
+
+
+/*
+ @doc: dcc.connectionType
+ @type:
+ function
+ @title:
+ $dcc.connectionType
+ @short:
+ Returns the connection type of the specified DCC
+ @syntax:
+ <string> $dcc.connectionType
+ <string> $dcc.connectionType(<dcc_id:uint>)
+ @description:
+ Returns the connection type of the specified DCC.[br]
+ Returns the string "ACTIVE" for active DCC connections
+ and the string "PASSIVE" for passive DCC connections.
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_connectionType(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->isActive() ? "ACTIVE" : "PASSIVE");
+ return true;
+}
+
+
+/*
+ @doc: dcc.isFileTransfer
+ @type:
+ function
+ @title:
+ $dcc.isFileTransfer
+ @short:
+ Checks if a DCC is a file transfer
+ @syntax:
+ <boolean> $dcc.isFileTransfer
+ <boolean> $dcc.isFileTransfer(<dcc_id:uint>)
+ @description:
+ Returns 1 if the specified Direct Client Connection
+ is a file transfer and 0 otherwise.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this and returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_isFileTransfer(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c,false);
+
+ if(dcc)c->returnValue()->setBoolean(dcc->isFileTransfer());
+ return true;
+}
+
+
+/*
+ @doc: dcc.isFileUpload
+ @type:
+ function
+ @title:
+ $dcc.isFileUpload
+ @short:
+ Checks if a DCC is an upload file transfer
+ @syntax:
+ <boolean> $dcc.isFileUpload
+ <boolean> $dcc.isFileUpload(<dcc_id:uint>)
+ @description:
+ Returns 1 if the specified Direct Client Connection
+ is an upload file transfer and 0 otherwise.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_isFileUpload(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setBoolean(dcc->isFileUpload());
+ return true;
+}
+
+
+/*
+ @doc: dcc.isFileDownload
+ @type:
+ function
+ @title:
+ $dcc.isFileDownload
+ @short:
+ Checks if a DCC is a download file transfer
+ @syntax:
+ <boolean> $dcc.isFileDownload
+ <boolean> $dcc.isFileDownload(<dcc_id:uint>)
+ @description:
+ Returns 1 if the specified Direct Client Connection
+ is a download file transfer and 0 otherwise.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_isFileDownload(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setBoolean(dcc->isFileDownload());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localNick
+ @type:
+ function
+ @title:
+ $dcc.localNick
+ @short:
+ Returns the local nickname associated to the specified DCC
+ @syntax:
+ <string> $dcc.localNick
+ <string> $dcc.localNick(<dcc_id:uint>)
+ @description:
+ Returns the local nickname associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_localNick(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localNick());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localUser
+ @type:
+ function
+ @title:
+ $dcc.localUser
+ @short:
+ Returns the local username associated to the specified DCC
+ @syntax:
+ <string> $dcc.localUser
+ <string> $dcc.localUser(<dcc_id:uint>)
+ @description:
+ Returns the local username associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_localUser(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localUser());
+ return true;
+}
+
+/*
+ @doc: dcc.localHost
+ @type:
+ function
+ @title:
+ $dcc.localHost
+ @short:
+ Returns the local hostname associated to the specified DCC
+ @syntax:
+ <string> $dcc.localHost
+ <string> $dcc.localHost(<dcc_id:uint>)
+ @description:
+ Returns the local hostname associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_localHost(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localHost());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localIp
+ @type:
+ function
+ @title:
+ $dcc.localIp
+ @short:
+ Returns the local ip address associated to the specified DCC
+ @syntax:
+ <string> $dcc.localIp
+ <string> $dcc.localIp(<dcc_id:uint>)
+ @description:
+ Returns the local ip address associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_localIp(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localIp());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localPort
+ @type:
+ function
+ @title:
+ $dcc.localPort
+ @short:
+ Returns the local port associated to the specified DCC
+ @syntax:
+ <integer> $dcc.localPort
+ <integer> $dcc.localPort(<dcc_id:uint>)
+ @description:
+ Returns the local port associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_localPort(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localPort());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localFileName
+ @type:
+ function
+ @title:
+ $dcc.localFileName
+ @short:
+ Returns the local file name associated to the specified DCC
+ @syntax:
+ <string> $dcc.localFileName(<dcc_id:uint>)
+ @description:
+ Returns the local file name associated to the specified DCC.[br]
+ If <dcc_id> does not identify a file transfer DCC then this
+ function returns an empty string.
+ If <dcc_id> is not a valid Direct Client Connection identifier
+ then this function prints a warning and returns an empty string.
+*/
+
+static bool dcc_kvs_fnc_localFileName(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localFileName());
+ return true;
+}
+
+
+/*
+ @doc: dcc.localFileSize
+ @type:
+ function
+ @title:
+ $dcc.localFileSize
+ @short:
+ Returns the local file size associated to the specified DCC
+ @syntax:
+ <integer> $dcc.localFileSize(<dcc_id:uint>)
+ @description:
+ Returns the local file size associated to the specified DCC.[br]
+ If <dcc_id> does not identify a file transfer DCC then this
+ function returns '0'[br]
+ If <dcc_id> is not a valid Direct Client Connection identifier
+ then this function prints a warning and returns '0'[br]
+ In upload transfers the local file size rappresents the
+ total size of the file to be transferred. In download transfers
+ the local file size is non zero only if the transfer has to resume
+ a file already existing on the local disk and it rappresents the
+ size of that file (and thus the offset that the transfer started on).
+*/
+
+static bool dcc_kvs_fnc_localFileSize(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->localFileSize().isEmpty() ? QString("0") : dcc->localFileSize());
+ return true;
+}
+
+
+/*
+ @doc: dcc.remoteNick
+ @type:
+ function
+ @title:
+ $dcc.remoteNick
+ @short:
+ Returns the remote nickname associated to the specified DCC
+ @syntax:
+ <string> $dcc.remoteNick
+ <string> $dcc.remoteNick(<dcc_id:uint>)
+ @description:
+ Returns the remote nickname associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_remoteNick(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteNick());
+ return true;
+}
+
+
+/*
+ @doc: dcc.remoteUser
+ @type:
+ function
+ @title:
+ $dcc.remoteUser
+ @short:
+ Returns the remote username associated to the specified DCC
+ @syntax:
+ <string> $dcc.remoteUser
+ <string> $dcc.remoteUser(<dcc_id:uint>)
+ @description:
+ Returns the remote username associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_remoteUser(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteUser());
+ return true;
+}
+
+/*
+ @doc: dcc.remoteHost
+ @type:
+ function
+ @title:
+ $dcc.remoteHost
+ @short:
+ Returns the remote hostname associated to the specified DCC
+ @syntax:
+ <string> $dcc.remoteHost
+ <string> $dcc.remoteHost(<dcc_id:uint>)
+ @description:
+ Returns the remote hostname associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_remoteHost(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteHost());
+ return true;
+}
+
+/*
+ @doc: dcc.remoteIp
+ @type:
+ function
+ @title:
+ $dcc.remoteIp
+ @short:
+ Returns the remote ip address associated to the specified DCC
+ @syntax:
+ <string> $dcc.remoteIp
+ <string> $dcc.remoteIp(<dcc_id:uint>)
+ @description:
+ Returns the remote ip address associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_remoteIp(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteIp());
+ return true;
+}
+
+
+/*
+ @doc: dcc.remotePort
+ @type:
+ function
+ @title:
+ $dcc.remotePort
+ @short:
+ Returns the remote port associated to the specified DCC
+ @syntax:
+ <integer> $dcc.remotePort
+ <integer> $dcc.remotePort(<dcc_id:uint>)
+ @description:
+ Returns the remote port associated to the specified DCC.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_remotePort(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remotePort());
+ return true;
+}
+
+
+/*
+ @doc: dcc.remoteFileName
+ @type:
+ function
+ @title:
+ $dcc.remoteFileName
+ @short:
+ Returns the remote file name associated to the specified DCC
+ @syntax:
+ <string> $dcc.remoteFileName(<dcc_id:uint>)
+ @description:
+ Returns the remote file name associated to the specified DCC.[br]
+ If <dcc_id> does not identify a file transfer DCC then this
+ function returns an empty string.
+ If <dcc_id> is not a valid Direct Client Connection identifier
+ then this function prints a warning and returns an empty string.
+*/
+
+static bool dcc_kvs_fnc_remoteFileName(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteFileName());
+ return true;
+}
+
+
+/*
+ @doc: dcc.remoteFileSize
+ @type:
+ function
+ @title:
+ $dcc.remoteFileSize
+ @short:
+ Returns the remote file size associated to the specified DCC
+ @syntax:
+ <integer> $dcc.remoteFileSize(<dcc_id:uint>)
+ @description:
+ Returns the remote file size associated to the specified DCC.[br]
+ If <dcc_id> does not identify a file transfer DCC then this
+ function returns '0'[br]
+ If <dcc_id> is not a valid Direct Client Connection identifier
+ then this function prints a warning and returns '0'[br]
+ In download transfers the remote file size rappresents the
+ total size of the file to be transferred (advertished by the remote end).[br]
+ In upload transfers the remote file size is non zero only if the
+ remote user has issued a resume request and is rappresents the requested offset
+ in bytes from which the transfer has started.
+*/
+
+static bool dcc_kvs_fnc_remoteFileSize(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setString(dcc->remoteFileSize().isEmpty() ? QString("0") : dcc->remoteFileSize());
+ return true;
+}
+
+
+/*
+ @doc: dcc.ircContext
+ @type:
+ function
+ @title:
+ $dcc.ircContext
+ @short:
+ Returns the ircContext from which this DCC has originated
+ @syntax:
+ <integer> $dcc.ircContext
+ <integer> $dcc.ircContext(<dcc_id:uint>)
+ @description:
+ Returns the identifier of the irc context from which
+ the specified DCC has been originated.[br]
+ When the DCC is not originated from an IRC context
+ then this function returns '0' : an invalid irc context id.
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_ircContext(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)c->returnValue()->setInteger(dcc->console()->context()->id());
+ return true;
+}
+
+
+
+/*
+ @doc: dcc.transferStatus
+ @type:
+ function
+ @title:
+ $dcc.transferStatus
+ @short:
+ Returns the current status of a dcc file transfer
+ @syntax:
+ <string> $dcc.transferStatus
+ <string> $dcc.transferStatus(<dcc_id:uint>)
+ @description:
+ Returns the status in the specified DCC session.[br]
+ The status is one of the strings "connecting", "transferring", "success" and "failure".
+ "success" and "failure" are reported when the transfer is terminated.
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ If the DCC session does not refer to a file transfer then
+ this function returns "".[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_transferStatus(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)
+ {
+ if(dcc->transfer())
+ {
+ QString tmp;
+ dcc->transfer()->fillStatusString(tmp);
+ c->returnValue()->setString(tmp);
+ }
+ }
+ return true;
+}
+
+
+/*
+ @doc: dcc.transferredBytes
+ @type:
+ function
+ @title:
+ $dcc.transferredBytes
+ @short:
+ Returns the number of transferred bytes in a dcc file transfer
+ @syntax:
+ <integer> $dcc.transferredBytes
+ <integer> $dcc.transferredBytes(<dcc_id:uint>)
+ @description:
+ Returns the number of transferred bytes in the specified DCC session.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ If the DCC session does not refer to a file transfer then
+ this function returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_transferredBytes(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)
+ {
+ if(dcc->transfer())
+ {
+ c->returnValue()->setInteger(dcc->transfer()->transferredBytes());
+ } else {
+ c->returnValue()->setInteger(0);
+ }
+ }
+ return true;
+}
+
+
+
+/*
+ @doc: dcc.averageSpeed
+ @type:
+ function
+ @title:
+ $dcc.averageSpeed
+ @short:
+ Returns the average speed of a dcc file transfer
+ @syntax:
+ $dcc.averageSpeed
+ $dcc.averageSpeed(<dcc_id>)
+ @description:
+ Returns the average speed (in bytes/sec) of the specified DCC session.[br]
+ If <dcc_id> is omitted then the DCC Session associated
+ to the current window is assumed.[br]
+ If <dcc_id> is not a valid DCC session identifier (or it is omitted
+ and the current window has no associated DCC session) then
+ this function prints a warning and returns an empty sting.[br]
+ If the DCC session does not refer to a file transfer then
+ this function returns 0.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_averageSpeed(KviKvsModuleFunctionCall * c)
+{
+ kvs_uint_t uDccId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("dcc_id",KVS_PT_UINT,KVS_PF_OPTIONAL,uDccId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = dcc_kvs_find_dcc_descriptor(uDccId,c);
+
+ if(dcc)
+ {
+ if(dcc->transfer())
+ {
+ c->returnValue()->setInteger(dcc->transfer()->averageSpeed());
+ } else {
+ c->returnValue()->setInteger(0);
+ }
+ }
+ return true;
+}
+
+
+
+/*
+ @doc: dcc.session
+ @type:
+ function
+ @title:
+ $dcc.session
+ @short:
+ Returns the DCC session identifier associated to a window
+ @syntax:
+ <uint> $dcc.session
+ <uint> $dcc.session(<window_id>)
+ @description:
+ Returns the DCC session identifier associated to the DCC window specified
+ by <window_id>. If <window_id> is omitted then the DCC session identifier
+ associated to the current window is returned. If the specified window
+ has no associated DCC session then a warning is printed and 0 is returned.[br]
+*/
+
+static bool dcc_kvs_fnc_session(KviKvsModuleFunctionCall * c)
+{
+ QString szWinId;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("window_id",KVS_PT_STRING,KVS_PF_OPTIONAL,szWinId)
+ KVSM_PARAMETERS_END(c)
+
+ KviDccDescriptor * dcc = 0;
+ if(szWinId.isEmpty())
+ {
+ if(c->window()->inherits("KviDccWindow"))
+ dcc = ((KviDccWindow *)(c->window()))->descriptor();
+ if(!dcc)
+ {
+ c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc"));
+ c->returnValue()->setInteger(0);
+ } else {
+ c->returnValue()->setInteger(dcc->id());
+ }
+ return true;
+ }
+
+ KviWindow * pWnd = g_pApp->findWindow(szWinId);
+ if(!pWnd)
+ {
+ c->warning(__tr2qs_ctx("The specified window identifier is not valid","dcc"));
+ c->returnValue()->setInteger(0);
+ return true;
+ }
+
+ if(pWnd->inherits("KviDccWindow"))
+ dcc = ((KviDccWindow *)pWnd)->descriptor();
+ if(!dcc)
+ {
+ c->warning(__tr2qs_ctx("The current window has no associated DCC session","dcc"));
+ c->returnValue()->setInteger(0);
+ } else {
+ c->returnValue()->setInteger(dcc->id());
+ }
+ return true;
+}
+
+
+/*
+ @doc: dcc.sessionList
+ @type:
+ function
+ @title:
+ $dcc.sessionList
+ @short:
+ List the existing dcc session identifiers
+ @syntax:
+ <array> $dcc.sessionList
+ <array> $dcc.sessionList(<filter:string>)
+ @description:
+ The first form returns an array with all the currently existing dcc session
+ identifiers. The second form returns an array with the session types specified
+ in <filter> which may be a combination of the flags 'u' (for file upload),
+ 'd' (for file download) and 'c' (for dcc chat). To select all the file transfers
+ please use the combination 'ud'.[br]
+ See the [module:dcc]dcc module[/module] documentation for more informations.[br]
+*/
+
+static bool dcc_kvs_fnc_sessionList(KviKvsModuleFunctionCall * c)
+{
+ QString szFlags;
+ KVSM_PARAMETERS_BEGIN(c)
+ KVSM_PARAMETER("filter",KVS_PT_STRING,KVS_PF_OPTIONAL,szFlags)
+ KVSM_PARAMETERS_END(c)
+
+ KviKvsArray * a = new KviKvsArray();
+ c->returnValue()->setArray(a);
+
+ KviPointerHashTable<int,KviDccDescriptor> * dict = KviDccDescriptor::descriptorDict();
+ if(!dict)return true;
+
+ KviPointerHashTableIterator<int,KviDccDescriptor> it(*dict);
+
+ int idx = 0;
+
+ if(szFlags.isEmpty())
+ {
+ // all
+ while(KviDccDescriptor * dcc = it.current())
+ {
+ a->set(idx++,new KviKvsVariant((kvs_int_t)(dcc->id())));
+ ++it;
+ }
+ } else {
+ bool bWantFileUploads = szFlags.find('u',false) != -1;
+ bool bWantFileDownloads = szFlags.contains('d',false) != -1;
+ bool bWantChats = szFlags.contains('c',false) != -1;
+
+ while(KviDccDescriptor * dcc = it.current())
+ {
+ if((dcc->isFileUpload() && bWantFileUploads) ||
+ (dcc->isFileDownload() && bWantFileDownloads) ||
+ (dcc->isDccChat() && bWantChats))
+ {
+ a->set(idx++,new KviKvsVariant((kvs_int_t)(dcc->id())));
+ }
+ ++it;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ @doc: dcc
+ @type:
+ module
+ @short:
+ Direct Client Connections
+ @title:
+ The DCC module
+ @body:
+ [big]Overview[/big][br]
+ The DCC module handles the Direct Client Connection
+ protocol layer and all it's sub-protocols.[br]
+ The sub-protocols include the standard CHAT
+ the standard SEND and its variants plus several
+ KVIrc extensions like RECV,RSEND,GET and VOICE.[br]
+ [br]
+ [big]Initiating a DCC negotiation[/big][br]
+ The following commands initiate a specific DCC session
+ with a remote client:[br]
+ [cmd]dcc.chat[/cmd][br]
+ [cmd]dcc.send[/cmd][br]
+ [cmd]dcc.rsend[/cmd][br]
+ [cmd]dcc.recv[/cmd][br]
+ [cmd]dcc.get[/cmd][br]
+ [cmd]dcc.voice[/cmd][br]
+ [br]
+ [big]Handling the DCC events[/big][br]
+ Each DCC session has an associated unique identifier (&lt;dcc_id&gt;).[br]
+ You can interact with the session by using several commands
+ and functions exported by this module and by passing the above session
+ id as parameter.[br]
+ The session related commands and functions are the following:[br]
+ [fnc]$dcc.sessionList[/fnc][br]
+ [fnc]$dcc.protocol[/fnc][br]
+ [fnc]$dcc.connectionType[/fnc][br]
+ [fnc]$dcc.transferStatus[/fnc][br]
+ [fnc]$dcc.isFileTransfer[/fnc][br]
+ [fnc]$dcc.isFileUpload[/fnc][br]
+ [fnc]$dcc.isFileDownload[/fnc][br]
+ [fnc]$dcc.localNick[/fnc][br]
+ [fnc]$dcc.localUser[/fnc][br]
+ [fnc]$dcc.localHost[/fnc][br]
+ [fnc]$dcc.localIp[/fnc][br]
+ [fnc]$dcc.localPort[/fnc][br]
+ [fnc]$dcc.localFileName[/fnc][br]
+ [fnc]$dcc.localFileSize[/fnc][br]
+ [fnc]$dcc.remoteNick[/fnc][br]
+ [fnc]$dcc.remoteUser[/fnc][br]
+ [fnc]$dcc.remoteHost[/fnc][br]
+ [fnc]$dcc.remoteIp[/fnc][br]
+ [fnc]$dcc.remotePort[/fnc][br]
+ [fnc]$dcc.remoteFileName[/fnc][br]
+ [fnc]$dcc.remoteFileSize[/fnc][br]
+ [fnc]$dcc.ircContext[/fnc][br]
+ [fnc]$dcc.session[/fnc][br]
+*/
+
+
+static bool dcc_module_init(KviModule * m)
+{
+ g_pDccBroker = new KviDccBroker();
+
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"send",dcc_kvs_cmd_send);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"chat",dcc_kvs_cmd_chat);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"voice",dcc_kvs_cmd_voice);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"recv",dcc_kvs_cmd_recv);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"rsend",dcc_kvs_cmd_rsend);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"get",dcc_kvs_cmd_get);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"abort",dcc_kvs_cmd_abort);
+ KVSM_REGISTER_SIMPLE_COMMAND(m,"setBandwidthLimit",dcc_kvs_cmd_setBandwidthLimit);
+
+
+ // FIXME: file upload / download state ?
+
+ KVSM_REGISTER_FUNCTION(m,"transferStatus",dcc_kvs_fnc_transferStatus);
+ KVSM_REGISTER_FUNCTION(m,"protocol",dcc_kvs_fnc_protocol);
+ KVSM_REGISTER_FUNCTION(m,"connectionType",dcc_kvs_fnc_connectionType);
+ KVSM_REGISTER_FUNCTION(m,"isFileTransfer",dcc_kvs_fnc_isFileTransfer);
+ KVSM_REGISTER_FUNCTION(m,"isFileUpload",dcc_kvs_fnc_isFileUpload);
+ KVSM_REGISTER_FUNCTION(m,"isFileDownload",dcc_kvs_fnc_isFileDownload);
+ KVSM_REGISTER_FUNCTION(m,"localNick",dcc_kvs_fnc_localNick);
+ KVSM_REGISTER_FUNCTION(m,"localUser",dcc_kvs_fnc_localUser);
+ KVSM_REGISTER_FUNCTION(m,"localHost",dcc_kvs_fnc_localHost);
+ KVSM_REGISTER_FUNCTION(m,"localIp",dcc_kvs_fnc_localIp);
+ KVSM_REGISTER_FUNCTION(m,"localPort",dcc_kvs_fnc_localPort);
+ KVSM_REGISTER_FUNCTION(m,"localFileName",dcc_kvs_fnc_localFileName);
+ KVSM_REGISTER_FUNCTION(m,"localFileSize",dcc_kvs_fnc_localFileSize);
+ KVSM_REGISTER_FUNCTION(m,"remoteNick",dcc_kvs_fnc_remoteNick);
+ KVSM_REGISTER_FUNCTION(m,"remoteUser",dcc_kvs_fnc_remoteUser);
+ KVSM_REGISTER_FUNCTION(m,"remoteHost",dcc_kvs_fnc_remoteHost);
+ KVSM_REGISTER_FUNCTION(m,"remoteIp",dcc_kvs_fnc_remoteIp);
+ KVSM_REGISTER_FUNCTION(m,"remotePort",dcc_kvs_fnc_remotePort);
+ KVSM_REGISTER_FUNCTION(m,"remoteFileName",dcc_kvs_fnc_remoteFileName);
+ KVSM_REGISTER_FUNCTION(m,"remoteFileSize",dcc_kvs_fnc_remoteFileSize);
+ KVSM_REGISTER_FUNCTION(m,"averageSpeed",dcc_kvs_fnc_averageSpeed);
+ KVSM_REGISTER_FUNCTION(m,"transferredBytes",dcc_kvs_fnc_transferredBytes);
+ KVSM_REGISTER_FUNCTION(m,"ircContext",dcc_kvs_fnc_ircContext);
+ KVSM_REGISTER_FUNCTION(m,"session",dcc_kvs_fnc_session);
+ KVSM_REGISTER_FUNCTION(m,"sessionList",dcc_kvs_fnc_sessionList);
+
+ return true;
+}
+
+static bool dcc_module_cleanup(KviModule *m)
+{
+ delete g_pDccBroker;
+ g_pDccBroker = 0;
+#ifdef COMPILE_USE_GSM
+ kvi_gsm_codec_done();
+#endif
+
+ return true;
+}
+
+static bool dcc_module_can_unload(KviModule *m)
+{
+ return g_pDccBroker ? g_pDccBroker->canUnload() : true;
+}
+
+KVIRC_MODULE(
+ "Dcc",
+ "1.0.0",
+ "Copyright (C) 2000-2004:\n" \
+ " Szymon Stefanek (pragma at kvirc dot net)\n",
+ "DCC extension for KVIrc\n",
+ dcc_module_init,
+ dcc_module_can_unload,
+ 0,
+ dcc_module_cleanup
+)
diff --git a/src/modules/dcc/marshal.cpp b/src/modules/dcc/marshal.cpp
new file mode 100644
index 00000000..9aedec41
--- /dev/null
+++ b/src/modules/dcc/marshal.cpp
@@ -0,0 +1,647 @@
+//
+// File : marshal.cpp
+// Creation date : Sun Sep 17 2000 15:59:11 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "marshal.h"
+
+#include "kvi_settings.h"
+#include "kvi_netutils.h"
+#include "kvi_error.h"
+#include "kvi_options.h"
+#include "kvi_locale.h"
+#include "kvi_memmove.h"
+#include "kvi_socket.h"
+#include "kvi_fileutils.h"
+
+
+#include <stdlib.h> //for exit()
+
+
+
+
+KviDccMarshal::KviDccMarshal(KviDccMarshalOutputContext * ctx)
+: QObject(0,"dcc_marshal")
+{
+ m_pSn = 0;
+ m_fd = KVI_INVALID_SOCKET;
+ m_pTimeoutTimer = 0;
+ m_bIpV6 = false;
+ m_pOutputContext = ctx;
+#ifdef COMPILE_SSL_SUPPORT
+ m_pSSL = 0;
+#endif
+ m_szIp = "";
+ m_szPort = "";
+ m_szSecondaryIp = "";
+ m_szSecondaryPort = "";
+}
+
+KviDccMarshal::~KviDccMarshal()
+{
+ reset();
+}
+
+kvi_socket_t KviDccMarshal::releaseSocket()
+{
+ kvi_socket_t aux_fd = m_fd;
+ m_fd = KVI_INVALID_SOCKET;
+ return aux_fd;
+}
+
+#ifdef COMPILE_SSL_SUPPORT
+KviSSL * KviDccMarshal::releaseSSL()
+{
+ KviSSL * theSSL = m_pSSL;
+ m_pSSL = 0;
+ return theSSL;
+}
+#endif
+
+void KviDccMarshal::reset()
+{
+ if(m_pSn)
+ {
+ delete m_pSn;
+ m_pSn = 0;
+ }
+ if(m_fd != KVI_INVALID_SOCKET)
+ {
+ kvi_socket_close(m_fd);
+ m_fd = KVI_INVALID_SOCKET;
+ }
+#ifdef COMPILE_SSL_SUPPORT
+// debug("MARSHAL RESETTING (SSL=%d)",m_pSSL);
+ if(m_pSSL)
+ {
+// debug("MARSHAL CLEARING THE SSL");
+ KviSSLMaster::freeSSL(m_pSSL);
+ m_pSSL = 0;
+ }
+#endif
+ if(m_pTimeoutTimer)
+ {
+ delete m_pTimeoutTimer;
+ m_pTimeoutTimer = 0;
+ }
+ m_bIpV6 = false;
+}
+
+int KviDccMarshal::dccListen(const QString &ip,const QString &port,bool bUseTimeout,bool bUseSSL)
+{
+ if(m_fd != KVI_INVALID_SOCKET)return KviError_anotherConnectionInProgress;
+
+ m_szIp = ip;
+ m_szPort = port;
+
+ m_bOutgoing = false;
+
+ m_bUseTimeout = bUseTimeout;
+
+#ifdef COMPILE_SSL_SUPPORT
+ m_bUseSSL = bUseSSL;
+#else
+ if(bUseSSL)return KviError_noSSLSupport;
+#endif
+
+ if(m_pTimeoutTimer)delete m_pTimeoutTimer;
+ m_pTimeoutTimer = new QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,SLOT(doListen()));
+ m_pTimeoutTimer->start(100,true);
+
+ return KviError_success;
+}
+
+void KviDccMarshal::doListen()
+{
+ if(m_pTimeoutTimer)
+ {
+ delete m_pTimeoutTimer;
+ m_pTimeoutTimer = 0;
+ }
+
+ // Check the address type
+ if(!kvi_isValidStringIp(m_szIp))
+ {
+#ifdef COMPILE_IPV6_SUPPORT
+ if(!kvi_isValidStringIp_V6(m_szIp))
+ {
+ emit error(KviError_invalidIpAddress);
+ return;
+ } else m_bIpV6 = true;
+#else
+ emit error(KviError_invalidIpAddress);
+ return;
+#endif
+ }
+
+ bool bOk;
+ m_uPort = m_szPort.toUInt(&bOk);
+ if(!bOk)
+ {
+ emit error(KviError_invalidPortNumber);
+ return;
+ }
+
+
+#ifndef COMPILE_IPV6_SUPPORT
+ if(m_bIpV6)
+ {
+ emit error(KviError_noIpV6Support);
+ return;
+ }
+#endif
+
+
+#ifdef COMPILE_IPV6_SUPPORT
+ m_fd = kvi_socket_create(m_bIpV6 ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,
+ KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
+#else
+ m_fd = kvi_socket_create(KVI_SOCKET_PF_INET,KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
+#endif
+
+ if(m_fd == KVI_INVALID_SOCKET)
+ {
+ emit error(KviError_socketCreationFailed);
+ return;
+ }
+
+ if((!KVI_OPTION_BOOL(KviOption_boolUserDefinedPortRange)) || (m_uPort != 0))
+ {
+#ifdef COMPILE_IPV6_SUPPORT
+ KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
+#else
+ KviSockaddr sa(m_szIp,m_uPort,false);
+#endif
+
+ if(!sa.socketAddress())
+ {
+ reset();
+ emit error(KviError_bindFailed);
+ return;
+ }
+
+ if(!kvi_socket_bind(m_fd,sa.socketAddress(),sa.addressLength()))
+ {
+ reset();
+ emit error(KviError_bindFailed);
+ return;
+ }
+
+ } else {
+ m_uPort = KVI_OPTION_UINT(KviOption_uintDccMinPort);
+ if(KVI_OPTION_UINT(KviOption_uintDccMaxPort) > 65535)KVI_OPTION_UINT(KviOption_uintDccMaxPort) = 65535;
+ bool bBindSuccess;
+ do {
+#ifdef COMPILE_IPV6_SUPPORT
+ KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
+#else
+ KviSockaddr sa(m_szIp,m_uPort,false);
+#endif
+ if(!sa.socketAddress())
+ {
+ reset();
+ emit error(KviError_bindFailed);
+ return;
+ }
+
+ bBindSuccess = kvi_socket_bind(m_fd,sa.socketAddress(),sa.addressLength());
+
+ if(!bBindSuccess)
+ {
+ if(m_uPort == 65535)
+ {
+ reset();
+ emit error(KviError_bindFailed);
+ return;
+ }
+ m_uPort++;
+ }
+
+ } while((!bBindSuccess) && (m_uPort <= KVI_OPTION_UINT(KviOption_uintDccMaxPort)));
+
+ if(!bBindSuccess)
+ {
+ reset();
+ emit error(KviError_bindFailed);
+ return;
+ }
+
+ }
+
+ if(!kvi_socket_listen(m_fd,1))
+ {
+ reset();
+ emit error(KviError_listenFailed);
+ return;
+ }
+
+
+ // Reread the port in case we're binding to a random one (0)
+
+#ifdef COMPILE_IPV6_SUPPORT
+ KviSockaddr sareal(0,m_bIpV6);
+#else
+ KviSockaddr sareal(0,false);
+#endif
+
+
+ int size = sareal.addressLength();
+
+ if(kvi_socket_getsockname(m_fd,sareal.socketAddress(),&size))
+ {
+// debug("GETSOCKNAMEOK");
+ m_szPort.setNum(sareal.port());
+ m_uPort = sareal.port();
+// debug("REALPORT %u",m_uPort);
+ } else {
+// debug("GETSOCKNAMEFAILED");
+ }
+
+ // and setup the READ notifier...
+ m_pSn = new QSocketNotifier(m_fd,QSocketNotifier::Read);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(snActivated(int)));
+ m_pSn->setEnabled(true);
+
+ // set the timer
+ if(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) < 5)
+ KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) = 5;
+
+ if(m_bUseTimeout)
+ {
+ m_pTimeoutTimer = new QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,SLOT(connectionTimedOut()));
+ m_pTimeoutTimer->start(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) * 1000,true);
+ }
+ // and wait for connect
+
+ emit inProgress();
+}
+
+int KviDccMarshal::dccConnect(const char * ip,const char * port,bool bUseTimeout,bool bUseSSL)
+{
+ if(m_fd != KVI_INVALID_SOCKET)return KviError_anotherConnectionInProgress;
+
+ m_bUseTimeout = bUseTimeout;
+ m_szIp = ip;
+ m_szPort = port;
+ m_bOutgoing = true;
+
+#ifdef COMPILE_SSL_SUPPORT
+ m_bUseSSL = bUseSSL;
+#else
+ if(bUseSSL)return KviError_noSSLSupport;
+#endif
+
+ if(m_pTimeoutTimer)delete m_pTimeoutTimer;
+ m_pTimeoutTimer = new QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,SLOT(doConnect()));
+ m_pTimeoutTimer->start(100,true);
+
+ return KviError_success;
+}
+
+void KviDccMarshal::doConnect()
+{
+ if(m_pTimeoutTimer)
+ {
+ delete m_pTimeoutTimer;
+ m_pTimeoutTimer = 0;
+ }
+
+ // Check the address type
+ if(!kvi_isValidStringIp(m_szIp))
+ {
+#ifdef COMPILE_IPV6_SUPPORT
+ if(!kvi_isValidStringIp_V6(m_szIp))
+ {
+ emit error(KviError_invalidIpAddress);
+ return;
+ } else m_bIpV6 = true;
+#else
+ emit error(KviError_invalidIpAddress);
+ return;
+#endif
+ }
+
+ bool bOk;
+ m_uPort = m_szPort.toUInt(&bOk);
+ if(!bOk)
+ {
+ emit error(KviError_invalidPortNumber);
+ return;
+ }
+
+
+ // create the socket
+#ifdef COMPILE_IPV6_SUPPORT
+ m_fd = kvi_socket_create(m_bIpV6 ? KVI_SOCKET_PF_INET6 : KVI_SOCKET_PF_INET,
+ KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
+#else
+ m_fd = kvi_socket_create(KVI_SOCKET_PF_INET,
+ KVI_SOCKET_TYPE_STREAM,KVI_SOCKET_PROTO_TCP);
+#endif
+ if(m_fd == KVI_INVALID_SOCKET)
+ {
+ emit error(KviError_socketCreationFailed);
+ return;
+ }
+
+ // make it non blocking
+ if(!kvi_socket_setNonBlocking(m_fd))
+ {
+ reset();
+ emit error(KviError_asyncSocketFailed);
+ return;
+ }
+
+
+ // fill the sockaddr structure
+
+#ifdef COMPILE_IPV6_SUPPORT
+ KviSockaddr sa(m_szIp,m_uPort,m_bIpV6);
+#else
+ KviSockaddr sa(m_szIp,m_uPort,false);
+#endif
+
+ if(!sa.socketAddress())
+ {
+ reset();
+ emit error(KviError_socketCreationFailed);
+ return;
+ }
+
+ if(!kvi_socket_connect(m_fd,sa.socketAddress(),sa.addressLength()))
+ {
+ int err = kvi_socket_error();
+
+ if(!kvi_socket_recoverableConnectError(err))
+ {
+ // Ops...
+ int sockError=err;
+ if(sockError==0)
+ {
+ // Zero error ?...let's look closer
+ int iSize=sizeof(int);
+ if(!kvi_socket_getsockopt(m_fd,SOL_SOCKET,SO_ERROR,
+ (void *)&sockError,&iSize))sockError=0;
+ }
+ // Die
+ reset();
+ // And declare problems :)
+ if(sockError)emit error(KviError::translateSystemError(sockError));
+ else emit error(KviError_unknownError); //Error 0 ?
+ return;
+ }
+ }
+
+
+ // and setup the WRITE notifier...
+ m_pSn = new QSocketNotifier(m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(snActivated(int)));
+ m_pSn->setEnabled(true);
+
+ // set the timer
+ if(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) < 5)
+ KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) = 5;
+
+ if(m_bUseTimeout)
+ {
+ m_pTimeoutTimer = new QTimer();
+ connect(m_pTimeoutTimer,SIGNAL(timeout()),this,SLOT(connectionTimedOut()));
+ m_pTimeoutTimer->start(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) * 1000,true);
+ }
+
+ // and wait for connect
+ emit inProgress();
+}
+
+
+void KviDccMarshal::snActivated(int)
+{
+ if(m_pTimeoutTimer)
+ {
+ delete m_pTimeoutTimer;
+ m_pTimeoutTimer = 0;
+ }
+
+#ifdef COMPILE_IPV6_SUPPORT
+ struct sockaddr_in6 hostSockAddr6;
+#endif
+ struct sockaddr_in hostSockAddr;
+
+ int size = sizeof(hostSockAddr);
+ struct sockaddr * addr = (struct sockaddr *)&hostSockAddr;
+
+#ifdef COMPILE_IPV6_SUPPORT
+ if(m_bIpV6)
+ {
+ addr = (struct sockaddr *)&hostSockAddr6;
+ size = sizeof(hostSockAddr6);
+ }
+#endif
+
+ if(m_bOutgoing)
+ {
+ // outgoing connection (we have called connect())
+ // Check for errors...
+ int sockError;
+ int iSize=sizeof(int);
+ if(!kvi_socket_getsockopt(m_fd,SOL_SOCKET,SO_ERROR,(void *)&sockError,&iSize))sockError = -1;
+ if(sockError != 0)
+ {
+ //failed
+ if(sockError > 0)sockError = KviError::translateSystemError(sockError);
+ else sockError = KviError_unknownError; //Error 0 ?
+ reset();
+ emit error(sockError);
+ return;
+ }
+ //Succesfully connected...
+ delete m_pSn;
+ m_pSn = 0;
+ // get the local address
+ if(!kvi_socket_getsockname(m_fd,addr,&size))
+ {
+ m_szSecondaryIp = "localhost";
+ m_szSecondaryPort = __tr2qs_ctx("unknown","dcc");
+ } else {
+#ifdef COMPILE_IPV6_SUPPORT
+ if(m_bIpV6)
+ {
+ m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
+ if(!kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)addr)->sin6_addr,m_szSecondaryIp))
+ m_szSecondaryIp = "localhost";
+ } else {
+#endif
+ m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in *)addr)->sin_port));
+ if(!kvi_binaryIpToStringIp(((struct sockaddr_in *)addr)->sin_addr,m_szSecondaryIp))
+ m_szSecondaryIp = "localhost";
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ }
+ } else {
+ // Incoming connection
+ int newsock = kvi_socket_accept(m_fd,addr,&size);
+ if(newsock != KVI_INVALID_SOCKET)
+ {
+ // Connected
+ delete m_pSn;
+ m_pSn = 0;
+#ifdef COMPILE_IPV6_SUPPORT
+ if(m_bIpV6)
+ {
+ m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in6 *)addr)->sin6_port));
+ if(!kvi_binaryIpToStringIp_V6(((struct sockaddr_in6 *)addr)->sin6_addr,m_szSecondaryIp))
+ m_szSecondaryIp = __tr2qs_ctx("unknown","dcc");
+ } else {
+#endif
+ m_szSecondaryPort.setNum(ntohs(((struct sockaddr_in *)addr)->sin_port));
+ if(!kvi_binaryIpToStringIp(((struct sockaddr_in *)addr)->sin_addr,m_szSecondaryIp))
+ m_szSecondaryIp = __tr2qs_ctx("unknown","dcc");
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ kvi_socket_close(m_fd);
+ m_fd = newsock;
+ if(!kvi_socket_setNonBlocking(m_fd))
+ {
+ reset();
+ emit error(KviError_asyncSocketFailed);
+ return;
+ }
+
+ } else {
+ // Huh ?.. wait for the next notifier call
+ return;
+ }
+ }
+
+#ifdef COMPILE_SSL_SUPPORT
+ // SSL Handshake needed ?
+ if(m_bUseSSL)
+ {
+ m_pSSL = KviSSLMaster::allocSSL(m_pOutputContext->dccMarshalOutputWindow(),m_fd,m_bOutgoing ? KviSSL::Client : KviSSL::Server,m_pOutputContext->dccMarshalOutputContextString());
+
+ if(m_pSSL)
+ {
+ emit startingSSLHandshake();
+ doSSLHandshake(0);
+ } else {
+ reset();
+ emit error(KviError_SSLError);
+ }
+ return;
+ }
+#endif
+
+ emit connected();
+}
+
+
+void KviDccMarshal::doSSLHandshake(int)
+{
+#ifdef COMPILE_SSL_SUPPORT
+// debug("DO SSL HANDSHAKE");
+ if(m_pSn)
+ {
+ delete m_pSn;
+ m_pSn = 0;
+ }
+
+ if(!m_pSSL)
+ {
+ debug("Ops... I've lost the SSL class ?");
+ reset();
+ emit error(KviError_internalError);
+ return; // ops ?
+ }
+
+ KviSSL::Result r = m_bOutgoing ? m_pSSL->connect() : m_pSSL->accept();
+
+ switch(r)
+ {
+ case KviSSL::Success:
+ // done!
+// debug("EMITTING CONNECTED");
+ emit connected();
+// debug("CONNECTED EMITTED");
+ break;
+ case KviSSL::WantRead:
+ m_pSn = new QSocketNotifier((int)m_fd,QSocketNotifier::Read);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(doSSLHandshake(int)));
+ m_pSn->setEnabled(true);
+ break;
+ case KviSSL::WantWrite:
+ m_pSn = new QSocketNotifier((int)m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(doSSLHandshake(int)));
+ m_pSn->setEnabled(true);
+ break;
+ case KviSSL::RemoteEndClosedConnection:
+ reset();
+ emit error(KviError_remoteEndClosedConnection);
+ break;
+ case KviSSL::SyscallError:
+ {
+ // syscall problem
+ int err = kvi_socket_error();
+ if(kvi_socket_recoverableError(err))
+ {
+ // can recover ? (EAGAIN , EINTR ?)
+ m_pSn = new QSocketNotifier((int)m_fd,QSocketNotifier::Write);
+ QObject::connect(m_pSn,SIGNAL(activated(int)),this,SLOT(doSSLHandshake(int)));
+ m_pSn->setEnabled(true);
+ return;
+ } else {
+ // Declare problems :)
+ reset();
+ emit error(err ? KviError::translateSystemError(err) : KviError_unknownError);
+ }
+ }
+ break;
+ default:
+ {
+ KviStr buffer;
+ while(m_pSSL->getLastErrorString(buffer))emit sslError(buffer.ptr());
+ reset();
+ emit error(KviError_SSLError);
+ }
+ break;
+ }
+#else //!COMPILE_SSL_SUPPORT
+ debug("Ops.. ssl handshake without ssl support!...aborting!");
+ exit(-1);
+#endif //!COMPILE_SSL_SUPPORT
+}
+
+void KviDccMarshal::abort()
+{
+ reset();
+}
+
+void KviDccMarshal::connectionTimedOut()
+{
+ reset();
+ emit error(KviError_connectionTimedOut);
+}
+
+
+#include "m_marshal.moc"
diff --git a/src/modules/dcc/marshal.h b/src/modules/dcc/marshal.h
new file mode 100644
index 00000000..9665e09a
--- /dev/null
+++ b/src/modules/dcc/marshal.h
@@ -0,0 +1,112 @@
+#ifndef _MARSHAL_H_
+#define _MARSHAL_H_
+//
+// File marshal.h
+// Creation date : Sun Sep 17 2000 10:44:20 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_string.h"
+#include "kvi_sockettype.h"
+#include <qobject.h>
+#include <qsocketnotifier.h>
+#include <qtimer.h>
+#include "kvi_inttypes.h"
+
+class KviWindow;
+
+#ifdef COMPILE_SSL_SUPPORT
+ #include "kvi_sslmaster.h"
+#endif
+
+class KviDccMarshal;
+
+class KviDccMarshalOutputContext
+{
+ friend class KviDccMarshal;
+public:
+ KviDccMarshalOutputContext(){};
+ virtual ~KviDccMarshalOutputContext(){};
+protected:
+ virtual KviWindow * dccMarshalOutputWindow() = 0;
+ virtual const char * dccMarshalOutputContextString() = 0;
+};
+
+class KviDccMarshal : public QObject
+{
+ Q_OBJECT
+public:
+ KviDccMarshal(KviDccMarshalOutputContext * ctx);
+ ~KviDccMarshal();
+protected:
+ // DCC DESCRIPTOR
+ QString m_szIp; // Dcc initiator ip address (the one that listens)
+ QString m_szPort; // Dcc initiator port (the one that listens)
+ // other info
+ bool m_bIpV6; // Dcc mode
+ kvi_u32_t m_uPort; // Dcc initiator port
+ bool m_bOutgoing; // true if WE have connected to the remote host (so m_szIp is the remote host ip)
+ QString m_szSecondaryIp; // Ip of the client that has connected to the remote host
+ QString m_szSecondaryPort; // Port of the client that has connected to the remote host
+ // internals
+ kvi_socket_t m_fd; // socket
+ QSocketNotifier * m_pSn;
+ QTimer * m_pTimeoutTimer;
+ bool m_bUseTimeout;
+#ifdef COMPILE_SSL_SUPPORT
+ KviSSL * m_pSSL;
+ bool m_bUseSSL;
+#endif
+ KviDccMarshalOutputContext * m_pOutputContext;
+public:
+ const QString & dccIp() const { return m_szIp; };
+ const QString & dccPort() const { return m_szPort; };
+ const QString & localIp() const { return m_bOutgoing ? m_szSecondaryIp : m_szIp; };
+ const QString & localPort() const { return m_bOutgoing ? m_szSecondaryPort : m_szPort; };
+ const QString & remoteIp() const { return m_bOutgoing ? m_szIp : m_szSecondaryIp; };
+ const QString & remotePort() const { return m_bOutgoing ? m_szPort : m_szSecondaryPort; };
+ int dccListen(const QString &ip,const QString &port,bool bUseTimeout,bool bUseSSL = false);
+ int dccConnect(const char * ip,const char * port,bool bUseTimeout,bool bUseSSL = false);
+ kvi_socket_t releaseSocket();
+#ifdef COMPILE_SSL_SUPPORT
+ KviSSL * releaseSSL();
+#endif
+ void abort();
+private:
+ void reset();
+//#ifdef COMPILE_SSL_SUPPORT
+// bool trySSLCertificate();
+//#endif
+private slots:
+ void doSSLHandshake(int);
+// void doListenSSLHandshake();
+ void snActivated(int);
+ void connectionTimedOut();
+ void doListen();
+ void doConnect();
+signals:
+ void startingSSLHandshake();
+ void sslError(const char * msg);
+ void connected();
+ void inProgress();
+ void error(int);
+};
+
+
+#endif //_MARSHAL_H_
diff --git a/src/modules/dcc/requests.cpp b/src/modules/dcc/requests.cpp
new file mode 100644
index 00000000..e1e18143
--- /dev/null
+++ b/src/modules/dcc/requests.cpp
@@ -0,0 +1,1154 @@
+//=============================================================================
+//
+// File : requests.cpp
+// Creation date : Tue Jul 23 02:44:38 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_settings.h"
+#include "kvi_string.h"
+#include "kvi_module.h"
+#include "kvi_sparser.h"
+#include "kvi_locale.h"
+#include "kvi_out.h"
+#include "kvi_console.h"
+#include "kvi_netutils.h"
+#include "kvi_frame.h"
+#include "kvi_console.h"
+
+#include "kvi_error.h"
+#include "kvi_options.h"
+#include "kvi_defaults.h"
+#include "kvi_sharedfiles.h"
+#include "kvi_mirccntrl.h"
+#include "kvi_app.h"
+#include "kvi_ircconnection.h"
+#include "kvi_ircconnectionuserinfo.h"
+
+#include "gsmcodec.h"
+#include "broker.h"
+#include "voice.h"
+#include "utils.h"
+#include "send.h"
+
+#include <qfileinfo.h>
+
+#ifdef COMPILE_ON_WINDOWS
+ // Ugly Windoze compiler...
+ #include "dialogs.h"
+#endif
+
+//#warning "KviOption_boolIgnoreDccChat and other types too"
+
+extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager;
+extern KviDccBroker * g_pDccBroker;
+
+static void dcc_module_reply_errmsg(KviDccRequest * dcc,const QString& errText)
+{
+ dcc->ctcpMsg->msg->console()->connection()->sendFmtData(
+ "NOTICE %s :%cERRMSG %s%c",
+ dcc->ctcpMsg->msg->console()->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),0x01,
+ dcc->ctcpMsg->msg->console()->connection()->encodeText(errText).data()
+ ,0x01);
+}
+
+static void dcc_module_request_error(KviDccRequest * dcc,const QString& errText)
+{
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("Unable to process the above request: %Q, %Q","dcc"),
+ &errText,
+ KVI_OPTION_BOOL(KviOption_boolNotifyFailedDccHandshakes) ? &(__tr2qs_ctx("Ignoring and notifying failure","dcc")) : &(__tr2qs_ctx("Ignoring","dcc")));
+
+ if(KVI_OPTION_BOOL(KviOption_boolNotifyFailedDccHandshakes))
+ {
+ QString szError = QString("Sorry, your DCC %1 request can't be satisfied: %2").arg(dcc->szType.ptr()).arg(errText);
+ dcc_module_reply_errmsg(dcc,szError);
+ }
+}
+
+static bool dcc_module_check_concurrent_transfers_limit(KviDccRequest * dcc)
+{
+ if(KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers) > 0)
+ {
+ unsigned int uTransfers = KviDccFileTransfer::runningTransfersCount();
+ if(uTransfers >= KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers))
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Concurrent transfer limit reached (%u of %u transfers running)","dcc"),
+ uTransfers,KVI_OPTION_UINT(KviOption_uintMaxDccSendTransfers));
+ dcc_module_request_error(dcc,szError.ptr());
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool dcc_module_check_limits(KviDccRequest * dcc)
+{
+ if(KVI_OPTION_UINT(KviOption_uintMaxDccSlots) > 0)
+ {
+ unsigned int uWindows = g_pDccBroker->dccWindowsCount();
+ if(uWindows >= KVI_OPTION_UINT(KviOption_uintMaxDccSlots))
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Slot limit reached (%u slots of %u)","dcc"),
+ uWindows,KVI_OPTION_UINT(KviOption_uintMaxDccSlots));
+ dcc_module_request_error(dcc,szError.ptr());
+ return false;
+ }
+ }
+ if(g_pDccBroker->dccBoxCount() >= 32)
+ {
+ // there are too many pending dcc requests: the user isn't watching....
+ dcc_module_request_error(dcc,__tr2qs_ctx("Too many pending connections","dcc"));
+ return false;
+ }
+ return true;
+}
+
+static void dcc_fill_local_nick_user_host(KviDccDescriptor * d,KviDccRequest * dcc)
+{
+ if(dcc->pConsole->connection())
+ {
+ d->szLocalNick = dcc->pConsole->connection()->userInfo()->nickName();
+ d->szLocalUser = dcc->pConsole->connection()->userInfo()->userName();
+ d->szLocalHost = dcc->pConsole->connection()->userInfo()->hostName();
+ } else {
+ d->szLocalNick = __tr_ctx("unknown","dcc");
+ d->szLocalUser = __tr2qs_ctx("unknown","dcc");
+ d->szLocalHost = __tr2qs_ctx("unknown","dcc");
+ }
+}
+
+static void dcc_module_set_dcc_type(KviDccDescriptor * d,const char * szBaseType)
+{
+ d->szType = szBaseType;
+#ifdef COMPILE_SSL_SUPPORT
+ if(d->bIsSSL)d->szType.prepend('S');
+#endif
+ if(d->bIsTdcc)d->szType.prepend('T');
+}
+
+
+static bool dcc_module_normalize_target_data(KviDccRequest * dcc,KviStr &ipaddr,KviStr &port)
+{
+ if(!port.isUnsignedNum())
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid port number %s","dcc"),port.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+
+ struct in_addr addr;
+
+ if(ipaddr.isUnsignedNum())
+ {
+ addr.s_addr = htonl((unsigned long)ipaddr.toULong());
+ QString tmp;
+ if(!kvi_binaryIpToStringIp(addr,tmp))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid IP address in old format %s","dcc"),ipaddr.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+ ipaddr = tmp;
+ } else {
+ if(!kvi_stringIpToBinaryIp(ipaddr,&addr))
+ {
+#ifdef COMPILE_IPV6_SUPPORT
+ struct in6_addr addr6;
+ if(kvi_stringIpToBinaryIp_V6(ipaddr,&addr6))
+ {
+ dcc->bIpV6 = true;
+ return true; // IPV6 address.
+ }
+#endif
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid IP address %s","dcc"),ipaddr.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return false;
+ }
+ }
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CHAT
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccChat(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC CHAT request in the following form:
+ //
+ // DCC CHAT chat <ipaddress> <port>
+ //
+ // This means that we're requested to setup an ACTIVE chat connection
+ // ... Easy task :)
+ //
+ // Anybody understands the meaning of the secondo "chat" in there ?
+ // It was meant to simplify the parsing ? :DDD
+ //
+ // There is a mIrc extension that allows <port> to be 0
+ // and adds a last parameter that seems to be a random number (thnx YaP :)
+ // that is used to keep track of the connection.
+ // This extension is used by firewalled machines to initiate a DCC CHAT:
+ // the receiving side should respond with a DCC CHAT offer
+ // with the same random number appended, and then should listen for a connection.
+ //
+ // when a zero port request is initiated by another party we get
+ //
+ // DCC CHAT chat <fakeipaddress> 0 <tag>
+ //
+ // and we reply with
+ //
+ // DCC CHAT chat <ourip> <ourport> <tag>
+ //
+ // when a zero port request is initiated by us we send out
+ //
+ // DCC CHAT chat <fakeipaddress> 0 <tag>
+ //
+ // and we get
+ //
+ // DCC CHAT chat <remoteip> <remoteport> <tag>
+ //
+ // Thus if there is a <tag> and the port is 0, then the remote party
+ // wanted to estabilish a dcc with us and wants us to listen, but if the port is nonzero then
+ // we have sent out a zero port request and the remote party acked it
+ // thus we have to connect instead!
+ //
+
+ // First of all we check the dcc slot limits
+ if(!dcc_module_check_limits(dcc))return;
+
+ // Then we check the target host data
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!kvi_strEqualCI(dcc->szParam1.ptr(),"chat"))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The second parameter is '%s' and should be 'chat', trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off CHAT
+
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC CHAT is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+
+
+ if(dcc->szParam4.hasData())
+ {
+ // zero port tag ?
+ if(d->szPort == "0")
+ {
+ // zero port request
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+ d->setZeroPortRequestTag(dcc->szParam4.ptr());
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))d->szListenIp = "0.0.0.0";
+ else d->szListenIp=tmp;
+ d->szListenPort = "0"; // any port is OK
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccChat);
+ d->bActive = false; // we must listen then...
+ } else {
+ // zero port acknowledge
+ // check if this is a tag that we have sent out
+ QString szTag = QString(dcc->szParam4.ptr());
+ KviDccZeroPortTag * t = g_pDccBroker->findZeroPortTag(szTag);
+ if(!t)
+ {
+ // hum.. not our tag
+
+ // FIXME: As segnaled by PRAEDO, ezbounce seems to send a fourth parameter in response to /quote ezb log
+ // Pragma: That's a bug in ezbounce, it sends the filesize of the log as a DCC CHAT parameter...
+ // The author probably copied and pasted the CTCP line from DCC SEND and forgot to remove the filesize.
+ // We *could* add an option to ignore the last parameter and treat it as a standard dcc chat
+ // request, but since we don't encourage bugs, we don't do it :D
+ // Mail me at pragma at kvirc dot net if you really think it's necessary.
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: it looks like a zero port tag acknowledge but I have either never seen this tag or it was sent more than 120 seconds ago","dcc"));
+ dcc_module_request_error(dcc,__tr2qs_ctx("It seems that I haven't requested this dcc chat","dcc"));
+ delete d;
+ return;
+ } else {
+ g_pDccBroker->removeZeroPortTag(szTag);
+ }
+
+ d->bAutoAccept = true; // auto-accept it (we have sent it out)
+ d->bActive = true;
+ }
+ } else {
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccChat);
+ d->bActive = true; // we have to connct (standard active chat)
+ }
+
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+
+ dcc_module_set_dcc_type(d,"CHAT");
+ d->triggerCreationEvent();
+
+ g_pDccBroker->handleChatRequest(d);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// SEND
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRecv(KviDccRequest * dcc);
+
+static void dccModuleParseDccSend(KviDccRequest *dcc)
+{
+//#warning "Ignore files depending on file type ? (MediaType ?)"
+ //
+ // We have received a DCC SEND request in the following form
+ //
+ // DCC [ST]SEND <filename> <ipaddress> <port> <filesize>
+ //
+ // Now the things are a bit tricky... we eventually can
+ // reply with a DCC RESUME and receive a DCC ACCEPT then
+ // The format of these requests is:
+ //
+ // DCC RESUME <filename> <port> <resumepos>
+ // ACCEPT <filename> <port> <resumepos>
+ //
+ // There is a mIrc extension that allows <port> to be 0
+ // and adds a last parameter that seems to be a random number (thnx YaP :)
+ // that is used to keep track of the connection.
+ // This extension is used by firewalled machines to initiate a DCC SEND:
+ // the receiving side should respond with a DCC SEND offer
+ // with the same random number appended, listen for a connection, and receive the file
+ // instead of sending it.
+ //
+ // when a zero port request is initiated by another party we get
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // if (and only if) we want to resume we reply with
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // in this case the remote part replies again with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally reply with
+ // DCC SEND <filename> <ourip> <ourport> <filesize> <tag>
+ //
+ // when a zero port request is initiated by us we send out
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // and if the remote party wants to resume then we get
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // and we eventually reply with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally get
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+ //
+ // Thus if there is a <tag> and the port is 0, then the remote party
+ // is trying to send a file to us, but if the port is nonzero then
+ // we have sent out a zero port request and the remote party acked it
+ //
+
+ if((!kvi_strEqualCS(dcc->szParam3.ptr(),"0")) && dcc->szParam5.hasData())
+ {
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+ // zero port acknowledge: treat as a RECV that should look like
+ // DCC [TS]RECV <filename> <remoteip> <remoteport> <resume-filesize>
+ // but since we have stored the sharedfile with the name <tag>
+ // we do exchange the params :)
+
+ KviDccZeroPortTag * t = g_pDccBroker->findZeroPortTag(dcc->szParam5.ptr());
+ if(t)
+ {
+ dcc->szParam4.sprintf("%u",t->m_uResumePosition);
+ g_pDccBroker->removeZeroPortTag(dcc->szParam5.ptr());
+ } else {
+ // this should never happen since we always add
+ // a zero port tag for out outgoing requests
+ // but well... maybe the user did something behing our back...
+ dcc->szParam4 = "0"; // no resume possible in this case
+ }
+
+ // swap the tag and the filename (we have added a fileoffer with this tag)
+ dcc->szParam1 = dcc->szParam5;
+ dcc->szParam5 = "";
+
+ dccModuleParseDccRecv(dcc);
+ return;
+ }
+
+ // First of all we check the transfer limits
+ dcc->szParam1=dcc->pConsole->decodeText(dcc->szParam1);
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ // Then we ensure that the data that the remote end has sent are valid
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!(dcc->szParam4.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The fourth parameter should be the file size but does not appear to be an unsigned number, trying to continue","dcc"),dcc->szParam4.ptr());
+ }
+ dcc->szParam4 = __tr2qs_ctx("<unknown size>","dcc");
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off SEND
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC SEND is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam4.ptr();
+
+ if(d->szPort=="0" && dcc->szParam5.hasData())
+ {
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+ d->setZeroPortRequestTag(dcc->szParam5.ptr());
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))d->szListenIp = "0.0.0.0";
+ else d->szListenIp=QString(tmp);
+ d->szListenPort = "0"; // any port is OK
+ d->bSendRequest = true;
+ d->szLocalFileSize = d->szFileSize;
+ }
+
+ d->bActive = !d->isZeroPortRequest(); // we have to connect unless it is a zero port request
+
+ d->bResume = false;
+ d->bRecvFile = true;
+ d->bIsTdcc = bTurboExtension;
+ d->bNoAcks = d->bIsTdcc;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend);
+
+ d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(dcc->pConsole,d->szNick,d->szFileName);
+ dcc_module_set_dcc_type(d,"RECV");
+ if(KVI_OPTION_BOOL(KviOption_boolAutoAcceptIncomingAvatars))d->bAutoAccept = d->bAutoAccept || d->bIsIncomingAvatar;
+ d->triggerCreationEvent();
+
+ g_pDccBroker->recvFileManage(d);
+}
+
+static void dccModuleParseDccAccept(KviDccRequest *dcc)
+{
+ // this is usually DCC ACCEPT <filename> <port> <resumesize>
+ // but may be also
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ if(!g_pDccBroker->handleResumeAccepted(dcc->szParam1.ptr(),dcc->szParam2.ptr(),dcc->szParam4.ptr()))
+ {
+//#warning "IF KviOption_boolReplyCtcpErrmsgOnInvalidAccept..."
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Can't proceed with DCC RECV: Transfer not initiated for file %s on port %s","dcc"),dcc->szParam1.ptr(),dcc->szParam2.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ }
+}
+
+static void dccModuleParseDccResume(KviDccRequest *dcc)
+{
+ // This is usually RESUME <filename> <port> <resumesize>
+
+ // when a zero port request is initiated by us we send out
+ // DCC SEND <filename> <fakeipaddress> 0 <filesize> <tag>
+ // and if the remote party wants to resume then we get
+ // DCC RESUME <filename> 0 <resumesize> <tag>
+ // and we eventually reply with
+ // DCC ACCEPT <filename> 0 <resumesize> <tag>
+ // and we finally get
+ // DCC SEND <filename> <remoteip> <remoteport> <filesize> <tag>
+
+ bool bOk;
+ unsigned int filePos = dcc->szParam3.toUInt(&bOk);
+ if(!bOk)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,__tr2qs_ctx("Invalid resume position argument '%s'","dcc"),dcc->szParam3.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return;
+ }
+
+ if(!g_pDccBroker->handleResumeRequest(dcc,dcc->szParam1.ptr(),dcc->szParam2.ptr(),filePos,dcc->szParam4.ptr()))
+ {
+//#warning "IF KviOption_boolReplyCtcpErrmsgOnInvalidResume..."
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Can't proceed with DCC SEND: Transfer not initiated for file %s on port %s, or invalid resume size","dcc"),
+ dcc->szParam1.ptr(),dcc->szParam2.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// RECV
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRecv(KviDccRequest * dcc)
+{
+ // DCC [TS]RECV <filename> <ipaddr> <port> <resume-filesize>
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+ if(!(dcc->szParam4.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->outputNoFmt(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request has resume file size missing, assuming a resume file size of 0","dcc"));
+ }
+ dcc->szParam4 = "0";
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off RECV
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC RECV is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ // If we have a file offer for this...do it automatically
+ KviSharedFile * o = g_pSharedFilesManager->lookupSharedFile(dcc->szParam1.ptr(),dcc->ctcpMsg->pSource,0);
+ if(o)
+ {
+
+ unsigned int uResumeSize = dcc->szParam4.toUInt(); // this will NEVER fail
+ if(uResumeSize >= o->fileSize())
+ {
+ // senseless request
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Invalid RECV request: Position %u is is larger than file size","dcc"),uResumeSize);
+ dcc_module_request_error(dcc,szError.ptr());
+ return;
+ }
+
+ // ok...we have requested this send
+// #warning "Maybe remove this file offer now ?"
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->user();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam4.ptr();
+
+ //d->bResume = false; // This is actually useless
+
+ d->szLocalFileName = o->absFilePath();
+ d->szLocalFileSize.setNum(o->fileSize()); // Should we look it up again ?
+
+
+ d->bRecvFile = false;
+ d->bNoAcks = bTurboExtension;
+
+ d->bAutoAccept = true;
+ d->bIsIncomingAvatar = false;
+
+ d->bIsTdcc = bTurboExtension;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+
+ d->bOverrideMinimize = false;
+
+ // We know everything
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->bDoTimeout = true;
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+
+ d->bActive = true;
+ dcc_module_set_dcc_type(d,"SEND");
+ d->triggerCreationEvent();
+ g_pDccBroker->sendFileExecute(0,d);
+
+ return;
+
+ } else {
+
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("%Q [%Q@%Q] is ready to receive the file \"%s\"","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),
+ dcc->szParam1.ptr());
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The remote client is listening on interface %s and port %s","dcc"),dcc->szParam2.ptr(),dcc->szParam3.ptr());
+ KviStr szSwitches = "-c";
+ if(bTurboExtension)szSwitches.prepend("-t ");
+#ifdef COMPILE_SSL_SUPPORT
+ if(bSSLExtension)szSwitches.prepend("-s ");
+#endif
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Use %c\r![!dbl]dcc.send %s -i=%s -p=%s %Q\r/dcc.send %s -i=%s -p=%s %Q\r%c to send the file (or double-click on the socket)","dcc"),
+ KVI_TEXT_BOLD,
+ szSwitches.ptr(),
+ dcc->szParam2.ptr(),dcc->szParam3.ptr(),&(dcc->ctcpMsg->pSource->nick()),
+ szSwitches.ptr(),
+ dcc->szParam2.ptr(),dcc->szParam3.ptr(),&(dcc->ctcpMsg->pSource->nick()),
+ KVI_TEXT_BOLD);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// RSEND
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccRSend(KviDccRequest *dcc)
+{
+ // DCC RSEND <filename> <filesize>
+//#warning "Ignore files depending on file type ? (MediaType ?)"
+ //
+ // We have received a DCC RSEND request in the following form
+ //
+ // DCC [ST]RSEND <filename> <filesize>
+ //
+ dcc->szParam1 = dcc->pConsole->decodeText(dcc->szParam1);
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ if(!(dcc->szParam2.isUnsignedNum()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The fourth parameter should be the file size but does not appear to be an unsigned number; trying to continue","dcc"),dcc->szParam2.ptr());
+ }
+ dcc->szParam2 = __tr_ctx("<unknown size>","dcc");
+ }
+
+ if(dcc->szParam1.contains('/'))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request is broken: The filename contains path components, stripping the leading path and trying to continue","dcc"),dcc->szParam1.ptr());
+ }
+ dcc->szParam1.cutToLast('/');
+ }
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(4); // cut off SEND
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC RSEND is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+//#warning "When behind a firewall, we should reply an error message and avoid setting up the listening connection"
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ d->szIp = __tr2qs_ctx("(unknown)","dcc");
+ d->szPort = d->szIp;
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))
+ {
+ d->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("No suitable interface to listen on, trying to continue anyway...","dcc"));
+ d->szListenIp = "0.0.0.0";
+ } else
+ d->szListenIp=QString(tmp);
+
+ d->szListenPort = "0";
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szFileName = dcc->szParam1.ptr();
+ d->szFileSize = dcc->szParam2.ptr();
+ d->bActive = false; // we have to listen!
+ d->bResume = false;
+ d->bRecvFile = true; // we have to receive the file!
+
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bIsTdcc = bTurboExtension;
+ d->bSendRequest = true; // we have to send the [ST]RECV request back
+ d->bNoAcks = d->bIsTdcc;
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccSend);
+ d->bIsIncomingAvatar = g_pApp->findPendingAvatarChange(dcc->pConsole,d->szNick.utf8().data(),d->szFileName.utf8().data());
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+
+ if(KVI_OPTION_BOOL(KviOption_boolAutoAcceptIncomingAvatars))d->bAutoAccept = d->bAutoAccept || d->bIsIncomingAvatar;
+
+ dcc_module_set_dcc_type(d,"RECV");
+ d->triggerCreationEvent();
+ g_pDccBroker->recvFileManage(d);
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// GET
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccGet(KviDccRequest *dcc)
+{
+ // DCC [TS]GET <filename> [filesize]
+ // -> DCC [TS]SEND <filename> <ipaddr> <port> <filesize>
+ // ...
+ dcc->szParam1=dcc->pConsole->decodeText(dcc->szParam1);
+ bool bOk;
+ unsigned int uSize = dcc->szParam2.toUInt(&bOk);
+ if(!bOk)uSize = 0;
+
+ if(!dcc_module_check_limits(dcc))return;
+ if(!dcc_module_check_concurrent_transfers_limit(dcc))return;
+
+ KviStr szExtensions = dcc->szType;
+ szExtensions.cutRight(3); // cut off GET
+
+ bool bTurboExtension = szExtensions.contains('T',false);
+#ifdef COMPILE_SSL_SUPPORT
+ bool bSSLExtension = szExtensions.contains('S',false);
+#else //!COMPILE_SSL_SUPPORT
+ if(szExtensions.contains('S',false))
+ {
+ dcc_module_request_error(dcc,__tr2qs_ctx("This executable has been compiled without SSL support, the SSL extension to DCC GET is not available","dcc"));
+ return;
+ }
+#endif //!COMPILE_SSL_SUPPORT
+
+ KviSharedFile * o = g_pSharedFilesManager->lookupSharedFile(dcc->szParam1.ptr(),dcc->ctcpMsg->pSource,uSize);
+ if(!o)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("No file offer named '%s' (with size %s) available for %Q [%Q@%Q]","dcc"),
+ dcc->szParam1.ptr(),uSize > 0 ? dcc->szParam2.ptr() : __tr_ctx("\"any\"","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()));
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+ return;
+ }
+
+//#warning "IF NOT IGNORE DCC GET!"
+
+//#warning "CREATE IT MINIMIZED ETC..."
+//#warning "MAYBE USE A DIALOG TO ACCEPT THE REQUEST ?"
+//#warning "DO NOT ACCEPT /etc/* requests..."
+
+ if(KVI_OPTION_BOOL(KviOption_boolCantAcceptIncomingDccConnections))
+ {
+ // we have to use DCC RSEND , otherwise it will not work
+ KviStr szSubproto("RSEND");
+ szSubproto.prepend(szExtensions);
+
+
+ QString szFileName = QFileInfo(o->absFilePath()).fileName();
+ if(o->name() != szFileName)
+ {
+ // BUG
+ // If the file offer was added with a name that is senseless (like "mediaXYZ" for an *.mp3 file)
+ // then we would be going to RSEND that name here: the remote user woulnd't be
+ // able to recognize the file.
+ // Here we add another temporary offer with the right filename.
+
+ // now add a file offer , so he we will accept it automatically
+ // 120 secs is a reasonable timeout
+ QString szMask;
+ dcc->ctcpMsg->pSource->mask(szMask,KviIrcMask::NickUserHost);
+
+ KviSharedFile * pOld = o;
+ o = g_pSharedFilesManager->addSharedFile(szFileName,o->absFilePath(),szMask,120);
+ if(!o)o = pOld; // give up (FIXME: should we notify that ?)
+ }
+
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Accepting file request from %Q [%Q@%Q] for '%s' (real file: %Q), offering DCC %s since we can't accept incoming connections (user option)","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),dcc->szParam1.ptr(),
+ &(o->absFilePath()),szSubproto.ptr());
+ }
+
+ dcc->pConsole->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %u%c",
+ dcc->pConsole->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),
+ 0x01,szSubproto.ptr(),
+ dcc->pConsole->connection()->encodeText(dcc->szParam1.ptr()).data(),o->fileSize(),0x01);
+ return;
+ }
+
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szLocalFileName = o->absFilePath();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ d->bRecvFile = false;
+ dcc_fill_local_nick_user_host(d,dcc);
+
+ QString tmp;
+ if(!dcc_kvs_get_listen_ip_address(0,d->console(),tmp))
+ {
+ d->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("No suitable interface to listen on, trying to continue anyway...","dcc"));
+ d->szListenIp = "0.0.0.0";
+ } else
+ d->szListenIp=QString(tmp);
+//#warning "DO STH WITH THIS PORT (HOW TO SPECIFY IT ?)"
+ d->szListenPort = "0"; // any port is ok
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault))
+ {
+ d->szFakeIp = KVI_OPTION_STRING(KviOption_stringDefaultDccFakeAddress);
+ if(d->szFakeIp.isEmpty())KVI_OPTION_BOOL(KviOption_boolDccSendFakeAddressByDefault) = false;
+ }
+
+ d->bDoTimeout = true;
+ d->szIp = __tr2qs_ctx("(unknown)","dcc");
+ d->szPort = d->szIp;
+ d->bActive = false;
+ d->bSendRequest = true;
+ d->bIsTdcc = bTurboExtension;
+#ifdef COMPILE_SSL_SUPPORT
+ d->bIsSSL = bSSLExtension;
+#endif
+ d->bNoAcks = d->bIsTdcc;
+ d->bOverrideMinimize = false;
+
+ dcc_module_set_dcc_type(d,"SEND");
+
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("Accepting file request from %Q [%Q@%Q] for '%s' (real file: %Q), offering DCC %Q","dcc"),
+ &(dcc->ctcpMsg->pSource->nick()),
+ &(dcc->ctcpMsg->pSource->username()),
+ &(dcc->ctcpMsg->pSource->host()),
+ dcc->szParam1.ptr(),
+ &(o->absFilePath()),&(d->szType));
+ }
+ d->triggerCreationEvent();
+ g_pDccBroker->sendFileExecute(0,d);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// VOICE
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccVoice(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC VOICE request in the following form:
+ //
+ // DCC VOICE codec <ipaddress> <port> <sample-rate>
+ //
+ // This means that we're requested to setup an ACTIVE voice connection
+ // ... Easy task :)
+ //
+
+ if(!dcc_module_check_limits(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+#ifdef COMPILE_DISABLE_DCC_VOICE
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("The above request cannot be accepted: DCC VOICE support not enabled at compilation time ","dcc"));
+ return;
+ }
+#endif
+ // Actually unused parameter
+ if(!kvi_dcc_voice_is_valid_codec(dcc->szParam1.ptr()))
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCERROR,
+ __tr2qs_ctx("The above request cannot be accepted: Unsupported codec '%s'","dcc"),dcc->szParam1.ptr());
+ return;
+ }
+ }
+
+ bool bOk;
+
+ int iSampleRate = dcc->szParam4.toInt(&bOk);
+ if(!bOk)
+ {
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+ __tr2qs_ctx("The above request appears to be broken: Invalid sample-rate '%s', defaulting to 8000","dcc"),dcc->szParam4.ptr());
+ }
+ iSampleRate = 8000;
+ }
+
+
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->bActive = true; // we have to connect
+ d->bIsTdcc = false;
+ d->bNoAcks = false; // this has no meaning in voice
+ d->szCodec = dcc->szParam1;
+ d->iSampleRate = iSampleRate;
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccVoice);
+ dcc_module_set_dcc_type(d,"VOICE");
+ d->triggerCreationEvent();
+ g_pDccBroker->activeVoiceManage(d);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// CANVAS
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void dccModuleParseDccCanvas(KviDccRequest *dcc)
+{
+ //
+ // We have received a DCC CANVAS request in the following form:
+ //
+ // DCC CANVAS unused <ipaddress> <port>
+ //
+ // This means that we're requested to setup an ACTIVE canvas connection
+ // ... Easy task :)
+ //
+ if(!dcc_module_check_limits(dcc))return;
+
+ if(!dcc_module_normalize_target_data(dcc,dcc->szParam2,dcc->szParam3))return;
+
+// Actually unused parameter
+// if(!(kvi_strEqualCI("canvas",dcc->szParam1.ptr())))
+// {
+// if(!dcc->ctcpMsg->msg->haltOutput())
+// {
+// dcc->ctcpMsg->msg->console()->output(KVI_OUT_DCCMSG,
+// __tr("The above request is broken: the second parameter is '%s' and shoud be 'chat'; trying to continue"),dcc->szParam1.ptr());
+// }
+// }
+#ifdef COMPILE_DCC_CANVAS
+ KviDccDescriptor * d = new KviDccDescriptor(dcc->pConsole);
+ d->szNick = dcc->ctcpMsg->pSource->nick();
+ d->szUser = dcc->ctcpMsg->pSource->username();
+ d->szHost = dcc->ctcpMsg->pSource->host();
+ dcc_fill_local_nick_user_host(d,dcc);
+
+
+ d->szIp = dcc->szParam2.ptr();
+ d->szPort = dcc->szParam3.ptr();
+ d->bActive = true; // we have to connect
+ d->bIsTdcc = false;
+ d->bNoAcks = false; // this has no meaning in canvas
+ d->bOverrideMinimize = false;
+ d->bAutoAccept = KVI_OPTION_BOOL(KviOption_boolAutoAcceptDccCanvas);
+ dcc_module_set_dcc_type(d,"CANVAS");
+ d->triggerCreationEvent();
+ g_pDccBroker->activeCanvasManage(d);
+#endif
+}
+
+
+static void dccModuleParseDccList(KviDccRequest *dcc)
+{
+ // DCC LIST <mask> <ipaddr> <port>
+ // FIXME!
+}
+
+
+
+typedef void (*dccParseProc)(KviDccRequest *);
+typedef struct _dccParseProcEntry
+{
+ const char * type;
+ dccParseProc proc;
+} dccParseProcEntry;
+
+#define KVI_NUM_KNOWN_DCC_TYPES 27
+
+static dccParseProcEntry dccParseProcTable[KVI_NUM_KNOWN_DCC_TYPES]=
+{
+ { "CHAT" , dccModuleParseDccChat },
+ { "SCHAT" , dccModuleParseDccChat },
+ { "SEND" , dccModuleParseDccSend },
+ { "TSEND" , dccModuleParseDccSend },
+ { "SSEND" , dccModuleParseDccSend },
+ { "TSSEND" , dccModuleParseDccSend },
+ { "STSEND" , dccModuleParseDccSend },
+ { "GET" , dccModuleParseDccGet },
+ { "SGET" , dccModuleParseDccGet },
+ { "TGET" , dccModuleParseDccGet },
+ { "STGET" , dccModuleParseDccGet },
+ { "TSGET" , dccModuleParseDccGet },
+ { "LIST" , dccModuleParseDccList },
+ { "ACCEPT" , dccModuleParseDccAccept },
+ { "RESUME" , dccModuleParseDccResume },
+ { "RECV" , dccModuleParseDccRecv },
+ { "SRECV" , dccModuleParseDccRecv },
+ { "TRECV" , dccModuleParseDccRecv },
+ { "TSRECV" , dccModuleParseDccRecv },
+ { "STRECV" , dccModuleParseDccRecv },
+ { "RSEND" , dccModuleParseDccRSend },
+ { "SRSEND" , dccModuleParseDccRSend },
+ { "TRSEND" , dccModuleParseDccRSend },
+ { "STRSEND", dccModuleParseDccRSend },
+ { "TSRSEND", dccModuleParseDccRSend },
+ { "CANVAS" , dccModuleParseDccCanvas },
+ { "VOICE" , dccModuleParseDccVoice }
+};
+
+
+
+// We want C linkage on this one: we want to be able to dlsym() it with a simple name
+// FIXME: Is this portable enough ? Or is better to have a table entry ?
+
+KVIMODULEEXPORTFUNC void dccModuleCtcpDccParseRoutine(KviDccRequest *dcc)
+{
+ dcc->szType.toUpper();
+
+ for(int i=0;i<KVI_NUM_KNOWN_DCC_TYPES;i++)
+ {
+ if(kvi_strEqualCS(dccParseProcTable[i].type,dcc->szType.ptr()))
+ {
+ (dccParseProcTable[i].proc)(dcc);
+ return;
+ }
+ }
+ // ops...we don't know this dcc type
+ if(!dcc->ctcpMsg->msg->haltOutput())
+ {
+ KviStr szError(KviStr::Format,
+ __tr2qs_ctx("Unknown DCC type '%s'","dcc"),dcc->szType.ptr());
+ dcc_module_request_error(dcc,szError.ptr());
+ }
+}
diff --git a/src/modules/dcc/send.cpp b/src/modules/dcc/send.cpp
new file mode 100644
index 00000000..c6cc1182
--- /dev/null
+++ b/src/modules/dcc/send.cpp
@@ -0,0 +1,1898 @@
+//=============================================================================
+//
+// File : send.cpp
+// Creation date : Tue Sep 20 09 2000 15:14:14 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2000-2005 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "send.h"
+#include "broker.h"
+#include "marshal.h"
+#include "broker.h"
+#include "window.h"
+#include "kvi_styled_controls.h"
+
+#ifdef COMPILE_ON_WINDOWS
+ // Ugly Windoze compiler...
+ #include "dialogs.h"
+#endif
+
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_app.h"
+#include "kvi_options.h"
+#include "kvi_ircview.h"
+#include "kvi_iconmanager.h"
+#include "kvi_locale.h"
+#include "kvi_error.h"
+#include "kvi_out.h"
+#include "kvi_netutils.h"
+#include "kvi_console.h"
+#include "kvi_frame.h"
+#include "kvi_malloc.h"
+#include "kvi_memmove.h"
+#include "kvi_thread.h"
+#include "kvi_ircsocket.h"
+
+#include "kvi_mediatype.h"
+#include "kvi_socket.h"
+#include "kvi_kvs_eventtriggers.h"
+#include "kvi_parameterlist.h"
+#include "kvi_ircconnection.h"
+#include "kvi_ircconnectionuserinfo.h"
+#include "kvi_sparser.h"
+#include "kvi_kvs_script.h"
+
+#include <qevent.h>
+#include <qfile.h>
+#include <qpainter.h>
+#include <qdatetime.h>
+#include <qglobal.h>
+#include <qcheckbox.h>
+#include <qspinbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+
+#define INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS 3000
+#define INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS 3
+
+// This limit, when multiplied by INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS
+// must fit in 31 bits (0x7fffffff)! (because of data size limits)
+#define MAX_DCC_BANDWIDTH_LIMIT 0x1fffffff
+
+//#include <unistd.h> //close()
+
+// FIXME: SSL Support here!
+// FIXME: The events OnDCCConnect etc are in wrong places here...!
+
+extern KviDccBroker * g_pDccBroker;
+
+extern KVIRC_API KviMediaManager * g_pMediaManager; // kvi_app.cpp
+
+
+static KviPointerList<KviDccFileTransfer> * g_pDccFileTransfers = 0;
+static QPixmap * g_pDccFileTransferIcon = 0;
+
+//#warning "The events that have a KviStr data pointer should become real classes, that take care of deleting the data pointer!"
+//#warning "Otherwise, when left undispatched we will be leaking memory (event class destroyed but not the data ptr)"
+
+KviDccRecvThread::KviDccRecvThread(QObject * par,kvi_socket_t fd,KviDccRecvThreadOptions * opt)
+: KviDccThread(par,fd)
+{
+ m_pOpt = opt;
+ m_iAverageSpeed = -1;
+ m_iInstantSpeed = -1;
+ m_iFilePosition = 0;
+
+ m_iTotalReceivedBytes = 0;
+ m_iInstantReceivedBytes = 0;
+ m_pFile = 0;
+ m_pTimeInterval = new KviMSecTimeInterval();
+ m_uStartTime = 0;
+ m_uInstantSpeedInterval = 0;
+}
+
+KviDccRecvThread::~KviDccRecvThread()
+{
+ if(m_pOpt)delete m_pOpt;
+ if(m_pFile)delete m_pFile;
+ delete m_pTimeInterval;
+}
+
+bool KviDccRecvThread::sendAck(int filePos)
+{
+ int size = htonl(filePos);
+ if(kvi_socket_send(m_fd,(void *)(&size),4) != 4)
+ {
+ postErrorEvent(KviError_acknowledgeError);
+ return false;
+ }
+ return true;
+}
+
+void KviDccRecvThread::updateStats()
+{
+ m_uInstantSpeedInterval += m_pTimeInterval->mark();
+ unsigned long uCurTime = m_pTimeInterval->secondsCounter();
+
+ m_pMutex->lock();
+ unsigned long uElapsedTime = uCurTime - m_uStartTime;
+ if(uElapsedTime < 1)uElapsedTime = 1;
+
+ m_iFilePosition = m_pFile->at();
+ m_iAverageSpeed = m_iTotalReceivedBytes / uElapsedTime;
+
+ if(m_uInstantSpeedInterval > INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS)
+ {
+ unsigned int uMSecsOfTheNextInterval = 0;
+ if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS + (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS / 2)))
+ uMSecsOfTheNextInterval = m_uInstantSpeedInterval - INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS;
+ m_iInstantSpeed = (m_iInstantReceivedBytes * 1000) / m_uInstantSpeedInterval;
+ m_iInstantReceivedBytes = 0;
+ m_uInstantSpeedInterval = uMSecsOfTheNextInterval;
+ } else {
+ if(uElapsedTime <= INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS)
+ m_iInstantSpeed = m_iAverageSpeed;
+ }
+ m_pMutex->unlock();
+}
+
+void KviDccRecvThread::postMessageEvent(const char * m)
+{
+ KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_MESSAGE);
+ e->setData(new KviStr(m));
+ postEvent(parent(),e);
+}
+
+// FIXME: This stuff should be somewhat related to the 1448 bytes TCP basic packet size
+#define KVI_DCC_RECV_BLOCK_SIZE 8192
+#define KVI_DCC_RECV_75PERCENTOF_BLOCK_SIZE 6150
+
+void KviDccRecvThread::run()
+{
+ // take care of sleeping a bit if we can't read stuff
+ // so we don't hog the CPU too much...
+ int iFailedSelects = 0;
+ // take care of sleeping a bit if we get a lot of short reads
+ // so we don't hog the CPU too much...
+ int iShortReadQuantifier = 0;
+ // the algorithm is as follows:
+ // attempt to read KVI_DCC_RECV_BLOCK_SIZE bytes
+ // iShortReadQuantifier += ((KVI_DCC_RECV_75PERCENT_OF_BLOCK_SIZE - realReadedBytes) / 42);
+ // thus we gain points if we read less than 75% of the requested size
+ // and we loose points otherwise
+ // there are nearly 24 points per KB
+ // if(iShortReadQuantifier > 10)
+ // msleep(iShortReadQuantifier);
+ // also never sleep more than 500 msecs since it will
+ // rise our exit latency too much
+
+ m_pTimeInterval->mark();
+ m_pMutex->lock();
+ m_uStartTime = m_pTimeInterval->secondsCounter();
+ m_pMutex->unlock();
+
+ int iProbableTerminationTime = 0;
+
+ m_pFile = new QFile(QString::fromUtf8(m_pOpt->szFileName.ptr()));
+
+ if(m_pOpt->bResume)
+ {
+ if(!m_pFile->open(IO_WriteOnly | IO_Append))
+ {
+ postErrorEvent(KviError_cantOpenFileForAppending);
+ goto exit_dcc;
+ } // else pFile is already at end
+ } else {
+ if(!m_pFile->open(IO_WriteOnly))
+ {
+ postErrorEvent(KviError_cantOpenFileForWriting);
+ goto exit_dcc;
+ }
+ }
+
+ if(m_pOpt->bSendZeroAck && (!m_pOpt->bNoAcks))
+ {
+ if(!sendAck(m_pFile->at()))goto exit_dcc;
+ }
+
+ for(;;)
+ {
+ // Dequeue events
+ while(KviThreadEvent * e = dequeueEvent())
+ {
+ if(e->id() == KVI_THREAD_EVENT_TERMINATE)
+ {
+ delete e;
+ goto exit_dcc;
+ } else {
+ // Other events are senseless to us
+ delete e;
+ }
+ }
+
+ bool bCanRead;
+ bool bDummy;
+
+ if(kvi_select(m_fd,&bCanRead,&bDummy,15000))
+ {
+ // reset sleep time
+
+ if(bCanRead)
+ {
+ iFailedSelects = 0;
+
+ // Read a data block
+ char buffer[KVI_DCC_RECV_BLOCK_SIZE];
+
+ m_pMutex->lock(); // FIXME: how to remove this lock ?
+ unsigned int uMaxPossible = (m_pOpt->uMaxBandwidth < MAX_DCC_BANDWIDTH_LIMIT) ? m_pOpt->uMaxBandwidth * INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS : MAX_DCC_BANDWIDTH_LIMIT * INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS;
+ m_pMutex->unlock();
+ unsigned int uToRead = uMaxPossible > ((unsigned int)(m_iInstantReceivedBytes)) ? uMaxPossible - m_iInstantReceivedBytes : 0;
+ if(uToRead > KVI_DCC_RECV_BLOCK_SIZE)uToRead = KVI_DCC_RECV_BLOCK_SIZE;
+
+ if(uToRead > 0)
+ {
+ int readLen = kvi_socket_recv(m_fd,buffer,uToRead);
+
+ if(readLen > 0)
+ {
+ // Readed something useful...write back
+ if((m_pOpt->iTotalFileSize > -1) && ((readLen + (int)m_pFile->at()) > m_pOpt->iTotalFileSize))
+ {
+ postMessageEvent(__tr2qs_ctx("WARNING: The peer is sending garbage data past the end of the file","dcc"));
+ postMessageEvent(__tr2qs_ctx("WARNING: Ignoring data past the declared end of file and closing the connection","dcc"));
+
+ readLen = m_pOpt->iTotalFileSize - m_pFile->at();
+ if(readLen > 0)
+ {
+ if(m_pFile->writeBlock(buffer,readLen) != readLen)
+ postErrorEvent(KviError_fileIOError);
+ }
+ break;
+
+ } else {
+ if(m_pFile->writeBlock(buffer,readLen) != readLen)
+ {
+ postErrorEvent(KviError_fileIOError);
+ break;
+ }
+ }
+
+ // Update stats
+ m_iTotalReceivedBytes += readLen;
+ m_iInstantReceivedBytes += readLen;
+
+ updateStats();
+ // Now send the ack
+ if(m_pOpt->bNoAcks)
+ {
+ // No acks...
+ // Interrupt if the whole file has been received
+ if(m_pOpt->iTotalFileSize > 0)
+ {
+ if(((int)(m_pFile->at())) == m_pOpt->iTotalFileSize)
+ {
+ // Received the whole file...die
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ }
+ }
+ } else {
+ // Must send the ack... the peer must close the connection
+ if(!sendAck(m_pFile->at()))break;
+ }
+
+ // now take care of short reads
+ iShortReadQuantifier += ((KVI_DCC_RECV_75PERCENTOF_BLOCK_SIZE - readLen) / 42);
+ if(iShortReadQuantifier > 10)
+ {
+ // we're having short reads.. sleep a while
+ // but don't allow it to go too high: 0.45 sec is really a lot
+ if(iShortReadQuantifier > 500)
+ iShortReadQuantifier = 500;
+ msleep(iShortReadQuantifier);
+ } else {
+ // don't allow it to go too low
+ if(iShortReadQuantifier < -500)
+ iShortReadQuantifier = -500;
+ }
+
+ } else {
+ updateStats();
+ // Read problem...
+
+ if(readLen == 0)
+ {
+ // readed EOF..
+ if((((int)(m_pFile->at())) == m_pOpt->iTotalFileSize) || (m_pOpt->iTotalFileSize < 0))
+ {
+ // success if we got the whole file or if we don't know the file size (we trust the peer)
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ }
+ }
+ if(!handleInvalidSocketRead(readLen))break;
+ }
+ } else {
+ updateStats();
+
+ // reached the bandwidth limit: slow down a bit
+ if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS - 100))
+ msleep(100);
+ else if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS - 20))
+ msleep(20);
+ }
+ } else {
+ // Can't read stuff (can just write)
+ updateStats();
+
+ // sleep up to 300 msecs (if data arrives...we want low exit latency here)
+ if(iFailedSelects < 100)iFailedSelects++;
+ updateStats();
+ if(iFailedSelects > 3)
+ msleep(3 * iFailedSelects);
+
+ if(((int)(m_pFile->at())) == m_pOpt->iTotalFileSize)
+ {
+ // Wait for the peer to close the connection
+ if(iProbableTerminationTime == 0)
+ {
+ iProbableTerminationTime = (int)kvi_unixTime();
+ m_pFile->flush();
+ postMessageEvent(__tr2qs_ctx("Data transfer terminated, waiting 30 seconds for the peer to close the connection...","dcc"));
+ // FIXME: Close the file ?
+ } else {
+ int iDiff = (((int)kvi_unixTime()) - iProbableTerminationTime);
+ if(iDiff > 30)
+ {
+ // success if we got the whole file or if we don't know the file size (we trust the peer)
+ postMessageEvent(__tr2qs_ctx("Data transfer was terminated 30 seconds ago, closing the connection","dcc"));
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ }
+ }
+ }
+ }
+ // include the artificial delay if needed
+ if(m_pOpt->iIdleStepLengthInMSec > 0)
+ {
+ debug("LOOP: artificial delay");
+ msleep(m_pOpt->iIdleStepLengthInMSec);
+ }
+ } else {
+ // sleep up to 200 msecs (if data arrives...we want low exit latency here)
+ if(iFailedSelects < 100)iFailedSelects++;
+ updateStats();
+ if(iFailedSelects > 3)
+ msleep(2 * iFailedSelects);
+ }
+ }
+
+exit_dcc:
+ if(m_pFile)
+ {
+ m_pFile->close();
+ delete m_pFile;
+ m_pFile = 0;
+ }
+ kvi_socket_close(m_fd);
+ m_fd = KVI_INVALID_SOCKET;
+}
+
+void KviDccRecvThread::initGetInfo()
+{
+ m_pMutex->lock();
+}
+
+void KviDccRecvThread::doneGetInfo()
+{
+ m_pMutex->unlock();
+}
+
+KviDccSendThread::KviDccSendThread(QObject * par,kvi_socket_t fd,KviDccSendThreadOptions * opt)
+: KviDccThread(par,fd)
+{
+ m_pOpt = opt;
+ // stats
+ m_iAverageSpeed = -1;
+ m_iInstantSpeed = -1;
+ m_iFilePosition = 0;
+ m_iTotalSentBytes = 0;
+ m_pTimeInterval = new KviMSecTimeInterval();
+ m_uStartTime = 0;
+ m_uInstantSpeedInterval = 0;
+}
+
+KviDccSendThread::~KviDccSendThread()
+{
+ if(m_pOpt)delete m_pOpt;
+ delete m_pTimeInterval;
+}
+
+void KviDccSendThread::updateStats()
+{
+ m_uInstantSpeedInterval += m_pTimeInterval->mark();
+
+ m_pMutex->lock();
+ unsigned long uElapsedTime = m_pTimeInterval->secondsCounter() - m_uStartTime;
+ if(uElapsedTime < 1)uElapsedTime = 1;
+
+ if(m_pOpt->bNoAcks)
+ {
+ // There are no acks : the avg bandwidth is based on the sent bytes
+ m_iAverageSpeed = m_iTotalSentBytes / uElapsedTime;
+ } else {
+ // acknowledges : we compute the avg bandwidth based on the acks we receive
+ m_iAverageSpeed = (m_iAckedBytes - m_pOpt->iStartPosition) / uElapsedTime;
+ }
+
+ if(m_uInstantSpeedInterval >= INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS)
+ {
+ // we often overcount the time interval of 10-20 msecs
+ // and thus our bandwidth is used less than requested.
+ // for this reason we try to account the time in excess
+ // to the next period in order to balance the bandwidth usage.
+ unsigned long uMSecsOfNextPeriodUsed = 0;
+ if(m_uInstantSpeedInterval > INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS)
+ {
+ if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS + (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS / 2)))
+ {
+ uMSecsOfNextPeriodUsed = m_uInstantSpeedInterval - INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS;
+ m_uInstantSpeedInterval = INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS;
+ }
+ // else we have been delayed for a time comparable to a period
+ // and thus we can't recover the bandwidth... let it go as it does...
+ }
+ m_iInstantSpeed = (m_iInstantSentBytes * 1000) / m_uInstantSpeedInterval;
+ m_uInstantSpeedInterval = uMSecsOfNextPeriodUsed;
+ m_iInstantSentBytes = 0;
+ } else {
+ if(uElapsedTime <= INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS)
+ m_iInstantSpeed = m_iAverageSpeed;
+ }
+ m_pMutex->unlock();
+}
+
+void KviDccSendThread::run()
+{
+ m_pTimeInterval->mark();
+ m_pMutex->lock();
+ m_uStartTime = m_pTimeInterval->secondsCounter();
+ m_pMutex->unlock();
+
+ m_iTotalSentBytes = 0;
+ m_iInstantSentBytes = 0;
+ int iFailedSelects = 0;
+ char ackbuffer[4];
+ int iBytesInAckBuffer = 0;
+ Q_UINT32 iLastAck = 0;
+
+ if(m_pOpt->iPacketSize < 32)m_pOpt->iPacketSize = 32;
+ char * buffer = (char *)kvi_malloc(m_pOpt->iPacketSize * sizeof(char));
+
+ QFile * pFile = new QFile(QString::fromUtf8(m_pOpt->szFileName.ptr()));
+
+ if(!pFile->open(IO_ReadOnly))
+ {
+ postErrorEvent(KviError_cantOpenFileForReading);
+ goto exit_dcc;
+ }
+
+ if(pFile->size() < 1)
+ {
+ postErrorEvent(KviError_cantSendAZeroSizeFile);
+ goto exit_dcc;
+ }
+
+ if(m_pOpt->iStartPosition > 0)
+ {
+ // seek
+ if(!(pFile->at(m_pOpt->iStartPosition)))
+ {
+ postErrorEvent(KviError_fileIOError);
+ goto exit_dcc;
+ }
+ }
+
+ iLastAck = m_pOpt->iStartPosition;
+
+ for(;;)
+ {
+ // Dequeue events
+ while(KviThreadEvent * e = dequeueEvent())
+ {
+ if(e->id() == KVI_THREAD_EVENT_TERMINATE)
+ {
+ delete e;
+ goto exit_dcc;
+ } else {
+ // Other events are senseless to us
+ delete e;
+ }
+ }
+
+ bool bCanRead;
+ bool bCanWrite;
+
+ if(kvi_select(m_fd,&bCanRead,&bCanWrite,15000))
+ {
+ // reset the sleep time
+ iFailedSelects = 0;
+ if(bCanRead)
+ {
+ if(!m_pOpt->bNoAcks)
+ {
+ int iAckBytesToRead = 4 - iBytesInAckBuffer;
+ int readLen = kvi_socket_recv(m_fd,(void *)(ackbuffer + iBytesInAckBuffer),iAckBytesToRead);
+ if(readLen > 0)
+ {
+ iBytesInAckBuffer += readLen;
+ if(iBytesInAckBuffer == 4)
+ {
+ Q_UINT32 iNewAck = ntohl(*((Q_UINT32 *)ackbuffer));
+ if((iNewAck > pFile->at()) || (iNewAck < iLastAck))
+ {
+ // the peer is drunk or is trying to fool us
+ postErrorEvent(KviError_acknowledgeError);
+ break;
+ }
+ iLastAck = iNewAck;
+ iBytesInAckBuffer = 0;
+ }
+ } else {
+ if(!handleInvalidSocketRead(readLen))break;
+ }
+
+ // update stats
+ m_pMutex->lock(); // is this really necessary ?
+ m_iAckedBytes = iLastAck;
+ m_pMutex->unlock();
+
+ if(iLastAck >= pFile->size())
+ {
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ }
+ } else {
+ // No acknowledges
+ if(m_pOpt->bIsTdcc)
+ {
+ // We expect the remote end to close the connection when the whole file has been sent
+ if(pFile->atEnd())
+ {
+ int iAck;
+ int readLen = kvi_socket_recv(m_fd,(void *)&iAck,4);
+ if(readLen == 0)
+ {
+ // done...success
+ updateStats();
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ } else {
+ if(readLen < 0)
+ {
+ if(!handleInvalidSocketRead(readLen))break;
+ } else {
+ KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_MESSAGE);
+ e->setData(new KviStr(__tr2qs_ctx("WARNING: Received data in a DCC TSEND, there should be no acknowledges","dcc")));
+ postEvent(parent(),e);
+ }
+ }
+ }
+ }
+ }
+ }
+ if(bCanWrite)
+ {
+ if(!pFile->atEnd())
+ {
+ if(m_pOpt->bFastSend || m_pOpt->bNoAcks || (iLastAck == pFile->at()))
+ {
+ // maximum readable size
+ int toRead = pFile->size() - pFile->at();
+ // the max number of bytes we can send in this interval (bandwidth limit)
+ m_pMutex->lock(); // FIXME: how to remove this lock ?
+ int iMaxPossible = m_pOpt->uMaxBandwidth < MAX_DCC_BANDWIDTH_LIMIT ? m_pOpt->uMaxBandwidth * INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS : MAX_DCC_BANDWIDTH_LIMIT * INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_SECS;
+ m_pMutex->unlock();
+ if(iMaxPossible < m_iInstantSentBytes)toRead = 0; // already sent too much!
+ else {
+ iMaxPossible -= m_iInstantSentBytes;
+ if(toRead > iMaxPossible)toRead = iMaxPossible;
+ }
+ // limit to packet size
+ if(toRead > m_pOpt->iPacketSize)toRead = m_pOpt->iPacketSize;
+
+ int written = 0;
+ if(toRead > 0)
+ {
+ // read data
+ int readed = pFile->readBlock(buffer,toRead);
+ if(readed < toRead)
+ {
+ postErrorEvent(KviError_fileIOError);
+ break;
+ }
+ // send it out
+ written = kvi_socket_send(m_fd,buffer,toRead);
+ if(written < toRead)
+ {
+ if(written < 0)
+ {
+ // error ?
+ if(!handleInvalidSocketRead(written))break;
+ } else {
+ // seek back to the right position
+ pFile->at(pFile->at() - (toRead - written));
+ }
+ }
+ } else {
+ // just nothing to send out in this interval
+ // sleep a while
+ if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS - 100))
+ {
+ msleep(100);
+ } else if(m_uInstantSpeedInterval < (INSTANT_BANDWIDTH_CHECK_INTERVAL_IN_MSECS - 20))
+ {
+ msleep(20);
+ }
+ }
+
+ m_iTotalSentBytes += written;
+ m_iInstantSentBytes += written;
+ m_iFilePosition = pFile->at();
+ updateStats();
+ }
+ } else {
+ if(m_pOpt->bNoAcks && !m_pOpt->bIsTdcc)
+ {
+ // at end of the file in a blind dcc send...
+ // not in a tdcc: we can close the file...
+ updateStats();
+ KviThreadEvent * e = new KviThreadEvent(KVI_DCC_THREAD_EVENT_SUCCESS);
+ postEvent(parent(),e);
+ break;
+ } else {
+ // upload finished but we're waiting for the last ack
+ // sleep a bit: don't lag the kernie too much while waiting
+ msleep(100);
+ }
+ }
+ }
+ } else {
+ // after 2 failed selects start to sleep
+ if(iFailedSelects > 3)
+ {
+ // sleep up to 200 msecs
+ if(iFailedSelects < 100)iFailedSelects++;
+ msleep(3 * iFailedSelects);
+ } else {
+ iFailedSelects++;
+ }
+ }
+
+ // include the artificial delay if needed
+ if(m_pOpt->iIdleStepLengthInMSec > 0)
+ {
+ msleep(m_pOpt->iIdleStepLengthInMSec);
+ }
+ }
+
+exit_dcc:
+ kvi_free(buffer);
+ pFile->close();
+ delete pFile;
+ pFile = 0;
+ kvi_socket_close(m_fd);
+ m_fd = KVI_INVALID_SOCKET;
+}
+
+void KviDccSendThread::initGetInfo()
+{
+ m_pMutex->lock();
+}
+
+void KviDccSendThread::doneGetInfo()
+{
+ m_pMutex->unlock();
+}
+
+
+KviDccFileTransfer::KviDccFileTransfer(KviDccDescriptor * dcc)
+: KviFileTransfer()
+{
+ init(); // ensure we're initialized
+ g_pDccFileTransfers->append(this);
+
+ m_pResumeTimer = 0;
+ m_pBandwidthDialog = 0;
+
+ KviQString::sprintf(m_szTransferIdString,__tr2qs_ctx("TRANSFER %d","dcc"),id());
+
+ m_pDescriptor = dcc;
+ m_pDescriptor->setTransfer(this);
+
+ m_pMarshal = new KviDccMarshal(this);
+
+ connect(m_pMarshal,SIGNAL(error(int)),this,SLOT(handleMarshalError(int)));
+ connect(m_pMarshal,SIGNAL(connected()),this,SLOT(connected()));
+ connect(m_pMarshal,SIGNAL(inProgress()),this,SLOT(connectionInProgress()));
+#ifdef COMPILE_SSL_SUPPORT
+ connect(m_pMarshal,SIGNAL(startingSSLHandshake()),this,SLOT(startingSSLHandshake()));
+ connect(m_pMarshal,SIGNAL(sslError(const char *)),this,SLOT(sslError(const char *)));
+#endif
+
+ m_szDccType = dcc->bIsTdcc ? (dcc->bRecvFile ? "TRECV" : "TSEND") : (dcc->bRecvFile ? "RECV" : "SEND");
+
+ m_pSlaveRecvThread = 0;
+ m_pSlaveSendThread = 0;
+
+ m_tTransferStartTime = 0;
+ m_tTransferEndTime = 0;
+
+ m_szStatusString = __tr2qs_ctx("Setting up the connection","dcc");
+ m_eGeneralStatus = Connecting;
+
+ bool bOk;
+ m_uTotalFileSize = dcc->bRecvFile ? dcc->szFileSize.toUInt(&bOk) : dcc->szLocalFileSize.toUInt(&bOk);
+ if(!bOk)m_uTotalFileSize = 0;
+
+ if(m_pDescriptor->bRecvFile)
+ m_uMaxBandwidth = KVI_OPTION_BOOL(KviOption_boolLimitDccRecvSpeed) ? KVI_OPTION_UINT(KviOption_uintMaxDccRecvSpeed) : MAX_DCC_BANDWIDTH_LIMIT;
+ else
+ m_uMaxBandwidth = KVI_OPTION_BOOL(KviOption_boolLimitDccSendSpeed) ? KVI_OPTION_UINT(KviOption_uintMaxDccSendSpeed) : MAX_DCC_BANDWIDTH_LIMIT;
+
+ startConnection();
+}
+
+KviDccFileTransfer::~KviDccFileTransfer()
+{
+ g_pDccFileTransfers->removeRef(this);
+
+ if(m_pResumeTimer)delete m_pResumeTimer;
+ if(m_pBandwidthDialog)delete m_pBandwidthDialog;
+
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->terminate();
+ delete m_pSlaveRecvThread;
+ m_pSlaveRecvThread = 0;
+ }
+
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->terminate();
+ delete m_pSlaveSendThread;
+ m_pSlaveSendThread = 0;
+ }
+
+ KviThreadManager::killPendingEvents(this);
+
+ delete m_pDescriptor;
+ delete m_pMarshal;
+}
+
+void KviDccFileTransfer::bandwidthDialogDestroyed()
+{
+ m_pBandwidthDialog = 0;
+}
+
+KviWindow * KviDccFileTransfer::eventWindow()
+{
+ KviWindow *w = transferWindow();
+ if(w)return w;
+ return m_pDescriptor->console();
+}
+
+void KviDccFileTransfer::startConnection()
+{
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION
+ m_szStatusString = __tr2qs_ctx("Attempting a passive DCC %1 connection","dcc").arg(m_szDccType.ptr());
+ outputAndLog(m_szStatusString);
+ } else {
+ // ACTIVE CONNECTION
+ m_szStatusString = __tr2qs_ctx("Attempting an active DCC %1 connection","dcc").arg(m_szDccType.ptr());
+ outputAndLog(m_szStatusString);
+ }
+
+
+ if(m_pDescriptor->bResume && m_pDescriptor->bRecvFile)
+ {
+ QString fName;
+ KviServerParser::encodeCtcpParameter(m_pDescriptor->szFileName.utf8().data(),fName);
+ if(m_pDescriptor->isZeroPortRequest())
+ {
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC RESUME %s %s %s %s%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,
+ m_pDescriptor->console()->connection()->encodeText(fName).data(),
+ m_pDescriptor->szPort.utf8().data(),
+ m_pDescriptor->szLocalFileSize.utf8().data(),
+ m_pDescriptor->zeroPortRequestTag(),0x01);
+ } else {
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC RESUME %s %s %s%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,
+ m_pDescriptor->console()->connection()->encodeText(fName).data(),
+ m_pDescriptor->szPort.utf8().data(),
+ m_pDescriptor->szLocalFileSize.utf8().data(),0x01);
+ }
+ m_szStatusString = __tr2qs_ctx("Sent DCC RESUME request to %1, waiting for ACCEPT","dcc").arg(m_pDescriptor->szNick);
+ outputAndLog(m_szStatusString);
+
+ // setup the resume timer: we don't want to wait forever
+
+ if(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) < 5)
+ KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) = 5;
+
+ if(m_pResumeTimer)delete m_pResumeTimer;
+ m_pResumeTimer = new QTimer(this);
+ connect(m_pResumeTimer,SIGNAL(timeout()),this,SLOT(resumeTimedOut()));
+ m_pResumeTimer->start(KVI_OPTION_UINT(KviOption_uintDccSocketTimeout) * 1000,true);
+ } else {
+ listenOrConnect();
+ }
+
+ displayUpdate();
+}
+
+void KviDccFileTransfer::listenOrConnect()
+{
+ if(!(m_pDescriptor->bActive))
+ {
+ int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ } else {
+ int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ }
+
+ displayUpdate();
+}
+
+void KviDccFileTransfer::resumeTimedOut()
+{
+ if(m_pResumeTimer)
+ {
+ delete m_pResumeTimer;
+ m_pResumeTimer = 0;
+ }
+ handleMarshalError(KviError_connectionTimedOut);
+}
+
+KviWindow * KviDccFileTransfer::dccMarshalOutputWindow()
+{
+ return transferWindow();
+}
+
+const char * KviDccFileTransfer::dccMarshalOutputContextString()
+{
+ return m_szTransferIdString.utf8().data();
+}
+
+void KviDccFileTransfer::die()
+{
+ delete this;
+}
+
+QString KviDccFileTransfer::localFileName()
+{
+ return m_pDescriptor->szLocalFileName;
+}
+
+void KviDccFileTransfer::abort()
+{
+ if(m_pSlaveRecvThread)m_pSlaveRecvThread->terminate();
+ if(m_pSlaveSendThread)m_pSlaveSendThread->terminate();
+ if(m_pMarshal)m_pMarshal->abort();
+
+ if(m_pDescriptor->bRecvFile)
+ g_pApp->fileDownloadTerminated(false,m_pDescriptor->szFileName.utf8().data(),m_pDescriptor->szLocalFileName.utf8().data(),m_pDescriptor->szNick.utf8().data(),__tr_ctx("Aborted","dcc"));
+
+ KviStr tmp;
+
+ if(m_pSlaveRecvThread)tmp.setNum(m_pSlaveRecvThread->receivedBytes());
+ else if(m_pSlaveSendThread)tmp.setNum(m_pSlaveSendThread->sentBytes());
+ else tmp = '0';
+
+ m_eGeneralStatus = Failure;
+ m_tTransferEndTime = kvi_unixTime();
+ m_szStatusString = __tr2qs_ctx("Transfer failed: ","dcc");
+ m_szStatusString += __tr2qs_ctx("Aborted","dcc");
+
+ KVS_TRIGGER_EVENT_3(KviEvent_OnDCCFileTransferFailed,eventWindow(),QString("Aborted by user"),QString(tmp.ptr()),m_pDescriptor->idString());
+
+ outputAndLog(KVI_OUT_DCCERROR,m_szStatusString);
+ displayUpdate();
+}
+
+
+void KviDccFileTransfer::fillContextPopup(KviTalPopupMenu * m,int column)
+{
+ m->insertItem(__tr2qs_ctx("Configure Bandwidth...","dcc"),this,SLOT(configureBandwidth()));
+ m->insertSeparator();
+ m->insertItem(__tr2qs_ctx("Resend DCC","dcc"),this,SLOT(retryDCC()));
+ m->insertItem(__tr2qs_ctx("Resend TDCC","dcc"),this,SLOT(retryTDCC()));
+ m->insertItem(__tr2qs_ctx("Resend RevDCC","dcc"),this,SLOT(retryRevDCC()));
+ /* FIX ME credo che il problema sia che se riavvio un trasferimento, a sua volta gia'
+ avviato, questo non ha irc contex, perche' la finestra "in cui e' nato"e' sta
+ quella della dcc. Conservarsi l'id della finestra? */
+ int id = m->insertItem(__tr2qs_ctx("Abort","dcc"),this,SLOT(abort()));
+ if(!active())m->setItemEnabled(id,false);
+}
+
+void KviDccFileTransfer::configureBandwidth()
+{
+ if(m_pBandwidthDialog)return;
+ m_pBandwidthDialog = new KviDccFileTransferBandwidthDialog(g_pFrame,this);
+ connect(m_pBandwidthDialog,SIGNAL(destroyed()),this,SLOT(bandwidthDialogDestroyed()));
+ m_pBandwidthDialog->setModal(true);
+ m_pBandwidthDialog->show();
+}
+
+void KviDccFileTransfer::retryDCC()
+{
+ abort();
+ QString szRemoteNick = m_pDescriptor->remoteNick();
+ QString szFileName = m_pDescriptor->localFileName();
+ QString szId;
+ szId.setNum(m_pDescriptor->id());
+ QString szCommand = "dcc.send -r=$console($dcc.irccontext(" + szId + ")) " + szRemoteNick + " " + "\"" + szFileName + "\"";
+ KviKvsScript::run(szCommand,g_pActiveWindow);
+}
+
+void KviDccFileTransfer::retryTDCC()
+{
+ abort();
+ QString szRemoteNick = m_pDescriptor->remoteNick();
+ QString szFileName = m_pDescriptor->localFileName();
+ QString szId;
+ szId.setNum(m_pDescriptor->id());
+ QString szCommand = "dcc.send -r=$console($dcc.irccontext(" + szId + ")) -t " + szRemoteNick + " " + "\"" + szFileName + "\"";
+ KviKvsScript::run(szCommand,g_pActiveWindow);
+}
+void KviDccFileTransfer::retryRevDCC()
+{
+ abort();
+ QString szRemoteNick = m_pDescriptor->remoteNick();
+ QString szFileName = m_pDescriptor->localFileName();
+ QString szId;
+ szId.setNum(m_pDescriptor->id());
+ QString szCommand = "dcc.rsend -z -r=$console($dcc.irccontext(" + szId + ")) " + szRemoteNick + " " + "\"" + szFileName + "\"";
+ KviKvsScript::run(szCommand,g_pActiveWindow);
+}
+
+void KviDccFileTransfer::fillStatusString(QString &szBuffer)
+{
+ switch(m_eGeneralStatus)
+ {
+ case Connecting:
+ szBuffer = "connecting";
+ break;
+ case Transferring:
+ szBuffer = "transferring";
+ break;
+ case Failure:
+ szBuffer = "failure";
+ break;
+ case Success:
+ szBuffer = "success";
+ break;
+ default:
+ szBuffer = "unknown";
+ break;
+ }
+}
+
+bool KviDccFileTransfer::active()
+{
+ return ((m_eGeneralStatus == Connecting) || (m_eGeneralStatus == Transferring));
+}
+
+int KviDccFileTransfer::bandwidthLimit()
+{
+ int iLimit = m_uMaxBandwidth; // we have the cached value anyway...
+ if(m_pDescriptor->bRecvFile)
+ {
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->initGetInfo();
+ iLimit = (int)m_pSlaveRecvThread->bandwidthLimit();
+ m_pSlaveRecvThread->doneGetInfo();
+ if(iLimit < 0)iLimit = MAX_DCC_BANDWIDTH_LIMIT;
+ }
+ } else {
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->initGetInfo();
+ iLimit = (int)m_pSlaveSendThread->bandwidthLimit();
+ m_pSlaveSendThread->doneGetInfo();
+ if(iLimit < 0)iLimit = MAX_DCC_BANDWIDTH_LIMIT;
+ }
+ }
+ return iLimit;
+}
+
+void KviDccFileTransfer::setBandwidthLimit(int iVal)
+{
+ if(iVal < 0)iVal = MAX_DCC_BANDWIDTH_LIMIT;
+ if(iVal > MAX_DCC_BANDWIDTH_LIMIT)iVal = MAX_DCC_BANDWIDTH_LIMIT;
+ m_uMaxBandwidth = iVal;
+ if(m_pDescriptor->bRecvFile)
+ {
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->initGetInfo();
+ m_pSlaveRecvThread->setBandwidthLimit(iVal);
+ m_pSlaveRecvThread->doneGetInfo();
+ }
+ } else {
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->initGetInfo();
+ m_pSlaveSendThread->setBandwidthLimit(iVal);
+ m_pSlaveSendThread->doneGetInfo();
+ }
+ }
+}
+
+unsigned int KviDccFileTransfer::averageSpeed()
+{
+ unsigned int iAvgBandwidth = 0;
+ if(m_pDescriptor->bRecvFile)
+ {
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->initGetInfo();
+ iAvgBandwidth = (unsigned int)m_pSlaveRecvThread->averageSpeed();
+ m_pSlaveRecvThread->doneGetInfo();
+ }
+ } else {
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->initGetInfo();
+ iAvgBandwidth = (unsigned int)m_pSlaveSendThread->averageSpeed();
+ m_pSlaveSendThread->doneGetInfo();
+ }
+ }
+ return iAvgBandwidth;
+}
+
+unsigned int KviDccFileTransfer::transferredBytes()
+{
+ unsigned int uTransferred = 0;
+ if(m_pDescriptor->bRecvFile)
+ {
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->initGetInfo();
+ uTransferred = m_pSlaveRecvThread->filePosition();
+ m_pSlaveRecvThread->doneGetInfo();
+ }
+ } else {
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->initGetInfo();
+ uTransferred = m_pSlaveSendThread->filePosition();
+ m_pSlaveSendThread->doneGetInfo();
+ }
+ }
+ return uTransferred;
+}
+
+void KviDccFileTransfer::displayPaint(QPainter * p,int column,int width,int height)
+{
+
+ QString txt;
+ bool bIsTerminated = ((m_eGeneralStatus == Success) || (m_eGeneralStatus == Failure));
+
+ switch(column)
+ {
+ case COLUMN_TRANSFERTYPE:
+ {
+ int xoffset = 0;
+ int yoffset = 0;
+ if(m_pDescriptor->bRecvFile)yoffset = 64;
+ switch(m_eGeneralStatus)
+ {
+ case Connecting: xoffset = 0; break;
+ case Transferring: xoffset = 48; break;
+ case Success: xoffset = 96; break;
+ case Failure: xoffset = 144; break;
+ }
+ p->drawPixmap(3,3,*g_pDccFileTransferIcon,xoffset,yoffset,48,64);
+ }
+ break;
+ case COLUMN_FILEINFO:
+ {
+
+ QFontMetrics fm(p->font());
+
+ QString szFrom = __tr2qs_ctx("From: ","dcc");
+ QString szTo = __tr2qs_ctx("To: ","dcc");
+
+ int daW1 = fm.width(szFrom);
+ int daW2 = fm.width(szTo);
+ if(daW1 < daW2)daW1 = daW2;
+ int iLineSpacing = fm.lineSpacing();
+
+ int iY = 4;
+
+ p->setPen(Qt::black);
+
+ KviStr szRemote(KviStr::Format,"dcc://%s@%s:%s/%s",m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),
+ m_pDescriptor->szFileName.utf8().data());
+
+ p->drawText(4 + daW1,iY,width - (8 + daW1),height - 8,Qt::AlignTop | Qt::AlignLeft,
+ m_pDescriptor->bRecvFile ? szRemote.ptr() : m_pDescriptor->szLocalFileName.utf8().data());
+ iY += iLineSpacing;
+
+ p->drawText(4 + daW1,iY,width - (8 + daW1),height - 8,Qt::AlignTop | Qt::AlignLeft,
+ m_pDescriptor->bRecvFile ? m_pDescriptor->szLocalFileName.utf8().data() : szRemote.ptr());
+ iY += iLineSpacing;
+
+
+ p->setPen(Qt::darkGray);
+
+ p->drawText(4,4,width - 8,height - 8,Qt::AlignTop | Qt::AlignLeft,szFrom);
+ p->drawText(4,4 + iLineSpacing,width - 8,height - 8,Qt::AlignTop | Qt::AlignLeft,szTo);
+
+
+ p->setPen(QColor(180,180,200));
+
+ iLineSpacing += 2;
+
+ p->drawRect(4,height - (iLineSpacing + 4),width - 8,iLineSpacing);
+ p->fillRect(5,height - (iLineSpacing + 3),width - 10,iLineSpacing - 2,bIsTerminated ? QColor(210,210,210) : QColor(190,190,240));
+
+ p->setPen(Qt::black);
+
+ p->drawText(7,height - (iLineSpacing + 4),width - 14,iLineSpacing,Qt::AlignVCenter | Qt::AlignLeft,m_szStatusString);
+
+ }
+ break;
+ case COLUMN_PROGRESS:
+ {
+
+ QFontMetrics fm(p->font());
+
+ int iW = width - 8;
+ int iAvgBandwidth = -1;
+ int iInstantSpeed = -1;
+ int iAckedBytes = -1;
+
+ int iEta = -1;
+
+ unsigned int uTransferred = 0;
+
+ if(m_pDescriptor->bRecvFile)
+ {
+ if(m_pSlaveRecvThread)
+ {
+ m_pSlaveRecvThread->initGetInfo();
+ iAvgBandwidth = m_pSlaveRecvThread->averageSpeed();
+ iInstantSpeed = m_pSlaveRecvThread->instantSpeed();
+ uTransferred = m_pSlaveRecvThread->filePosition();
+ m_pSlaveRecvThread->doneGetInfo();
+ }
+ } else {
+ if(m_pSlaveSendThread)
+ {
+ m_pSlaveSendThread->initGetInfo();
+ iAvgBandwidth = m_pSlaveSendThread->averageSpeed();
+ iInstantSpeed = m_pSlaveSendThread->instantSpeed();
+ uTransferred = m_pSlaveSendThread->filePosition();
+ iAckedBytes = m_pSlaveSendThread->ackedBytes();
+ m_pSlaveSendThread->doneGetInfo();
+ }
+ }
+
+ p->setPen(bIsTerminated ? Qt::lightGray : QColor(210,210,240));
+ p->drawRect(4,4,iW,12);
+
+ iW -= 2;
+
+ if(m_uTotalFileSize > 0)
+ {
+ if(iAvgBandwidth > 0)
+ {
+ unsigned int uRemaining = m_uTotalFileSize - uTransferred;
+ iEta = uRemaining / iAvgBandwidth;
+ }
+
+ if(!m_pDescriptor->bNoAcks && (iAckedBytes > 0) && (iAckedBytes < ((int)(uTransferred))))
+ {
+ // we are sending a file and are getting acks
+
+ double dPerc1 = (double)(((double)uTransferred) * 100.0) / (double)m_uTotalFileSize;
+ int iL1 = (int) ((((double)iW) * dPerc1) / 100.0);
+ double dPerc2 = (double)(((double)iAckedBytes) * 100.0) / (double)m_uTotalFileSize;
+ int iL2 = (int) ((((double)iW) * dPerc2) / 100.0);
+ int iW2 = iL1 - iL2;
+ if(iW2 > 0)p->fillRect(5 + iL2,5,iW2,10,bIsTerminated ? QColor(150,130,110) : QColor(220,170,100));
+ p->fillRect(5,5,iL2,10,bIsTerminated ? QColor(140,110,110) : QColor(200,100,100));
+
+ txt = QString(__tr2qs_ctx("%1 of %2 (%3%)","dcc")).arg(KviQString::makeSizeReadable(iAckedBytes)).arg(KviQString::makeSizeReadable(m_uTotalFileSize)).arg(dPerc2,0,'f',2);
+ } else {
+ // we are receiving a file or not sending acks
+ double dPerc = (double)(((double)uTransferred) * 100.0) / (double)m_uTotalFileSize;
+ int iL = (int) ((((double)iW) * dPerc) / 100.0);
+ p->fillRect(5,5,iL,10,bIsTerminated ? QColor(140,110,110) : QColor(200,100,100));
+
+ txt = QString(__tr2qs_ctx("%1 of %2 (%3%)","dcc")).arg(KviQString::makeSizeReadable(uTransferred)).arg(KviQString::makeSizeReadable(m_uTotalFileSize)).arg(dPerc,0,'f',2);
+ }
+
+ } else {
+ txt = QString(__tr2qs_ctx("%1","dcc")).arg(KviQString::makeSizeReadable(uTransferred));
+ }
+
+ p->setPen(Qt::black);
+
+ p->drawText(4,19,width - 8,height - 8,Qt::AlignTop | Qt::AlignLeft,txt);
+
+ int iLeftHalf = (iW - 2) / 2;
+ int iRightHalf = iW - (iLeftHalf + 1);
+ int iLineSpacing = fm.lineSpacing() + 2;
+
+ if(!bIsTerminated)
+ {
+ txt = __tr2qs_ctx("Spd:","dcc");
+ txt += " ";
+ if(iInstantSpeed >= 0)
+ {
+ QString tmpisp;
+ KviNetUtils::formatNetworkBandwidthString(tmpisp,iInstantSpeed);
+ txt += tmpisp;
+ } else {
+ txt += "? B/s";
+ }
+ txt += " [";
+ } else {
+ txt = "";
+ }
+
+ txt += __tr2qs_ctx("Avg:","dcc");
+ txt += " ";
+ if(iAvgBandwidth >= 0)
+ {
+ QString tmpspd;
+ KviNetUtils::formatNetworkBandwidthString(tmpspd,iAvgBandwidth);
+ txt += tmpspd;
+ } else {
+ txt += "? B/s";
+ }
+
+ if(!bIsTerminated)
+ {
+ txt += "]";
+ }
+
+ int iDaH = height - (iLineSpacing + 4);
+
+ p->setPen(QColor(180,180,200));
+ p->drawRect(4,iDaH,iLeftHalf,iLineSpacing);
+ p->fillRect(5,iDaH + 1,iLeftHalf - 2,iLineSpacing - 2,bIsTerminated ? QColor(210,210,210) : QColor(190,190,240));
+ p->setPen(bIsTerminated ? Qt::darkGray : Qt::black);
+ p->drawText(6,iDaH,iLeftHalf - 4,iLineSpacing,Qt::AlignLeft | Qt::AlignVCenter,txt);
+
+ if(bIsTerminated)
+ {
+ if((m_tTransferStartTime != 0) && (m_tTransferEndTime != 0))
+ {
+ QString tot = KviTimeUtils::formatTimeInterval(kvi_timeSpan(m_tTransferEndTime,m_tTransferStartTime),KviTimeUtils::NoLeadingEmptyIntervals | KviTimeUtils::NoLeadingZeroes);
+ txt = "TOT: ";
+ txt += tot;
+ } else {
+ txt = "";
+ }
+ } else {
+ if(iEta >= 0)
+ {
+ QString eta = KviTimeUtils::formatTimeInterval(iEta,KviTimeUtils::NoLeadingEmptyIntervals | KviTimeUtils::NoLeadingZeroes);
+ txt = "ETA: ";
+ txt += eta;
+ } else {
+ txt = "ETA: ?";
+ }
+ }
+
+ p->setPen(QColor(180,180,200));
+ p->drawRect(width - (4 + iRightHalf),iDaH,iRightHalf,iLineSpacing);
+ p->fillRect(width - (3 + iRightHalf),iDaH + 1,iRightHalf - 2,iLineSpacing - 2,bIsTerminated ? QColor(210,210,210) : QColor(190,190,240));
+ p->setPen(bIsTerminated ? Qt::darkGray : Qt::black);
+ p->drawText(width - (2 + iRightHalf),iDaH,iRightHalf - 4,iLineSpacing,Qt::AlignLeft | Qt::AlignVCenter,txt);
+
+ }
+ break;
+ }
+
+}
+
+int KviDccFileTransfer::displayHeight(int iLineSpacing)
+{
+ int iH = (iLineSpacing * 3) + 10;
+ return iH >= 70 ? iH : 70;
+}
+
+QString KviDccFileTransfer::tipText()
+{
+
+ QString s;
+
+ s = QString("<table><tr><td bgcolor=\"#000000\"><font color=\"#FFFFFF\"><b>DCC %1 (ID %2)</b></font></td></tr>").arg(m_szDccType.ptr()).arg(id());
+
+ s += "<tr><td bgcolor=\"#404040\"><font color=\"#FFFFFF\">";
+ s += __tr2qs_ctx("Transfer Log","dcc");
+ s += "</font></td></tr>";
+ s += "<tr><td bgcolor=\"#C0C0C0\">";
+ s += m_szTransferLog;
+ s += "</td></tr>";
+ s += "<table>";
+
+ return s;
+}
+
+void KviDccFileTransfer::init()
+{
+ if(g_pDccFileTransfers)return;
+ g_pDccFileTransfers = new KviPointerList<KviDccFileTransfer>;
+ g_pDccFileTransfers->setAutoDelete(false);
+
+ QPixmap * pix = g_pIconManager->getImage("kvi_dccfiletransfericons.png");
+ if(pix)g_pDccFileTransferIcon = new QPixmap(*pix);
+ else g_pDccFileTransferIcon = new QPixmap(192,128);
+}
+
+void KviDccFileTransfer::done()
+{
+ if(!g_pDccFileTransfers)return;
+ while(KviDccFileTransfer * t = g_pDccFileTransfers->first())
+ delete t;
+ delete g_pDccFileTransfers;
+ g_pDccFileTransfers = 0;
+ delete g_pDccFileTransferIcon;
+ g_pDccFileTransferIcon = 0;
+}
+
+unsigned int KviDccFileTransfer::transferCount()
+{
+ if(!g_pDccFileTransfers)return 0;
+ return g_pDccFileTransfers->count();
+}
+
+KviDccFileTransfer * KviDccFileTransfer::nonFailedTransferWithLocalFileName(const QString &szLocalFileName)
+{
+ if(!g_pDccFileTransfers)return 0;
+ for(KviDccFileTransfer * t = g_pDccFileTransfers->first();t;t = g_pDccFileTransfers->next())
+ {
+#ifdef COMPILE_ON_WINDOWS
+ // on windows the file names are case insensitive
+ if(t->localFileName().lower() == szLocalFileName.lower())
+#else
+ if(t->localFileName() == szLocalFileName)
+#endif
+ {
+ if(t->m_eGeneralStatus != Failure)
+ return t;
+ }
+ }
+ return 0;
+}
+
+
+unsigned int KviDccFileTransfer::runningTransfersCount()
+{
+ if(!g_pDccFileTransfers)return 0;
+ unsigned int cnt = 0;
+ for(KviDccFileTransfer * t = g_pDccFileTransfers->first();t;t = g_pDccFileTransfers->next())
+ {
+ if(t->active())cnt++;
+ }
+ return cnt;
+}
+
+bool KviDccFileTransfer::handleResumeAccepted(const char * filename,const char * port,const char * szZeroPortTag)
+{
+ if(!g_pDccFileTransfers)return false;
+
+ for(KviDccFileTransfer * t = g_pDccFileTransfers->first();t;t = g_pDccFileTransfers->next())
+ {
+ if(t->resumeAccepted(filename,port,szZeroPortTag))return true;
+ }
+
+ return false;
+}
+
+bool KviDccFileTransfer::handleResumeRequest(const char * filename,const char * port,unsigned int filePos)
+{
+ if(!g_pDccFileTransfers)return false;
+
+ for(KviDccFileTransfer * t = g_pDccFileTransfers->first();t;t = g_pDccFileTransfers->next())
+ {
+ if(t->doResume(filename,port,filePos))return true;
+ }
+
+ return false;
+}
+
+void KviDccFileTransfer::outputAndLog(const QString &s)
+{
+ KviWindow * out = transferWindow();
+ addToTransferLog(s);
+ if(out)out->output(KVI_OUT_DCCMSG,"[%Q]: %Q",&m_szTransferIdString,&s);
+}
+
+void KviDccFileTransfer::outputAndLog(int msgtype,const QString &s)
+{
+ KviWindow * out = transferWindow();
+ addToTransferLog(s);
+ if(out)out->output(msgtype,"[%Q]: %Q",&m_szTransferIdString,&s);
+}
+
+
+void KviDccFileTransfer::addToTransferLog(const QString &s)
+{
+ QDateTime dt = QDateTime::currentDateTime();
+ QString ts;
+ ts.sprintf("[%4d.%2d.%2d %2d:%2d:%2d] ",dt.date().year(),dt.date().month(),dt.date().day(),dt.time().hour(),dt.time().minute(),dt.time().second());
+ m_szTransferLog += ts+s;
+ m_szTransferLog += "<br>";
+}
+
+
+void KviDccFileTransfer::connectionInProgress()
+{
+ if(m_pDescriptor->bActive)
+ {
+ // ACTIVE CONNECTION
+// if((kvi_strEqualCS(m_szDccType.ptr(), "RECV")) || (kvi_strEqualCS(m_szDccType.ptr(),"TRECV")))
+// {
+// // FIXME: that's not true!... we're NOT connected here
+// if(TRIGGER_EVENT_5PARAM_RETVALUE(KviEvent_OnDCCGetConnected,this,m_pDescriptor->szPort.ptr(),m_pDescriptor->szFileName.ptr(),m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),m_pDescriptor->szHost.ptr()));
+// } else {
+// if(TRIGGER_EVENT_5PARAM_RETVALUE(KviEvent_OnDCCSendConnected,this,m_pDescriptor->szPort.ptr(),m_pDescriptor->szFileName.ptr(),m_pDescriptor->szNick.ptr(),m_pDescriptor->szUser.ptr(),m_pDescriptor->szHost.ptr()));
+// }
+//
+ m_szStatusString = __tr2qs_ctx("Contacting host %1 on port %2","dcc").arg(m_pDescriptor->szIp).arg(m_pDescriptor->szPort);
+ outputAndLog(m_szStatusString);
+ displayUpdate();
+ return;
+ }
+
+ // PASSIVE CONNECTION
+ m_szStatusString = __tr2qs_ctx("Listening on interface %1 port %2","dcc").arg(m_pMarshal->localIp()).arg(m_pMarshal->localPort());
+ outputAndLog(m_szStatusString);
+
+ if(m_pDescriptor->bSendRequest)
+ {
+ QString ip;
+ if(!m_pDescriptor->szFakeIp.isEmpty())
+ {
+ ip = m_pDescriptor->szFakeIp;
+ } else {
+ ip = m_pDescriptor->szListenIp;
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccGuessIpFromServerWhenLocalIsUnroutable))
+ {
+ if(!KviNetUtils::isRoutableIpString(ip))
+ {
+ // try to get the IP that the IRC server can see
+ if(m_pDescriptor->console())
+ {
+ QString tmp = m_pDescriptor->console()->connection() ? m_pDescriptor->console()->connection()->userInfo()->hostIp() : "";
+ if(!tmp.isEmpty())
+ {
+ ip = tmp;
+ outputAndLog(__tr2qs_ctx("The local IP address is private, determining from IRC server: %1","dcc").arg(ip));
+ } else {
+ outputAndLog(__tr2qs_ctx("The local IP address is private, but unable to determine it from the IRC server","dcc"));
+ }
+ } else {
+ outputAndLog(__tr2qs_ctx("The local IP address is private, but have no IRC server to determine it from","dcc"));
+ }
+ }
+ }
+ }
+
+ KviStr port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : m_pMarshal->localPort();
+ //#warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned nuumber)"
+ struct in_addr a;
+ if(KviNetUtils::stringIpToBinaryIp(ip,&a))ip.setNum(htonl(a.s_addr));
+
+ QString tmp = m_pDescriptor->szFileName;
+ // just to be sure
+ KviQString::cutToLast(tmp,'/');
+ KviQString::cutToLast(tmp,'\\');
+
+ QString fName;
+
+ // BUG-TO-BUG mIrc compatibility
+ if(KVI_OPTION_BOOL(KviOption_boolDCCFileTransferReplaceOutgoingSpacesWithUnderscores))
+ tmp.replace(" ","_");
+
+ KviServerParser::encodeCtcpParameter(tmp.utf8().data(),fName);
+ // Zero port requests want DCC SEND as back-request
+ KviStr szReq;
+
+ if(m_pDescriptor->isZeroPortRequest())
+ {
+ szReq = "SEND";
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %s %s %s %s%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,
+ m_pDescriptor->console()->connection()->encodeText(szReq.ptr()).data(),
+ m_pDescriptor->console()->connection()->encodeText(fName).data(),
+ ip.utf8().data(),port.ptr(),
+ m_pDescriptor->szFileSize.utf8().data(),m_pDescriptor->zeroPortRequestTag(),0x01);
+ } else {
+ szReq = m_szDccType;
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %s %s %Q%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,
+ m_pDescriptor->console()->connection()->encodeText(szReq.ptr()).data(),
+ m_pDescriptor->console()->connection()->encodeText(fName).data(),
+ ip.utf8().data(),port.ptr(),
+ &(m_pDescriptor->szLocalFileSize),0x01);
+ }
+ outputAndLog(__tr2qs_ctx("Sent DCC %1 request to %2, waiting for remote client to connect...","dcc").arg(szReq.ptr()).arg(m_pDescriptor->szNick));
+ } else {
+ outputAndLog(__tr2qs_ctx("DCC %1 request not sent, awaiting manual connection","dcc").arg(m_szDccType.ptr()));
+ }
+
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCFileTransferConnectionInProgress,eventWindow(),m_pDescriptor->idString());
+
+ displayUpdate();
+}
+
+void KviDccFileTransfer::startingSSLHandshake()
+{
+#ifdef COMPILE_SSL_SUPPORT
+ outputAndLog(KVI_OUT_SSL,__tr2qs_ctx("Low-level transport connection established","dcc"));
+ outputAndLog(KVI_OUT_SSL,__tr2qs_ctx("Starting Secure Socket Layer handshake","dcc"));
+#endif
+}
+
+void KviDccFileTransfer::sslError(const char * msg)
+{
+#ifdef COMPILE_SSL_SUPPORT
+ outputAndLog(KVI_OUT_DCCERROR,__tr2qs_ctx("[SSL ERROR]: %1","dcc").arg(msg));
+#endif
+}
+
+
+
+
+bool KviDccFileTransfer::event(QEvent *e)
+{
+ if(e->type() == KVI_THREAD_EVENT)
+ {
+ switch(((KviThreadEvent *)e)->id())
+ {
+ case KVI_DCC_THREAD_EVENT_ERROR:
+ {
+ int * err = ((KviThreadDataEvent<int> *)e)->getData();
+ QString szErrorString = KviError::getDescription(*err);
+ delete err;
+ if(m_pDescriptor->bRecvFile)
+ g_pApp->fileDownloadTerminated(false,m_pDescriptor->szFileName.utf8().data(),m_pDescriptor->szLocalFileName.utf8().data(),m_pDescriptor->szNick.utf8().data(),szErrorString.utf8().data());
+
+ m_szStatusString = __tr2qs_ctx("Transfer failed: ","dcc");
+ m_szStatusString += szErrorString;
+ m_eGeneralStatus = Failure;
+ m_tTransferEndTime = kvi_unixTime();
+
+ KVS_TRIGGER_EVENT_3(KviEvent_OnDCCFileTransferFailed,
+ eventWindow(),
+ szErrorString,
+ (kvs_int_t)(m_pSlaveRecvThread ? m_pSlaveRecvThread->receivedBytes() : m_pSlaveSendThread->sentBytes()),
+ m_pDescriptor->idString());
+
+ outputAndLog(KVI_OUT_DCCERROR,m_szStatusString);
+ displayUpdate();
+ return true;
+ }
+ break;
+ case KVI_DCC_THREAD_EVENT_SUCCESS:
+ {
+ // FIXME: for >= 3.2.0 change this text to
+ // File Upload/Download terminated, or something like this
+ if(KVI_OPTION_BOOL(KviOption_boolNotifyDccSendSuccessInConsole))
+ {
+ KviConsole *c;
+ if(!g_pApp->windowExists(m_pDescriptor->console())) c=g_pApp->activeConsole();
+ else c=m_pDescriptor->console();
+ c->output(KVI_OUT_DCCMSG,__tr2qs_ctx("DCC %s transfer with %Q@%Q:%Q completed: \r![!dbl]play $0\r%s\r","dcc"),
+ m_pDescriptor->bIsTdcc ? (m_pDescriptor->bRecvFile ? "TRECV" : "TSEND") : (m_pDescriptor->bRecvFile ? "RECV" : "SEND"),
+ &(m_pDescriptor->szNick),&(m_pDescriptor->szIp),&(m_pDescriptor->szPort),
+ &(m_pDescriptor->szLocalFileName));
+ }
+ /*
+ // Also add an optional message to the notifier, unless it is an AVATAR download!
+ if(KVI_OPTION_BOOL(KviOption_boolNotifiDccDownloadSuccessInNotifier))
+ {
+ QString szMsg;
+ KviQString::sprintf(szMsg,__tr2qs_ctx(""));
+ g_pApp->notifierMessage(0,KVI_SMALLICON_DCCMSG,szMsg,30);
+ }
+ */
+ if(m_pDescriptor->bRecvFile)g_pApp->fileDownloadTerminated(true,m_pDescriptor->szFileName.utf8().data(),m_pDescriptor->szLocalFileName.utf8().data(),m_pDescriptor->szNick.utf8().data());
+ m_szStatusString = __tr2qs_ctx("Transfer completed","dcc");
+ outputAndLog(m_szStatusString);
+ m_eGeneralStatus = Success;
+ m_tTransferEndTime = kvi_unixTime();
+
+ KVS_TRIGGER_EVENT_2(KviEvent_OnDCCFileTransferSuccess,
+ eventWindow(),
+ (kvs_int_t)(m_pSlaveRecvThread ? m_pSlaveRecvThread->receivedBytes() : m_pSlaveSendThread->sentBytes()),
+ m_pDescriptor->idString());
+
+ displayUpdate();
+
+ if(KVI_OPTION_BOOL(KviOption_boolAutoCloseDccSendOnSuccess))die();
+ return true;
+ }
+ break;
+ case KVI_DCC_THREAD_EVENT_MESSAGE:
+ {
+ KviStr * str = ((KviThreadDataEvent<KviStr> *)e)->getData();
+ outputAndLog(QString(__tr_no_xgettext_ctx(str->ptr(),"dcc")));
+ delete str;
+ return true;
+ }
+ break;
+ default:
+ debug("Invalid event type %d received",((KviThreadEvent *)e)->id());
+ break;
+ }
+ }
+//#warning "Remove this!"
+// if(e->type() == QEvent::Close)debug("Close event received");
+ return KviFileTransfer::event(e);
+}
+
+void KviDccFileTransfer::handleMarshalError(int err)
+{
+ QString szErr = KviError::getDescription(err);
+ m_eGeneralStatus = Failure;
+ m_szStatusString = __tr2qs_ctx("Transfer failed: ","dcc");
+ m_szStatusString += szErr;
+ outputAndLog(m_szStatusString);
+ KVS_TRIGGER_EVENT_3(KviEvent_OnDCCFileTransferFailed,eventWindow(),szErr,(kvs_int_t)0,m_pDescriptor->idString());
+ displayUpdate();
+}
+
+void KviDccFileTransfer::connected()
+{
+ outputAndLog(__tr2qs_ctx("Connected to %1:%2","dcc").arg(m_pMarshal->remoteIp()).arg(m_pMarshal->remotePort()));
+ outputAndLog(__tr2qs_ctx("Local end is %1:%2","dcc").arg(m_pMarshal->localIp()).arg(m_pMarshal->localPort()));
+
+ m_tTransferStartTime = kvi_unixTime();
+
+ if(!(m_pDescriptor->bActive))
+ {
+ m_pDescriptor->szIp = m_pMarshal->remoteIp();
+ m_pDescriptor->szPort = m_pMarshal->remotePort();
+ m_pDescriptor->szHost = m_pMarshal->remoteIp();
+ }
+
+ if(m_pDescriptor->bRecvFile)
+ {
+ KviDccRecvThreadOptions * o = new KviDccRecvThreadOptions;
+ o->szFileName = m_pDescriptor->szLocalFileName.utf8().data();
+ bool bOk;
+ o->iTotalFileSize = m_pDescriptor->szFileSize.toInt(&bOk);
+ if(!bOk)o->iTotalFileSize = -1;
+ o->bResume = m_pDescriptor->bResume;
+ o->iIdleStepLengthInMSec = KVI_OPTION_BOOL(KviOption_boolDccSendForceIdleStep) ? KVI_OPTION_UINT(KviOption_uintDccSendIdleStepInMSec) : 0;
+ o->bIsTdcc = m_pDescriptor->bIsTdcc;
+ o->bSendZeroAck = KVI_OPTION_BOOL(KviOption_boolSendZeroAckInDccRecv);
+ o->bNoAcks = m_pDescriptor->bNoAcks;
+ o->uMaxBandwidth = m_uMaxBandwidth;
+ m_pSlaveRecvThread = new KviDccRecvThread(this,m_pMarshal->releaseSocket(),o);
+ m_pSlaveRecvThread->start();
+ } else {
+ KviDccSendThreadOptions * o = new KviDccSendThreadOptions;
+ o->szFileName = m_pDescriptor->szLocalFileName.utf8().data();
+ o->bFastSend = KVI_OPTION_BOOL(KviOption_boolUseFastDccSend);
+ o->iIdleStepLengthInMSec = KVI_OPTION_BOOL(KviOption_boolDccSendForceIdleStep) ? KVI_OPTION_UINT(KviOption_uintDccSendIdleStepInMSec) : 0;
+ bool bOk;
+ o->bIsTdcc = m_pDescriptor->bIsTdcc;
+ o->iStartPosition = m_pDescriptor->szFileSize.toInt(&bOk);
+ if(!bOk || (o->iStartPosition < 0))o->iStartPosition = 0;
+ o->iPacketSize = KVI_OPTION_UINT(KviOption_uintDccSendPacketSize);
+ if(o->iPacketSize < 32)o->iPacketSize = 32;
+ o->uMaxBandwidth = m_uMaxBandwidth;
+ o->bNoAcks = m_pDescriptor->bNoAcks;
+ m_pSlaveSendThread = new KviDccSendThread(this,m_pMarshal->releaseSocket(),o);
+ m_pSlaveSendThread->start();
+ }
+
+ m_eGeneralStatus = Transferring;
+ m_szStatusString = __tr2qs_ctx("Transferring data","dcc");
+
+ KVS_TRIGGER_EVENT_1(KviEvent_OnDCCFileTransferBegin,eventWindow(),m_pDescriptor->idString());
+
+ outputAndLog(m_szStatusString);
+ displayUpdate();
+}
+
+bool KviDccFileTransfer::resumeAccepted(const char *filename,const char *port,const char *szZeroPortTag)
+{
+ if(!(kvi_strEqualCI(filename,m_pDescriptor->szFileName.utf8().data()) || KVI_OPTION_BOOL(KviOption_boolAcceptBrokenFileNameDccResumeRequests)))
+ return false;
+
+ if(!(kvi_strEqualCI(port,m_pDescriptor->szPort.utf8().data()) &&
+ (!m_pSlaveRecvThread) && m_pDescriptor->bResume && m_pDescriptor->bRecvFile && m_pResumeTimer))
+ return false;
+
+ if(kvi_strEqualCI(port,"0"))
+ {
+ if(!kvi_strEqualCI(szZeroPortTag,m_pDescriptor->zeroPortRequestTag()))
+ return false;
+ }
+
+ delete m_pResumeTimer;
+ m_pResumeTimer = 0;
+
+ outputAndLog(__tr2qs_ctx("RESUME accepted, transfer will begin at position %1","dcc").arg(m_pDescriptor->szLocalFileSize));
+
+ listenOrConnect();
+
+ /*
+ int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),
+ m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout);
+
+ if(ret != KviError_success)handleMarshalError(ret);
+ else {
+ m_szStatusString = __tr2qs_ctx("Contacting host %1 on port %2","dcc").arg(m_pDescriptor->szIp).arg(m_pDescriptor->szPort);
+ outputAndLog(m_szStatusString);
+ displayUpdate();
+ }
+ */
+
+ return true;
+}
+
+bool KviDccFileTransfer::doResume(const char * filename,const char * port,unsigned int filePos)
+{
+ if(KviQString::equalCI(port,m_pMarshal->dccPort()) &&
+ (!m_pSlaveRecvThread) && (!m_pDescriptor->bRecvFile))
+ {
+ if(KviQString::equalCI(filename,m_pDescriptor->szFileName) || KVI_OPTION_BOOL(KviOption_boolAcceptBrokenFileNameDccResumeRequests))
+ {
+ bool bOk;
+ unsigned int iLocalFileSize = m_pDescriptor->szLocalFileSize.toUInt(&bOk);
+ if(!bOk)
+ {
+ // ops...internal error
+ outputAndLog(KVI_OUT_DCCERROR,__tr2qs_ctx("Internal error in RESUME request","dcc"));
+ return false;
+ }
+ if(iLocalFileSize <= filePos)
+ {
+ outputAndLog(KVI_OUT_DCCERROR,__tr2qs_ctx("Invalid RESUME request: Position %1 is larger than file size","dcc").arg(filePos));
+ return false;
+ }
+
+ outputAndLog(KVI_OUT_DCCERROR,__tr2qs_ctx("Accepting RESUME request, transfer will begin at position %1","dcc").arg(filePos));
+
+ m_pDescriptor->szFileSize.setNum(filePos);
+
+
+ KviStr szBuffy;
+ KviServerParser::encodeCtcpParameter(filename,szBuffy);
+
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC ACCEPT %s %s %u%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,
+ m_pDescriptor->console()->connection()->encodeText(szBuffy.ptr()).data(),
+ port,filePos,0x01);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+
+KviDccFileTransferBandwidthDialog::KviDccFileTransferBandwidthDialog(QWidget * pParent,KviDccFileTransfer * t)
+: QDialog(pParent)
+{
+ QGridLayout * g = new QGridLayout(this,3,3,4,4);
+
+ m_pTransfer = t;
+ int iVal = m_pTransfer->bandwidthLimit();
+
+ QString szText = __tr2qs_ctx("Configure bandwidth for DCC transfer %1","dcc").arg(t->id());
+ setCaption(szText);
+
+ szText = t->isFileUpload() ? __tr2qs_ctx("Limit upload bandwidth to","dcc") : __tr2qs_ctx("Limit download bandwidth to","dcc");
+
+ m_pEnableLimitCheck = new KviStyledCheckBox(szText,this);
+ g->addWidget(m_pEnableLimitCheck,0,0);
+
+ m_pEnableLimitCheck->setChecked((iVal >= 0) && (iVal < MAX_DCC_BANDWIDTH_LIMIT));
+
+ m_pLimitBox = new QSpinBox(0,MAX_DCC_BANDWIDTH_LIMIT-1,1,this);
+ m_pLimitBox->setEnabled((iVal >= 0) && (iVal < MAX_DCC_BANDWIDTH_LIMIT));
+ connect(m_pEnableLimitCheck,SIGNAL(toggled(bool)),m_pLimitBox,SLOT(setEnabled(bool)));
+ g->addMultiCellWidget(m_pLimitBox,0,0,1,2);
+
+ szText = " ";
+ szText += __tr2qs_ctx("bytes/sec","dcc");
+ m_pLimitBox->setSuffix(szText);
+ m_pLimitBox->setValue(iVal < MAX_DCC_BANDWIDTH_LIMIT ? iVal : 0);
+
+ QPushButton * pb = new QPushButton(__tr2qs_ctx("OK","dcc"),this);
+ connect(pb,SIGNAL(clicked()),this,SLOT(okClicked()));
+ pb->setMinimumWidth(80);
+ g->addWidget(pb,2,2);
+
+ pb = new QPushButton(__tr2qs_ctx("Cancel","dcc"),this);
+ connect(pb,SIGNAL(clicked()),this,SLOT(cancelClicked()));
+ pb->setMinimumWidth(80);
+ g->addWidget(pb,2,1);
+
+ g->setColStretch(0,1);
+ g->setRowStretch(1,1);
+}
+
+KviDccFileTransferBandwidthDialog::~KviDccFileTransferBandwidthDialog()
+{
+}
+
+void KviDccFileTransferBandwidthDialog::okClicked()
+{
+ int iVal = MAX_DCC_BANDWIDTH_LIMIT;
+ if(m_pEnableLimitCheck->isChecked())
+ {
+ iVal = m_pLimitBox->value();
+ if(iVal < 0)iVal = MAX_DCC_BANDWIDTH_LIMIT;
+ if(iVal > MAX_DCC_BANDWIDTH_LIMIT)iVal = MAX_DCC_BANDWIDTH_LIMIT;
+ }
+ m_pTransfer->setBandwidthLimit(iVal);
+ delete this;
+}
+
+void KviDccFileTransferBandwidthDialog::cancelClicked()
+{
+ delete this;
+}
+
+void KviDccFileTransferBandwidthDialog::closeEvent(QCloseEvent * e)
+{
+ e->ignore();
+ delete this;
+}
+
+
+
+
+#include "m_send.moc"
diff --git a/src/modules/dcc/send.h b/src/modules/dcc/send.h
new file mode 100644
index 00000000..636f2b3b
--- /dev/null
+++ b/src/modules/dcc/send.h
@@ -0,0 +1,260 @@
+#ifndef _SEND_H_
+#define _SEND_H_
+//=============================================================================
+//
+// File : send.h
+// Creation date : Tue Sep 24 09 2000 15:06:12 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2000-2005 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+//=============================================================================
+
+#include "kvi_window.h"
+#include "kvi_string.h"
+
+#include "descriptor.h"
+#include "window.h"
+#include "thread.h"
+
+#include "kvi_sockettype.h"
+
+#include "kvi_pointerlist.h"
+#include <qlabel.h>
+#include <qprogressbar.h>
+#include "kvi_tal_popupmenu.h"
+#include "kvi_tal_hbox.h"
+#include "kvi_tal_vbox.h"
+#include <qfile.h>
+#include <qdialog.h>
+
+#include "kvi_filetransfer.h"
+#include "kvi_time.h"
+
+
+typedef struct _KviDccSendThreadOptions
+{
+ KviStr szFileName;
+ int iStartPosition;
+ int iPacketSize;
+ int iIdleStepLengthInMSec;
+ bool bFastSend;
+ bool bNoAcks;
+ bool bIsTdcc;
+ unsigned int uMaxBandwidth;
+} KviDccSendThreadOptions;
+
+
+class KviDccSendThread : public KviDccThread
+{
+public:
+ KviDccSendThread(QObject * par,kvi_socket_t fd,KviDccSendThreadOptions * opt);
+ ~KviDccSendThread();
+private:
+ // stats: SHARED!!!
+ int m_iAverageSpeed;
+ int m_iInstantSpeed;
+ int m_iFilePosition;
+ int m_iAckedBytes;
+ int m_iTotalSentBytes;
+ // internal
+ unsigned long m_uStartTime;
+ unsigned long m_uInstantSpeedInterval;
+ int m_iInstantSentBytes;
+ KviDccSendThreadOptions * m_pOpt;
+ KviMSecTimeInterval * m_pTimeInterval; // used for computing the instant bandwidth but not only
+public:
+ void initGetInfo();
+ int averageSpeed(){ return m_iAverageSpeed; };
+ int instantSpeed(){ return m_iInstantSpeed; };
+ int filePosition(){ return m_iFilePosition; };
+ // sent ONLY in this session
+ int sentBytes(){ return m_iTotalSentBytes; };
+ int ackedBytes(){ return m_iAckedBytes; };
+ unsigned int bandwidthLimit(){ return m_pOpt->uMaxBandwidth; };
+ void setBandwidthLimit(unsigned int uMaxBandwidth){ m_pOpt->uMaxBandwidth = uMaxBandwidth; };
+ void doneGetInfo();
+protected:
+ void updateStats();
+ virtual void run();
+};
+
+typedef struct _KviDccRecvThreadOptions
+{
+ bool bResume;
+ KviStr szFileName;
+ int iTotalFileSize;
+ int iIdleStepLengthInMSec;
+ bool bSendZeroAck;
+ bool bNoAcks;
+ bool bIsTdcc;
+ unsigned int uMaxBandwidth;
+} KviDccRecvThreadOptions;
+
+class KviDccRecvThread : public KviDccThread
+{
+public:
+ KviDccRecvThread(QObject * par,kvi_socket_t fd,KviDccRecvThreadOptions * opt);
+ ~KviDccRecvThread();
+protected:
+ KviDccRecvThreadOptions * m_pOpt;
+
+ // stats: SHARED!
+ int m_iAverageSpeed;
+ int m_iInstantSpeed;
+ int m_iFilePosition;
+ int m_iTotalReceivedBytes;
+
+ // internal
+ unsigned long m_uStartTime;
+ KviMSecTimeInterval * m_pTimeInterval; // used for computing the instant bandwidth
+ int m_iInstantReceivedBytes;
+ unsigned long m_uInstantSpeedInterval;
+ QFile * m_pFile;
+public:
+ void initGetInfo();
+ int averageSpeed(){ return m_iAverageSpeed; };
+ int instantSpeed(){ return m_iInstantSpeed; };
+ int filePosition(){ return m_iFilePosition; };
+ // received ONLY in this session
+ int receivedBytes(){ return m_iTotalReceivedBytes; };
+ unsigned int bandwidthLimit(){ return m_pOpt->uMaxBandwidth; };
+ void setBandwidthLimit(unsigned int uMaxBandwidth){ m_pOpt->uMaxBandwidth = uMaxBandwidth; };
+ void doneGetInfo();
+protected:
+ void postMessageEvent(const char * msg);
+ void updateStats();
+ bool sendAck(int filePos);
+ virtual void run();
+};
+
+class KviDccFileTransfer;
+class QSpinBox;
+class QTimer;
+
+#include "kvi_styled_controls.h"
+
+class KviDccFileTransferBandwidthDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ KviDccFileTransferBandwidthDialog(QWidget * pParent,KviDccFileTransfer * t);
+ ~KviDccFileTransferBandwidthDialog();
+protected:
+ KviDccFileTransfer * m_pTransfer;
+ KviStyledCheckBox * m_pEnableLimitCheck;
+ QSpinBox * m_pLimitBox;
+protected:
+ virtual void closeEvent(QCloseEvent *e);
+protected slots:
+ void okClicked();
+ void cancelClicked();
+};
+
+
+class KviDccMarshal;
+class QPainter;
+class KviTalPopupMenu;
+
+class KviDccFileTransfer : public KviFileTransfer, public KviDccMarshalOutputContext
+{
+ enum GeneralStatus { Connecting , Transferring , Success , Failure };
+ Q_OBJECT
+public:
+ KviDccFileTransfer(KviDccDescriptor * dcc);
+ ~KviDccFileTransfer();
+private:
+ KviDccSendThread * m_pSlaveSendThread;
+ KviDccRecvThread * m_pSlaveRecvThread;
+ KviDccDescriptor * m_pDescriptor;
+ KviDccMarshal * m_pMarshal;
+
+ KviStr m_szTarget;
+ KviStr m_szDccType;
+ QString m_szTransferIdString;
+
+ QString m_szStatusString;
+ GeneralStatus m_eGeneralStatus;
+
+ QString m_szTransferLog; // html
+
+ kvi_time_t m_tTransferStartTime;
+ kvi_time_t m_tTransferEndTime;
+ // cached stats
+ unsigned int m_uTotalFileSize; // total file size to transfer
+
+ unsigned int m_uMaxBandwidth;
+ KviDccFileTransferBandwidthDialog * m_pBandwidthDialog;
+
+ QTimer * m_pResumeTimer; // used to signal resume timeout
+public:
+ bool resumeAccepted(const char * filename,const char * port,const char *szZeroPortTag);
+ bool doResume(const char * filename,const char * port,unsigned int filePos);
+
+ static void init();
+ static void done();
+ static unsigned int runningTransfersCount();
+ static KviDccFileTransfer * nonFailedTransferWithLocalFileName(const QString &szLocalFileName);
+ static unsigned int transferCount();
+ static bool handleResumeAccepted(const char * filename,const char * port,const char * szZeroPortTag);
+ static bool handleResumeRequest(const char * filename,const char * port,unsigned int filePos);
+
+ virtual bool event(QEvent *e);
+
+ virtual KviWindow * dccMarshalOutputWindow();
+ virtual const char * dccMarshalOutputContextString();
+
+ virtual void displayPaint(QPainter * p,int column,int width,int height);
+ virtual int displayHeight(int iLineSpacing);
+ virtual void fillContextPopup(KviTalPopupMenu * m,int column);
+ virtual void fillStatusString(QString &szBuffer);
+ virtual bool active();
+ virtual void die();
+ virtual QString tipText();
+ virtual QString localFileName();
+
+ bool isFileUpload(){ return m_pDescriptor->isFileUpload(); };
+
+ unsigned int averageSpeed();
+ unsigned int transferredBytes();
+
+ int bandwidthLimit();
+ void setBandwidthLimit(int iVal);
+protected:
+ void startConnection();
+ void listenOrConnect();
+ void addToTransferLog(const QString &s);
+ void outputAndLog(const QString &s);
+ void outputAndLog(int msgtype,const QString &s);
+ KviWindow * eventWindow();
+protected slots:
+ void connectionInProgress();
+ void sslError(const char * msg);
+ void startingSSLHandshake();
+ void handleMarshalError(int err);
+ void connected();
+ void bandwidthDialogDestroyed();
+ void configureBandwidth();
+ void resumeTimedOut();
+public slots:
+ void abort();
+ void retryDCC();
+ void retryTDCC();
+ void retryRevDCC();
+};
+
+#endif //_SEND_H_
diff --git a/src/modules/dcc/thread.cpp b/src/modules/dcc/thread.cpp
new file mode 100644
index 00000000..7ae38bbe
--- /dev/null
+++ b/src/modules/dcc/thread.cpp
@@ -0,0 +1,111 @@
+//
+// File : thread.cpp
+// Creation date : Tue Sep 20 09 2000 18:29:51 CEST Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "thread.h"
+#define _KVI_DEBUG_CHECK_RANGE_
+#include "kvi_debug.h"
+#include "kvi_window.h"
+#include "kvi_error.h"
+#include "kvi_memmove.h"
+#include "kvi_malloc.h"
+#include "kvi_netutils.h"
+#include "kvi_socket.h"
+#ifdef COMPILE_SSL_SUPPORT
+ #include "kvi_sslmaster.h"
+#endif
+
+KviDccThread::KviDccThread(QObject * par,kvi_socket_t fd)
+: KviSensitiveThread()
+{
+ m_pParent = par;
+ m_fd = fd;
+ m_pMutex = new KviMutex();
+#ifdef COMPILE_SSL_SUPPORT
+// debug("CLEARING SSL IN KviDccThread constructor");
+ m_pSSL = 0;
+#endif
+}
+
+KviDccThread::~KviDccThread()
+{
+#ifdef COMPILE_SSL_SUPPORT
+ if(m_pSSL)KviSSLMaster::freeSSL(m_pSSL);
+ m_pSSL = 0;
+#endif
+ if(m_fd != KVI_INVALID_SOCKET)kvi_socket_close(m_fd);
+ __range_invalid(m_pMutex->locked());
+ delete m_pMutex;
+}
+
+#ifdef COMPILE_SSL_SUPPORT
+void KviDccThread::setSSL(KviSSL * s)
+{
+ if(m_pSSL)KviSSLMaster::freeSSL(m_pSSL);
+ m_pSSL = s;
+}
+#endif
+
+bool KviDccThread::handleInvalidSocketRead(int readLen)
+{
+ __range_valid(readLen < 1);
+ if(readLen == 0)
+ {
+ // connection closed
+ postErrorEvent(KviError_remoteEndClosedConnection);
+ return false;
+ } else {
+ // error ?
+ int err = kvi_socket_error();
+ if((err != EINTR) && (err != EAGAIN))
+ {
+ postErrorEvent(KviError::translateSystemError(err));
+ return false;
+ }
+ }
+ return true; // continue
+}
+
+#ifdef COMPILE_SSL_SUPPORT
+void KviDccThread::raiseSSLError()
+{
+ KviStr buffer;
+ while(m_pSSL->getLastErrorString(buffer))
+ {
+ KviStr msg(KviStr::Format,"[SSL ERROR]: %s",buffer.ptr());
+ postMessageEvent(msg.ptr());
+ }
+}
+#endif
+
+void KviDccThread::postErrorEvent(int err)
+{
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ERROR);
+ e->setData(new int(err));
+ postEvent(m_pParent,e);
+}
+
+void KviDccThread::postMessageEvent(const char * message)
+{
+ KviThreadDataEvent<KviStr> * e = new KviThreadDataEvent<KviStr>(KVI_DCC_THREAD_EVENT_MESSAGE);
+ e->setData(new KviStr(message));
+ postEvent(m_pParent,e);
+}
diff --git a/src/modules/dcc/thread.h b/src/modules/dcc/thread.h
new file mode 100644
index 00000000..3d56661d
--- /dev/null
+++ b/src/modules/dcc/thread.h
@@ -0,0 +1,79 @@
+#ifndef _THREAD_H_
+#define _THREAD_H_
+//
+// File : thread.h
+// Creation date : Tue Sep 20 09 2000 18:28:44 by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_settings.h"
+#include "kvi_thread.h"
+#include "kvi_sockettype.h"
+
+#include "kvi_pointerlist.h"
+
+#include <qobject.h>
+
+#ifdef COMPILE_SSL_SUPPORT
+ #include "kvi_ssl.h"
+#endif
+
+// KviThreadDataEvent<int>
+#define KVI_DCC_THREAD_EVENT_ERROR (KVI_THREAD_USER_EVENT_BASE + 1)
+// KviThreadDataEvent<KviStr>
+#define KVI_DCC_THREAD_EVENT_DATA (KVI_THREAD_USER_EVENT_BASE + 2)
+// KviThreadEvent
+#define KVI_DCC_THREAD_EVENT_SUCCESS (KVI_THREAD_USER_EVENT_BASE + 3)
+// KviThreadDataEvent<KviStr>
+#define KVI_DCC_THREAD_EVENT_MESSAGE (KVI_THREAD_USER_EVENT_BASE + 4)
+// KviThreadDataEvent<int>
+#define KVI_DCC_THREAD_EVENT_ACTION (KVI_THREAD_USER_EVENT_BASE + 5)
+
+typedef struct _KviDccThreadIncomingData
+{
+ int iLen;
+ char * buffer;
+} KviDccThreadIncomingData;
+
+class KviDccThread : public KviSensitiveThread
+{
+public:
+ KviDccThread(QObject * par,kvi_socket_t fd);
+ ~KviDccThread();
+protected:
+ KviMutex * m_pMutex; // OWNED! PROTECTS m_pOutBuffers
+ kvi_socket_t m_fd;
+ QObject * m_pParent; // READ ONLY!
+#ifdef COMPILE_SSL_SUPPORT
+ KviSSL * m_pSSL;
+#endif
+protected:
+ bool handleInvalidSocketRead(int readLen);
+public:
+ QObject * parent(){ return m_pParent; };
+ void postErrorEvent(int err);
+ // Warning!..newer call __tr() here!...use __tr_no_lookup()
+ void postMessageEvent(const char * message);
+#ifdef COMPILE_SSL_SUPPORT
+ void raiseSSLError();
+ void setSSL(KviSSL * s);
+#endif
+};
+
+#endif //_THREAD_H_
diff --git a/src/modules/dcc/utils.cpp b/src/modules/dcc/utils.cpp
new file mode 100644
index 00000000..8d3229e7
--- /dev/null
+++ b/src/modules/dcc/utils.cpp
@@ -0,0 +1,169 @@
+//
+// File : utils.cpp
+// Creation date : Tue Jul 23 02:54:44 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#define _UTILS_CPP_
+
+#include "utils.h"
+
+#include "kvi_options.h"
+#include "kvi_locale.h"
+#include "kvi_netutils.h"
+#include "kvi_ircsocket.h"
+#include "kvi_qstring.h"
+
+/*
+bool dcc_module_get_listen_ip_address(KviCommand *c,KviConsole * pConsole,QString &szListenIp)
+{
+ //
+ // Find an interface suitable for listening....
+ // Either from user options or from the current connection...
+ //
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault))
+ {
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).stripWhiteSpace();
+ if(!KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).isEmpty())
+ {
+ if(kvi_isValidStringIp(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data()))
+ {
+ if(KviQString::equalCI(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface),"0.0.0.0"))
+ {
+ // Try to find the first available IpV4 interface
+ if(!kvi_getLocalHostAddress(szListenIp))
+ {
+ if(c)c->warning(__tr2qs_ctx("Can't retrieve a suitable local IPV4 address","dcc"),
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data());
+ return false;
+ }
+ } else {
+ szListenIp = KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface);
+ }
+ return true;
+ }
+#ifdef COMPILE_IPV6_SUPPORT
+ if(kvi_isValidStringIp_V6(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data()))
+ {
+ szListenIp = KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface);
+ } else {
+#endif
+ if(!kvi_getInterfaceAddress(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data(),szListenIp))
+ {
+ KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault) = false;
+ if(c)c->warning(__tr2qs_ctx("Can't listen on default interface '%s': fix it in the options dialog, disabling the option (so the next dcc will work)","dcc"),
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data());
+ return false;
+ }
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ return true;
+ } else {
+ // the option was empty.. disable it
+ KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault) = false;
+ }
+ }
+
+ if(pConsole)
+ {
+ if(pConsole->isConnected())
+ {
+ //#warning "The IPV6 choice is not OK here.... and maybe allow to bind to specified ports"
+ pConsole->socket()->getLocalHostIp(szListenIp,pConsole->isIpV6Connection());
+ } else {
+ szListenIp = "0.0.0.0"; // huh ? :)
+ }
+ } else {
+ szListenIp = "0.0.0.0";
+ }
+
+ return true;
+}
+*/
+
+
+bool dcc_kvs_get_listen_ip_address(KviKvsModuleCommandCall *c,KviConsole * pConsole,QString &szListenIp)
+{
+ //
+ // Find an interface suitable for listening....
+ // Either from user options or from the current connection...
+ //
+
+ if(KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault))
+ {
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).stripWhiteSpace();
+ if(!KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).isEmpty())
+ {
+ if(kvi_isValidStringIp(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data()))
+ {
+ if(KviQString::equalCI(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface),"0.0.0.0"))
+ {
+ // Try to find the first available IpV4 interface
+ if(!kvi_getLocalHostAddress(szListenIp))
+ {
+ if(c)c->warning(__tr2qs_ctx("Can't retrieve a suitable local IPV4 address","dcc"),
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data());
+ return false;
+ }
+ } else {
+ szListenIp = KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface);
+ }
+ return true;
+ }
+#ifdef COMPILE_IPV6_SUPPORT
+ if(kvi_isValidStringIp_V6(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data()))
+ {
+ szListenIp = KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface);
+ } else {
+#endif
+ if(!kvi_getInterfaceAddress(KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data(),szListenIp))
+ {
+ KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault) = false;
+ if(c)c->warning(__tr2qs_ctx("Can't listen on default interface '%s': fix it in the options dialog, disabling the option (so the next dcc will work)","dcc"),
+ KVI_OPTION_STRING(KviOption_stringDccListenDefaultInterface).utf8().data());
+ return false;
+ }
+#ifdef COMPILE_IPV6_SUPPORT
+ }
+#endif
+ return true;
+ } else {
+ // the option was empty.. disable it
+ KVI_OPTION_BOOL(KviOption_boolDccListenOnSpecifiedInterfaceByDefault) = false;
+ }
+ }
+
+ if(pConsole)
+ {
+ if(pConsole->isConnected())
+ {
+ //#warning "The IPV6 choice is not OK here.... and maybe allow to bind to specified ports"
+ pConsole->socket()->getLocalHostIp(szListenIp,pConsole->isIpV6Connection());
+ } else {
+ szListenIp = "0.0.0.0"; // huh ? :)
+ }
+ } else {
+ szListenIp = "0.0.0.0";
+ }
+
+ return true;
+}
+
diff --git a/src/modules/dcc/utils.h b/src/modules/dcc/utils.h
new file mode 100644
index 00000000..992814f2
--- /dev/null
+++ b/src/modules/dcc/utils.h
@@ -0,0 +1,34 @@
+#ifndef _UTILS_H_
+#define _UTILS_H_
+//
+// File : utils.h
+// Creation date : Tue Jul 23 02:54:45 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_settings.h"
+#include "kvi_string.h"
+#include "kvi_console.h"
+#include "kvi_kvs_moduleinterface.h"
+
+#ifndef _UTILS_CPP_
+ extern bool dcc_kvs_get_listen_ip_address(KviKvsModuleCommandCall *c,KviConsole * pConsole,QString &szListenIp);
+#endif
+
+#endif //_UTILS_H_
diff --git a/src/modules/dcc/voice.cpp b/src/modules/dcc/voice.cpp
new file mode 100644
index 00000000..2e34df2f
--- /dev/null
+++ b/src/modules/dcc/voice.cpp
@@ -0,0 +1,1041 @@
+//
+// File : voice.cpp
+// Creation date : Thu Aug 23 04:08:09 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "voice.h"
+#include "marshal.h"
+#include "broker.h"
+
+#include "kvi_settings.h"
+#include "kvi_iconmanager.h"
+#include "kvi_ircview.h"
+#include "kvi_locale.h"
+#include "kvi_out.h"
+#include "kvi_error.h"
+#include "kvi_netutils.h"
+#include "kvi_options.h"
+#include "kvi_console.h"
+#include "kvi_malloc.h"
+#include "kvi_socket.h"
+#include "kvi_ircconnection.h"
+
+#include "adpcmcodec.h"
+#include "gsmcodec.h"
+
+#include <qframe.h>
+#include <qsplitter.h>
+#include "kvi_tal_vbox.h"
+#include <qslider.h>
+#include <qtooltip.h>
+
+#ifndef COMPILE_ON_WINDOWS
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+ #include <errno.h>
+ #include <fcntl.h>
+//#include "kvi_error.h"
+
+#include <sys/stat.h> // for open()
+#include <sys/ioctl.h> // for ioctl()
+#endif //!COMPILE_ON_WIDNOWS
+
+extern KviDccBroker * g_pDccBroker;
+
+//Check for the *SS Api....we don't want to fail here...
+
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ #ifdef HAVE_LINUX_SOUNDCARD_H
+ #include <linux/soundcard.h>
+ #else
+ #ifdef HAVE_SYS_SOUNDCARD_H
+ #include <sys/soundcard.h>
+ #else
+ #ifdef HAVE_SOUNDCARD_H
+ #include <soundcard.h>
+ #else
+ //CAN NOT COMPILE :(
+ #define COMPILE_DISABLE_DCC_VOICE
+ #ifndef COMPILE_ON_WINDOWS
+ #warning "Cannot find the soundcard.h header; you will NOT be able to use DCC Voice"
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+
+
+//#define KVI_AUDIO_DEVICE "/dev/dsp"
+// 32 fragments , 512 bytes
+#define KVI_SNDCTL_FRAG_SIZE 0x00B00009
+#define KVI_FRAGMENT_SIZE_IN_BYTES 512
+#define KVI_FORMAT AFMT_S16_LE
+#define KVI_NUM_CHANNELS 1
+
+
+bool kvi_dcc_voice_is_valid_codec(const char * codecName)
+{
+#ifdef COMPILE_USE_GSM
+ if(kvi_strEqualCI("gsm",codecName))
+ {
+ return kvi_gsm_codec_init();
+ }
+#endif
+ if(kvi_strEqualCI("adpcm",codecName))return true;
+ if(kvi_strEqualCI("null",codecName))return true;
+ return false;
+}
+
+static KviDccVoiceCodec * kvi_dcc_voice_get_codec(const char * codecName)
+{
+#ifdef COMPILE_USE_GSM
+ if(kvi_strEqualCI("gsm",codecName))
+ {
+ if(kvi_gsm_codec_init())return new KviDccVoiceGsmCodec();
+ }
+#endif
+ if(kvi_strEqualCI("adpcm",codecName))return new KviDccVoiceAdpcmCodec();
+ if(kvi_strEqualCI("null",codecName))return new KviDccVoiceNullCodec();
+ return new KviDccVoiceAdpcmCodec();
+}
+
+
+KviDccVoiceThread::KviDccVoiceThread(KviWindow * wnd,kvi_socket_t fd,KviDccVoiceThreadOptions * opt)
+: KviDccThread(wnd,fd)
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ m_pOpt = opt;
+ m_bPlaying = false;
+ m_bRecording = false;
+ m_bSoundcardChecked = false;
+ m_soundFd = -1;
+ m_soundFdMode = 0;
+ m_pInfoMutex = new KviMutex();
+ m_bRecordingRequestPending = false;
+#endif
+}
+
+KviDccVoiceThread::~KviDccVoiceThread()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ delete m_pOpt->pCodec;
+ delete m_pOpt;
+ delete m_pInfoMutex;
+#endif
+}
+
+
+bool KviDccVoiceThread::checkSoundcard()
+{
+#ifdef COMPILE_DISABLE_DCC_VOICE
+ return false;
+#else
+ bool bOpened = false;
+ if(m_soundFd == -1)
+ {
+ if(!openSoundcard(O_RDONLY))return false;
+ bOpened = true;
+ }
+
+ int caps;
+
+ m_bSoundcardChecked = true;
+
+ if(ioctl(m_soundFd,SNDCTL_DSP_GETCAPS,&caps) < 0)
+ {
+ postMessageEvent(__tr2qs_ctx("WARNING: failed to check the soundcard duplex capabilities: if this is a half-duplex soundcard , use the DCC VOICE option to force half-duplex algorithm","dcc"));
+ if(bOpened)closeSoundcard();
+ return false;
+ }
+
+ if(!(caps & DSP_CAP_DUPLEX))
+ {
+ m_pOpt->bForceHalfDuplex = true; // the device is half duplex...use it in that way
+ postMessageEvent(__tr2qs_ctx("Half duplex soundcard detected, you will not be able to talk and listen at the same time","dcc"));
+ }
+
+ if(bOpened)closeSoundcard();
+
+ return true;
+#endif
+}
+
+
+bool KviDccVoiceThread::openSoundcard(int mode)
+{
+#ifdef COMPILE_DISABLE_DCC_VOICE
+ return false;
+#else
+ int speed = m_pOpt->iSampleRate;
+ static int chans=KVI_NUM_CHANNELS;
+ static int fmt=KVI_FORMAT;
+ static int frag = KVI_SNDCTL_FRAG_SIZE;
+
+
+ if(m_soundFd != -1)
+ {
+ if(m_soundFdMode == mode)return true; // already open
+ closeSoundcard();
+ }
+
+ m_soundFd = ::open(m_pOpt->szSoundDevice.ptr(),mode | O_NONBLOCK);
+ if(m_soundFd < 0)return false;
+
+ if(!m_pOpt->bForceHalfDuplex)
+ {
+ if(ioctl(m_soundFd,SNDCTL_DSP_SETDUPLEX,0) < 0)goto exit_false;
+ }
+
+ if(ioctl(m_soundFd,SNDCTL_DSP_SETFRAGMENT,&frag)<0)goto exit_false;
+ if(ioctl(m_soundFd,SNDCTL_DSP_SETFMT,&fmt)<0)goto exit_false;
+ if(ioctl(m_soundFd,SNDCTL_DSP_CHANNELS,&chans)<0)goto exit_false;
+ if(ioctl(m_soundFd,SNDCTL_DSP_SPEED,&speed)<0)goto exit_false;
+ if(speed != m_pOpt->iSampleRate)
+ {
+ KviStr tmp(KviStr::Format,__tr2qs_ctx("WARNING: failed to set the requested sample rate (%d): the device used closest match (%d)","dcc"),
+ m_pOpt->iSampleRate,speed);
+ postMessageEvent(tmp.ptr());
+ }
+
+ // TODO: #warning "We could also support blocking operations mode"
+
+ m_soundFdMode = mode;
+
+
+ return true;
+
+exit_false:
+ closeSoundcard();
+ return false;
+#endif
+}
+
+bool KviDccVoiceThread::openSoundcardForWriting()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ return openSoundcardWithDuplexOption(O_WRONLY,O_RDONLY);
+#else
+ return false;
+#endif
+}
+
+bool KviDccVoiceThread::openSoundcardForReading()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ return openSoundcardWithDuplexOption(O_RDONLY,O_WRONLY);
+#else
+ return false;
+#endif
+}
+
+bool KviDccVoiceThread::openSoundcardWithDuplexOption(int openMode,int failMode)
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ if(m_soundFd == -1)
+ {
+ // soundcard not open yet...open for write (at least)
+ if(m_pOpt->bForceHalfDuplex)
+ {
+ // Forcing half duplex... (user option or the card does not support full duplex mode)
+ if(!openSoundcard(openMode))return false;
+ } else {
+ // Try read/write open
+ if(!openSoundcard(O_RDWR))
+ {
+ // half-duplex sound card ?
+ if(!m_bSoundcardChecked)
+ {
+ // We haven't checked the full-duplex support yet...
+ // Try to open in RDONLY o WRONLY mode
+ if(!openSoundcard(openMode))return false;
+ if(!checkSoundcard())
+ {
+ postMessageEvent(__tr2qs_ctx("Ops...failed to test the soundcard capabilities...expect problems...","dcc"));
+ }
+ } // else the test has been done and it is a full duplex card that is just busy
+ }
+ }
+ } else {
+ // Hmmm...already open
+ // If it is open in O_RDWR or O_WRONLY mode...it is ok for us
+ // but if it is open in O_RDONLY mode...we can do nothing...just wait
+ return (m_soundFdMode != failMode);
+ }
+
+
+
+ return true;
+#else
+ return false;
+#endif
+}
+
+void KviDccVoiceThread::closeSoundcard()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ if(m_soundFd != -1)
+ {
+ ::close(m_soundFd);
+ m_soundFd = -1;
+ m_soundFdMode = 0;
+ }
+#endif
+}
+
+
+
+bool KviDccVoiceThread::readWriteStep()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ // Socket management
+ bool bCanRead;
+ bool bCanWrite;
+
+ if(kvi_select(m_fd,&bCanRead,&bCanWrite))
+ {
+ if(bCanRead)
+ {
+ unsigned int actualSize = m_inFrameBuffer.size();
+ m_inFrameBuffer.resize(actualSize + 1024);
+ int readLen = kvi_socket_recv(m_fd,(void *)(m_inFrameBuffer.data() + actualSize),1024);
+ if(readLen > 0)
+ {
+ if(readLen < 1024)m_inFrameBuffer.resize(actualSize + readLen);
+ m_pOpt->pCodec->decode(&m_inFrameBuffer,&m_inSignalBuffer);
+//#warning "A maximum length for the signal buffer is actually needed!!!"
+ } else {
+ if(!handleInvalidSocketRead(readLen))return false;
+ m_inFrameBuffer.resize(actualSize);
+ }
+ }// else {
+ // m_uSleepTime += 100;
+ //}
+
+ if(bCanWrite)
+ {
+ // Have somethihg to write ?
+ if(m_outFrameBuffer.size() > 0)
+ {
+ int written = kvi_socket_send(m_fd,m_outFrameBuffer.data(),m_outFrameBuffer.size());
+ if(written > 0)
+ {
+ m_outFrameBuffer.remove(written);
+ } else {
+ if(!handleInvalidSocketRead(written))return false;
+ }
+ }// else {
+ // m_uSleepTime += 100;
+ // }
+ }// else {
+ // m_uSleepTime += 100;
+// }
+//#warning "Usleep here ?"
+ }// else {
+// if(!(m_bPlaying || m_bRecording))
+// {
+// // Really NOTHING is happening...sleep a bit more
+// m_uSleepTime += 800;
+// } else {
+// m_uSleepTime += 100;
+// }
+// }
+
+#endif // !COMPILE_DISABLE_DCC_VOICE
+ return true;
+}
+
+bool KviDccVoiceThread::soundStep()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ // Are we playing ?
+ if(m_bPlaying)
+ {
+ // Do we have something to write ?
+ audio_buf_info info;
+ if(m_inSignalBuffer.size() > 0)
+ {
+ // Get the number of fragments that can be written to the soundcard without blocking
+
+ if(ioctl(m_soundFd,SNDCTL_DSP_GETOSPACE,&info) < 0)
+ {
+ debug("get o space failed");
+ info.bytes = KVI_FRAGMENT_SIZE_IN_BYTES; // dummy... if this is not correct...well...we will block for 1024/16000 of a sec
+ info.fragments = 1;
+ info.fragsize = KVI_FRAGMENT_SIZE_IN_BYTES;
+ }
+ if(info.fragments > 0)
+ {
+ int toWrite = info.fragments * info.fragsize;
+ //debug("Can write %d bytes",toWrite);
+ if(m_inSignalBuffer.size() < toWrite)toWrite = m_inSignalBuffer.size();
+ int written = write(m_soundFd,m_inSignalBuffer.data(),toWrite);
+ if(written > 0)m_inSignalBuffer.remove(written);
+ else {
+//#warning "Do something for -1 here ?"
+//#warning "Usleep ?"
+ }
+ } //else {
+ // No stuff can be written...we are running too fast ?
+ // m_uSleepTime += 100; // sleep for a while
+ //}
+ } else {
+ // hmmmm....playing , but nothing to write , possible underrun or EOF
+ // a nice idea would be to use SNDCTL_DSP_GETODELAY here...
+ // but it appears to be broken on some audio devices
+ if(ioctl(m_soundFd,SNDCTL_DSP_GETOSPACE,&info) < 0)info.fragstotal = info.fragments; // dummy...but what should we do ?
+ if(info.fragstotal == info.fragments)
+ {
+ // underrun or EOF: close the device
+ stopPlaying();
+ }
+ }
+ } else {
+ // do we have anything to play ?
+ if(m_inSignalBuffer.size() > 0)
+ {
+ if(m_inSignalBuffer.size() >= m_pOpt->iPreBufferSize)
+ {
+ // yep...stuff to play... open the soundcard , if possible
+ startPlaying();
+
+ m_iLastSignalBufferSize = m_inSignalBuffer.size();
+ } else {
+ // have stuff to play , but it's not enough to fill the pre-buffer
+ //
+ struct timeval tv;
+ gettimeofday(&tv,0);
+
+ long int sigBufferTime = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+
+ if(m_inSignalBuffer.size() == m_iLastSignalBufferSize)
+ {
+ // the same signal buffer size... check the time
+ // m_pOpt->iPreBufferSize / 16 gives us the preBufferTime in msecs
+ // we calc the remaining preBufferTime by subtracting the
+ // size of buffer already filled and we also add 50 milliseconds... smart heuristic
+ int preBufferTime = ((m_pOpt->iPreBufferSize - m_iLastSignalBufferSize) / 16) + 50;
+ // if the buffer size hasn't changed since preBufferTime
+ // it's time to start playing anyway, since there is
+ // either a network stall or it was just a really short data stream
+ if((sigBufferTime - m_iLastSignalBufferTime) > preBufferTime)
+ {
+ startPlaying();
+ if(m_bPlaying)m_iLastSignalBufferSize = 0;
+ }
+ } else {
+ // signal buffer size differs...we have received new packets
+ // and still pre-buffering
+ m_iLastSignalBufferSize = m_inSignalBuffer.size();
+ m_iLastSignalBufferTime = sigBufferTime;
+ }
+ }
+
+ }
+ }
+
+ // Are we recording ?
+ if(m_bRecording)
+ {
+ fd_set rs;
+ FD_ZERO(&rs);
+ FD_SET(m_soundFd,&rs);
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 10;
+ int ret = select(m_soundFd + 1,&rs,0,0,&tv);
+ if(ret > 0)
+ {
+ // This is rather easy...
+ audio_buf_info info;
+ if(ioctl(m_soundFd,SNDCTL_DSP_GETISPACE,&info) < 0)
+ {
+ debug("Ispace failed");
+ info.fragments = 0; // dummy...
+ info.bytes = 0;
+ }
+
+ //debug("INFO: fragments: %d, fragstotal: %d, fragsize: %d, bytes: %d",info.fragments,info.fragstotal,info.fragsize,info.bytes);
+
+ if(info.fragments == 0 && info.bytes == 0)
+ {
+ // force a dummy read: the device needs to be triggered
+ info.fragments = 1;
+ }
+
+ if(info.fragments > 0)
+ {
+ int oldSize = m_outSignalBuffer.size();
+ int available = info.fragments * info.fragsize;
+ m_outSignalBuffer.addSize(available);
+ int readed = read(m_soundFd,m_outSignalBuffer.data() + oldSize,available);
+
+ if(readed < available)
+ {
+ // huh ? ...error ?
+ if(readed >= 0)m_outSignalBuffer.resize(oldSize + readed);
+ else {
+ if((errno == EINTR) || (errno == EAGAIN))
+ {
+ m_outSignalBuffer.resize(oldSize);
+ } else {
+//#warning "Critical error...do something reasonable!"
+ m_outSignalBuffer.resize(oldSize);
+ }
+ }
+ }
+/*
+ debug("Signal buffer:");
+ for(int i=0;i<200;i+=2)
+ {
+ if(i >= m_outSignalBuffer.size())break;
+ printf("%04x ",*(((unsigned short *)(m_outSignalBuffer.data() + i))));
+ if((i % 6) == 0)printf("\n");
+ }
+ debug("END\n");
+*/
+ m_pOpt->pCodec->encode(&m_outSignalBuffer,&m_outFrameBuffer);
+ }
+ }// else {
+ // Nothing to read
+ // m_uSleepTime += 100;
+ // }
+ }
+
+#endif // !COMPILE_DISABLE_DCC_VOICE
+ return true;
+}
+
+void KviDccVoiceThread::startRecording()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ //debug("Start recording");
+ if(m_bRecording)return; // already started
+ if(openSoundcardForReading())
+ {
+// debug("Posting event");
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(KVI_DCC_VOICE_THREAD_ACTION_START_RECORDING));
+ postEvent(parent(),e);
+
+ m_bRecording = true;
+ m_bRecordingRequestPending = false;
+ } else {
+ m_bRecordingRequestPending = true;
+ }
+#endif
+}
+
+void KviDccVoiceThread::stopRecording()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ //debug("Stop recording");
+ m_bRecordingRequestPending = false;
+ if(!m_bRecording)return; // already stopped
+
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(KVI_DCC_VOICE_THREAD_ACTION_STOP_RECORDING));
+ postEvent(parent(),e);
+
+ m_bRecording = false;
+ if(!m_bPlaying)closeSoundcard();
+#endif
+}
+
+void KviDccVoiceThread::startPlaying()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ //debug("Start playing");
+ if(m_bPlaying)return;
+
+ if(openSoundcardForWriting())
+ {
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(KVI_DCC_VOICE_THREAD_ACTION_START_PLAYING));
+ postEvent(parent(),e);
+ m_bPlaying = true;
+ }
+#endif
+}
+
+void KviDccVoiceThread::stopPlaying()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ //debug("Stop playing");
+ if(!m_bPlaying)return;
+
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(KVI_DCC_VOICE_THREAD_ACTION_STOP_PLAYING));
+ postEvent(parent(),e);
+
+ m_bPlaying = false;
+ if(!m_bRecording)closeSoundcard();
+#endif
+}
+
+void KviDccVoiceThread::run()
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ for(;;)
+ {
+// m_uSleepTime = 0;
+
+ // Dequeue events
+ while(KviThreadEvent * e = dequeueEvent())
+ {
+ if(e->id() == KVI_THREAD_EVENT_TERMINATE)
+ {
+ delete e;
+ goto exit_dcc;
+ } else if(e->id() == KVI_DCC_THREAD_EVENT_ACTION)
+ {
+ int * act = ((KviThreadDataEvent<int> *)e)->getData();
+ if(*act)startRecording();
+ else stopRecording();
+ delete act;
+ delete e;
+ } else {
+ // Other events are senseless to us
+ delete e;
+ }
+ }
+
+ if(!readWriteStep())goto exit_dcc;
+ if(!soundStep())goto exit_dcc;
+
+ m_pInfoMutex->lock();
+ m_iInputBufferSize = m_inSignalBuffer.size();
+ m_iOutputBufferSize = (m_outFrameBuffer.size() / m_pOpt->pCodec->encodedFrameSize()) * m_pOpt->pCodec->decodedFrameSize();
+ m_pInfoMutex->unlock();
+
+ // Actually the maximum that we can sleep here is
+ // around 500 usecs... = 0.0005 sec -> 8 bytes at 8 KHz
+
+ // if(m_uSleepTime)usleep(m_uSleepTime);
+
+ // Start recording if the request was not fulfilled yet
+ if(m_bRecordingRequestPending)startRecording();
+ }
+
+
+exit_dcc:
+
+#endif //! COMPILE_DISABLE_DCC_VOICE
+ closeSoundcard();
+ kvi_socket_close(m_fd);
+ m_fd = KVI_INVALID_SOCKET;
+}
+
+
+
+
+KviDccVoice::KviDccVoice(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name)
+: KviDccWindow(KVI_WINDOW_TYPE_DCCVOICE,pFrm,name,dcc)
+{
+ m_pDescriptor = dcc;
+ m_pSlaveThread = 0;
+
+ m_pSplitter = new QSplitter(Qt::Horizontal,this,"splitter");
+ m_pIrcView = new KviIrcView(m_pSplitter,pFrm,this);
+
+ m_pHBox = new KviTalHBox(this);
+
+ KviTalVBox * vbox = new KviTalVBox(m_pHBox);
+
+ m_pInputLabel = new QLabel(__tr2qs_ctx("Input buffer","dcc"),vbox);
+ m_pInputLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel);
+ m_pOutputLabel = new QLabel(__tr2qs_ctx("Output buffer","dcc"),vbox);
+ m_pOutputLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel);
+ vbox->setSpacing(1);
+
+ KviTalVBox * vbox2 = new KviTalVBox(m_pHBox);
+
+ m_pRecordingLabel = new QLabel(vbox2);
+ m_pRecordingLabel->setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_RECORD)));
+ m_pRecordingLabel->setEnabled(false);
+ m_pRecordingLabel->setFrameStyle(QFrame::Raised | QFrame::Panel);
+
+ m_pPlayingLabel = new QLabel(vbox2);
+ m_pPlayingLabel->setPixmap(*(g_pIconManager->getSmallIcon(KVI_SMALLICON_PLAY)));
+ m_pPlayingLabel->setEnabled(false);
+ m_pPlayingLabel->setFrameStyle(QFrame::Raised | QFrame::Panel);
+
+ vbox2->setSpacing(1);
+
+//#warning "The volume slider should be enabled only when receiving data"
+ m_pVolumeSlider = new QSlider(-100, 0, 10, 0, Qt::Vertical, m_pHBox, "dcc_voice_volume_slider");
+ m_pVolumeSlider->setValue(getMixerVolume());
+/* Update the tooltip */
+ setMixerVolume(m_pVolumeSlider->value());
+ m_pVolumeSlider->setMaximumWidth(16);
+ m_pVolumeSlider->setMaximumHeight(2*m_pPlayingLabel->height());
+ connect(m_pVolumeSlider, SIGNAL(valueChanged(int)), this, SLOT(setMixerVolume(int)));
+
+ m_pTalkButton = new QToolButton(m_pHBox);
+ m_pTalkButton->setEnabled(false);
+ m_pTalkButton->setToggleButton(true);
+#if QT_VERSION >= 300
+ QIconSet iset;
+ iset.setPixmap(*(g_pIconManager->getBigIcon(KVI_BIGICON_DISCONNECTED)),QIconSet::Large,QIconSet::Normal,QIconSet::Off);
+ iset.setPixmap(*(g_pIconManager->getBigIcon(KVI_BIGICON_CONNECTED)),QIconSet::Large,QIconSet::Normal,QIconSet::On);
+ m_pTalkButton->setIconSet(iset);
+#else
+ m_pTalkButton->setOffIconSet(*(g_pIconManager->getBigIcon(KVI_BIGICON_DISCONNECTED)));
+ m_pTalkButton->setOnIconSet(*(g_pIconManager->getBigIcon(KVI_BIGICON_CONNECTED)));
+#endif
+ m_pTalkButton->setUsesBigPixmap(true);
+
+ connect(m_pTalkButton,SIGNAL(toggled(bool)),this,SLOT(startOrStopTalking(bool)));
+
+ m_pHBox->setStretchFactor(vbox,1);
+ m_pHBox->setMargin(2);
+ m_pHBox->setSpacing(1);
+
+ //setFocusHandler(m_pIrcView,this);
+
+ m_pMarshal = new KviDccMarshal(this);
+ connect(m_pMarshal,SIGNAL(error(int)),this,SLOT(handleMarshalError(int)));
+ connect(m_pMarshal,SIGNAL(connected()),this,SLOT(connected()));
+ connect(m_pMarshal,SIGNAL(inProgress()),this,SLOT(connectionInProgress()));
+
+ m_pUpdateTimer = new QTimer();
+
+ startConnection();
+}
+
+KviDccVoice::~KviDccVoice()
+{
+ g_pDccBroker->unregisterDccWindow(this);
+ if(m_pSlaveThread)
+ {
+ m_pSlaveThread->terminate();
+ delete m_pSlaveThread;
+ m_pSlaveThread = 0;
+ }
+
+ KviThreadManager::killPendingEvents(this);
+
+ delete m_pUpdateTimer;
+// delete m_pDescriptor;
+// delete m_pMarshal;
+}
+
+
+void KviDccVoice::startConnection()
+{
+ if(!(m_pDescriptor->bActive))
+ {
+ // PASSIVE CONNECTION
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting a passive DCC VOICE connection","dcc"));
+ int ret = m_pMarshal->dccListen(m_pDescriptor->szListenIp,m_pDescriptor->szListenPort,m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ } else {
+ // ACTIVE CONNECTION
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Attempting an active DCC VOICE connection","dcc"));
+ int ret = m_pMarshal->dccConnect(m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),m_pDescriptor->bDoTimeout);
+ if(ret != KviError_success)handleMarshalError(ret);
+ }
+}
+
+void KviDccVoice::connectionInProgress()
+{
+ if(m_pDescriptor->bActive)
+ {
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Contacting host %Q on port %Q","dcc"),&(m_pDescriptor->szIp),&(m_pDescriptor->szPort));
+ } else {
+
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Listening on interface %Q port %Q","dcc"),
+ &(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+
+ if(m_pDescriptor->bSendRequest)
+ {
+ KviStr ip = !m_pDescriptor->szFakeIp.isEmpty() ? m_pDescriptor->szFakeIp : m_pDescriptor->szListenIp;
+ KviStr port = !m_pDescriptor->szFakePort.isEmpty() ? m_pDescriptor->szFakePort : m_pMarshal->localPort();
+//#warning "OPTION FOR SENDING 127.0.0.1 and so on (not an unsigned nuumber)"
+ struct in_addr a;
+ if(kvi_stringIpToBinaryIp(ip.ptr(),&a))ip.setNum(htonl(a.s_addr));
+
+ m_pDescriptor->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC VOICE %s %s %s %d%c",
+ m_pDescriptor->console()->connection()->encodeText(m_pDescriptor->szNick).data(),
+ 0x01,m_pDescriptor->szCodec.ptr(),
+ ip.ptr(),port.ptr(),m_pDescriptor->iSampleRate,0x01);
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Sent DCC VOICE (%s) request to %Q, waiting for the remote client to connect...","dcc"),
+ m_pDescriptor->szCodec.ptr(),&(m_pDescriptor->szNick));
+ } else output(KVI_OUT_DCCMSG,__tr2qs_ctx("DCC VOICE request not sent: awaiting manual connections","dcc"));
+ }
+}
+
+const QString & KviDccVoice::target()
+{
+ // This may change on the fly...
+ m_szTarget.sprintf("%s@%s:%s",
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data());
+ return m_szTarget;
+}
+
+void KviDccVoice::getBaseLogFileName(KviStr &buffer)
+{
+ buffer.sprintf("dccvoice_%s_%s_%s",m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szLocalFileName.utf8().data(),m_pDescriptor->szPort.utf8().data());
+}
+
+void KviDccVoice::fillCaptionBuffers()
+{
+ KviStr tmp(KviStr::Format,"DCC Voice %s@%s:%s %s",
+ m_pDescriptor->szNick.utf8().data(),m_pDescriptor->szIp.utf8().data(),m_pDescriptor->szPort.utf8().data(),
+ m_pDescriptor->szLocalFileName.utf8().data());
+
+ m_szPlainTextCaption = tmp;
+
+ m_szHtmlActiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextActive).name().ascii(),tmp.ptr());
+ m_szHtmlInactiveCaption.sprintf("<nobr><font color=\"%s\"><b>%s</b></font></nobr>",
+ KVI_OPTION_COLOR(KviOption_colorCaptionTextInactive).name().ascii(),tmp.ptr());
+}
+
+QPixmap * KviDccVoice::myIconPtr()
+{
+ return g_pIconManager->getSmallIcon(KVI_SMALLICON_DCCVOICE);
+}
+
+bool KviDccVoice::event(QEvent *e)
+{
+ if(e->type() == KVI_THREAD_EVENT)
+ {
+ switch(((KviThreadEvent *)e)->id())
+ {
+ case KVI_DCC_THREAD_EVENT_ERROR:
+ {
+ int * err = ((KviThreadDataEvent<int> *)e)->getData();
+ QString ssss = KviError::getDescription(*err);
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("ERROR: %Q","dcc"),&(ssss));
+ delete err;
+ m_pUpdateTimer->stop();
+ updateInfo();
+ m_pTalkButton->setEnabled(false);
+ m_pRecordingLabel->setEnabled(false);
+ m_pPlayingLabel->setEnabled(false);
+ return true;
+ }
+ break;
+ case KVI_DCC_THREAD_EVENT_MESSAGE:
+ {
+ KviStr * str = ((KviThreadDataEvent<KviStr> *)e)->getData();
+ outputNoFmt(KVI_OUT_DCCMSG,__tr_no_xgettext_ctx(str->ptr(),"dcc"));
+ delete str;
+ return true;
+ }
+ break;
+ case KVI_DCC_THREAD_EVENT_ACTION:
+ {
+ int * act = ((KviThreadDataEvent<int> *)e)->getData();
+ switch(*act)
+ {
+ case KVI_DCC_VOICE_THREAD_ACTION_START_RECORDING:
+ m_pRecordingLabel->setEnabled(true);
+ break;
+ case KVI_DCC_VOICE_THREAD_ACTION_STOP_RECORDING:
+ m_pRecordingLabel->setEnabled(false);
+ break;
+ case KVI_DCC_VOICE_THREAD_ACTION_START_PLAYING:
+ m_pPlayingLabel->setEnabled(true);
+ break;
+ case KVI_DCC_VOICE_THREAD_ACTION_STOP_PLAYING:
+ m_pPlayingLabel->setEnabled(false);
+ break;
+ }
+ delete act;
+ return true;
+ }
+ break;
+ default:
+ debug("Invalid event type %d received",((KviThreadEvent *)e)->id());
+ break;
+ }
+
+ }
+
+ return KviWindow::event(e);
+}
+
+void KviDccVoice::updateInfo()
+{
+ if(m_pSlaveThread)
+ {
+ m_pSlaveThread->m_pInfoMutex->lock();
+ int iOSize = m_pSlaveThread->m_iOutputBufferSize;
+ int iISize = m_pSlaveThread->m_iInputBufferSize;
+ m_pSlaveThread->m_pInfoMutex->unlock();
+ KviStr tmp(KviStr::Format,__tr_ctx("Input buffer: %d bytes","dcc"),iISize);
+ m_pInputLabel->setText(tmp.ptr());
+ tmp.sprintf(__tr_ctx("Output buffer: %d bytes","dcc"),iOSize);
+ m_pOutputLabel->setText(tmp.ptr());
+ }
+}
+
+void KviDccVoice::resizeEvent(QResizeEvent *e)
+{
+ int hght2 = m_pHBox->sizeHint().height();
+ m_pHBox->setGeometry(0,0,width(),hght2);
+ m_pSplitter->setGeometry(0,hght2,width(),height() - hght2);
+}
+
+QSize KviDccVoice::sizeHint() const
+{
+ int w = m_pIrcView->sizeHint().width();
+ int w2 = m_pHBox->sizeHint().width();
+ QSize ret(w > w2 ? w : w2, m_pIrcView->sizeHint().height() + m_pHBox->sizeHint().height());
+ return ret;
+}
+
+void KviDccVoice::handleMarshalError(int err)
+{
+ QString ssss = KviError::getDescription(err);
+ output(KVI_OUT_DCCERROR,__tr2qs_ctx("DCC Failed: %Q","dcc"),&ssss);
+ m_pTalkButton->setEnabled(false);
+ m_pTalkButton->setOn(false);
+ m_pRecordingLabel->setEnabled(false);
+ m_pPlayingLabel->setEnabled(false);
+}
+
+void KviDccVoice::connected()
+{
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Connected to %Q:%Q","dcc"),
+ &(m_pMarshal->remoteIp()),&(m_pMarshal->remotePort()));
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Local end is %Q:%Q","dcc"),
+ &(m_pMarshal->localIp()),&(m_pMarshal->localPort()));
+ if(!(m_pDescriptor->bActive))
+ {
+ m_pDescriptor->szIp = m_pMarshal->remoteIp();
+ m_pDescriptor->szPort = m_pMarshal->remotePort();
+ m_pDescriptor->szHost = m_pMarshal->remoteIp();
+ }
+ updateCaption();
+
+ connect(m_pUpdateTimer,SIGNAL(timeout()),this,SLOT(updateInfo()));
+ m_pUpdateTimer->start(1000);
+
+ KviDccVoiceThreadOptions * opt = new KviDccVoiceThreadOptions;
+
+
+ opt->pCodec = kvi_dcc_voice_get_codec(m_pDescriptor->szCodec.ptr());
+
+ output(KVI_OUT_DCCMSG,__tr2qs_ctx("Actual codec used is '%s'","dcc"),opt->pCodec->name());
+
+ opt->bForceHalfDuplex = KVI_OPTION_BOOL(KviOption_boolDccVoiceForceHalfDuplex);
+// opt->bForceDummyReadTrigger = false;
+ opt->iPreBufferSize = KVI_OPTION_UINT(KviOption_uintDccVoicePreBufferSize);
+ opt->szSoundDevice = KVI_OPTION_STRING(KviOption_stringDccVoiceSoundDevice).utf8().data();
+ opt->iSampleRate = m_pDescriptor->iSampleRate;
+
+ m_pSlaveThread = new KviDccVoiceThread(this,m_pMarshal->releaseSocket(),opt);
+ connect(m_pUpdateTimer,SIGNAL(timeout()),this,SLOT(updateInfo()));
+ m_pSlaveThread->start();
+
+ m_pTalkButton->setEnabled(true);
+}
+
+void KviDccVoice::stopTalking()
+{
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(0));
+ m_pSlaveThread->enqueueEvent(e);
+}
+
+void KviDccVoice::startTalking()
+{
+ KviThreadDataEvent<int> * e = new KviThreadDataEvent<int>(KVI_DCC_THREAD_EVENT_ACTION);
+ e->setData(new int(1));
+ m_pSlaveThread->enqueueEvent(e);
+}
+
+void KviDccVoice::startOrStopTalking(bool bStart)
+{
+ if(bStart)startTalking();
+ else stopTalking();
+}
+
+int KviDccVoice::getMixerVolume(void) const
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ int fd;
+ int ret;
+ int left; //, right;
+ int req;
+
+ if((fd = ::open(KVI_OPTION_STRING(KviOption_stringDccVoiceMixerDevice).utf8().data(), O_RDONLY)) == -1)
+ {
+ return 0;
+ }
+
+ req = KVI_OPTION_BOOL(KviOption_boolDccVoiceVolumeSliderControlsPCM) ? SOUND_MIXER_READ_PCM : SOUND_MIXER_READ_VOLUME;
+
+ if(::ioctl(fd,req,&ret))
+ {
+ ::close(fd);
+ return 0;
+ }
+
+ left = (ret & 0x00ff);
+// right = (ret & 0xff00) >> 8;
+
+ ::close(fd);
+
+ return -left;
+#else
+ return 0;
+#endif
+}
+
+void KviDccVoice::setMixerVolume(int vol)
+{
+#ifndef COMPILE_DISABLE_DCC_VOICE
+ int fd;
+ int val;
+ int req;
+
+ if((fd = ::open(KVI_OPTION_STRING(KviOption_stringDccVoiceMixerDevice).utf8().data(), O_WRONLY)) == -1)
+ return;
+
+ req = KVI_OPTION_BOOL(KviOption_boolDccVoiceVolumeSliderControlsPCM) ? SOUND_MIXER_WRITE_PCM : SOUND_MIXER_WRITE_VOLUME;
+
+ val = (-vol << 8) | -vol;
+ ::ioctl(fd, req, &val);
+ ::close(fd);
+
+ QString s;
+ s.sprintf(__tr_ctx("Volume: %i","dcc"), -vol);
+ QToolTip::add(m_pVolumeSlider, s);
+#endif
+}
+
+
+/* The code below doesn't work. Guess I have to catch some other widget's focusInEvent. Which one ? */
+/* The point is to move the volume slider to correct position if for example user switched to
+ * another KVirc window, fired up xmms, changed the volume, and returned to our dcc voice window */
+void KviDccVoice::focusInEvent(QFocusEvent *e)
+{
+// debug("focusInEvent()");
+ m_pVolumeSlider->setValue(getMixerVolume());
+ setMixerVolume(m_pVolumeSlider->value());
+
+ KviWindow::focusInEvent(e);
+}
+
+#include "m_voice.moc"
diff --git a/src/modules/dcc/voice.h b/src/modules/dcc/voice.h
new file mode 100644
index 00000000..c74c677c
--- /dev/null
+++ b/src/modules/dcc/voice.h
@@ -0,0 +1,440 @@
+#ifndef _VOICE_H_
+#define _VOICE_H_
+//
+// File : voice.h
+// Creation date : Thu Aug 23 04:08:10 2001 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_window.h"
+
+#include "kvi_databuffer.h"
+#include "kvi_sockettype.h"
+
+#include "codec.h"
+#include "descriptor.h"
+#include "thread.h"
+#include "window.h"
+
+#include "kvi_tal_hbox.h"
+#include <qlabel.h>
+#include <qtoolbutton.h>
+#include <qtimer.h>
+
+#ifndef _DCC_VOICE_CPP_
+ extern bool kvi_dcc_voice_is_valid_codec(const char * codecName);
+#endif
+
+#define KVI_DCC_VOICE_THREAD_ACTION_START_RECORDING 0
+#define KVI_DCC_VOICE_THREAD_ACTION_STOP_RECORDING 1
+#define KVI_DCC_VOICE_THREAD_ACTION_START_PLAYING 2
+#define KVI_DCC_VOICE_THREAD_ACTION_STOP_PLAYING 3
+
+typedef struct _KviDccVoiceThreadOptions
+{
+ bool bForceHalfDuplex;
+ int iPreBufferSize;
+ int iSampleRate;
+ KviStr szSoundDevice;
+ KviDccVoiceCodec * pCodec;
+} KviDccVoiceThreadOptions;
+
+class KviDccVoiceThread : public KviDccThread
+{
+ friend class KviDccVoice;
+public:
+ KviDccVoiceThread(KviWindow * wnd,kvi_socket_t fd,KviDccVoiceThreadOptions * opt);
+ ~KviDccVoiceThread();
+protected:
+// bool m_bUseGsm;
+ KviDccVoiceThreadOptions * m_pOpt;
+ int m_soundFd;
+ int m_soundFdMode;
+ KviDataBuffer m_outFrameBuffer;
+ KviDataBuffer m_inFrameBuffer;
+ KviDataBuffer m_inSignalBuffer;
+ KviDataBuffer m_outSignalBuffer;
+ bool m_bPlaying;
+ bool m_bRecording;
+ bool m_bRecordingRequestPending;
+ bool m_bSoundcardChecked;
+ int m_iLastSignalBufferSize;
+ long m_iLastSignalBufferTime;
+// unsigned int m_uSleepTime;
+ KviMutex * m_pInfoMutex;
+ // stuff protected by the mutex:
+ int m_iInputBufferSize;
+ int m_iOutputBufferSize;
+protected:
+ bool checkSoundcard();
+ bool openSoundcardWithDuplexOption(int openMode,int failMode);
+ bool openSoundcard(int mode);
+ bool openSoundcardForWriting();
+ bool openSoundcardForReading();
+ void closeSoundcard();
+ bool readWriteStep();
+ bool soundStep();
+ void startRecording();
+ void stopRecording();
+ void startPlaying();
+ void stopPlaying();
+ virtual void run();
+};
+
+class KviDccMarshal;
+class QSlider;
+
+class KviDccVoice : public KviDccWindow
+{
+ Q_OBJECT
+public:
+ KviDccVoice(KviFrame *pFrm,KviDccDescriptor * dcc,const char * name);
+ ~KviDccVoice();
+protected:
+ KviTalHBox * m_pHBox;
+ QSlider * m_pVolumeSlider;
+ QLabel * m_pInputLabel;
+ QLabel * m_pOutputLabel;
+ QLabel * m_pRecordingLabel;
+ QLabel * m_pPlayingLabel;
+ QToolButton * m_pTalkButton;
+ QTimer * m_pUpdateTimer;
+ QString m_szTarget;
+ KviDccVoiceThread * m_pSlaveThread;
+protected:
+ virtual void focusInEvent(QFocusEvent *);
+ virtual const QString & target();
+ virtual void fillCaptionBuffers();
+ virtual QPixmap * myIconPtr();
+ virtual void resizeEvent(QResizeEvent *e);
+ virtual QSize sizeHint() const;
+ virtual bool event(QEvent *e);
+ virtual void getBaseLogFileName(KviStr &buffer);
+ void startTalking();
+ void stopTalking();
+ void startConnection();
+ int getMixerVolume(void) const;
+protected slots:
+ void handleMarshalError(int err);
+ void connected();
+ void updateInfo();
+ void startOrStopTalking(bool bStart);
+ void setMixerVolume(int);
+ void connectionInProgress();
+// void stopTalking();
+};
+
+#if 0
+
+
+
+/*
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CODEC DEFINITION
+
+ Sample rate = samples/sec (ex. 8000)
+ Sample size = bits (ex. 16 bits)
+ Sample endianness = le/be
+
+ Sample compressor = name
+
+ <rate>:<bits>:<endianness>:<compressor>
+
+ 8000:16:le:null
+ 8000:16:le:gsm
+ 8000:16:le:adpcm
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class KviVoiceParty
+{
+public:
+ KviVoiceParty(const QString &szNick,const QString &szIp,unsigned short uPort);
+ ~KviVoiceParty();
+protected:
+ QString m_szIp;
+ unsigned short m_uPort;
+ QString m_szNick;
+ KviPointerList<KviVoiceParty> * m_pChildrenTree;
+public:
+ const QString & ip(){ return m_szIp; };
+ unsigned short port(){ return m_uPort; };
+ const QString & nick(){ return m_szNick; };
+ void addChild(KviVoiceParty * pChild);
+};
+
+
+KviVoiceParty::KviVoiceParty(const QString &szNick,const QString &szIp,unsigned short uPort)
+: m_szIp(szIp), m_uPort(uPort), m_szNick(szNick)
+{
+ m_pChildrenTree = 0;
+}
+
+KviVoiceParty::~KviVoiceParty()
+{
+ if(m_pChildrenTree)delete m_pChildrenTree;
+}
+
+void KviVoiceParty::addChild(KviVoiceParty * pChild)
+{
+ if(!m_pChildrenTree)
+ {
+ m_pChildrenTree = new KviPointerList<KviVoiceParty>;
+ m_pChildrenTree->setAutoDelete(true);
+ }
+ m_pChildrenTree->append(pChild);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class KviVoiceAudioEncoder
+{
+public:
+ KviVoiceAudioEncoder();
+ ~KviVoiceAudioEncoder();
+public:
+
+};
+
+class KviVoiceAudioDecoder
+{
+public:
+ KviVoiceAudioDecoder();
+ ~KviVoiceAudioDecoder();
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class KviVoiceLink
+{
+public:
+ KviVoiceLink(KviVoiceParty * pRemoteParty);
+ ~KviVoiceLink();
+protected:
+ QString m_szId;
+ KviVoiceParty * m_pRemoteParty;
+ KviVoiceAudioEncoder * m_pAudioEncoder;
+ KviVoiceAudioDecoder * m_pAudioDecoder;
+public:
+ const QStirng & id(){ return m_szId; };
+ KviVoiceParty * remoteParty(){ return m_pRemoteParty; };
+};
+
+KviVoiceLink::KviVoiceLink(KviVoiceParty * pRemoteParty)
+{
+ KviQString::sprintf("%Q:%u",&(pRemoteParty->nick()),pRemoteParty->port());
+ m_pRemoteParty = pRemoteParty;
+ m_pAudioEncoder = 0;
+ m_pAudioDecoder = 0;
+}
+
+KviVoiceLink::~KviVoiceLink()
+{
+ delete m_pRemoteParty;
+ if(m_pAudioEncoder)delete m_pAudioEncoder;
+ if(m_pAudioDecoder)delete m_pAudioDecoder;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+class KviVoice_r8000s16eL_to_r8000s16eB_Transformer
+{
+
+}
+
+class KviVoice_r8000s16eB_to_r8000s16eL_Transformer
+{
+
+}
+
+class KviVoice_r11025s16eL_to_r11025s16eB_Transformer
+{
+
+}
+
+class KviVoice_r11025s16eB_to_r11025s16eL_Transformer
+{
+
+}
+
+class KviVoice_r11025s16eL_to_r8000s16eL_Transformer
+{
+
+}
+
+class KviVoice_r11025s16eL_to_r8000s16eB_Transformer
+{
+
+}
+
+class KviVoice_r11025s16eB_to_r8000s16eL_Transformer
+{
+
+}
+
+class KviVoice_r8000s16eL_to_r11025s16eL_Transformer
+{
+
+}
+
+class KviVoice_r8000s16eL_to_r11025s16eB_Transformer
+{
+
+}
+
+class KviVoice_r8000s16eB_to_r11025s16eL_Transformer
+{
+
+}
+
+
+
+class KviVoiceConference
+{
+public:
+ KviVoiceConference();
+ ~KviVoiceConference();
+public:
+ KviPointerList<KviVoiceLink> * m_pLinks;
+ KviPointerHashTable<QString,KviVoiceLink> *
+ SOCKET m_hUdpSocket;
+ QString m_szLastError;
+
+ unsigned int m_uLocalAudioSampleRate; // samples/sec
+ unsigned int m_uLocalAudioSampleSize; // bits
+ unsigned int m_uLocalAudioEndianness; // 0 = le, 1 = be
+
+public:
+ void conferenceThread();
+protected:
+ void conferenceThreadMain();
+ bool setupUdpSocket();
+};
+
+KviVoiceConference::KviVoiceConference()
+{
+ m_pLinks = new KviPointerList<KviVoiceLink>;
+ m_pLinks->setAutoDelete(true);
+}
+
+KviVoiceConference::~KviVoiceConference()
+{
+ delete m_pLinks;
+}
+
+bool KviVoiceConference::setupUdpSocket()
+{
+ return true;
+}
+
+void KviVoiceConference::shutdownUdpSocket()
+{
+}
+
+void KviVoiceConference::conferenceThreadMain()
+{
+ for(;;)
+ {
+ readAndDecompressIncomingDataForEveryLink();
+
+ mixIncomingDataToASingleStream();
+ playIncomingDataSingleStream();
+
+ readLocalAudioStream();
+
+ foreach(link)
+ {
+ mixLocalAndOtherIncomingDataStreams()
+ compressAndSendOtherIncomingDataStreams()
+ }
+
+ }
+}
+
+void KviVoiceConference::conferenceThread()
+{
+ if(!setupUdpSocket())return;
+
+ conferenceThreadMain();
+
+ shutdownUdpSocket();
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+*/
+
+// DCC VOICE NG proto
+
+//
+// tcp control connection
+// --> HELLO: DccVoice protocol header
+// <-- HELLO: DccVoice protocol header
+// --> IACCEPT: Codec-description|CodecId,Codec-description|CodecID,Codec... (in order of preference)
+// <-- IACCEPT: Codec-description|CodecID,Codec-description|CodecId,Codec... (in order of preference)
+// --> MYADDRESS
+// <-- MYADDRESS
+// --> YOURIDIS: <local id for the remote end> (CID)
+// <-- YOURIDIS: <local id for the remote end> (CID)
+
+// Audio is sent in blocks broken in chunks broken in udp packets
+// Each block is a set of consecutive audio chunks that theoretically
+// should be played consecutively.
+// Each packet in a chunk has an ordinal
+// Chunks must be relatively short in order
+// to allow a remote end that looses a packet to
+// synchronize after a short period of time
+// The maximum number of packets in a chunk is 65535 (but a chunk should be no more than 24-32 KBytes in size
+// and in general they should be as small as possible, even one packet per chunk, if the codec allows it)
+// Each chunk should be encoded independently of the others
+// We can switch codec at each chunk (but not at each packet)
+// When some packets are lost we loose the entire chunk
+// A block is completly synchronized in time (unless we loose some chunks: in that case
+// we may decide to synchronize with silence or insert a glitch...)
+// Decoding never depends on the future
+
+// Each packet should be decompressable (eventually dependently on the previous in the chunk)
+// but playable independently
+
+// start UDP stream
+
+// UDP Packet format:
+
+// <magic byte>: byte
+// <magic byte>: byte
+// <local id>: word
+// <payload len>: word
+// <payload>
+
+
+// Payload format:
+
+// <codec id>: word
+// <ordinal in a chunk>: word (0 = beginning of a chunk)
+
+// read raw audio data at sample rate X, sample size Y
+// multiplex data always at this sample rate and sample size
+
+
+#endif
+
+#endif //_VOICE_H_
diff --git a/src/modules/dcc/window.cpp b/src/modules/dcc/window.cpp
new file mode 100644
index 00000000..4abd7f99
--- /dev/null
+++ b/src/modules/dcc/window.cpp
@@ -0,0 +1,53 @@
+//
+// File : window.cpp
+// Creation date : Fri Jul 26 02:04:40 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "window.h"
+#include "kvi_tal_hbox.h"
+
+KviDccWindow::KviDccWindow(int type,KviFrame * lpFrm,const char * name,KviDccDescriptor * d)
+: KviWindow(type,lpFrm,name)
+{
+ m_pDescriptor = d;
+ m_pDescriptor->setWindow(this);
+ m_pMarshal = 0;
+ m_pButtonBox = new KviTalHBox(this);
+ createTextEncodingButton(m_pButtonBox);
+}
+
+KviDccWindow::~KviDccWindow()
+{
+ if(m_pMarshal)delete m_pMarshal;
+ if(m_pDescriptor)delete m_pDescriptor;
+}
+
+KviWindow * KviDccWindow::dccMarshalOutputWindow()
+{
+ return this;
+}
+
+const char * KviDccWindow::dccMarshalOutputContextString()
+{
+ static const char * static_context = "DCC";
+ return static_context;
+}
+
+#include "m_window.moc"
diff --git a/src/modules/dcc/window.h b/src/modules/dcc/window.h
new file mode 100644
index 00000000..b4bfa43d
--- /dev/null
+++ b/src/modules/dcc/window.h
@@ -0,0 +1,49 @@
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+//
+// File : window.h
+// Creation date : Fri Jul 26 02:04:39 2002 GMT by Szymon Stefanek
+//
+// This file is part of the KVirc irc client distribution
+// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net)
+//
+// This program is FREE software. You can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your opinion) any later version.
+//
+// This program is distributed in the HOPE that it will be USEFUL,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+// See the GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, write to the Free Software Foundation,
+// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#include "kvi_window.h"
+
+#include "descriptor.h"
+#include "marshal.h"
+
+class KviDccWindow : public KviWindow , public KviDccMarshalOutputContext
+{
+ Q_OBJECT
+public:
+ KviDccWindow(int type,KviFrame * lpFrm,const char * name,KviDccDescriptor * d);
+ ~KviDccWindow();
+protected:
+ KviDccDescriptor * m_pDescriptor;
+ KviDccMarshal * m_pMarshal;
+public:
+ KviDccDescriptor * descriptor(){ return m_pDescriptor; };
+ const KviDccMarshal * marshal(){ return m_pMarshal; };
+
+ virtual KviWindow * dccMarshalOutputWindow();
+ virtual const char * dccMarshalOutputContextString();
+};
+
+
+
+#endif //_WINDOW_H_