summaryrefslogtreecommitdiff
path: root/src/core/os_news.cpp
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2010-06-19 11:54:08 -0400
committerAdam <Adam@anope.org>2010-06-19 11:54:08 -0400
commit52058fe87b4b0475b1775198c725af14e540d355 (patch)
treec3597d6411a006ffbb670d2ce761101b9640e9b6 /src/core/os_news.cpp
parent43e951aed54f838ba55a4c1552214773aee2fb2f (diff)
parentdf9d291bcba9788e51d11424ebaf6f05c26cc80f (diff)
Merge remote branch 'origin/1.9.3' into 1.9
Diffstat (limited to 'src/core/os_news.cpp')
-rw-r--r--src/core/os_news.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/src/core/os_news.cpp b/src/core/os_news.cpp
new file mode 100644
index 000000000..7be0bc5f7
--- /dev/null
+++ b/src/core/os_news.cpp
@@ -0,0 +1,486 @@
+/* OperServ core functions
+ *
+ * (C) 2003-2010 Anope Team
+ * Contact us at info@anope.org
+ *
+ * Please read COPYING and README for further details.
+ *
+ * Based on the original code of Epona by Lara.
+ * Based on the original code of Services by Andy Church.
+ *
+ *
+ */
+/*************************************************************************/
+
+#include "module.h"
+
+#define lenof(a) (sizeof(a) / sizeof(*(a)))
+
+/* List of messages for each news type. This simplifies message sending. */
+
+#define MSG_SYNTAX 0
+#define MSG_LIST_HEADER 1
+#define MSG_LIST_ENTRY 2
+#define MSG_LIST_NONE 3
+#define MSG_ADD_SYNTAX 4
+#define MSG_ADD_FULL 5
+#define MSG_ADDED 6
+#define MSG_DEL_SYNTAX 7
+#define MSG_DEL_NOT_FOUND 8
+#define MSG_DELETED 9
+#define MSG_DEL_NONE 10
+#define MSG_DELETED_ALL 11
+#define MSG_MAX 11
+
+struct newsmsgs msgarray[] = {
+ {NEWS_LOGON, "LOGON",
+ {NEWS_LOGON_SYNTAX,
+ NEWS_LOGON_LIST_HEADER,
+ NEWS_LOGON_LIST_ENTRY,
+ NEWS_LOGON_LIST_NONE,
+ NEWS_LOGON_ADD_SYNTAX,
+ NEWS_LOGON_ADD_FULL,
+ NEWS_LOGON_ADDED,
+ NEWS_LOGON_DEL_SYNTAX,
+ NEWS_LOGON_DEL_NOT_FOUND,
+ NEWS_LOGON_DELETED,
+ NEWS_LOGON_DEL_NONE,
+ NEWS_LOGON_DELETED_ALL}
+ },
+ {NEWS_OPER, "OPER",
+ {NEWS_OPER_SYNTAX,
+ NEWS_OPER_LIST_HEADER,
+ NEWS_OPER_LIST_ENTRY,
+ NEWS_OPER_LIST_NONE,
+ NEWS_OPER_ADD_SYNTAX,
+ NEWS_OPER_ADD_FULL,
+ NEWS_OPER_ADDED,
+ NEWS_OPER_DEL_SYNTAX,
+ NEWS_OPER_DEL_NOT_FOUND,
+ NEWS_OPER_DELETED,
+ NEWS_OPER_DEL_NONE,
+ NEWS_OPER_DELETED_ALL}
+ },
+ {NEWS_RANDOM, "RANDOM",
+ {NEWS_RANDOM_SYNTAX,
+ NEWS_RANDOM_LIST_HEADER,
+ NEWS_RANDOM_LIST_ENTRY,
+ NEWS_RANDOM_LIST_NONE,
+ NEWS_RANDOM_ADD_SYNTAX,
+ NEWS_RANDOM_ADD_FULL,
+ NEWS_RANDOM_ADDED,
+ NEWS_RANDOM_DEL_SYNTAX,
+ NEWS_RANDOM_DEL_NOT_FOUND,
+ NEWS_RANDOM_DELETED,
+ NEWS_RANDOM_DEL_NONE,
+ NEWS_RANDOM_DELETED_ALL}
+ }
+};
+
+static void DisplayNews(User *u, NewsType Type)
+{
+ int msg;
+ static unsigned current_news = 0;
+
+ if (Type == NEWS_LOGON)
+ msg = NEWS_LOGON_TEXT;
+ else if (Type == NEWS_OPER)
+ msg = NEWS_OPER_TEXT;
+ else if (Type == NEWS_RANDOM)
+ msg = NEWS_RANDOM_TEXT;
+ else
+ {
+ Alog() << "news: Invalid type (" << Type << ") to display_news()";
+ return;
+ }
+
+ unsigned displayed = 0;
+ bool NewsExists = false;
+ for (unsigned i = 0; i < News.size(); ++i)
+ {
+ if (News[i]->type == Type)
+ {
+ tm *tm;
+ char timebuf[64];
+
+ NewsExists = true;
+
+ if (Type == NEWS_RANDOM && i == current_news)
+ continue;
+
+ tm = localtime(&News[i]->time);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_SHORT_DATE_FORMAT, tm);
+ notice_lang(Config.s_GlobalNoticer, u, msg, timebuf, News[i]->Text.c_str());
+
+ ++displayed;
+
+ if (Type == NEWS_RANDOM)
+ {
+ current_news = i;
+ return;
+ }
+ else if (displayed >= Config.NewsCount)
+ return;
+ }
+
+ /* Reset to head of list to get first random news value */
+ if (i + 1 == News.size() && Type == NEWS_RANDOM && NewsExists)
+ i = 0;
+ }
+}
+
+static int add_newsitem(User * u, const char *text, NewsType type)
+{
+ int num = 0;
+
+ for (unsigned i = News.size(); i > 0; --i)
+ {
+ if (News[i - 1]->type == type)
+ {
+ num = News[i - 1]->num;
+ break;
+ }
+ }
+
+ NewsItem *news = new NewsItem;
+ news->type = type;
+ news->num = num + 1;
+ news->Text = text;
+ news->time = time(NULL);
+ news->who = u->nick;
+
+ News.push_back(news);
+
+ return num + 1;
+}
+
+static int del_newsitem(unsigned num, NewsType type)
+{
+ int count = 0;
+
+ for (unsigned i = News.size(); i > 0; --i)
+ {
+ if (News[i - 1]->type == type && (num == 0 || News[i - 1]->num == num))
+ {
+ delete News[i - 1];
+ News.erase(News.begin() + i - 1);
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+static int *findmsgs(NewsType type, const char **type_name)
+{
+ for (unsigned i = 0; i < lenof(msgarray); i++) {
+ if (msgarray[i].type == type) {
+ if (type_name)
+ *type_name = msgarray[i].name;
+ return msgarray[i].msgs;
+ }
+ }
+ return NULL;
+}
+
+class NewsBase : public Command
+{
+ protected:
+ CommandReturn DoList(User *u, NewsType type, int *msgs)
+ {
+ int count = 0;
+ char timebuf[64];
+ struct tm *tm;
+
+ for (unsigned i = 0; i < News.size(); ++i)
+ {
+ if (News[i]->type == type)
+ {
+ if (!count)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_HEADER]);
+ tm = localtime(&News[i]->time);
+ strftime_lang(timebuf, sizeof(timebuf), u, STRFTIME_DATE_TIME_FORMAT, tm);
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_ENTRY], News[i]->num, timebuf, !News[i]->who.empty() ? News[i]->who.c_str() : "<unknown>", News[i]->Text.c_str());
+ ++count;
+ }
+ }
+ if (!count)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_LIST_NONE]);
+ else
+ notice_lang(Config.s_OperServ, u, END_OF_ANY_LIST, "News");
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoAdd(User *u, const std::vector<ci::string> &params, NewsType type, int *msgs)
+ {
+ const char *text = params.size() > 1 ? params[1].c_str() : NULL;
+ int n;
+
+ if (!text)
+ this->OnSyntaxError(u, "ADD");
+ else
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+ n = add_newsitem(u, text, type);
+ if (n < 0)
+ notice_lang(Config.s_OperServ, u, msgs[MSG_ADD_FULL]);
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_ADDED], n);
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoDel(User *u, const std::vector<ci::string> &params, NewsType type, int *msgs)
+ {
+ const char *text = params.size() > 1 ? params[1].c_str() : NULL;
+ unsigned num;
+
+ if (!text)
+ this->OnSyntaxError(u, "DEL");
+ else
+ {
+ if (readonly)
+ {
+ notice_lang(Config.s_OperServ, u, READ_ONLY_MODE);
+ return MOD_CONT;
+ }
+ if (stricmp(text, "ALL"))
+ {
+ num = atoi(text);
+ if (num > 0 && del_newsitem(num, type))
+ {
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DELETED], num);
+ for (unsigned i = 0; i < News.size(); ++i)
+ {
+ if (News[i]->type == type && News[i]->num > num)
+ --News[i]->num;
+ }
+ }
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DEL_NOT_FOUND], num);
+ }
+ else
+ {
+ if (del_newsitem(0, type))
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DELETED_ALL]);
+ else
+ notice_lang(Config.s_OperServ, u, msgs[MSG_DEL_NONE]);
+ }
+ }
+
+ return MOD_CONT;
+ }
+
+ CommandReturn DoNews(User *u, const std::vector<ci::string> &params, NewsType type)
+ {
+ ci::string cmd = params[0];
+ const char *type_name;
+ int *msgs;
+
+ msgs = findmsgs(type, &type_name);
+ if (!msgs)
+ {
+ Alog() << "news: Invalid type to do_news()";
+ return MOD_CONT;
+ }
+
+ if (cmd == "LIST")
+ return this->DoList(u, type, msgs);
+ else if (cmd == "ADD")
+ return this->DoAdd(u, params, type, msgs);
+ else if (cmd == "DEL")
+ return this->DoDel(u, params, type, msgs);
+ else
+ this->OnSyntaxError(u, "");
+
+ return MOD_CONT;
+ }
+ public:
+ NewsBase(const ci::string &newstype) : Command(newstype, 1, 2, "operserv/news")
+ {
+ }
+
+ virtual ~NewsBase()
+ {
+ }
+
+ virtual CommandReturn Execute(User *u, const std::vector<ci::string> &params) = 0;
+
+ virtual bool OnHelp(User *u, const ci::string &subcommand) = 0;
+
+ virtual void OnSyntaxError(User *u, const ci::string &subcommand) = 0;
+};
+
+class CommandOSLogonNews : public NewsBase
+{
+ public:
+ CommandOSLogonNews() : NewsBase("LOGONNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_LOGON);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_LOGON, Config.NewsCount);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "LOGONNEWS", NEWS_LOGON_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_LOGONNEWS);
+ }
+};
+
+class CommandOSOperNews : public NewsBase
+{
+ public:
+ CommandOSOperNews() : NewsBase("OPERNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_OPER);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_OPER, Config.NewsCount);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "OPERNEWS", NEWS_OPER_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_OPERNEWS);
+ }
+};
+
+class CommandOSRandomNews : public NewsBase
+{
+ public:
+ CommandOSRandomNews() : NewsBase("RANDOMNEWS")
+ {
+ }
+
+ CommandReturn Execute(User *u, const std::vector<ci::string> &params)
+ {
+ return this->DoNews(u, params, NEWS_RANDOM);
+ }
+
+ bool OnHelp(User *u, const ci::string &subcommand)
+ {
+ notice_help(Config.s_OperServ, u, NEWS_HELP_RANDOM);
+ return true;
+ }
+
+ void OnSyntaxError(User *u, const ci::string &subcommand)
+ {
+ syntax_error(Config.s_OperServ, u, "RANDOMNEWS", NEWS_RANDOM_SYNTAX);
+ }
+
+ void OnServHelp(User *u)
+ {
+ notice_lang(Config.s_OperServ, u, OPER_HELP_CMD_RANDOMNEWS);
+ }
+};
+
+class OSNews : public Module
+{
+ public:
+ OSNews(const std::string &modname, const std::string &creator) : Module(modname, creator)
+ {
+ this->SetAuthor("Anope");
+ this->SetVersion(VERSION_STRING);
+ this->SetType(CORE);
+
+ this->AddCommand(OperServ, new CommandOSLogonNews());
+ this->AddCommand(OperServ, new CommandOSOperNews());
+ this->AddCommand(OperServ, new CommandOSRandomNews());
+
+ Implementation i[] = { I_OnUserModeSet, I_OnUserConnect, I_OnDatabaseRead, I_OnDatabaseWrite };
+ ModuleManager::Attach(i, this, 4);
+ }
+
+ ~OSNews()
+ {
+ for (std::vector<NewsItem *>::iterator it = News.begin(); it != News.end(); ++it)
+ delete *it;
+ News.clear();
+ }
+
+ void OnUserModeSet(User *u, UserModeName Name)
+ {
+ if (Name == UMODE_OPER)
+ {
+ DisplayNews(u, NEWS_OPER);
+ }
+ }
+
+ void OnUserConnect(User *u)
+ {
+ DisplayNews(u, NEWS_LOGON);
+ DisplayNews(u, NEWS_RANDOM);
+ }
+
+ EventReturn OnDatabaseRead(const std::vector<std::string> &params)
+ {
+ if (params[0] == "OS" && params.size() >= 7 && params[1] == "NEWS")
+ {
+ NewsItem *n = new NewsItem;
+ n->num = atoi(params[2].c_str());
+ n->time = strtol(params[3].c_str(), NULL, 10);
+ n->who = params[4];
+ if (params[5] == "LOGON")
+ n->type = NEWS_LOGON;
+ else if (params[5] == "RANDOM")
+ n->type = NEWS_RANDOM;
+ else if (params[5] == "OPER")
+ n->type = NEWS_OPER;
+ n->Text = params[6];
+ News.push_back(n);
+
+ return EVENT_STOP;
+ }
+
+ return EVENT_CONTINUE;
+ }
+
+ void OnDatabaseWrite(void (*Write)(const std::string &))
+ {
+ for (std::vector<NewsItem *>::iterator it = News.begin(); it != News.end(); ++it)
+ {
+ NewsItem *n = *it;
+ char buf[512]; const char* ntype;
+ if (n->type == NEWS_LOGON)
+ ntype = "LOGON";
+ else if (n->type == NEWS_RANDOM)
+ ntype = "RANDOM";
+ else if (n->type == NEWS_OPER)
+ ntype = "OPER";
+ snprintf(buf, sizeof(buf), "OS NEWS %d %ld %s %s :%s", n->num, n->time, n->who.c_str(), ntype, n->Text.c_str());
+ Write(buf);
+ }
+ }
+};
+
+MODULE_INIT(OSNews)