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 ircd-ratbox. 33 ERR_HELPNOTFOUND = 524, 34 RPL_HELPSTART = 704, 35 RPL_HELPTXT = 705, 36 RPL_ENDOFHELP = 706 37 }; 38 39 typedef std::vector<std::string> HelpMessage; 40 41 struct HelpTopic 42 { 43 // The body of the help topic. 44 const HelpMessage body; 45 46 // The title of the help topic. 47 const std::string title; 48 HelpTopicHelpTopic49 HelpTopic(const HelpMessage& Body, const std::string& Title) 50 : body(Body) 51 , title(Title) 52 { 53 } 54 }; 55 56 typedef std::map<std::string, HelpTopic, irc::insensitive_swo> HelpMap; 57 58 class CommandHelpop : public Command 59 { 60 private: 61 const std::string startkey; 62 63 public: 64 HelpMap help; 65 std::string nohelp; 66 CommandHelpop(Module * Creator)67 CommandHelpop(Module* Creator) 68 : Command(Creator, "HELPOP", 0) 69 , startkey("start") 70 { 71 syntax = "<any-text>"; 72 } 73 Handle(User * user,const Params & parameters)74 CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE 75 { 76 const std::string& topic = parameters.empty() ? startkey : parameters[0]; 77 HelpMap::const_iterator titer = help.find(topic); 78 if (titer == help.end()) 79 { 80 user->WriteNumeric(ERR_HELPNOTFOUND, topic, nohelp); 81 return CMD_FAILURE; 82 } 83 84 const HelpTopic& entry = titer->second; 85 user->WriteNumeric(RPL_HELPSTART, topic, entry.title); 86 for (HelpMessage::const_iterator liter = entry.body.begin(); liter != entry.body.end(); ++liter) 87 user->WriteNumeric(RPL_HELPTXT, topic, *liter); 88 user->WriteNumeric(RPL_ENDOFHELP, topic, "End of /HELPOP."); 89 return CMD_SUCCESS; 90 } 91 }; 92 93 class ModuleHelpop 94 : public Module 95 , public Whois::EventListener 96 { 97 private: 98 CommandHelpop cmd; 99 SimpleUserModeHandler ho; 100 101 public: ModuleHelpop()102 ModuleHelpop() 103 : Whois::EventListener(this) 104 , cmd(this) 105 , ho(this, "helpop", 'h', true) 106 { 107 } 108 ReadConfig(ConfigStatus & status)109 void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE 110 { 111 size_t longestkey = 0; 112 113 HelpMap newhelp; 114 ConfigTagList tags = ServerInstance->Config->ConfTags("helpop"); 115 if (tags.first == tags.second) 116 throw ModuleException("You have loaded the helpop module but not configured any help topics!"); 117 118 for (ConfigIter i = tags.first; i != tags.second; ++i) 119 { 120 ConfigTag* tag = i->second; 121 122 // Attempt to read the help key. 123 const std::string key = tag->getString("key"); 124 if (key.empty()) 125 throw ModuleException(InspIRCd::Format("<helpop:key> is empty at %s", tag->getTagLocation().c_str())); 126 else if (irc::equals(key, "index")) 127 throw ModuleException(InspIRCd::Format("<helpop:key> is set to \"index\" which is reserved at %s", tag->getTagLocation().c_str())); 128 else if (key.length() > longestkey) 129 longestkey = key.length(); 130 131 // Attempt to read the help value. 132 std::string value; 133 if (!tag->readString("value", value, true) || value.empty()) 134 throw ModuleException(InspIRCd::Format("<helpop:value> is empty at %s", tag->getTagLocation().c_str())); 135 136 // Parse the help body. Empty lines are replaced with a single 137 // space because some clients are unable to show blank lines. 138 HelpMessage helpmsg; 139 irc::sepstream linestream(value, '\n', true); 140 for (std::string line; linestream.GetToken(line); ) 141 helpmsg.push_back(line.empty() ? " " : line); 142 143 // Read the help title and store the topic. 144 const std::string title = tag->getString("title", InspIRCd::Format("*** Help for %s", key.c_str()), 1); 145 if (!newhelp.insert(std::make_pair(key, HelpTopic(helpmsg, title))).second) 146 { 147 throw ModuleException(InspIRCd::Format("<helpop> tag with duplicate key '%s' at %s", 148 key.c_str(), tag->getTagLocation().c_str())); 149 } 150 } 151 152 // The number of items we can fit on a page. 153 HelpMessage indexmsg; 154 size_t maxcolumns = 80 / (longestkey + 2); 155 for (HelpMap::iterator iter = newhelp.begin(); iter != newhelp.end(); ) 156 { 157 std::string indexline; 158 for (size_t column = 0; column != maxcolumns; ) 159 { 160 if (iter == newhelp.end()) 161 break; 162 163 indexline.append(iter->first); 164 if (++column != maxcolumns) 165 indexline.append(longestkey - iter->first.length() + 2, ' '); 166 iter++; 167 } 168 indexmsg.push_back(indexline); 169 } 170 newhelp.insert(std::make_pair("index", HelpTopic(indexmsg, "List of help topics"))); 171 cmd.help.swap(newhelp); 172 173 ConfigTag* tag = ServerInstance->Config->ConfValue("helpmsg"); 174 cmd.nohelp = tag->getString("nohelp", "There is no help for the topic you searched for. Please try again.", 1); 175 } 176 OnWhois(Whois::Context & whois)177 void OnWhois(Whois::Context& whois) CXX11_OVERRIDE 178 { 179 if (whois.GetTarget()->IsModeSet(ho)) 180 whois.SendLine(RPL_WHOISHELPOP, "is available for help."); 181 } 182 GetVersion()183 Version GetVersion() CXX11_OVERRIDE 184 { 185 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); 186 } 187 }; 188 189 MODULE_INIT(ModuleHelpop) 190