KviSharedFile * KviSharedFilesManager::lookupSharedFile(const QString & szName, KviIrcMask * mask, unsigned int uFileSize)
{
	KviSharedFileList * l = m_pSharedListDict->find(szName);
	if(!l)
		return nullptr;

	for(KviSharedFile * o = l->first(); o; o = l->next())
	{
		bool bMatch;
		if(mask)
		{
			KviIrcMask umask(o->userMask());
			bMatch = mask->matchedBy(umask);
		}
		else
			bMatch = KviQString::equalCS(o->userMask(), "*!*@*");
		if(bMatch)
		{
			if(uFileSize > 0)
			{
				if(uFileSize == o->fileSize())
					return o;
			}
			else
				return o;
		}
	}

	return nullptr;
}
void KviSharedFilesManager::save(const QString & szFilename)
{
	KviConfigurationFile cfg(szFilename, KviConfigurationFile::Write);
	cfg.clear();
	cfg.setGroup("PermanentFileOffers");

	KviPointerHashTableIterator<QString, KviSharedFileList> it(*m_pSharedListDict);
	int i = 0;
	while(KviSharedFileList * pList = it.current())
	{
		for(KviSharedFile * pFile = pList->first(); pFile; pFile = pList->next())
		{
			if(((int)(pFile->expireTime())) == 0)
			{
				QString szTmp;
				szTmp = QString("%1FName").arg(i);
				cfg.writeEntry(szTmp, it.currentKey());
				szTmp = QString("%1FilePath").arg(i);
				cfg.writeEntry(szTmp, pFile->absFilePath());
				szTmp = QString("%1UserMask").arg(i);
				cfg.writeEntry(szTmp, pFile->userMask());
				++i;
			}
		}
		++it;
	}
	cfg.writeEntry("NEntries", i);
}
KviSharedFile * KviSharedFilesManager::addSharedFile(const QString & szName, const QString & szAbsPath, const QString & szMask, int timeoutInSecs)
{
	QFileInfo inf(szAbsPath);
	if(inf.exists() && inf.isFile() && inf.isReadable() && (inf.size() > 0))
	{
		// First find the list
		KviSharedFileList * l = m_pSharedListDict->find(szName);
		if(!l)
		{
			l = new KviSharedFileList;
			l->setAutoDelete(true);
			m_pSharedListDict->replace(szName, l);
		}

		// Now insert
		KviSharedFile * o = new KviSharedFile(szName, szAbsPath, szMask, timeoutInSecs > 0 ? (((int)(time(nullptr))) + timeoutInSecs) : 0, inf.size());

		doInsert(l, o);

		if(((int)o->expireTime()) > 0)
		{
			if(!m_pCleanupTimer->isActive())
				m_pCleanupTimer->start(60000);
		}

		emit sharedFileAdded(o);

		return o;
	}
	else
	{
		qDebug("File %s unreadable: can't add offer", szAbsPath.toUtf8().data());
		return nullptr;
	}
}
void KviSharedFilesManager::cleanup()
{
	KviPointerHashTableIterator<QString, KviSharedFileList> it(*m_pSharedListDict);
	time_t curTime = time(nullptr);

	bool bOtherStuffToCleanup = false;

	KviPointerList<QString> lDying;
	lDying.setAutoDelete(true);

	while(KviSharedFileList * l = it.current())
	{
		KviPointerList<KviSharedFile> tmp;
		tmp.setAutoDelete(false);
		for(KviSharedFile * o = l->first(); o; o = l->next())
		{
			if(o->expireTime() > 0)
			{
				if(((int)o->expireTime()) <= ((int)curTime))
				{
					tmp.append(o);
				}
				else
				{
					bOtherStuffToCleanup = true;
				}
			}
		}
		for(KviSharedFile * fo = tmp.first(); fo; fo = tmp.next())
		{
			l->removeRef(fo);
			emit sharedFileRemoved(fo);
		}
		if(l->count() == 0)
			lDying.append(new QString(it.currentKey()));

		++it;
	}

	for(QString * pDyingKey = lDying.first(); pDyingKey; pDyingKey = lDying.next())
		m_pSharedListDict->remove(*pDyingKey);

	if(!bOtherStuffToCleanup)
		m_pCleanupTimer->stop();
}
void KviSharedFilesManager::doInsert(KviSharedFileList * l, KviSharedFile * o)
{
	int index = 0;
	for(KviSharedFile * fo = l->first(); fo; fo = l->next())
	{
		if(o->wildcardCount() > 0)
		{
			// the new mask has wildcards... if the current one has none, skip it
			if(fo->wildcardCount() > 0)
			{
				// the one in the list has wildcards too...
				// the ones with more non-wild chars go first...
				if(fo->nonWildcardCount() < o->nonWildcardCount())
				{
					// ok...the new one has more non-wildcards, insert
					l->insert(index, o);
					return;
				}
				else
				{
					if(o->nonWildcardCount() == fo->nonWildcardCount())
					{
						// the same number of non-wildcards
						// let the number of wildcards decide (it will be eventually equal)
						if(o->wildcardCount() < fo->wildcardCount())
						{
							// the new one has less wildcards... goes first
							l->insert(index, o);
							return;
						} // else the same number of wildcards and non-wildcards...skip
					}     // else the existing one has more non-wildcards...skip
				}
			} // else the current has no wildcards...skip
		}
		else
		{
			// the new mask has no wildcards....
			if(fo->wildcardCount() > 0)
			{
				// current one has wildcards...insert
				l->insert(index, o);
				return;
			}
			// the current one has no wildcards...
			// the longer masks go first....
			if(fo->maskLength() < o->maskLength())
			{
				// the current one is shorter than the new one...insert
				l->insert(index, o);
				return;
			} // else current one is longer...skip
		}
		index++;
	}
	l->append(o);
}
bool KviSharedFilesManager::removeSharedFile(const QString &szName,const QString &szMask,unsigned int uFileSize)
{
	KviSharedFileList * l = m_pSharedListDict->find(szName);
	if(!l)return false;
	for(KviSharedFile * o = l->first();o;o = l->next())
	{
		if(KviQString::equalCI(szMask,o->userMask()))
		{
			bool bMatch = uFileSize > 0 ? uFileSize == o->fileSize() : true;
			if(bMatch)
			{
				QString save = szName; // <-- szName MAY Be a pointer to o->name()
				l->removeRef(o);
				if(l->count() == 0)m_pSharedListDict->remove(save);
				emit sharedFileRemoved(o);
				return true;
			}
		}
	}
	return false;
}
Exemple #7
0
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;

	KviCString 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())
		{
			QString szError = QString(__tr2qs_ctx("No file offer named '%1' (with size %2) available for %3 [%4@%5]", "dcc")).arg(dcc->szParam1.ptr()).arg(uSize > 0 ? dcc->szParam2.ptr() : __tr_ctx("\"any\"", "dcc")).arg(dcc->ctcpMsg->pSource->nick(), dcc->ctcpMsg->pSource->user(), dcc->ctcpMsg->pSource->host());
			dcc_module_request_error(dcc, szError);
		}
		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
		KviCString 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->user()),
			    &(dcc->ctcpMsg->pSource->host()), dcc->szParam1.ptr(),
			    &(o->absFilePath()), szSubproto.ptr());
		}

		dcc->pConsole->connection()->sendFmtData("PRIVMSG %s :%cDCC %s %s %s%c",
		    dcc->pConsole->connection()->encodeText(dcc->ctcpMsg->pSource->nick()).data(),
		    0x01, szSubproto.ptr(),
		    dcc->pConsole->connection()->encodeText(dcc->szParam1.ptr()).data(),
		    dcc->pConsole->connection()->encodeText(QString::number(o->fileSize())).data(),
		    0x01);
		return;
	}

	DccDescriptor * d = new DccDescriptor(dcc->pConsole);
	d->szNick = dcc->ctcpMsg->pSource->nick();
	d->szLocalFileName = o->absFilePath();
	d->szUser = dcc->ctcpMsg->pSource->user();
	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(nullptr, 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->user()),
		    &(dcc->ctcpMsg->pSource->host()),
		    dcc->szParam1.ptr(),
		    &(o->absFilePath()), &(d->szType));
	}
	d->triggerCreationEvent();
	g_pDccBroker->sendFileExecute(nullptr, d);
}
Exemple #8
0
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('/');
	}

	KviCString 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
			QString szError = QString(__tr2qs_ctx("Invalid RECV request: position %1 is larger than file size", "dcc")).arg(uResumeSize);
			dcc_module_request_error(dcc, szError);
			return;
		}

		// ok...we have requested this send
		//		#warning "Maybe remove this file offer now ?"
		DccDescriptor * d = new DccDescriptor(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(nullptr, 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->user()),
		    &(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());
		KviCString 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"),
		    KviControlCodes::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()),
		    KviControlCodes::Bold);
	}
}