diff --git a/net/znc/Makefile b/net/znc/Makefile index 20af111972..a12c582bea 100644 --- a/net/znc/Makefile +++ b/net/znc/Makefile @@ -136,6 +136,41 @@ define webskin ZNC_MODULES += znc-webskin-$(strip $(1)) endef +define playback + define Package/znc-mod-playback + $(Package/znc/default) + TITLE+= (playback plugin) + DEPENDS:=znc + endef + + define Package/znc-mod-playback/description + Avoid repetitive playback buffers on re-connect with supported clients (e.g. mutter, colluquy) + endef + + define Package/znc-mod-playback/install + $(INSTALL_DIR) $$(1)/usr/lib/znc/ + $(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/lib/znc/playback.so $$(1)/usr/lib/znc/ + endef + + ZNC_MODULES += znc-mod-playback + $(if $(CONFIG_PACKAGE_znc-mod-playback),ZNC_MODULE_TARGETS += playback.so) +endef + +define Download/znc-playback + VERSION:=8dd128bfe2b24b2cc6a9ea2e2d28bfaa28d2a833 + SUBDIR:=znc-mod-playback + FILE:=znc-playback-$$(VERSION).tar.xz + URL:=https://github.com/jpnurmi/znc-playback.git + MIRROR_HASH:=ac89d69048d62c5b15f39cc0d357a111ce4053816062e7bc1e553392b36fbd71 + PROTO:=git +endef + +define Prepare/znc-playback + $(eval $(Download/znc-playback)) + xzcat $(DL_DIR)/$(FILE) | tar -C $(PKG_BUILD_DIR) $(TAR_OPTIONS) + $(LN) $(PKG_BUILD_DIR)/znc-mod-playback/playback.cpp $(PKG_BUILD_DIR)/modules/playback.cpp +endef + , := , $(eval $(call module,adminlog,Log user connects and disconnects and failed \ @@ -233,9 +268,6 @@ $(eval $(call module,notify-connect,Sends a notice to all admins when a user \ $(eval $(call module,perform,Performs commands on connect.)) -$(eval $(call module,playback,Avoid repetitive playback buffers on re-connect \ - with supported clients (e.g. mutter, colluquy))) - $(eval $(call module,raw,View all of the raw traffic.)) $(eval $(call module,route-replies,Routes back answers to the right client \ @@ -263,6 +295,8 @@ $(eval $(call module,stickychan,Keeps you sticked to specific channels.)) $(eval $(call module,watch,Monitor activity for specific text patterns from \ specific users and have the text sent to a special query window.)) +$(eval $(call playback)) + $(eval $(call webadmin)) $(eval $(call webskin,dark-clouds)) @@ -286,5 +320,14 @@ CMAKE_OPTIONS += \ -DWANT_TCL=OFF \ -DWANT_ZLIB=ON +define Build/Prepare + $(call Build/Prepare/Default) + +ifneq ($(CONFIG_PACKAGE_znc-mod-playback),) + $(eval $(call Download,znc-playback)) + $(Prepare/znc-playback) +endif +endef + $(eval $(call BuildPackage,znc)) $(foreach m,$(ZNC_MODULES),$(eval $(call BuildPackage,$(m)))) diff --git a/net/znc/patches/110-add-playback-module.patch b/net/znc/patches/110-add-playback-module.patch deleted file mode 100644 index 41a4f36c75..0000000000 --- a/net/znc/patches/110-add-playback-module.patch +++ /dev/null @@ -1,291 +0,0 @@ ---- /dev/null -+++ b/modules/playback.cpp -@@ -0,0 +1,288 @@ -+/* -+ * Copyright (C) 2015 J-P Nurmi -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 6) -+#error The playback module requires ZNC version 1.6.0 or later. -+#endif -+ -+static const char* PlaybackCap = "znc.in/playback"; -+ -+class CPlaybackMod : public CModule -+{ -+public: -+ MODCONSTRUCTOR(CPlaybackMod) -+ { -+ m_play = false; -+ AddHelpCommand(); -+ AddCommand("Clear", static_cast(&CPlaybackMod::ClearCommand), "", "Clears playback for given buffers."); -+ AddCommand("Play", static_cast(&CPlaybackMod::PlayCommand), " [from] [to]", "Sends playback for given buffers."); -+ AddCommand("List", static_cast(&CPlaybackMod::ListCommand), "[buffer(s)]", "Lists available/matching buffers."); -+ AddCommand("Limit", static_cast(&CPlaybackMod::LimitCommand), " [limit]", "Get/set the buffer limit (<= 0 to clear) for the given client."); -+ } -+ -+ void OnClientCapLs(CClient* client, SCString& caps) override -+ { -+ caps.insert(PlaybackCap); -+ } -+ -+ bool IsClientCapSupported(CClient* client, const CString& cap, bool state) override -+ { -+ return cap.Equals(PlaybackCap); -+ } -+ -+ EModRet OnChanBufferStarting(CChan& chan, CClient& client) override -+ { -+ if (!m_play && client.IsCapEnabled(PlaybackCap)) -+ return HALTCORE; -+ return CONTINUE; -+ } -+ -+ EModRet OnChanBufferPlayLine(CChan& chan, CClient& client, CString& line) override -+ { -+ if (!m_play && client.IsCapEnabled(PlaybackCap)) -+ return HALTCORE; -+ return CONTINUE; -+ } -+ -+ EModRet OnChanBufferEnding(CChan& chan, CClient& client) override -+ { -+ if (!m_play && client.IsCapEnabled(PlaybackCap)) -+ return HALTCORE; -+ return CONTINUE; -+ } -+ -+ EModRet OnPrivBufferPlayLine(CClient& client, CString& line) override -+ { -+ if (!m_play && client.IsCapEnabled(PlaybackCap)) -+ return HALTCORE; -+ return CONTINUE; -+ } -+ -+ void ClearCommand(const CString& line) -+ { -+ // CLEAR -+ const CString arg = line.Token(1); -+ if (arg.empty() || !line.Token(2).empty()) -+ return; -+ std::vector chans = FindChans(arg); -+ for (CChan* chan : chans) -+ chan->ClearBuffer(); -+ std::vector queries = FindQueries(arg); -+ for (CQuery* query : queries) -+ query->ClearBuffer(); -+ } -+ -+ void PlayCommand(const CString& line) -+ { -+ // PLAY [from] [to] -+ const CString arg = line.Token(1); -+ if (arg.empty() || !line.Token(4).empty()) -+ return; -+ double from = line.Token(2).ToDouble(); -+ double to = DBL_MAX; -+ if (!line.Token(3).empty()) -+ to = line.Token(3).ToDouble(); -+ int limit = -1; -+ if (CClient* client = GetClient()) -+ limit = GetLimit(client->GetIdentifier()); -+ std::vector chans = FindChans(arg); -+ for (CChan* chan : chans) { -+ if (chan->IsOn() && !chan->IsDetached()) { -+ CBuffer lines = GetLines(chan->GetBuffer(), from, to, limit); -+ m_play = true; -+ chan->SendBuffer(GetClient(), lines); -+ m_play = false; -+ } -+ } -+ std::vector queries = FindQueries(arg); -+ for (CQuery* query : queries) { -+ CBuffer lines = GetLines(query->GetBuffer(), from, to, limit); -+ m_play = true; -+ query->SendBuffer(GetClient(), lines); -+ m_play = false; -+ } -+ } -+ -+ void ListCommand(const CString& line) -+ { -+ // LIST [buffer(s)] -+ CString arg = line.Token(1); -+ if (arg.empty()) -+ arg = "*"; -+ std::vector chans = FindChans(arg); -+ for (CChan* chan : chans) { -+ if (chan->IsOn() && !chan->IsDetached()) { -+ CBuffer buffer = chan->GetBuffer(); -+ if (!buffer.IsEmpty()) { -+ timeval from = UniversalTime(buffer.GetBufLine(0).GetTime()); -+ timeval to = UniversalTime(buffer.GetBufLine(buffer.Size() - 1).GetTime()); -+ PutModule(chan->GetName() + " " + CString(Timestamp(from)) + " " + CString(Timestamp(to))); -+ } -+ } -+ } -+ std::vector queries = FindQueries(arg); -+ for (CQuery* query : queries) { -+ CBuffer buffer = query->GetBuffer(); -+ if (!buffer.IsEmpty()) { -+ timeval from = UniversalTime(buffer.GetBufLine(0).GetTime()); -+ timeval to = UniversalTime(buffer.GetBufLine(buffer.Size() - 1).GetTime()); -+ PutModule(query->GetName() + " " + CString(Timestamp(from)) + " " + CString(Timestamp(to))); -+ } -+ } -+ } -+ -+ void LimitCommand(const CString& line) -+ { -+ // LIMIT [limit] -+ const CString client = line.Token(1); -+ if (client.empty()) { -+ PutModule("Usage: LIMIT [limit]"); -+ return; -+ } -+ const CString arg = line.Token(2); -+ int limit = GetLimit(client); -+ if (!arg.empty()) { -+ limit = arg.ToInt(); -+ SetLimit(client, limit); -+ } -+ if (limit <= 0) -+ PutModule(client + " buffer limit: -"); -+ else -+ PutModule(client + " buffer limit: " + CString(limit)); -+ } -+ -+ EModRet OnSendToClient(CString& line, CClient& client) override -+ { -+ if (client.IsAttached() && client.IsCapEnabled(PlaybackCap) && !line.Token(0).Equals("CAP")) { -+ MCString tags = CUtils::GetMessageTags(line); -+ if (tags.find("time") == tags.end()) { -+ // CUtils::FormatServerTime() converts to UTC -+ tags["time"] = CUtils::FormatServerTime(LocalTime()); -+ CUtils::SetMessageTags(line, tags); -+ } -+ } -+ return CONTINUE; -+ } -+ -+private: -+ static double Timestamp(timeval tv) -+ { -+ return tv.tv_sec + tv.tv_usec / 1000000.0; -+ } -+ -+ static timeval LocalTime() -+ { -+ timeval tv; -+ if (gettimeofday(&tv, NULL) == -1) { -+ tv.tv_sec = time(NULL); -+ tv.tv_usec = 0; -+ } -+ return tv; -+ } -+ -+ static timeval UniversalTime(timeval tv = LocalTime()) -+ { -+ tm stm; -+ memset(&stm, 0, sizeof(stm)); -+ const time_t secs = tv.tv_sec; // OpenBSD has tv_sec as int, so explicitly convert it to time_t to make gmtime_r() happy -+ gmtime_r(&secs, &stm); -+ const char* tz = getenv("TZ"); -+ setenv("TZ", "UTC", 1); -+ tzset(); -+ tv.tv_sec = mktime(&stm); -+ if (tz) -+ setenv("TZ", tz, 1); -+ else -+ unsetenv("TZ"); -+ tzset(); -+ return tv; -+ } -+ -+ std::vector FindChans(const CString& arg) const -+ { -+ std::vector chans; -+ CIRCNetwork* network = GetNetwork(); -+ if (network) { -+ VCString vargs; -+ arg.Split(",", vargs, false); -+ -+ for (const CString& name : vargs) { -+ std::vector found = network->FindChans(name); -+ chans.insert(chans.end(), found.begin(), found.end()); -+ } -+ } -+ return chans; -+ } -+ -+ std::vector FindQueries(const CString& arg) const -+ { -+ std::vector queries; -+ CIRCNetwork* network = GetNetwork(); -+ if (network) { -+ VCString vargs; -+ arg.Split(",", vargs, false); -+ -+ for (const CString& name : vargs) { -+ std::vector found = network->FindQueries(name); -+ queries.insert(queries.end(), found.begin(), found.end()); -+ } -+ } -+ return queries; -+ } -+ -+ int GetLimit(const CString& client) const -+ { -+ return GetNV(client).ToInt(); -+ } -+ -+ void SetLimit(const CString& client, int limit) -+ { -+ if (limit > 0) -+ SetNV(client, CString(limit)); -+ else -+ DelNV(client); -+ } -+ -+ static CBuffer GetLines(const CBuffer& buffer, double from, double to, int limit) -+ { -+ CBuffer lines(buffer.Size()); -+ for (size_t i = 0; i < buffer.Size(); ++i) { -+ const CBufLine& line = buffer.GetBufLine(i); -+ timeval tv = UniversalTime(line.GetTime()); -+ if (from < Timestamp(tv) && to >= Timestamp(tv)) -+ lines.AddLine(line.GetFormat(), line.GetText(), &tv); -+ } -+ if (limit > 0) -+ lines.SetLineCount(limit); -+ return lines; -+ } -+ -+ bool m_play; -+}; -+ -+GLOBALMODULEDEFS(CPlaybackMod, "An advanced playback module for ZNC")