1 /*
2  *
3  * (C) 2012-2020 Anope Team
4  * Contact us at team@anope.org
5  *
6  * Please read COPYING and README for further details.
7  */
8 
9 #include "module.h"
10 #include "modules/sql.h"
11 
12 struct SQLOper : Oper
13 {
SQLOperSQLOper14 	SQLOper(const Anope::string &n, OperType *o) : Oper(n, o) { }
15 };
16 
17 class SQLOperResult : public SQL::Interface
18 {
19 	Reference<User> user;
20 
21 	struct SQLOperResultDeleter
22 	{
23 		SQLOperResult *res;
SQLOperResultDeleterSQLOperResult::SQLOperResultDeleter24 		SQLOperResultDeleter(SQLOperResult *r) : res(r) { }
~SQLOperResultDeleterSQLOperResult::SQLOperResultDeleter25 		~SQLOperResultDeleter() { delete res; }
26 	};
27 
Deoper()28 	void Deoper()
29 	{
30 		if (user->Account() && user->Account()->o && dynamic_cast<SQLOper *>(user->Account()->o))
31 		{
32 			delete user->Account()->o;
33 			user->Account()->o = NULL;
34 
35 			Log(this->owner) << "m_sql_oper: Removed services operator from " << user->nick << " (" << user->Account()->display << ")";
36 
37 			BotInfo *OperServ = Config->GetClient("OperServ");
38 			user->RemoveMode(OperServ, "OPER"); // Probably not set, just incase
39 		}
40 	}
41 
42  public:
SQLOperResult(Module * m,User * u)43 	SQLOperResult(Module *m, User *u) : SQL::Interface(m), user(u) { }
44 
OnResult(const SQL::Result & r)45 	void OnResult(const SQL::Result &r) anope_override
46 	{
47 		SQLOperResultDeleter d(this);
48 
49 		if (!user || !user->Account())
50 			return;
51 
52 		if (r.Rows() == 0)
53 		{
54 			Log(LOG_DEBUG) << "m_sql_oper: Got 0 rows for " << user->nick;
55 			Deoper();
56 			return;
57 		}
58 
59 		Anope::string opertype;
60 		try
61 		{
62 			opertype = r.Get(0, "opertype");
63 		}
64 		catch (const SQL::Exception &)
65 		{
66 			Log(this->owner) << "Expected column named \"opertype\" but one was not found";
67 			return;
68 		}
69 
70 		Log(LOG_DEBUG) << "m_sql_oper: Got result for " << user->nick << ", opertype " << opertype;
71 
72 		Anope::string modes;
73 		try
74 		{
75 			modes = r.Get(0, "modes");
76 		}
77 		catch (const SQL::Exception &)
78 		{
79 			// Common case here is an exception, but this probably doesn't get this far often
80 		}
81 
82 		BotInfo *OperServ = Config->GetClient("OperServ");
83 		if (opertype.empty())
84 		{
85 			Deoper();
86 			return;
87 		}
88 
89 		OperType *ot = OperType::Find(opertype);
90 		if (ot == NULL)
91 		{
92 			Log(this->owner) << "m_sql_oper: Oper " << user->nick << " has type " << opertype << ", but this opertype does not exist?";
93 			return;
94 		}
95 
96 		if (user->Account()->o && !dynamic_cast<SQLOper *>(user->Account()->o))
97 		{
98 			Log(this->owner) << "Oper " << user->Account()->display << " has type " << opertype << ", but is already configured as an oper of type " << user->Account()->o->ot->GetName();
99 			return;
100 		}
101 
102 		if (!user->Account()->o || user->Account()->o->ot != ot)
103 		{
104 			Log(this->owner) << "m_sql_oper: Tieing oper " << user->nick << " to type " << opertype;
105 
106 			delete user->Account()->o;
107 			user->Account()->o = new SQLOper(user->Account()->display, ot);
108 		}
109 
110 		if (!user->HasMode("OPER"))
111 		{
112 			IRCD->SendOper(user);
113 
114 			if (!modes.empty())
115 				user->SetModes(OperServ, "%s", modes.c_str());
116 		}
117 	}
118 
OnError(const SQL::Result & r)119 	void OnError(const SQL::Result &r) anope_override
120 	{
121 		SQLOperResultDeleter d(this);
122 		Log(this->owner) << "m_sql_oper: Error executing query " << r.GetQuery().query << ": " << r.GetError();
123 	}
124 };
125 
126 class ModuleSQLOper : public Module
127 {
128 	Anope::string engine;
129 	Anope::string query;
130 
131 	ServiceReference<SQL::Provider> SQL;
132 
133  public:
ModuleSQLOper(const Anope::string & modname,const Anope::string & creator)134 	ModuleSQLOper(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR)
135 	{
136 	}
137 
~ModuleSQLOper()138 	~ModuleSQLOper()
139 	{
140 		for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
141 		{
142 			NickCore *nc = it->second;
143 
144 			if (nc->o && dynamic_cast<SQLOper *>(nc->o))
145 			{
146 				delete nc->o;
147 				nc->o = NULL;
148 			}
149 		}
150 	}
151 
OnReload(Configuration::Conf * conf)152 	void OnReload(Configuration::Conf *conf) anope_override
153 	{
154 		Configuration::Block *config = conf->GetModule(this);
155 
156 		this->engine = config->Get<const Anope::string>("engine");
157 		this->query = config->Get<const Anope::string>("query");
158 
159 		this->SQL = ServiceReference<SQL::Provider>("SQL::Provider", this->engine);
160 	}
161 
OnNickIdentify(User * u)162 	void OnNickIdentify(User *u) anope_override
163 	{
164 		if (!this->SQL)
165 		{
166 			Log() << "Unable to find SQL engine";
167 			return;
168 		}
169 
170 		SQL::Query q(this->query);
171 		q.SetValue("a", u->Account()->display);
172 		q.SetValue("i", u->ip.addr());
173 
174 		this->SQL->Run(new SQLOperResult(this, u), q);
175 
176 		Log(LOG_DEBUG) << "m_sql_oper: Checking authentication for " << u->Account()->display;
177 	}
178 };
179 
180 MODULE_INIT(ModuleSQLOper)
181