summaryrefslogtreecommitdiffstats
path: root/src/modules/dcc/broker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/dcc/broker.cpp')
-rw-r--r--src/modules/dcc/broker.cpp898
1 files changed, 898 insertions, 0 deletions
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"