]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_chanhistory.cpp
Add support for the IRCv3 server-time specification.
[user/henk/code/inspircd.git] / src / modules / m_chanhistory.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *
6  * This file is part of InspIRCd.  InspIRCd is free software: you can
7  * redistribute it and/or modify it under the terms of the GNU General Public
8  * License as published by the Free Software Foundation, version 2.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 #include "inspircd.h"
21 #include "modules/ircv3_servertime.h"
22
23 struct HistoryItem
24 {
25         time_t ts;
26         std::string text;
27         std::string sourcemask;
28
29         HistoryItem(User* source, const std::string& Text)
30                 : ts(ServerInstance->Time())
31                 , text(Text)
32                 , sourcemask(source->GetFullHost())
33         {
34         }
35 };
36
37 struct HistoryList
38 {
39         std::deque<HistoryItem> lines;
40         unsigned int maxlen, maxtime;
41         std::string param;
42
43         HistoryList(unsigned int len, unsigned int time, const std::string& oparam)
44                 : maxlen(len), maxtime(time), param(oparam) { }
45 };
46
47 class HistoryMode : public ParamMode<HistoryMode, SimpleExtItem<HistoryList> >
48 {
49         bool IsValidDuration(const std::string& duration)
50         {
51                 for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
52                 {
53                         unsigned char c = *i;
54                         if (((c >= '0') && (c <= '9')) || (c == 's') || (c == 'S'))
55                                 continue;
56
57                         if (duration_multi[c] == 1)
58                                 return false;
59                 }
60                 return true;
61         }
62
63  public:
64         unsigned int maxlines;
65         HistoryMode(Module* Creator)
66                 : ParamMode<HistoryMode, SimpleExtItem<HistoryList> >(Creator, "history", 'H')
67         {
68         }
69
70         ModeAction OnSet(User* source, Channel* channel, std::string& parameter) CXX11_OVERRIDE
71         {
72                 std::string::size_type colon = parameter.find(':');
73                 if (colon == std::string::npos)
74                 {
75                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
76                         return MODEACTION_DENY;
77                 }
78
79                 std::string duration(parameter, colon+1);
80                 if ((IS_LOCAL(source)) && ((duration.length() > 10) || (!IsValidDuration(duration))))
81                 {
82                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
83                         return MODEACTION_DENY;
84                 }
85
86                 unsigned int len = ConvToInt(parameter.substr(0, colon));
87                 unsigned int time = InspIRCd::Duration(duration);
88                 if (len == 0 || (len > maxlines && IS_LOCAL(source)))
89                 {
90                         source->WriteNumeric(Numerics::InvalidModeParameter(channel, this, parameter));
91                         return MODEACTION_DENY;
92                 }
93                 if (len > maxlines)
94                         len = maxlines;
95
96                 HistoryList* history = ext.get(channel);
97                 if (history)
98                 {
99                         // Shrink the list if the new line number limit is lower than the old one
100                         if (len < history->lines.size())
101                                 history->lines.erase(history->lines.begin(), history->lines.begin() + (history->lines.size() - len));
102
103                         history->maxlen = len;
104                         history->maxtime = time;
105                         history->param = parameter;
106                 }
107                 else
108                 {
109                         ext.set(channel, new HistoryList(len, time, parameter));
110                 }
111                 return MODEACTION_ALLOW;
112         }
113
114         void SerializeParam(Channel* chan, const HistoryList* history, std::string& out)
115         {
116                 out.append(history->param);
117         }
118 };
119
120 class ModuleChanHistory : public Module
121 {
122         HistoryMode m;
123         bool sendnotice;
124         UserModeReference botmode;
125         bool dobots;
126         IRCv3::ServerTime::API servertimemanager;
127
128  public:
129         ModuleChanHistory()
130                 : m(this)
131                 , botmode(this, "bot")
132                 , servertimemanager(this)
133         {
134         }
135
136         void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
137         {
138                 ConfigTag* tag = ServerInstance->Config->ConfValue("chanhistory");
139                 m.maxlines = tag->getUInt("maxlines", 50, 1);
140                 sendnotice = tag->getBool("notice", true);
141                 dobots = tag->getBool("bots", true);
142         }
143
144         void OnUserPostMessage(User* user, const MessageTarget& target, const MessageDetails& details) CXX11_OVERRIDE
145         {
146                 if ((target.type == MessageTarget::TYPE_CHANNEL) && (target.status == 0) && (details.type == MSG_PRIVMSG))
147                 {
148                         Channel* c = target.Get<Channel>();
149                         HistoryList* list = m.ext.get(c);
150                         if (list)
151                         {
152                                 list->lines.push_back(HistoryItem(user, details.text));
153                                 if (list->lines.size() > list->maxlen)
154                                         list->lines.pop_front();
155                         }
156                 }
157         }
158
159         void OnPostJoin(Membership* memb) CXX11_OVERRIDE
160         {
161                 LocalUser* localuser = IS_LOCAL(memb->user);
162                 if (!localuser)
163                         return;
164
165                 if (memb->user->IsModeSet(botmode) && !dobots)
166                         return;
167
168                 HistoryList* list = m.ext.get(memb->chan);
169                 if (!list)
170                         return;
171                 time_t mintime = 0;
172                 if (list->maxtime)
173                         mintime = ServerInstance->Time() - list->maxtime;
174
175                 if (sendnotice)
176                 {
177                         std::string message("Replaying up to " + ConvToStr(list->maxlen) + " lines of pre-join history");
178                         if (list->maxtime > 0)
179                                 message.append(" spanning up to " + ConvToStr(list->maxtime) + " seconds");
180                         memb->WriteNotice(message);
181                 }
182
183                 for(std::deque<HistoryItem>::iterator i = list->lines.begin(); i != list->lines.end(); ++i)
184                 {
185                         const HistoryItem& item = *i;
186                         if (item.ts >= mintime)
187                         {
188                                 ClientProtocol::Messages::Privmsg msg(ClientProtocol::Messages::Privmsg::nocopy, item.sourcemask, memb->chan, item.text);
189                                 if (servertimemanager)
190                                         servertimemanager->Set(msg, item.ts);
191                                 localuser->Send(ServerInstance->GetRFCEvents().privmsg, msg);
192                         }
193                 }
194         }
195
196         Version GetVersion() CXX11_OVERRIDE
197         {
198                 return Version("Provides channel history replayed on join", VF_VENDOR);
199         }
200 };
201
202 MODULE_INIT(ModuleChanHistory)