#include #include #include #include #include class CFavorites : public IFavorites { protected: void OnConfigSave(IConfigManager *pConfigManager) override; public: TRISTATE IsFavorite(const NETADDR *pAddrs, int NumAddrs) const override; TRISTATE IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const override; void Add(const NETADDR *pAddrs, int NumAddrs) override; void AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) override; void Remove(const NETADDR *pAddrs, int NumAddrs) override; void AllEntries(const CEntry **ppEntries, int *pNumEntries) override; private: std::vector m_aEntries; std::unordered_map m_ByAddr; CEntry *Entry(const NETADDR &Addr); const CEntry *Entry(const NETADDR &Addr) const; // `pEntry` must come from the `m_aEntries` vector. void RemoveEntry(CEntry *pEntry); }; void CFavorites::OnConfigSave(IConfigManager *pConfigManager) { for(const auto &Entry : m_aEntries) { if(Entry.m_NumAddrs > 1) { pConfigManager->WriteLine("begin_favorite_group"); } for(int i = 0; i < Entry.m_NumAddrs; i++) { char aAddr[NETADDR_MAXSTRSIZE]; char aBuffer[128]; net_addr_str(&Entry.m_aAddrs[i], aAddr, sizeof(aAddr), true); if(!Entry.m_AllowPing) { str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddr); } else { // Add quotes to the first parameter for backward // compatibility with versions that took a `r` console // parameter. str_format(aBuffer, sizeof(aBuffer), "add_favorite \"%s\" allow_ping", aAddr); } pConfigManager->WriteLine(aBuffer); } if(Entry.m_NumAddrs > 1) { pConfigManager->WriteLine("end_favorite_group"); } } } TRISTATE CFavorites::IsFavorite(const NETADDR *pAddrs, int NumAddrs) const { bool All = true; bool None = true; for(int i = 0; i < NumAddrs && (All || None); i++) { const CEntry *pEntry = Entry(pAddrs[i]); if(pEntry) { None = false; } else { All = false; } } // Return ALL if no addresses were passed. if(All) { return TRISTATE::ALL; } else if(None) { return TRISTATE::NONE; } else { return TRISTATE::SOME; } } TRISTATE CFavorites::IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const { bool All = true; bool None = true; for(int i = 0; i < NumAddrs && (All || None); i++) { const CEntry *pEntry = Entry(pAddrs[i]); if(pEntry == nullptr) { continue; } if(pEntry->m_AllowPing) { None = false; } else { All = false; } } // Return ALL if no addresses were passed. if(All) { return TRISTATE::ALL; } else if(None) { return TRISTATE::NONE; } else { return TRISTATE::SOME; } } void CFavorites::Add(const NETADDR *pAddrs, int NumAddrs) { // First make sure that all the addresses are not registered for some // other favorite. for(int i = 0; i < NumAddrs; i++) { CEntry *pEntry = Entry(pAddrs[i]); if(pEntry == nullptr) { continue; } for(int j = 0; j < pEntry->m_NumAddrs; j++) { if(pEntry->m_aAddrs[j] == pAddrs[i]) { pEntry->m_aAddrs[j] = pEntry->m_aAddrs[pEntry->m_NumAddrs - 1]; pEntry->m_NumAddrs -= 1; break; } } // If the entry has become empty due to the cleaning, remove it // completely. if(pEntry->m_NumAddrs == 0) { RemoveEntry(pEntry); } } // Add the new entry. CEntry NewEntry; mem_zero(&NewEntry, sizeof(NewEntry)); NewEntry.m_NumAddrs = std::min(NumAddrs, (int)std::size(NewEntry.m_aAddrs)); for(int i = 0; i < NewEntry.m_NumAddrs; i++) { NewEntry.m_aAddrs[i] = pAddrs[i]; m_ByAddr[pAddrs[i]] = m_aEntries.size(); } NewEntry.m_AllowPing = false; m_aEntries.push_back(NewEntry); } void CFavorites::AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) { for(int i = 0; i < NumAddrs; i++) { CEntry *pEntry = Entry(pAddrs[i]); if(pEntry == nullptr) { continue; } pEntry->m_AllowPing = AllowPing; } } void CFavorites::Remove(const NETADDR *pAddrs, int NumAddrs) { for(int i = 0; i < NumAddrs; i++) { CEntry *pEntry = Entry(pAddrs[i]); if(pEntry == nullptr) { continue; } for(int j = 0; j < pEntry->m_NumAddrs; j++) { m_ByAddr.erase(pEntry->m_aAddrs[j]); } RemoveEntry(pEntry); } } void CFavorites::AllEntries(const CEntry **ppEntries, int *pNumEntries) { *ppEntries = m_aEntries.data(); *pNumEntries = m_aEntries.size(); } CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) { auto Entry = m_ByAddr.find(Addr); if(Entry == m_ByAddr.end()) { return nullptr; } return &m_aEntries[Entry->second]; } const CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const { auto Entry = m_ByAddr.find(Addr); if(Entry == m_ByAddr.end()) { return nullptr; } return &m_aEntries[Entry->second]; } void CFavorites::RemoveEntry(CEntry *pEntry) { // Replace the entry int Index = pEntry - m_aEntries.data(); *pEntry = m_aEntries[m_aEntries.size() - 1]; m_aEntries.pop_back(); if(Index != (int)m_aEntries.size()) { for(int i = 0; i < pEntry->m_NumAddrs; i++) { m_ByAddr.at(pEntry->m_aAddrs[i]) = Index; } } } void IFavorites::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData) { ((IFavorites *)pUserData)->OnConfigSave(pConfigManager); } std::unique_ptr CreateFavorites() { return std::make_unique(); }