//============================================================================= // // File : DccBroker.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) 2000-2010 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 option) 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 "DccBroker.h" #include "DccDialog.h" #include "DccChatWindow.h" #include "DccFileTransfer.h" #include "DccVoiceWindow.h" #ifndef COMPILE_DISABLE_DCC_VIDEO #include "DccVideoWindow.h" #endif #ifdef COMPILE_DCC_CANVAS #include "DccCanvasWindow.h" #endif #include "KviApplication.h" #include "KviMainWindow.h" #include "KviLocale.h" #include "KviFileDialog.h" #include "KviOptions.h" #include "KviConsoleWindow.h" #include "KviFileUtils.h" #include "kvi_out.h" #include "KviMediaManager.h" #include "KviIrcConnection.h" #include "KviSharedFilesManager.h" #include "KviIrcMessage.h" #include #include extern KVIRC_API KviMediaManager * g_pMediaManager; extern KVIRC_API KviSharedFilesManager * g_pSharedFilesManager; //#warning "The broker might lookup the remote host name" DccBroker::DccBroker() : QObject(nullptr) { setObjectName("dcc_broker"); DccFileTransfer::init(); m_pBoxList = new KviPointerList; m_pBoxList->setAutoDelete(false); m_pDccWindowList = new KviPointerList; m_pDccWindowList->setAutoDelete(false); m_pZeroPortTags = new KviPointerHashTable(17); m_pZeroPortTags->setAutoDelete(true); } DccBroker::~DccBroker() { delete m_pZeroPortTags; while(m_pBoxList->first()) delete m_pBoxList->first(); delete m_pBoxList; m_pBoxList = nullptr; while(m_pDccWindowList->first()) delete m_pDccWindowList->first(); delete m_pDccWindowList; DccFileTransfer::done(); } KviDccZeroPortTag * DccBroker::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 * DccBroker::findZeroPortTag(const QString & szTag) { KviDccZeroPortTag * t = m_pZeroPortTags->find(szTag); if(!t) return nullptr; if(t->m_tTimestamp.secsTo(QDateTime::currentDateTime()) > 180) { // too late man... m_pZeroPortTags->remove(szTag); return nullptr; } return t; } void DccBroker::removeZeroPortTag(const QString & szTag) { m_pZeroPortTags->remove(szTag); } unsigned int DccBroker::dccBoxCount() { return m_pBoxList->count(); } void DccBroker::unregisterDccWindow(KviWindow * wnd) { m_pDccWindowList->removeRef(wnd); } void DccBroker::unregisterDccBox(DccDialog * box) { //qDebug("Forgetting box %d",box); m_pBoxList->removeRef(box); } void DccBroker::cancelDcc(DccDescriptor * dcc) { delete dcc; dcc = nullptr; } void DccBroker::cancelDcc(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); delete dcc; dcc = nullptr; } // // RSEND // void DccBroker::rsendManage(DccDescriptor * dcc) { // We need the filename... QFileInfo fi(dcc->szLocalFileName); if(fi.exists()) rsendExecute(nullptr, dcc); else rsendAskForFileName(dcc); } void DccBroker::rsendAskForFileName(DccDescriptor * dcc) { QStringList filenames; if( KviFileDialog::askForOpenFileNames( filenames, __tr2qs_ctx("Choose Files to Send - KVIrc", "dcc"), QString(), QString(), false, true, g_pMainWindow)) { if(filenames.count() > 0) { DccDescriptor * d; QStringList::Iterator it = filenames.begin(); while(it != filenames.end()) { d = new DccDescriptor(*dcc); d->szLocalFileName = *(it); d->szLocalFileName = d->szLocalFileName.trimmed(); ++it; if(d->szLocalFileName.isEmpty()) cancelDcc(d); else rsendExecute(d); } delete dcc; } } else { cancelDcc(dcc); } } void DccBroker::rsendExecute(DccDescriptor * 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.toLatin1()); // latin1() should be ok here szTag = t->m_szTag; // DCC [ST]SEND dcc->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s 2130706433 0 %s %s%c", dcc->console()->connection()->encodeText(dcc->szNick).data(), 0x01, dcc->console()->connection()->encodeText(dcc->szType).data(), dcc->console()->connection()->encodeText(fName).data(), dcc->console()->connection()->encodeText(QString::number(fi.size())).data(), dcc->console()->connection()->encodeText(szTag).data(), 0x01); } else { dcc->console()->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %s%c", dcc->console()->connection()->encodeText(dcc->szNick).data(), 0x01, dcc->console()->connection()->encodeText(dcc->szType).data(), dcc->console()->connection()->encodeText(fName).data(), dcc->console()->connection()->encodeText(QString::number(fi.size())).data(), 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 DccBroker::rsendExecute(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); rsendExecute(dcc); } // // DCC CHAT // void DccBroker::handleChatRequest(DccDescriptor * dcc) { if(!dcc->bAutoAccept) { // FIXME: better message ? Secure Direct Client Connection...eventually // need confirmation QString tmp = __tr2qs_ctx( "%1 [%2@%3] requests a " "Direct Client Connection in %4 mode.
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szType); #ifdef COMPILE_SSL_SUPPORT if(dcc->bIsSSL) tmp += __tr2qs_ctx("The connection will be secured using SSL.
", "dcc"); #endif if(dcc->isZeroPortRequest()) { tmp += __tr2qs_ctx( "You will be the passive side of the connection.
", "dcc"); } else { tmp += __tr2qs_ctx( "The connection target will be host %1 on port %2
", "dcc") .arg(dcc->szIp, dcc->szPort); } QString caption = __tr2qs_ctx("DCC %1 Request - KVIrc", "dcc").arg(dcc->szType); DccAcceptDialog * box = new DccAcceptDialog(this, dcc, tmp, caption); m_pBoxList->append(box); connect(box, SIGNAL(accepted(DccDialog *, DccDescriptor *)), this, SLOT(executeChat(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(rejected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); box->show(); } else { // auto accept executeChat(nullptr, dcc); } } void DccBroker::executeChat(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(!g_pApp->windowExists(dcc->console())) { // rebind to the first available console.... dcc->setConsole(g_pApp->activeConsole()); } KviCString szSubProto = dcc->szType; szSubProto.toLower(); QString tmp = QString("DCC: %1 %2@%3:%4").arg(szSubProto.ptr(), dcc->szNick, dcc->szIp, dcc->szPort); DccChatWindow * chat = new DccChatWindow(dcc, tmp.toUtf8().data()); bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat) || (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChatWhenAutoAccepted))); g_pMainWindow->addWindow(chat, !bMinimized); m_pDccWindowList->append(chat); } // // ACTIVE VOICE // void DccBroker::activeVoiceManage(DccDescriptor * dcc) { if(!dcc->bAutoAccept) { // need confirmation QString tmp = __tr2qs_ctx( "%1 [%2@%3] requests a
" "Direct Client Connection in VOICE mode.
" "The connection target will be host %4 on port %5
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szIp, dcc->szPort); DccAcceptDialog * box = new DccAcceptDialog(this, dcc, tmp, __tr2qs_ctx("DCC VOICE request", "dcc")); m_pBoxList->append(box); connect(box, SIGNAL(accepted(DccDialog *, DccDescriptor *)), this, SLOT(activeVoiceExecute(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(rejected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); box->show(); } else { // auto accept activeVoiceExecute(nullptr, dcc); } } void DccBroker::activeVoiceExecute(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(!g_pApp->windowExists(dcc->console())) { // rebind to the first available console.... dcc->setConsole(g_pApp->activeConsole()); } KviCString tmp(KviCString::Format, "DCC: voice %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccVoiceWindow * v = new DccVoiceWindow(dcc, tmp.ptr()); bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVoice) || (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVoiceWhenAutoAccepted))); g_pMainWindow->addWindow(v, !bMinimized); m_pDccWindowList->append(v); } // // PASSIVE VOICE // void DccBroker::passiveVoiceExecute(DccDescriptor * dcc) { KviCString tmp(KviCString::Format, "DCC: voice %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccVoiceWindow * v = new DccVoiceWindow(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); g_pMainWindow->addWindow(v, !bMinimized); m_pDccWindowList->append(v); } // // ACTIVE VIDEO // #ifndef COMPILE_DISABLE_DCC_VIDEO void DccBroker::activeVideoManage(DccDescriptor * dcc) { if(!dcc->bAutoAccept) { // need confirmation QString tmp = __tr2qs_ctx( "%1 [%2@%3] requests a
" "Direct Client Connection in VIDEO mode.
" "The connection target will be host %4 on port %5
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szIp, dcc->szPort); DccAcceptDialog * box = new DccAcceptDialog(this, dcc, tmp, __tr2qs_ctx("DCC VIDEO request", "dcc")); m_pBoxList->append(box); connect(box, SIGNAL(accepted(DccDialog *, DccDescriptor *)), this, SLOT(activeVideoExecute(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(rejected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); box->show(); } else { // auto accept activeVideoExecute(0, dcc); } } void DccBroker::activeVideoExecute(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(!g_pApp->windowExists(dcc->console())) { // rebind to the first available console.... dcc->setConsole(g_pApp->activeConsole()); } KviCString tmp(KviCString::Format, "DCC: video %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccVideoWindow * v = new DccVideoWindow(dcc, tmp.ptr()); bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVideo) || (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccVideoWhenAutoAccepted))); g_pMainWindow->addWindow(v, !bMinimized); m_pDccWindowList->append(v); #else void DccBroker::activeVideoExecute(DccDialog *, DccDescriptor *) { #endif } // // PASSIVE VIDEO // #ifndef COMPILE_DISABLE_DCC_VIDEO void DccBroker::passiveVideoExecute(DccDescriptor * dcc) { KviCString tmp(KviCString::Format, "DCC: video %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccVideoWindow * v = new DccVideoWindow(dcc, tmp.ptr()); //#warning "Create minimized dcc video ?... or maybe it's too much ? :)" bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccChat); g_pMainWindow->addWindow(v, !bMinimized); m_pDccWindowList->append(v); #else void DccBroker::passiveVideoExecute(DccDescriptor *) { #endif } // // ACTIVE CANVAS // #ifdef COMPILE_DCC_CANVAS void DccBroker::activeCanvasManage(DccDescriptor * dcc) { if(!dcc->bAutoAccept) { // need confirmation QString tmp = __tr2qs_ctx( "%1 [%2@%3] requests a
" "Direct Client Connection in CANVAS mode.
" "The connection target will be host %4 on port %5
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szIp, dcc->szPort); DccAcceptDialog * box = new DccAcceptDialog(this, dcc, tmp, __tr2qs_ctx("DCC CANVAS request", "dcc")); m_pBoxList->append(box); connect(box, SIGNAL(accepted(DccDialog *, DccDescriptor *)), this, SLOT(activeCanvasExecute(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(rejected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); box->show(); } else { // auto accept activeCanvasExecute(0, dcc); } } #endif #ifdef COMPILE_DCC_CANVAS void DccBroker::activeCanvasExecute(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(!g_pApp->windowExists(dcc->console())) { // rebind to the first available console.... dcc->setConsole(g_pApp->activeConsole()); } KviCString tmp(KviCString::Format, "DCC: canvas %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccCanvasWindow * cnv = new DccCanvasWindow(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))); g_pMainWindow->addWindow(cnv, !bMinimized); m_pDccWindowList->append(cnv); #else void DccBroker::activeCanvasExecute(DccDialog *, DccDescriptor *) { #endif } // // PASSIVE CANVAS // #ifdef COMPILE_DCC_CANVAS void DccBroker::passiveCanvasExecute(DccDescriptor * dcc) { KviCString tmp(KviCString::Format, "DCC: canvas %s@%s:%s", dcc->szNick.toUtf8().data(), dcc->szIp.toUtf8().data(), dcc->szPort.toUtf8().data()); DccCanvasWindow * cnv = new DccCanvasWindow(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); g_pMainWindow->addWindow(cnv, !bMinimized); m_pDccWindowList->append(cnv); } #endif // // SEND // void DccBroker::recvFileManage(DccDescriptor * dcc) { if(dcc->bIsIncomingAvatar) { bool bOk; quint64 size = dcc->szFileSize.toULongLong(&bOk); if(bOk) { if(size >= KVI_OPTION_UINT(KviOption_uintMaximumRequestedAvatarSize)) { cancelDcc(nullptr, dcc); return; } } } if(!dcc->bAutoAccept) { // need confirmation QString tmp; if(dcc->bActive) { // Normal active send: we will be connecting tmp = __tr2qs_ctx( "%1 [%2@%3] " "wants to send you the file " "'%4', " "%5 large.
" "The connection target will be host %6 on port %7
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szFileName) .arg(KviQString::makeSizeReadable(dcc->szFileSize.toULongLong())) .arg(dcc->szIp, dcc->szPort); } else { // passive: we will be listening! tmp = __tr2qs_ctx( "%1 [%2@%3] " "wants to send you the file " "'%4', " "%5 large.
" "You will be the passive side of the connection.
", "dcc") .arg(dcc->szNick, dcc->szUser, dcc->szHost, dcc->szFileName) .arg(KviQString::makeSizeReadable(dcc->szFileSize.toULongLong())); } if(dcc->bIsIncomingAvatar) { tmp += __tr2qs_ctx( "Note:" "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 boolAutoAcceptIncomingAvatars to true.
", "dcc"); } //#warning "Maybe remove the pending avatar if rejected ?" QString title = __tr2qs_ctx("DCC %1 Request - KVIrc", "dcc").arg(dcc->szType); DccAcceptDialog * box = new DccAcceptDialog(this, dcc, tmp, title); m_pBoxList->append(box); connect(box, SIGNAL(accepted(DccDialog *, DccDescriptor *)), this, SLOT(chooseSaveFileName(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(rejected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); 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(nullptr, dcc); } } void DccBroker::chooseSaveFileName(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); // Lookup the suggested save directory dcc->szLocalFileName = ""; if(dcc->bIsIncomingAvatar) g_pApp->getLocalKvircDirectory(dcc->szLocalFileName, KviApplication::Avatars); else { if(KVI_OPTION_BOOL(KviOption_boolUseIncomingDccMediaTypeSavePath)) { g_pMediaManager->lock(); if(KviMediaType * mt = g_pMediaManager->findMediaType(dcc->szFileName.toUtf8().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); QString cleanNick = dcc->szNick; KviFileUtils::cleanFileName(cleanNick); dcc->szLocalFileName.append(cleanNick); KviFileUtils::adjustFilePath(dcc->szLocalFileName); } KviFileUtils::makeDir(dcc->szLocalFileName); } } g_pMediaManager->unlock(); } if(dcc->szLocalFileName.isEmpty()) { g_pApp->getLocalKvircDirectory(dcc->szLocalFileName, KviApplication::Incoming); if(KVI_OPTION_BOOL(KviOption_boolSortReceivedByDccFilesByNicks)) { KviQString::ensureLastCharIs(dcc->szLocalFileName, KVI_PATH_SEPARATOR_CHAR); QString cleanNick = dcc->szNick; KviFileUtils::cleanFileName(cleanNick); dcc->szLocalFileName.append(cleanNick); KviFileUtils::adjustFilePath(dcc->szLocalFileName); KviFileUtils::makeDir(dcc->szLocalFileName); } } } KviFileUtils::adjustFilePath(dcc->szLocalFileName); KviQString::ensureLastCharIs(dcc->szLocalFileName, KVI_PATH_SEPARATOR_CHAR); if(!(dcc->bAutoAccept)) { QString cleanFilename = dcc->szFileName; KviFileUtils::cleanFileName(cleanFilename); dcc->szLocalFileName.append(cleanFilename); if(KviFileDialog::askForSaveFileName( dcc->szLocalFileName, __tr2qs_ctx("Choose Files to Save - KVIrc", "dcc"), dcc->szLocalFileName, QString(), false, false, true, g_pMainWindow)) { renameOverwriteResume(nullptr, dcc); } else { cancelDcc(dcc); } } else { // auto accept // WE choose the filename QString cleanFilename = dcc->szFileName; KviFileUtils::cleanFileName(cleanFilename); dcc->szLocalFileName.append(cleanFilename); if(_OUTPUT_VERBOSE) { dcc->console()->output(KVI_OUT_DCCMSG, __tr2qs_ctx("Auto-saving DCC %Q file %Q as %Q", "dcc"), &(dcc->szType), &(dcc->szFileName), &(dcc->szLocalFileName)); } renameOverwriteResume(nullptr, dcc); } } void DccBroker::renameOverwriteResume(DccDialog * box, DccDescriptor * 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; quint64 iRemoteSize = dcc->szFileSize.toULongLong(&bOk); if(!bOk) iRemoteSize = 0; if(!dcc->bAutoAccept) { QString tmp; bool bDisableResume = false; if( (!bOk) || // remote size is unknown (iRemoteSize > (quint64)fi.size()) // or it is larger than the actual size on disk ) { tmp = __tr2qs_ctx( "The file '%1' already exists " "and is %2 large.
" "Do you wish to
" "auto-rename the new file,
" "overwrite the existing file, or
" "resume 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 '%1' already exists " "and is larger than the offered one.
" "Do you wish to
" "auto-rename the new file, or
" "overwrite the existing file?", "dcc") .arg(dcc->szLocalFileName); } DccRenameDialog * box = new DccRenameDialog(this, dcc, tmp, bDisableResume); m_pBoxList->append(box); connect(box, SIGNAL(renameSelected(DccDialog *, DccDescriptor *)), this, SLOT(renameDccSendFile(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(overwriteSelected(DccDialog *, DccDescriptor *)), this, SLOT(recvFileExecute(DccDialog *, DccDescriptor *))); connect(box, SIGNAL(cancelSelected(DccDialog *, DccDescriptor *)), this, SLOT(cancelDcc(DccDialog *, DccDescriptor *))); box->show(); return; } // auto resume ? if( KVI_OPTION_BOOL(KviOption_boolAutoResumeDccSendWhenAutoAccepted) && (bOk) && // only if the remote size is really known (iRemoteSize > (quint64)fi.size()) && // only if the remote size is larger than the local size (!DccFileTransfer::nonFailedTransferWithLocalFileName(dcc->szLocalFileName)) // only if there is no transfer with this local file name yet ) { // yep, auto resume... dcc->bResume = true; recvFileExecute(nullptr, dcc); } else if(iRemoteSize == (quint64)fi.size()) { dcc->console()->output(KVI_OUT_DCCMSG, "Transfer aborted: file %Q already completed", &(dcc->szLocalFileName)); cancelDcc(nullptr, dcc); } else { // otherwise auto rename renameDccSendFile(nullptr, dcc); } return; } dcc->szLocalFileSize = "0"; // everything OK recvFileExecute(nullptr, dcc); } void DccBroker::renameDccSendFile(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(QFileInfo(dcc->szLocalFileName).exists()) { KviCString szOrig = dcc->szLocalFileName; int i = 1; do { KviCString 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(nullptr, dcc); } void DccBroker::recvFileExecute(DccDialog * box, DccDescriptor * dcc) { if(box) box->forgetDescriptor(); if(!g_pApp->windowExists(dcc->console())) { // rebind to the first available console.... dcc->setConsole(g_pApp->activeConsole()); } DccFileTransfer * send = new DccFileTransfer(dcc); bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : (KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSend) || (dcc->bAutoAccept && KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSendWhenAutoAccepted))); send->invokeTransferWindow(bMinimized, bMinimized); } void DccBroker::sendFileManage(DccDescriptor * dcc) { QStringList filenames; if(KviFileDialog::askForOpenFileNames( filenames, __tr2qs_ctx("Choose Files to Send - KVIrc", "dcc"), QString(), QString(), false, true, g_pMainWindow)) { if(filenames.count() > 0) { DccDescriptor * d; QStringList::Iterator it = filenames.begin(); while(it != filenames.end()) { d = new DccDescriptor(*dcc); d->szLocalFileName = *(it); d->szLocalFileName = d->szLocalFileName.trimmed(); ++it; if(d->szLocalFileName.isEmpty()) cancelDcc(d); else sendFileExecute(nullptr, d); } delete dcc; } } else { cancelDcc(dcc); } } void DccBroker::sendFileExecute(DccDialog * box, DccDescriptor * 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()); DccFileTransfer * send = new DccFileTransfer(dcc); bool bMinimized = dcc->bOverrideMinimize ? dcc->bShowMinimized : KVI_OPTION_BOOL(KviOption_boolCreateMinimizedDccSend); send->invokeTransferWindow(bMinimized, bMinimized); } bool DccBroker::canUnload() { if(m_pBoxList) { if((m_pBoxList->count() != 0) || (m_pDccWindowList->count() != 0) || (DccFileTransfer::transferCount() != 0)) return false; } // else in the destructor anyway (going to die) return true; } bool DccBroker::handleResumeAccepted(const char * filename, const char * port, const char * szZeroPortTag) { return DccFileTransfer::handleResumeAccepted(filename, port, szZeroPortTag); } bool DccBroker::handleResumeRequest(KviDccRequest * dcc, const char * filename, const char * port, unsigned long filePos, const char * szZeroPortTag) { //qDebug("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 0 KviDccZeroPortTag * t = findZeroPortTag(QString(szZeroPortTag)); if(t) { // valid zero port resume request if(filePos >= t->m_uFileSize) return false; // invalid resume size // ok! t->m_uResumePosition = filePos; KviCString szBuffy; KviIrcServerParser::encodeCtcpParameter(filename, szBuffy); dcc->ctcpMsg->msg->console()->connection()->sendFmtData( "PRIVMSG %s :%cDCC ACCEPT %s %s %s %s%c", dcc->ctcpMsg->msg->console()->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(), 0x01, szBuffy.ptr(), port, dcc->ctcpMsg->msg->console()->connection()->encodeText(QString::number(filePos)).data(), szZeroPortTag, 0x01); return true; } } //qDebug("NOT A ZeRO PORT"); return DccFileTransfer::handleResumeRequest(filename, port, filePos); }