]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_helpop.cpp
Convert the ISO 8859-2 nationalchars files to codepage configs.
[user/henk/code/inspircd.git] / src / modules / m_helpop.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2013, 2017-2018, 2020 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2013, 2015 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
7  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
8  *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
9  *   Copyright (C) 2007 Robin Burchell <robin+git@viroteck.net>
10  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
11  *   Copyright (C) 2006 Craig Edwards <brain@inspircd.org>
12  *
13  * This file is part of InspIRCd.  InspIRCd is free software: you can
14  * redistribute it and/or modify it under the terms of the GNU General Public
15  * License as published by the Free Software Foundation, version 2.
16  *
17  * This program is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
20  * details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  */
25
26
27 #include "inspircd.h"
28 #include "modules/whois.h"
29
30 enum
31 {
32         // From UnrealIRCd.
33         RPL_WHOISHELPOP = 310,
34
35         // From ircd-ratbox.
36         ERR_HELPNOTFOUND = 524,
37         RPL_HELPSTART = 704,
38         RPL_HELPTXT = 705,
39         RPL_ENDOFHELP = 706
40 };
41
42 typedef std::vector<std::string> HelpMessage;
43
44 struct HelpTopic
45 {
46         // The body of the help topic.
47         const HelpMessage body;
48
49         // The title of the help topic.
50         const std::string title;
51
52         HelpTopic(const HelpMessage& Body, const std::string& Title)
53                 : body(Body)
54                 , title(Title)
55         {
56         }
57 };
58
59 typedef std::map<std::string, HelpTopic, irc::insensitive_swo> HelpMap;
60
61 class CommandHelpop : public Command
62 {
63  private:
64         const std::string startkey;
65
66  public:
67         HelpMap help;
68         std::string nohelp;
69
70         CommandHelpop(Module* Creator)
71                 : Command(Creator, "HELPOP", 0)
72                 , startkey("start")
73         {
74                 syntax = "<any-text>";
75         }
76
77         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
78         {
79                 const std::string& topic = parameters.empty() ? startkey : parameters[0];
80                 HelpMap::const_iterator titer = help.find(topic);
81                 if (titer == help.end())
82                 {
83                         user->WriteNumeric(ERR_HELPNOTFOUND, topic, nohelp);
84                         return CMD_FAILURE;
85                 }
86
87                 const HelpTopic& entry = titer->second;
88                 user->WriteNumeric(RPL_HELPSTART, topic, entry.title);
89                 for (HelpMessage::const_iterator liter = entry.body.begin(); liter != entry.body.end(); ++liter)
90                         user->WriteNumeric(RPL_HELPTXT, topic, *liter);
91                 user->WriteNumeric(RPL_ENDOFHELP, topic, "End of /HELPOP.");
92                 return CMD_SUCCESS;
93         }
94 };
95
96 class ModuleHelpop
97         : public Module
98         , public Whois::EventListener
99 {
100  private:
101                 CommandHelpop cmd;
102                 SimpleUserModeHandler ho;
103
104         public:
105                 ModuleHelpop()
106                         : Whois::EventListener(this)
107                         , cmd(this)
108                         , ho(this, "helpop", 'h', true)
109                 {
110                 }
111
112                 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
113                 {
114                         size_t longestkey = 0;
115
116                         HelpMap newhelp;
117                         ConfigTagList tags = ServerInstance->Config->ConfTags("helpop");
118                         if (tags.first == tags.second)
119                                 throw ModuleException("You have loaded the helpop module but not configured any help topics!");
120
121                         for (ConfigIter i = tags.first; i != tags.second; ++i)
122                         {
123                                 ConfigTag* tag = i->second;
124
125                                 // Attempt to read the help key.
126                                 const std::string key = tag->getString("key");
127                                 if (key.empty())
128                                         throw ModuleException(InspIRCd::Format("<helpop:key> is empty at %s", tag->getTagLocation().c_str()));
129                                 else if (irc::equals(key, "index"))
130                                         throw ModuleException(InspIRCd::Format("<helpop:key> is set to \"index\" which is reserved at %s", tag->getTagLocation().c_str()));
131                                 else if (key.length() > longestkey)
132                                         longestkey = key.length();
133
134                                 // Attempt to read the help value.
135                                 std::string value;
136                                 if (!tag->readString("value", value, true) || value.empty())
137                                         throw ModuleException(InspIRCd::Format("<helpop:value> is empty at %s", tag->getTagLocation().c_str()));
138
139                                 // Parse the help body. Empty lines are replaced with a single
140                                 // space because some clients are unable to show blank lines.
141                                 HelpMessage helpmsg;
142                                 irc::sepstream linestream(value, '\n', true);
143                                 for (std::string line; linestream.GetToken(line); )
144                                         helpmsg.push_back(line.empty() ? " " : line);
145
146                                 // Read the help title and store the topic.
147                                 const std::string title = tag->getString("title", InspIRCd::Format("*** Help for %s", key.c_str()), 1);
148                                 if (!newhelp.insert(std::make_pair(key, HelpTopic(helpmsg, title))).second)
149                                 {
150                                         throw ModuleException(InspIRCd::Format("<helpop> tag with duplicate key '%s' at %s",
151                                                 key.c_str(), tag->getTagLocation().c_str()));
152                                 }
153                         }
154
155                         // The number of items we can fit on a page.
156                         HelpMessage indexmsg;
157                         size_t maxcolumns = 80 / (longestkey + 2);
158                         for (HelpMap::iterator iter = newhelp.begin(); iter != newhelp.end(); )
159                         {
160                                 std::string indexline;
161                                 for (size_t column = 0; column != maxcolumns; )
162                                 {
163                                         if (iter == newhelp.end())
164                                                 break;
165
166                                         indexline.append(iter->first);
167                                         if (++column != maxcolumns)
168                                                 indexline.append(longestkey - iter->first.length() + 2, ' ');
169                                         iter++;
170                                 }
171                                 indexmsg.push_back(indexline);
172                         }
173                         newhelp.insert(std::make_pair("index", HelpTopic(indexmsg, "List of help topics")));
174                         cmd.help.swap(newhelp);
175
176                         ConfigTag* tag = ServerInstance->Config->ConfValue("helpmsg");
177                         cmd.nohelp = tag->getString("nohelp", "There is no help for the topic you searched for. Please try again.", 1);
178                 }
179
180                 void OnWhois(Whois::Context& whois) CXX11_OVERRIDE
181                 {
182                         if (whois.GetTarget()->IsModeSet(ho))
183                                 whois.SendLine(RPL_WHOISHELPOP, "is available for help.");
184                 }
185
186                 Version GetVersion() CXX11_OVERRIDE
187                 {
188                         return Version("Adds the /HELPOP command which allows users to view help on various topics and user mode h (helpop) which marks a server operator as being available for help.", VF_VENDOR);
189                 }
190 };
191
192 MODULE_INIT(ModuleHelpop)