lede-packages-rs

git clone git://archive.git.mtrnord.blog/MTRNord/lede-packages-rs.git
Log | Files | Refs | README | LICENSE

110-add-playback-module.patch (9869B)


      1 --- /dev/null
      2 +++ b/modules/playback.cpp
      3 @@ -0,0 +1,288 @@
      4 +/*
      5 + * Copyright (C) 2015 J-P Nurmi
      6 + *
      7 + * Licensed under the Apache License, Version 2.0 (the "License");
      8 + * you may not use this file except in compliance with the License.
      9 + * You may obtain a copy of the License at
     10 + *
     11 + *     http://www.apache.org/licenses/LICENSE-2.0
     12 + *
     13 + * Unless required by applicable law or agreed to in writing, software
     14 + * distributed under the License is distributed on an "AS IS" BASIS,
     15 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 + * See the License for the specific language governing permissions and
     17 + * limitations under the License.
     18 + */
     19 +
     20 +#include <znc/Modules.h>
     21 +#include <znc/IRCNetwork.h>
     22 +#include <znc/Client.h>
     23 +#include <znc/Buffer.h>
     24 +#include <znc/Utils.h>
     25 +#include <znc/Query.h>
     26 +#include <znc/Chan.h>
     27 +#include <znc/znc.h>
     28 +#include <znc/version.h>
     29 +#include <sys/time.h>
     30 +#include <cfloat>
     31 +
     32 +#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 6)
     33 +#error The playback module requires ZNC version 1.6.0 or later.
     34 +#endif
     35 +
     36 +static const char* PlaybackCap = "znc.in/playback";
     37 +
     38 +class CPlaybackMod : public CModule
     39 +{
     40 +public:
     41 +    MODCONSTRUCTOR(CPlaybackMod)
     42 +    {
     43 +        m_play = false;
     44 +        AddHelpCommand();
     45 +        AddCommand("Clear", static_cast<CModCommand::ModCmdFunc>(&CPlaybackMod::ClearCommand), "<buffer(s)>", "Clears playback for given buffers.");
     46 +        AddCommand("Play", static_cast<CModCommand::ModCmdFunc>(&CPlaybackMod::PlayCommand), "<buffer(s)> [from] [to]", "Sends playback for given buffers.");
     47 +        AddCommand("List", static_cast<CModCommand::ModCmdFunc>(&CPlaybackMod::ListCommand), "[buffer(s)]", "Lists available/matching buffers.");
     48 +        AddCommand("Limit", static_cast<CModCommand::ModCmdFunc>(&CPlaybackMod::LimitCommand), "<client> [limit]", "Get/set the buffer limit (<= 0 to clear) for the given client.");
     49 +    }
     50 +
     51 +    void OnClientCapLs(CClient* client, SCString& caps) override
     52 +    {
     53 +        caps.insert(PlaybackCap);
     54 +    }
     55 +
     56 +    bool IsClientCapSupported(CClient* client, const CString& cap, bool state) override
     57 +    {
     58 +        return cap.Equals(PlaybackCap);
     59 +    }
     60 +
     61 +    EModRet OnChanBufferStarting(CChan& chan, CClient& client) override
     62 +    {
     63 +        if (!m_play && client.IsCapEnabled(PlaybackCap))
     64 +            return HALTCORE;
     65 +        return CONTINUE;
     66 +    }
     67 +
     68 +    EModRet OnChanBufferPlayLine(CChan& chan, CClient& client, CString& line) override
     69 +    {
     70 +        if (!m_play && client.IsCapEnabled(PlaybackCap))
     71 +            return HALTCORE;
     72 +        return CONTINUE;
     73 +    }
     74 +
     75 +    EModRet OnChanBufferEnding(CChan& chan, CClient& client) override
     76 +    {
     77 +        if (!m_play && client.IsCapEnabled(PlaybackCap))
     78 +            return HALTCORE;
     79 +        return CONTINUE;
     80 +    }
     81 +
     82 +    EModRet OnPrivBufferPlayLine(CClient& client, CString& line) override
     83 +    {
     84 +        if (!m_play && client.IsCapEnabled(PlaybackCap))
     85 +            return HALTCORE;
     86 +        return CONTINUE;
     87 +    }
     88 +
     89 +    void ClearCommand(const CString& line)
     90 +    {
     91 +        // CLEAR <buffer(s)>
     92 +        const CString arg = line.Token(1);
     93 +        if (arg.empty() || !line.Token(2).empty())
     94 +            return;
     95 +        std::vector<CChan*> chans = FindChans(arg);
     96 +        for (CChan* chan : chans)
     97 +            chan->ClearBuffer();
     98 +        std::vector<CQuery*> queries = FindQueries(arg);
     99 +        for (CQuery* query : queries)
    100 +            query->ClearBuffer();
    101 +    }
    102 +
    103 +    void PlayCommand(const CString& line)
    104 +    {
    105 +        // PLAY <buffer(s)> [from] [to]
    106 +        const CString arg = line.Token(1);
    107 +        if (arg.empty() || !line.Token(4).empty())
    108 +            return;
    109 +        double from = line.Token(2).ToDouble();
    110 +        double to = DBL_MAX;
    111 +        if (!line.Token(3).empty())
    112 +            to = line.Token(3).ToDouble();
    113 +        int limit = -1;
    114 +        if (CClient* client = GetClient())
    115 +            limit = GetLimit(client->GetIdentifier());
    116 +        std::vector<CChan*> chans = FindChans(arg);
    117 +        for (CChan* chan : chans) {
    118 +            if (chan->IsOn() && !chan->IsDetached()) {
    119 +                CBuffer lines = GetLines(chan->GetBuffer(), from, to, limit);
    120 +                m_play = true;
    121 +                chan->SendBuffer(GetClient(), lines);
    122 +                m_play = false;
    123 +            }
    124 +        }
    125 +        std::vector<CQuery*> queries = FindQueries(arg);
    126 +        for (CQuery* query : queries) {
    127 +            CBuffer lines = GetLines(query->GetBuffer(), from, to, limit);
    128 +            m_play = true;
    129 +            query->SendBuffer(GetClient(), lines);
    130 +            m_play = false;
    131 +        }
    132 +    }
    133 +
    134 +    void ListCommand(const CString& line)
    135 +    {
    136 +        // LIST [buffer(s)]
    137 +        CString arg = line.Token(1);
    138 +        if (arg.empty())
    139 +            arg = "*";
    140 +        std::vector<CChan*> chans = FindChans(arg);
    141 +        for (CChan* chan : chans) {
    142 +            if (chan->IsOn() && !chan->IsDetached()) {
    143 +                CBuffer buffer = chan->GetBuffer();
    144 +                if (!buffer.IsEmpty()) {
    145 +                    timeval from = UniversalTime(buffer.GetBufLine(0).GetTime());
    146 +                    timeval to = UniversalTime(buffer.GetBufLine(buffer.Size() - 1).GetTime());
    147 +                    PutModule(chan->GetName() + " " + CString(Timestamp(from)) + " " + CString(Timestamp(to)));
    148 +                }
    149 +            }
    150 +        }
    151 +        std::vector<CQuery*> queries = FindQueries(arg);
    152 +        for (CQuery* query : queries) {
    153 +            CBuffer buffer = query->GetBuffer();
    154 +            if (!buffer.IsEmpty()) {
    155 +                timeval from = UniversalTime(buffer.GetBufLine(0).GetTime());
    156 +                timeval to = UniversalTime(buffer.GetBufLine(buffer.Size() - 1).GetTime());
    157 +                PutModule(query->GetName() + " " + CString(Timestamp(from)) + " " + CString(Timestamp(to)));
    158 +            }
    159 +        }
    160 +    }
    161 +
    162 +    void LimitCommand(const CString& line)
    163 +    {
    164 +        // LIMIT <client> [limit]
    165 +        const CString client = line.Token(1);
    166 +        if (client.empty()) {
    167 +            PutModule("Usage: LIMIT <client> [limit]");
    168 +            return;
    169 +        }
    170 +        const CString arg = line.Token(2);
    171 +        int limit = GetLimit(client);
    172 +        if (!arg.empty()) {
    173 +            limit = arg.ToInt();
    174 +            SetLimit(client, limit);
    175 +        }
    176 +        if (limit <= 0)
    177 +            PutModule(client + " buffer limit: -");
    178 +        else
    179 +            PutModule(client + " buffer limit: " + CString(limit));
    180 +    }
    181 +
    182 +    EModRet OnSendToClient(CString& line, CClient& client) override
    183 +    {
    184 +        if (client.IsAttached() && client.IsCapEnabled(PlaybackCap) && !line.Token(0).Equals("CAP")) {
    185 +            MCString tags = CUtils::GetMessageTags(line);
    186 +            if (tags.find("time") == tags.end()) {
    187 +                // CUtils::FormatServerTime() converts to UTC
    188 +                tags["time"] = CUtils::FormatServerTime(LocalTime());
    189 +                CUtils::SetMessageTags(line, tags);
    190 +            }
    191 +        }
    192 +        return CONTINUE;
    193 +    }
    194 +
    195 +private:
    196 +    static double Timestamp(timeval tv)
    197 +    {
    198 +        return tv.tv_sec + tv.tv_usec / 1000000.0;
    199 +    }
    200 +
    201 +    static timeval LocalTime()
    202 +    {
    203 +        timeval tv;
    204 +        if (gettimeofday(&tv, NULL) == -1) {
    205 +            tv.tv_sec = time(NULL);
    206 +            tv.tv_usec = 0;
    207 +        }
    208 +        return tv;
    209 +    }
    210 +
    211 +    static timeval UniversalTime(timeval tv = LocalTime())
    212 +    {
    213 +        tm stm;
    214 +        memset(&stm, 0, sizeof(stm));
    215 +        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
    216 +        gmtime_r(&secs, &stm);
    217 +        const char* tz = getenv("TZ");
    218 +        setenv("TZ", "UTC", 1);
    219 +        tzset();
    220 +        tv.tv_sec = mktime(&stm);
    221 +        if (tz)
    222 +            setenv("TZ", tz, 1);
    223 +        else
    224 +            unsetenv("TZ");
    225 +        tzset();
    226 +        return tv;
    227 +    }
    228 +
    229 +    std::vector<CChan*> FindChans(const CString& arg) const
    230 +    {
    231 +        std::vector<CChan*> chans;
    232 +        CIRCNetwork* network = GetNetwork();
    233 +        if (network) {
    234 +            VCString vargs;
    235 +            arg.Split(",", vargs, false);
    236 +
    237 +            for (const CString& name : vargs) {
    238 +                std::vector<CChan*> found = network->FindChans(name);
    239 +                chans.insert(chans.end(), found.begin(), found.end());
    240 +            }
    241 +        }
    242 +        return chans;
    243 +    }
    244 +
    245 +    std::vector<CQuery*> FindQueries(const CString& arg) const
    246 +    {
    247 +        std::vector<CQuery*> queries;
    248 +        CIRCNetwork* network = GetNetwork();
    249 +        if (network) {
    250 +            VCString vargs;
    251 +            arg.Split(",", vargs, false);
    252 +
    253 +            for (const CString& name : vargs) {
    254 +                std::vector<CQuery*> found = network->FindQueries(name);
    255 +                queries.insert(queries.end(), found.begin(), found.end());
    256 +            }
    257 +        }
    258 +        return queries;
    259 +    }
    260 +
    261 +    int GetLimit(const CString& client) const
    262 +    {
    263 +        return GetNV(client).ToInt();
    264 +    }
    265 +
    266 +    void SetLimit(const CString& client, int limit)
    267 +    {
    268 +        if (limit > 0)
    269 +            SetNV(client, CString(limit));
    270 +        else
    271 +            DelNV(client);
    272 +    }
    273 +
    274 +    static CBuffer GetLines(const CBuffer& buffer, double from, double to, int limit)
    275 +    {
    276 +        CBuffer lines(buffer.Size());
    277 +        for (size_t i = 0; i < buffer.Size(); ++i) {
    278 +            const CBufLine& line = buffer.GetBufLine(i);
    279 +            timeval tv = UniversalTime(line.GetTime());
    280 +            if (from < Timestamp(tv) && to >= Timestamp(tv))
    281 +                lines.AddLine(line.GetFormat(), line.GetText(), &tv);
    282 +        }
    283 +        if (limit > 0)
    284 +            lines.SetLineCount(limit);
    285 +        return lines;
    286 +    }
    287 +
    288 +    bool m_play;
    289 +};
    290 +
    291 +GLOBALMODULEDEFS(CPlaybackMod, "An advanced playback module for ZNC")