cf7105b13e4450c715033a985abf702405edad8b
[NeonServV5.git] / src / DBHelper.c
1 /* DBHelper.c - NeonServ v5.3
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #include "DBHelper.h"
19 #include "UserNode.h"
20 #include "ChanNode.h"
21 #include "ChanUser.h"
22 #include "mysqlConn.h"
23 #include "lang.h"
24 #include "tools.h"
25 #include "IRCEvents.h"
26 #include "HandleInfoHandler.h"
27
28 void _loadUserSettings(struct UserNode *user) {
29     MYSQL_RES *res;
30     MYSQL_ROW row;
31     printf_mysql_query("SELECT `user_lang`, `user_reply_privmsg`, `user_god`, `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
32     res = mysql_use();
33     if ((row = mysql_fetch_row(res)) != NULL) {
34         user->language = get_language_by_tag(row[0]);
35         if(user->language == NULL) user->language = get_default_language();
36         if(strcmp(row[1], "0"))
37             user->flags |= USERFLAG_REPLY_PRIVMSG;
38         if(strcmp(row[2], "0"))
39             user->flags |= USERFLAG_GOD_MODE;
40         user->user_id = atoi(row[3]);
41         user->flags |= USERFLAG_HAS_USERID;
42     } else
43         user->language = get_default_language();
44     user->flags |= USERFLAG_LOADED_SETTINGS;
45 }
46
47 int isGodMode(struct UserNode *user) {
48     loadUserSettings(user);
49     return (user->flags & USERFLAG_GOD_MODE);
50 }
51
52 int getChannelAccess(struct UserNode *user, struct ChanNode *chan) {
53     if(!(user->flags & USERFLAG_ISAUTHED)) return 0;
54     loadChannelSettings(chan);
55     if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0;
56     MYSQL_RES *res;
57     MYSQL_ROW row;
58     int caccess = 0;
59     int userid;
60     if(user->flags & USERFLAG_HAS_USERID)
61         userid = user->user_id;
62     else {
63         printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
64         res = mysql_use();
65         if ((row = mysql_fetch_row(res)) != NULL) {
66             userid = atoi(row[0]);
67             user->user_id = userid;
68             user->flags |= USERFLAG_HAS_USERID;
69         } else
70             return 0;
71     }
72     printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d' AND `chanuser_cid` = '%d'", userid, chan->channel_id);
73     res = mysql_use();
74     if ((row = mysql_fetch_row(res)) != NULL) {
75         int cflags = atoi(row[1]);
76         if(!(cflags & DB_CHANUSER_SUSPENDED) && atoi(row[0]) > caccess)
77             caccess = atoi(row[0]);
78     }
79     return caccess;
80 }
81
82 char *getChanDefault(char *channel_setting) {
83     MYSQL_RES *res;
84     MYSQL_ROW row;
85     printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_setting);
86     res = mysql_use();
87     if ((row = mysql_fetch_row(res)) == NULL) return "";
88     return row[0];
89 }
90
91 int checkChannelAccess(struct UserNode *user, struct ChanNode *chan, char *channel_setting, int allow_501) {
92     loadChannelSettings(chan);
93     if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0;
94     if((user->flags & USERFLAG_ISIRCOP)) return 1;
95     MYSQL_RES *res;
96     MYSQL_ROW row;
97     printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_setting, chan->channel_id);
98     res = mysql_use();
99     if ((row = mysql_fetch_row(res)) == NULL) return 0;
100     int require_access = atoi((row[0] ? row[0] : getChanDefault(channel_setting)));
101     if(require_access == 0) return 1;
102     if(!(user->flags & USERFLAG_ISAUTHED)) return 0;
103     int caccess = 0;
104     int userid;
105     if(user->flags & USERFLAG_HAS_USERID)
106         userid = user->user_id;
107     else {
108         printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
109         res = mysql_use();
110         if ((row = mysql_fetch_row(res)) != NULL) {
111             userid = atoi(row[0]);
112             user->user_id = userid;
113             user->flags |= USERFLAG_HAS_USERID;
114         } else
115             userid = -1;
116     }
117     if(userid > -1) {
118         printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d' AND `chanuser_cid` = '%d'", userid, chan->channel_id);
119         res = mysql_use();
120         if ((row = mysql_fetch_row(res)) != NULL) {
121             int cflags = atoi(row[1]);
122             if(!(cflags & DB_CHANUSER_SUSPENDED))
123                 caccess = atoi(row[0]);
124         }
125     }
126     if(caccess >= require_access) return 1;
127     if(caccess == 500 && require_access == 501 && allow_501) return 1;
128     return 0;
129 }
130
131 void _loadChannelSettings(struct ChanNode *chan) {
132     MYSQL_RES *res;
133     MYSQL_ROW row;
134     printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(chan->name));
135     res = mysql_use();
136     if ((row = mysql_fetch_row(res)) != NULL) {
137         chan->flags |= CHANFLAG_CHAN_REGISTERED;
138         chan->channel_id = atoi(row[0]);
139     }
140     chan->flags |= CHANFLAG_REQUESTED_CHANINFO;
141 }
142
143 //TODO: fix performance: we should cache the user access
144 int isUserProtected(struct ChanNode *chan, struct UserNode *victim, struct UserNode *issuer) {
145     /* Don't protect if someone is attacking himself, or if the aggressor is an IRC Operator. */
146     if(victim == issuer || (issuer->flags & USERFLAG_ISIRCOP)) return 0;
147     
148     /* Don't protect if no one is to be protected. */
149     MYSQL_RES *res;
150     MYSQL_ROW row;
151     char protection;
152     loadChannelSettings(chan);
153     if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0;
154     printf_mysql_query("SELECT `channel_protect` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
155     res = mysql_use();
156     if(!(row = mysql_fetch_row(res))) return 0;
157     if(row[0]) {
158         protection = (char) atoi(row[0]);
159     } else {
160          printf_mysql_query("SELECT `channel_protect` FROM `channels` WHERE `channel_name` = 'defaults'");
161         res = mysql_use();
162         row = mysql_fetch_row(res);
163         protection = (char) atoi(row[0]);
164     }
165     if(protection == 3) return 0;
166     
167     /* Don't protect if the victim isn't added to the channel, unless we are to protect non-users also. */
168     int victim_access = getChannelAccess(victim, chan);
169     if (!victim_access && protection != 0) return 0;
170     
171     /* Protect if the aggressor isn't a user because at this point, the aggressor can only be less than or equal to the victim. */
172     int issuer_access = getChannelAccess(issuer, chan);
173     if (!issuer_access) return 1;
174     
175     /* If the aggressor was a user, then the victim can't be helped. */
176     if(!victim_access) return 0;
177     
178     switch(protection) {
179         case 0:
180         case 1:
181             if(victim_access >= issuer_access) return 1;
182             break;
183         case 2:
184             if(victim_access > issuer_access) return 1;
185             break;
186     }
187     return 0;
188 }
189
190 char *getBanAffectingMask(struct ChanNode *chan, char *mask) {
191     loadChannelSettings(chan);
192     if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0;
193     MYSQL_RES *res;
194     MYSQL_ROW row;
195     printf_mysql_query("SELECT `ban_mask` FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id);
196     res = mysql_use();
197     while ((row = mysql_fetch_row(res)) != NULL) {
198         if(!match(row[0], mask))
199             return row[0];
200     }
201     return NULL;
202 }
203
204 int renameAccount(char *oldauth, char *newauth) {
205     MYSQL_RES *res, *res2;
206     MYSQL_ROW row, row2;
207     printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(oldauth));
208     res = mysql_use();
209     if ((row = mysql_fetch_row(res)) != NULL) {
210         int userid = atoi(row[0]);
211         printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(newauth));
212         res = mysql_use();
213         if((row = mysql_fetch_row(res)) != NULL) {
214             //merge
215             int newuid = atoi(row[0]);
216             printf_mysql_query("SELECT `chanuser_id`, `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d'", newuid);
217             res = mysql_use();
218             while((row = mysql_fetch_row(res)) != NULL) {
219                 printf_mysql_query("SELECT `chanuser_id`, `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d'", userid);
220                 res2 = mysql_use();
221                 if((row2 = mysql_fetch_row(res2)) != NULL) {
222                     if(atoi(row[0]) > atoi(row2[0])) {
223                         printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%s' WHERE `chanuser_id` = '%s'", row[0], row2[0]);
224                     }
225                     printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[0]);
226                 } else
227                     printf_mysql_query("UPDATE `chanusers` SET `chanuser_uid` = '%d' WHERE `chanuser_id` = '%s'", userid, row[0]);
228             }
229             printf_mysql_query("UPDATE `channels` SET `channel_registrator` = '%d' WHERE `channel_registrator` = '%d'", userid, newuid);
230             printf_mysql_query("UPDATE `bans` SET `ban_owner` = '%d' WHERE `ban_owner` = '%d'", userid, newuid);
231             printf_mysql_query("UPDATE `events` SET `auth` = '%s' WHERE `auth` = '%s'", escape_string(newauth), escape_string(oldauth));
232             printf_mysql_query("UPDATE `godlog` SET `godlog_uid` = '%d' WHERE `godlog_uid` = '%d'", userid, newuid);
233             printf_mysql_query("UPDATE `owner_history` SET `owner_history_to_uid` = '%d' WHERE `owner_history_to_uid` = '%d'", userid, newuid);
234             printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid);
235             printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid);
236             printf_mysql_query("UPDATE `noinvite` SET `uid` = '%d' WHERE `uid` = '%d'", userid, newuid);
237             printf_mysql_query("DELETE FROM `users` WHERE `chanuser_id` = '%d'", newuid);
238         } else {
239             //simply rename the account
240             printf_mysql_query("UPDATE `users` SET `user_user` = '%s' WHERE `user_id` = '%d'", escape_string(newauth), userid);
241         }
242         return 1;
243     }
244     return 0;
245 }
246
247 static AUTHLOOKUP_CALLBACK(event_user_registered_auth_lookup);
248
249 struct event_user_registered_cache {
250     struct UserNode *new_user;
251     char *oldauth;
252 };
253
254 static int event_user_registered(struct UserNode *old_user, struct UserNode *new_user) {
255     //check if there is a fakehost on both sides...
256     if(!isFakeHost(old_user->host) || !isFakeHost(new_user->host)) return 0;
257     //extract user names
258     char oldauth[AUTHLEN], newauth[AUTHLEN];
259     char *p;
260     if((p = strstr(old_user->host, "."))) {
261         *p = '\0';
262         strcpy(oldauth, old_user->host);
263         *p = '.';
264     }
265     if((p = strstr(new_user->host, "."))) {
266         *p = '\0';
267         strcpy(newauth, new_user->host);
268         *p = '.';
269     }
270     if(!stricmp(oldauth, newauth))
271         return 0;
272     //check if we know this user; then check the new auth
273     MYSQL_RES *res;
274     MYSQL_ROW row;
275     printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(oldauth));
276     res = mysql_use();
277     if ((row = mysql_fetch_row(res)) != NULL) {
278         struct event_user_registered_cache *cache = malloc(sizeof(*cache));
279         if (!cache) {
280             perror("malloc() failed");
281             return 1;
282         }
283         cache->new_user = new_user;
284         cache->oldauth = strdup(oldauth);
285         lookup_authname(newauth, event_user_registered_auth_lookup, cache);
286     }
287     return 1;
288 }
289
290 static AUTHLOOKUP_CALLBACK(event_user_registered_auth_lookup) {
291     struct event_user_registered_cache *cache = data;
292     if(exists) {
293         renameAccount(cache->oldauth, auth);
294         strcpy(cache->new_user->auth, auth);
295         cache->new_user->flags |= USERFLAG_ISAUTHED;
296     }
297     free(cache->oldauth);
298 }
299
300 void init_DBHelper() {
301     bind_registered(event_user_registered);
302 }
303