void CChan::SendBuffer(CClient* pClient) { if (m_pNetwork && m_pNetwork->IsUserAttached()) { // in the event that pClient is NULL, need to send this to all clients for the user // I'm presuming here that pClient is listed inside vClients thus vClients at this // point can't be empty. // // This loop has to be cycled twice to maintain the existing behavior which is // 1. OnChanBufferStarting // 2. OnChanBufferPlayLine // 3. ClearBuffer() if not keeping the buffer // 4. OnChanBufferEnding // // With the exception of ClearBuffer(), this needs to happen per client, and // if pClient is not NULL, the loops break after the first iteration. // // Rework this if you like ... if (!m_Buffer.IsEmpty()) { const vector<CClient*> & vClients = m_pNetwork->GetClients(); for (size_t uClient = 0; uClient < vClients.size(); ++uClient) { CClient * pUseClient = (pClient ? pClient : vClients[uClient]); bool bSkipStatusMsg = pUseClient->HasServerTime(); NETWORKMODULECALL(OnChanBufferStarting(*this, *pUseClient), m_pNetwork->GetUser(), m_pNetwork, NULL, &bSkipStatusMsg); if (!bSkipStatusMsg) { m_pNetwork->PutUser(":***[email protected] PRIVMSG " + GetName() + " :Buffer Playback...", pUseClient); } unsigned int uSize = m_Buffer.Size(); for (unsigned int uIdx = 0; uIdx < uSize; uIdx++) { CString sLine = m_Buffer.GetLine(uIdx, *pUseClient); bool bNotShowThisLine = false; NETWORKMODULECALL(OnChanBufferPlayLine(*this, *pUseClient, sLine), m_pNetwork->GetUser(), m_pNetwork, NULL, &bNotShowThisLine); if (bNotShowThisLine) continue; m_pNetwork->PutUser(sLine, pUseClient); } bSkipStatusMsg = pUseClient->HasServerTime(); NETWORKMODULECALL(OnChanBufferEnding(*this, *pUseClient), m_pNetwork->GetUser(), m_pNetwork, NULL, &bSkipStatusMsg); if (!bSkipStatusMsg) { m_pNetwork->PutUser(":***[email protected] PRIVMSG " + GetName() + " :Playback Complete.", pUseClient); } if (pClient) break; } if (AutoClearChanBuffer()) { ClearBuffer(); } } } }
CString CBufLine::GetLine(const CClient& Client, const MCString& msParams) const { MCString msThisParams = msParams; if (Client.HasServerTime()) { msThisParams["text"] = m_sText; CString sStr = CString::NamedFormat(m_sFormat, msThisParams); return "@time=" + CUtils::FormatServerTime(m_time) + " " + sStr; } else { msThisParams["text"] = Client.GetUser()->AddTimestamp(m_time.tv_sec, m_sText); return CString::NamedFormat(m_sFormat, msThisParams); } }
CString CBufLine::GetLine(const CClient& Client, const MCString& mssParams) const { CMessage Line = ToMessage(Client, mssParams); // Note: Discard all tags (except the time tag, conditionally) to // keep the same behavior as ZNC versions 1.6 and earlier had. See // CClient::PutClient(CMessage) documentation for more details. Line.SetTags(MCString::EmptyMap); if (Client.HasServerTime()) { CString sTime = m_Message.GetTag("time"); if (sTime.empty()) { sTime = CUtils::FormatServerTime(m_Message.GetTime()); } Line.SetTag("time", sTime); } return Line.ToString(); }
CString CBufLine::GetLine(const CClient& Client, const MCString& msParams) const { MCString msThisParams = msParams; if (Client.HasServerTime()) { msThisParams["text"] = m_sText; CString sStr = CString::NamedFormat(m_sFormat, msThisParams); CString s_msec(m_time.tv_usec / 1000); while (s_msec.length() < 3) { s_msec = "0" + s_msec; } // TODO support leap seconds properly // TODO support message-tags properly struct tm stm; memset(&stm, 0, sizeof(stm)); gmtime_r(&m_time.tv_sec, &stm); char sTime[20] = {}; strftime(sTime, sizeof(sTime), "%Y-%m-%dT%H:%M:%S", &stm); return "@time=" + CString(sTime) + "." + s_msec + "Z " + sStr; } else { msThisParams["text"] = Client.GetUser()->AddTimestamp(m_time.tv_sec, m_sText); return CString::NamedFormat(m_sFormat, msThisParams); } }
CMessage CBufLine::ToMessage(const CClient& Client, const MCString& mssParams) const { CMessage Line = m_Message; CString sSender = Line.GetNick().GetNickMask(); Line.SetNick(CNick(CString::NamedFormat(sSender, mssParams))); MCString mssThisParams = mssParams; if (Client.HasServerTime()) { mssThisParams["text"] = m_sText; } else { mssThisParams["text"] = Client.GetUser()->AddTimestamp(Line.GetTime().tv_sec, m_sText); } // make a copy of params, because the following loop modifies the original VCString vsParams = Line.GetParams(); for (unsigned int uIdx = 0; uIdx < vsParams.size(); ++uIdx) { Line.SetParam(uIdx, CString::NamedFormat(vsParams[uIdx], mssThisParams)); } return Line; }
void CChan::SendBuffer(CClient* pClient, const CBuffer& Buffer) { if (m_pNetwork && m_pNetwork->IsUserAttached()) { // in the event that pClient is nullptr, need to send this to all // clients for the user I'm presuming here that pClient is listed // inside vClients thus vClients at this point can't be empty. // // This loop has to be cycled twice to maintain the existing behavior // which is // 1. OnChanBufferStarting // 2. OnChanBufferPlayLine // 3. ClearBuffer() if not keeping the buffer // 4. OnChanBufferEnding // // With the exception of ClearBuffer(), this needs to happen per // client, and if pClient is not nullptr, the loops break after the // first iteration. // // Rework this if you like ... if (!Buffer.IsEmpty()) { const vector<CClient*>& vClients = m_pNetwork->GetClients(); for (CClient* pEachClient : vClients) { CClient* pUseClient = (pClient ? pClient : pEachClient); bool bWasPlaybackActive = pUseClient->IsPlaybackActive(); pUseClient->SetPlaybackActive(true); bool bSkipStatusMsg = pUseClient->HasServerTime(); NETWORKMODULECALL(OnChanBufferStarting(*this, *pUseClient), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bSkipStatusMsg); if (!bSkipStatusMsg) { m_pNetwork->PutUser(":***[email protected] PRIVMSG " + GetName() + " :Buffer Playback...", pUseClient); } bool bBatch = pUseClient->HasBatch(); CString sBatchName = GetName().MD5(); if (bBatch) { m_pNetwork->PutUser(":znc.in BATCH +" + sBatchName + " znc.in/playback " + GetName(), pUseClient); } size_t uSize = Buffer.Size(); for (size_t uIdx = 0; uIdx < uSize; uIdx++) { const CBufLine& BufLine = Buffer.GetBufLine(uIdx); CMessage Message = BufLine.ToMessage(*pUseClient, MCString::EmptyMap); Message.SetChan(this); Message.SetNetwork(m_pNetwork); Message.SetClient(pUseClient); if (bBatch) { Message.SetTag("batch", sBatchName); } bool bNotShowThisLine = false; NETWORKMODULECALL(OnChanBufferPlayMessage(Message), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bNotShowThisLine); if (bNotShowThisLine) continue; m_pNetwork->PutUser(Message, pUseClient); } bSkipStatusMsg = pUseClient->HasServerTime(); NETWORKMODULECALL(OnChanBufferEnding(*this, *pUseClient), m_pNetwork->GetUser(), m_pNetwork, nullptr, &bSkipStatusMsg); if (!bSkipStatusMsg) { m_pNetwork->PutUser(":***[email protected] PRIVMSG " + GetName() + " :Playback Complete.", pUseClient); } if (bBatch) { m_pNetwork->PutUser(":znc.in BATCH -" + sBatchName, pUseClient); } pUseClient->SetPlaybackActive(bWasPlaybackActive); if (pClient) break; } } } }