KVIRC_API void printSSLCertificate(KviWindow * wnd, const char * description, KviSSLCertificate * c) { wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: %c%s"), KviControlCodes::Bold, description); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Version: %c%d"), KviControlCodes::Bold, c->version()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Serial number: %c%d"), KviControlCodes::Bold, c->serialNumber()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Subject:")); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Common name: %c%s"), KviControlCodes::Bold, c->subjectCommonName()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Organization: %c%s"), KviControlCodes::Bold, c->subjectOrganization()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Organizational unit: %c%s"), KviControlCodes::Bold, c->subjectOrganizationalUnit()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Country: %c%s"), KviControlCodes::Bold, c->subjectCountry()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: State or province: %c%s"), KviControlCodes::Bold, c->subjectStateOrProvince()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Locality: %c%s"), KviControlCodes::Bold, c->subjectLocality()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Issuer:")); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Common name: %c%s"), KviControlCodes::Bold, c->issuerCommonName()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Organization: %c%s"), KviControlCodes::Bold, c->issuerOrganization()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Organizational unit: %c%s"), KviControlCodes::Bold, c->issuerOrganizationalUnit()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Country: %c%s"), KviControlCodes::Bold, c->issuerCountry()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: State or province: %c%s"), KviControlCodes::Bold, c->issuerStateOrProvince()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Locality: %c%s"), KviControlCodes::Bold, c->issuerLocality()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Public key: %c%s (%d bits)"), KviControlCodes::Bold, c->publicKeyType(), c->publicKeyBits()); wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Signature type: %c%s"), KviControlCodes::Bold, c->signatureType()); KviCString tmp = c->signatureContents(); if(tmp.len() > 40) { tmp.cutRight(tmp.len() - 40); tmp.append("..."); } wnd->output(KVI_OUT_SSL, __tr2qs("[SSL]: Signature contents: %c%s"), KviControlCodes::Bold, tmp.ptr()); }
void bufferFromBlock(KviCString & szBuffer) { szBuffer.trim(); if((*(szBuffer.ptr()) == '{') && szBuffer.lastCharIs('}')) { // leading and trailing { must be stripped szBuffer.cutLeft(1); szBuffer.cutRight(1); } unindent(szBuffer); szBuffer.trim(); }
bool KviIsOnNotifyListManager::handleUserhost(KviIrcMessage * msg) { if(!m_bExpectingUserhost) return false; // first check for consistency: all the replies must be on the USERHOST list KviPointerList<KviIrcMask> tmplist; tmplist.setAutoDelete(true); KviCString nk; const char * aux = msg->trailing(); while(*aux) { nk = ""; aux = kvi_extractToken(nk, aux, ' '); if(nk.hasData()) { // split it in a mask KviCString nick; KviCString user; KviCString host; int idx = nk.findFirstIdx('='); if(idx != -1) { nick = nk.left(idx); if(nick.lastCharIs('*')) nick.cutRight(1); nk.cutLeft(idx + 1); if(nk.firstCharIs('+') || nk.firstCharIs('-')) nk.cutLeft(1); idx = nk.findFirstIdx('@'); if(idx != -1) { user = nk.left(idx); nk.cutLeft(idx + 1); host = nk; } else { user = "******"; host = nk; } bool bGotIt = false; QString szNick = m_pConnection->decodeText(nick.ptr()); QString szUser = m_pConnection->decodeText(user.ptr()); QString szHost = m_pConnection->decodeText(host.ptr()); for(QString * s = m_pUserhostList->first(); s && (!bGotIt); s = m_pUserhostList->next()) { if(KviQString::equalCI(*s, szNick)) { KviIrcMask * mk = new KviIrcMask(szNick, szUser, szHost); tmplist.append(mk); bGotIt = true; m_pUserhostList->removeRef(s); } } if(!bGotIt) { // ops...not my userhost! if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Hey! You've used USERHOST behind my back? (I might be confused now...)")); return false; } } else { if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Broken USERHOST reply from the server? (%s)"), nk.ptr()); } } } // Ok...looks to be my usershot (still not sure at 100%, but can't do better) if(m_pConnection->lagMeter()) m_pConnection->lagMeter()->lagCheckComplete("@notify_userhost"); m_bExpectingUserhost = false; for(KviIrcMask * mk = tmplist.first(); mk; mk = tmplist.next()) { if(!doMatchUser(mk->nick(), *mk)) return true; // have to restart!!! } if(!(m_pUserhostList->isEmpty())) { // ops...someone is no longer online ? while(QString * s = m_pUserhostList->first()) { if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Notify list: \r!n\r%Q\r appears to have gone offline before USERHOST reply was received, will recheck in the next loop"), s); m_pUserhostList->removeFirst(); } } if(m_pOnlineList->isEmpty()) { if(m_pNotifyList->isEmpty()) delayedNotifySession(); else delayedIsOnSession(); } else delayedUserhostSession(); return true; }
bool DccChatThread::handleIncomingData(KviDccThreadIncomingData * data, bool bCritical) { KVI_ASSERT(data->iLen); KVI_ASSERT(data->buffer); char * aux = data->buffer; char * end = data->buffer + data->iLen; while(aux != end) { if((*aux == '\n') || (*aux == '\0')) { KviThreadDataEvent<KviCString> * e = new KviThreadDataEvent<KviCString>(KVI_DCC_THREAD_EVENT_DATA); // The left part is len chars long int len = aux - data->buffer; // qDebug("LEN = %d, iLen = %d",len,data->iLen); //#warning "DO IT BETTER (the \r cutting)" KviCString * s = new KviCString(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); // qDebug("iLen now = %d",data->iLen); KVI_ASSERT(data->iLen >= 0); if(data->iLen > 0) { // memmove the remaining part to the beginning // aux points after \n or \0 KviMemory::move(data->buffer, aux, data->iLen); data->buffer = (char *)KviMemory::reallocate(data->buffer, data->iLen); end = data->buffer + data->iLen; aux = data->buffer; } else { // no more data in the buffer KVI_ASSERT(data->iLen == 0); KviMemory::free(data->buffer); data->buffer = end = aux = nullptr; } postEvent(parent(), e); } else aux++; // qDebug("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<KviCString> * e = new KviThreadDataEvent<KviCString>(KVI_DCC_THREAD_EVENT_DATA); KviCString * s = new KviCString(data->buffer, data->iLen); if(s->lastCharIs('\r')) s->cutRight(1); e->setData(s); data->iLen = 0; KviMemory::free(data->buffer); data->buffer = nullptr; postEvent(parent(), e); } } return true; }
bool DccChatWindow::event(QEvent * e) { if(e->type() == KVI_THREAD_EVENT) { switch(((KviThreadEvent *)e)->id()) { case KVI_DCC_THREAD_EVENT_ERROR: { KviError::Code * pError = ((KviThreadDataEvent<KviError::Code> *)e)->getData(); QString szErr = KviError::getDescription(*pError); 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 pError; return true; } break; case KVI_DCC_THREAD_EVENT_DATA: { KviCString * encoded = ((KviThreadDataEvent<KviCString> *)e)->getData(); KviCString d = KviCString(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()); if(!hasAttention(KviWindow::MainWindowIsVisible)) { if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) { demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) { QString szMsg = "<b>"; szMsg += m_pDescriptor->szNick; szMsg += "</b> "; szMsg += KviQString::toHtmlEscaped(QString(d.ptr())); //qDebug("KviIrcServerParser_ctcp.cpp:975 debug: %s",szMsg.data()); g_pApp->notifierMessage(this, KVI_OPTION_MSGTYPE(KVI_OUT_ACTION).pixId(), szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); } } } else { #ifdef COMPILE_CRYPT_SUPPORT if(KviCryptSessionInfo * cinf = cryptSessionInfo()) { if(cinf->m_bDoDecrypt) { KviCString decryptedStuff; switch(cinf->m_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())) { g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG, m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(), m_pDescriptor->szHost.toUtf8().data(), decryptedStuff.ptr()); } delete encoded; return true; break; default: // also case KviCryptEngine::DecryptError { QString szErr = cinf->m_pEngine->lastError(); output(KVI_OUT_SYSTEMERROR, __tr2qs_ctx("The following message appears to be encrypted, but the encryption 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())) { g_pMainWindow->firstConsole()->outputPrivmsg(this, KVI_OUT_DCCCHATMSG, m_pDescriptor->szNick.toUtf8().data(), m_pDescriptor->szUser.toUtf8().data(), m_pDescriptor->szHost.toUtf8().data(), d.ptr()); if(!hasAttention(KviWindow::MainWindowIsVisible)) { if(KVI_OPTION_BOOL(KviOption_boolFlashDccChatWindowOnNewMessages)) { demandAttention(); } if(KVI_OPTION_BOOL(KviOption_boolPopupNotifierOnNewDccChatMessages)) { QString szMsg = KviQString::toHtmlEscaped(QString(d.ptr())); g_pApp->notifierMessage(this, KviIconManager::DccChatMsg, szMsg, KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime)); } } } #ifdef COMPILE_CRYPT_SUPPORT } #endif } delete encoded; return true; } break; } } return KviWindow::event(e); }
bool KviIsOnNotifyListManager::handleUserhost(KviIrcMessage * msg) { if(!m_bExpectingUserhost) return false; // first check for consistency: all the replies must be on the USERHOST list std::map<std::size_t, std::unique_ptr<KviIrcMask>> tmplist; KviCString nk; const char * aux = msg->trailing(); while(*aux) { nk = ""; aux = kvi_extractToken(nk, aux, ' '); if(nk.hasData()) { // split it in a mask KviCString nick; KviCString user; KviCString host; int idx = nk.findFirstIdx('='); if(idx != -1) { nick = nk.left(idx); if(nick.lastCharIs('*')) nick.cutRight(1); nk.cutLeft(idx + 1); if(nk.firstCharIs('+') || nk.firstCharIs('-')) nk.cutLeft(1); idx = nk.findFirstIdx('@'); if(idx != -1) { user = nk.left(idx); nk.cutLeft(idx + 1); host = nk; } else { user = "******"; host = nk; } bool bGotIt = false; QString szNick = m_pConnection->decodeText(nick.ptr()); QString szUser = m_pConnection->decodeText(user.ptr()); QString szHost = m_pConnection->decodeText(host.ptr()); std::size_t i = 0; for(const auto & s : m_UserhostList) { if(KviQString::equalCI(s, szNick)) { tmplist.emplace(i, std::make_unique<KviIrcMask>(szNick, szUser, szHost)); bGotIt = true; break; } } if(!bGotIt) { // oops... not my userhost! if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Hey! You've used USERHOST behind my back? (I might be confused now...)")); return false; } } else { if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMWARNING, __tr2qs("Notify list: Broken USERHOST reply from the server? (%s)"), nk.ptr()); } } } // Ok... looks to be my usershot (still not sure at 100%, but can't do better) if(m_pConnection->lagMeter()) m_pConnection->lagMeter()->lagCheckComplete("@notify_userhost"); m_bExpectingUserhost = false; for(auto & pair : tmplist) { KviIrcMask * mk = pair.second.get(); if(!doMatchUser(mk->nick(), *mk)) return true; // have to restart!!! } for(auto i = tmplist.rbegin(); i != tmplist.rend(); ++i) m_UserhostList.erase(m_UserhostList.begin() + i->first); for(const auto & s : m_UserhostList) { // oops... someone is no longer online ? if(_OUTPUT_VERBOSE) m_pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Notify list: \r!n\r%Q\r appears to have gone offline before USERHOST reply was received, will recheck in the next loop"), &s); } m_UserhostList.clear(); if(m_OnlineList.empty()) { if(m_NotifyList.empty()) delayedNotifySession(); else delayedIsOnSession(); } else { delayedUserhostSession(); } return true; }
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); }
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 doesn't 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('/'); } KviCString 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" 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->szIp = __tr2qs_ctx("(unknown)", "dcc"); d->szPort = d->szIp; 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); 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.toUtf8().data(), d->szFileName.toUtf8().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); }
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); } }
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) { // FIXME: #warning "%u will not support 64-bit numbers and won't work as intended here" 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 behind 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 doesn't 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('/'); } if(dcc->szParam1.contains("%2F")) { 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("%2F"); } KviCString 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 DccDescriptor * d = new DccDescriptor(dcc->pConsole); d->szNick = dcc->ctcpMsg->pSource->nick(); d->szUser = dcc->ctcpMsg->pSource->user(); 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(nullptr, 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 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()); } } KviCString 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 DccDescriptor * d = new DccDescriptor(dcc->pConsole); d->szNick = dcc->ctcpMsg->pSource->nick(); d->szUser = dcc->ctcpMsg->pSource->user(); 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(nullptr, 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 signalled 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 connect (standard active chat) } #ifdef COMPILE_SSL_SUPPORT d->bIsSSL = bSSLExtension; #endif dcc_module_set_dcc_type(d, "CHAT"); d->triggerCreationEvent(); g_pDccBroker->handleChatRequest(d); }