Anope IRC Services  Version 2.0
cs_topic.cpp
Go to the documentation of this file.
1 /* ChanServ core functions
2  *
3  * (C) 2003-2014 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  *
8  * Based on the original code of Epona by Lara.
9  * Based on the original code of Services by Andy Church.
10  */
11 
12 #include "module.h"
13 #include "modules/cs_mode.h"
14 
16 {
17  public:
18  CommandCSSetKeepTopic(Module *creator, const Anope::string &cname = "chanserv/set/keeptopic") : Command(creator, cname, 2, 2)
19  {
20  this->SetDesc(_("Retain topic when channel is not in use"));
21  this->SetSyntax(_("\037channel\037 {ON | OFF}"));
22  }
23 
24  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
25  {
26  if (Anope::ReadOnly)
27  {
28  source.Reply(READ_ONLY_MODE);
29  return;
30  }
31 
32  ChannelInfo *ci = ChannelInfo::Find(params[0]);
33  if (ci == NULL)
34  {
35  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
36  return;
37  }
38 
39  EventReturn MOD_RESULT;
40  FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, params[1]));
41  if (MOD_RESULT == EVENT_STOP)
42  return;
43 
44  if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
45  {
46  source.Reply(ACCESS_DENIED);
47  return;
48  }
49 
50  if (params[1].equals_ci("ON"))
51  {
52  Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to enable keeptopic";
53  ci->Extend<bool>("KEEPTOPIC");
54  source.Reply(_("Topic retention option for %s is now \002on\002."), ci->name.c_str());
55  }
56  else if (params[1].equals_ci("OFF"))
57  {
58  Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to disable keeptopic";
59  ci->Shrink<bool>("KEEPTOPIC");
60  source.Reply(_("Topic retention option for %s is now \002off\002."), ci->name.c_str());
61  }
62  else
63  this->OnSyntaxError(source, "KEEPTOPIC");
64  }
65 
67  {
68  this->SendSyntax(source);
69  source.Reply(" ");
70  source.Reply(_("Enables or disables the \002topic retention\002 option for a\n"
71  "channel. When \002%s\002 is set, the topic for the\n"
72  "channel will be remembered by %s even after the\n"
73  "last user leaves the channel, and will be restored the\n"
74  "next time the channel is created."), source.command.c_str(), source.service->nick.c_str());
75  return true;
76  }
77 };
78 
79 class CommandCSTopic : public Command
80 {
82 
83  void Lock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
84  {
85  if (Anope::ReadOnly)
86  {
87  source.Reply(READ_ONLY_MODE);
88  return;
89  }
90 
91  EventReturn MOD_RESULT;
92  FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock on"));
93  if (MOD_RESULT == EVENT_STOP)
94  return;
95 
96  topiclock->Set(ci, true);
97  source.Reply(_("Topic lock option for %s is now \002on\002."), ci->name.c_str());
98  }
99 
100  void Unlock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
101  {
102  if (Anope::ReadOnly)
103  {
104  source.Reply(READ_ONLY_MODE);
105  return;
106  }
107 
108  EventReturn MOD_RESULT;
109  FOREACH_RESULT(OnSetChannelOption, MOD_RESULT, (source, this, ci, "topiclock off"));
110  if (MOD_RESULT == EVENT_STOP)
111  return;
112 
113  topiclock->Unset(ci);
114  source.Reply(_("Topic lock option for %s is now \002off\002."), ci->name.c_str());
115  }
116 
117  void Set(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
118  {
119  const Anope::string &topic = params.size() > 2 ? params[2] : "";
120 
121  bool has_topiclock = topiclock->HasExt(ci);
122  topiclock->Unset(ci);
123  ci->c->ChangeTopic(source.GetNick(), topic, Anope::CurTime);
124  if (has_topiclock)
125  topiclock->Set(ci);
126 
127  bool override = !source.AccessFor(ci).HasPriv("TOPIC");
128  Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << (!topic.empty() ? "to change the topic to: " : "to unset the topic") << (!topic.empty() ? topic : "");
129  }
130 
131  void Append(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> &params)
132  {
133  const Anope::string &topic = params[2];
134 
135  Anope::string new_topic;
136  if (!ci->c->topic.empty())
137  {
138  new_topic = ci->c->topic + " " + topic;
139  ci->last_topic.clear();
140  }
141  else
142  new_topic = topic;
143 
144  std::vector<Anope::string> new_params;
145  new_params.push_back("SET");
146  new_params.push_back(ci->name);
147  new_params.push_back(new_topic);
148 
149  this->Set(source, ci, new_params);
150  }
151 
152  public:
153  CommandCSTopic(Module *creator) : Command(creator, "chanserv/topic", 2, 3),
154  topiclock("TOPICLOCK")
155  {
156  this->SetDesc(_("Manipulate the topic of the specified channel"));
157  this->SetSyntax(_("\037channel\037 SET [\037topic\037]"));
158  this->SetSyntax(_("\037channel\037 APPEND \037topic\037"));
159  this->SetSyntax(_("\037channel\037 [UNLOCK|LOCK]"));
160  }
161 
162  void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
163  {
164  const Anope::string &subcmd = params[1];
165 
166  ChannelInfo *ci = ChannelInfo::Find(params[0]);
167  if (ci == NULL)
168  source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
169  else if (!source.AccessFor(ci).HasPriv("TOPIC") && !source.HasCommand("chanserv/topic"))
170  source.Reply(ACCESS_DENIED);
171  else if (subcmd.equals_ci("LOCK"))
172  this->Lock(source, ci, params);
173  else if (subcmd.equals_ci("UNLOCK"))
174  this->Unlock(source, ci, params);
175  else if (!ci->c)
176  source.Reply(CHAN_X_NOT_IN_USE, ci->name.c_str());
177  else if (subcmd.equals_ci("SET"))
178  this->Set(source, ci, params);
179  else if (subcmd.equals_ci("APPEND") && params.size() > 2)
180  this->Append(source, ci, params);
181  else
182  this->SendSyntax(source);
183  }
184 
185  bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
186  {
187  this->SendSyntax(source);
188  source.Reply(" ");
189  source.Reply(_("Allows manipulating the topic of the specified channel.\n"
190  "The \002SET\002 command changes the topic of the channel to the given topic\n"
191  "or unsets the topic if no topic is given. The \002APPEND\002 command appends\n"
192  "the given topic to the existing topic.\n"
193  " \n"
194  "\002LOCK\002 and \002UNLOCK\002 may be used to enable and disable topic lock. When\n"
195  "topic lock is set, the channel topic will be unchangeable except via this command."));
196  return true;
197  }
198 };
199 
200 class CSTopic : public Module
201 {
204 
206 
207  public:
208  CSTopic(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
209  commandcstopic(this), commandcssetkeeptopic(this), topiclock(this, "TOPICLOCK"), keeptopic(this, "KEEPTOPIC")
210  {
211 
212  }
213 
215  {
216  if (c->ci)
217  {
218  /* Update channel topic */
219  if ((topiclock.HasExt(c->ci) || keeptopic.HasExt(c->ci)) && c->ci->last_topic != c->topic)
220  {
221  c->ChangeTopic(!c->ci->last_topic_setter.empty() ? c->ci->last_topic_setter : c->ci->WhoSends()->nick, c->ci->last_topic, c->ci->last_topic_time ? c->ci->last_topic_time : Anope::CurTime);
222  }
223  }
224  }
225 
227  {
228  if (!c->ci)
229  return;
230 
231  /* We only compare the topics here, not the time or setter. This is because some (old) IRCds do not
232  * allow us to set the topic as someone else, meaning we have to bump the TS and change the setter to us.
233  * This desyncs what is really set with what we have stored, and we end up resetting the topic often when
234  * it is not required
235  */
236  if (topiclock.HasExt(c->ci) && c->ci->last_topic != c->topic)
237  {
238  c->ChangeTopic(c->ci->last_topic_setter, c->ci->last_topic, c->ci->last_topic_time);
239  }
240  else
241  {
242  c->ci->last_topic = c->topic;
243  c->ci->last_topic_setter = c->topic_setter;
244  c->ci->last_topic_time = c->topic_ts;
245  }
246  }
247 
248  void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
249  {
250  if (keeptopic.HasExt(ci))
251  info.AddOption(_("Topic retention"));
252  if (topiclock.HasExt(ci))
253  info.AddOption(_("Topic lock"));
254 
255  ModeLocks *ml = ci->GetExt<ModeLocks>("modelocks");
256  const ModeLock *secret = ml ? ml->GetMLock("SECRET") : NULL;
257  if (!ci->last_topic.empty() && (show_all || ((!secret || secret->set == false) && (!ci->c || !ci->c->HasMode("SECRET")))))
258  {
259  info[_("Last topic")] = ci->last_topic;
260  info[_("Topic set by")] = ci->last_topic_setter;
261  }
262  }
263 };
264 
CoreExport bool ReadOnly
Definition: main.cpp:28
SerializableExtensibleItem< bool > topiclock
Definition: cs_topic.cpp:205
Definition: hashcomp.h:84
void OnTopicUpdated(Channel *c, const Anope::string &user, const Anope::string &topic) anope_override
Definition: cs_topic.cpp:226
void clear()
Definition: anope.h:187
CommandCSTopic(Module *creator)
Definition: cs_topic.cpp:153
void OnChanInfo(CommandSource &source, ChannelInfo *ci, InfoFormatter &info, bool show_all) anope_override
Definition: cs_topic.cpp:248
Anope::string name
Definition: regchannel.h:63
void OnChannelSync(Channel *c) anope_override
Definition: cs_topic.cpp:214
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: cs_topic.cpp:24
#define CHAN_X_NOT_IN_USE
Definition: language.h:85
void push_back(char c)
Definition: anope.h:142
#define ACCESS_DENIED
Definition: language.h:73
CommandCSTopic commandcstopic
Definition: cs_topic.cpp:202
T * Extend(const Anope::string &name, const T &what)
Definition: extensible.h:224
void Set(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_topic.cpp:117
CSTopic(const Anope::string &modname, const Anope::string &creator)
Definition: cs_topic.cpp:208
#define READ_ONLY_MODE
Definition: language.h:71
#define FOREACH_RESULT(ename, ret, args)
Definition: modules.h:95
void SetDesc(const Anope::string &d)
Definition: command.cpp:130
bool equals_ci(const char *_str) const
Definition: anope.h:78
CoreExport time_t CurTime
Definition: main.cpp:41
bool OnHelp(CommandSource &source, const Anope::string &) anope_override
Definition: cs_topic.cpp:66
Anope::string topic
Definition: channels.h:59
bool HasPriv(const Anope::string &priv) const
Definition: access.cpp:384
const Anope::string & GetNick() const
Definition: command.cpp:26
static ChannelInfo * Find(const Anope::string &name)
Definition: regchannel.cpp:630
Channel * c
Definition: regchannel.h:79
void Shrink(const Anope::string &name)
Definition: extensible.h:253
CommandCSSetKeepTopic commandcssetkeeptopic
Definition: cs_topic.cpp:203
virtual void OnSyntaxError(CommandSource &source, const Anope::string &subcommand)
Definition: command.cpp:191
Anope::string command
Definition: commands.h:69
void Reply(const char *message,...)
Definition: command.cpp:96
Reference< BotInfo > service
Definition: commands.h:67
virtual const ModeList & GetMLock() const =0
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
void Unlock(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_topic.cpp:100
ExtensibleRef< bool > topiclock
Definition: cs_topic.cpp:81
EventReturn
Definition: modules.h:129
#define MODULE_INIT(x)
Definition: modules.h:45
void SetSyntax(const Anope::string &s)
Definition: command.cpp:140
void Append(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_topic.cpp:131
CommandCSSetKeepTopic(Module *creator, const Anope::string &cname="chanserv/set/keeptopic")
Definition: cs_topic.cpp:18
Anope::string last_topic
Definition: regchannel.h:69
bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
Definition: cs_topic.cpp:185
#define CHAN_X_NOT_REGISTERED
Definition: language.h:84
Anope::string nick
Definition: users.h:62
void Lock(CommandSource &source, ChannelInfo *ci, const std::vector< Anope::string > &params)
Definition: cs_topic.cpp:83
AccessGroup AccessFor(ChannelInfo *ci)
Definition: command.cpp:41
SerializableExtensibleItem< bool > keeptopic
Definition: cs_topic.cpp:205
void SendSyntax(CommandSource &)
Definition: command.cpp:145
const char * c_str() const
Definition: anope.h:117
Definition: logger.h:53
void ChangeTopic(const Anope::string &user, const Anope::string &newtopic, time_t ts=Anope::CurTime)
Definition: channels.cpp:800
bool HasExt(const Extensible *obj) const
Definition: extensible.h:111
#define _(x)
Definition: services.h:50
void Execute(CommandSource &source, const std::vector< Anope::string > &params) anope_override
Definition: cs_topic.cpp:162