fixed crash in cmd_NeonServ.mod/cmd_neonserv_mode.c
[NeonServV5.git] / src / modules / NeonServ.mod / cmd_neonserv_mode.c
1 /* cmd_neonserv_mode.c - NeonServ v5.6
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 "cmd_neonserv.h"
19
20 /*
21 * argv[0] - modes
22 * argv[1-*] - parameters
23 */
24 static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup);
25 static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode);
26
27 struct neonserv_cmd_mode_cache {
28     struct ClientSocket *client, *textclient;
29     struct UserNode *user;
30     struct Event *event;
31     char *mode;
32 };
33
34 CMD_BIND(neonserv_cmd_mode) {
35     struct neonserv_cmd_mode_cache *cache = malloc(sizeof(*cache));
36     if (!cache) {
37         printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
38         return;
39     }
40     cache->client = client;
41     cache->textclient = textclient;
42     cache->user = user;
43     cache->event = event;
44     cache->mode = strdup(merge_argv(argv, 0, argc));
45     get_userlist_with_invisible(chan, module_id, neonserv_cmd_mode_userlist_lookup, cache);
46 }
47
48 static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup) {
49     struct neonserv_cmd_mode_cache *cache = data;
50     neonserv_cmd_mode_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->mode);
51     free(cache->mode);
52     free(cache);
53 }
54
55 static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode) {
56     MYSQL_ROW row, defaults = NULL;
57     int i, arg, add = 1, skip = 0;
58     unsigned int modetype;
59     int db_canop, db_canvoice, db_canban, db_enfmodes;
60     struct ModeNode *modelock = createModeNode(NULL), *changemodes = createModeNode(NULL);
61     struct ModeBuffer *modeBuf;
62     struct UserNode *cuser;
63     struct ChanUser *chanuser;
64     modeBuf = initModeBuffer(client, chan);
65     printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
66     row = mysql_fetch_row(mysql_use());
67     if(row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[4] == NULL) {
68         printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'");
69         defaults = mysql_fetch_row(mysql_use());
70     }
71     db_canop = atoi((row[0] ? row[0] : defaults[0]));
72     db_canvoice = atoi((row[1] ? row[1] : defaults[1]));
73     db_canban = atoi((row[2] ? row[2] : defaults[2]));
74     db_enfmodes = atoi((row[3] ? row[3] : defaults[3]));
75     if(row[4])
76         parseModeString(modelock, row[4]);
77     else if(defaults[4])
78         parseModeString(modelock, defaults[4]);
79     int uaccess = getChannelAccess(user, chan);
80     char *a, *b = mode;
81     char *argv[MAXNUMPARAMS];
82     char *carg;
83     char tmp[MAXLEN];
84     int argc = 0;
85     do {
86         a = strstr(b, " ");
87         if(a) *a = '\0';
88         argv[argc++] = b;
89         if(argc == MAXNUMPARAMS) break;
90         if(a) b = a+1;
91     } while(a);
92     arg = 0;
93     while(arg < argc) {
94         char *modeStr = argv[arg++];
95         char modeStrBuf[3];
96         for(i = 0; i < strlen(modeStr); i++) {
97             switch(modeStr[i]) {
98                 case '+':
99                     add = 1;
100                     break;
101                 case '-':
102                     add = 0;
103                     break;
104                 case 'o':
105                 case 'v':
106                     if(arg == argc) {
107                         sprintf(modeStrBuf, "%c%c", (add ? '+' : '-'), modeStr[i]);
108                         reply(textclient, user, "NS_MODE_INVALID", modeStrBuf);
109                         return;
110                     }
111                     carg = argv[arg++];
112                     if(modeStr[i] == 'o') {
113                         if(uaccess < db_canop) {
114                             reply(textclient, user, "NS_MODE_ENFOPS", chan->name);
115                             db_canop = -1;
116                             break;
117                         }
118                         if(db_canop == -1) break;
119                     } else {
120                         if(uaccess < db_canvoice) {
121                             reply(textclient, user, "NS_MODE_ENFVOICE", chan->name);
122                             db_canvoice = -1;
123                             break;
124                         }
125                         if(db_canvoice == -1) break;
126                     }
127                     cuser = searchUserByNick(carg);
128                     if(!cuser) {
129                         //check for an invisible user
130                         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
131                             if(!stricmp(chanuser->user->nick, carg)) {
132                                 cuser = chanuser->user;
133                                 break;
134                             }
135                         }
136                         if(!cuser) break;
137                     } else {
138                         chanuser = getChanUser(cuser, chan);
139                         if(!chanuser) break;
140                     }
141                     if(!(add ^ (chanuser->flags & (modeStr[i] == 'o' ? CHANUSERFLAG_OPPED : CHANUSERFLAG_VOICED)))) break;
142                     if(!add) {
143                         //check protection
144                         if(modeStr[i] == 'o' && isNetworkService(cuser)) {
145                             reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick);
146                             break;
147                         }
148                         if(isUserProtected(chan, cuser, user)) {
149                             reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
150                             break;
151                         }
152                     }
153                     modeBufferSet(modeBuf, add, modeStr[i], carg);
154                     break;
155                 case 'b':
156                     if(arg == argc) {
157                         sprintf(modeStrBuf, "%c%c", (add ? '+' : '-'), modeStr[i]);
158                         reply(textclient, user, "NS_MODE_INVALID", modeStrBuf);
159                         return;
160                     }
161                     carg = argv[arg++];
162                     if(uaccess < db_canban) {
163                         reply(textclient, user, "NS_MODE_CANBAN", chan->name);
164                         db_canban = -1;
165                         break;
166                     }
167                     if(db_canban == -1) break;
168                     char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3];
169                     char usermask[NICKLEN+USERLEN+HOSTLEN+3];
170                     struct BanNode *ban;
171                     int match_count = 0;
172                     carg = make_banmask(carg, hostmask_buffer);
173                     if(add) {
174                         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
175                             cuser = chanuser->user;
176                             sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host);
177                             if(!match(carg, usermask)) {
178                                 if(isNetworkService(chanuser->user)) {
179                                     reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
180                                     skip = 1;
181                                     break;
182                                 }
183                                 if(isUserProtected(chan, cuser, user)) {
184                                     reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
185                                     skip = 1;
186                                     break;
187                                 }
188                                 match_count++;
189                                 if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) {
190                                     skip = 1;
191                                     reply(textclient, user, "NS_LAME_MASK", carg);
192                                     break;
193                                 }
194                             }
195                         }
196                     } else {
197                         skip = 1;
198                         for(ban = chan->bans; ban; ban = ban->next) {
199                             if(!match(carg, ban->mask)) {
200                                 skip = 0;
201                                 break;
202                             }
203                         }
204                     }
205                     if(!skip) {
206                         modeBufferSet(modeBuf, add, 'b', carg);
207                     }
208                     break;
209                 default:
210                     modetype = getModeType(modelock, modeStr[i]);
211                     if(modetype == 0) {
212                         sprintf(modeStrBuf, "%c%c", (add ? '+' : '-'), modeStr[i]);
213                         reply(textclient, user, "NS_MODE_INVALID", modeStrBuf);
214                         return;
215                     }
216                     if(isModeAffected(modelock, modeStr[i]) && add == !isModeSet(modelock, modeStr[i]) && uaccess < db_enfmodes) {
217                         if(isGodMode(user))
218                             event->flags |= CMDFLAG_OPLOG;
219                         else {
220                             getFullModeString(modelock, tmp);
221                             reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
222                             return;
223                         }
224                     }
225                     if(add && (modetype & CHANNEL_MODE_TYPE) != CHANNEL_MODE_TYPE_D) {
226                         if(arg == argc) {
227                             reply(textclient, user, "NS_MODE_INVALID", modeStr);
228                             return;
229                         }
230                         carg = argv[arg++];
231                         if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
232                             char *modelock_val = getModeValue(modelock, modeStr[i]);
233                             if(stricmp(carg, modelock_val)) {
234                                 if(isGodMode(user))
235                                     event->flags |= CMDFLAG_OPLOG;
236                                 else {
237                                     getFullModeString(modelock, tmp);
238                                     reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
239                                     return;
240                                 }
241                             }
242                         }
243                         if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
244                             int *modelock_val = getModeValue(modelock, modeStr[i]);
245                             if(atoi(carg) != *modelock_val) {
246                                 if(isGodMode(user))
247                                     event->flags |= CMDFLAG_OPLOG;
248                                 else {
249                                     getFullModeString(modelock, tmp);
250                                     reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
251                                     return;
252                                 }
253                             }
254                         }
255                     } else if(!add && (modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_B) {
256                         if(arg == argc && !(modetype & CHANNEL_MODE_KEY)) {
257                             reply(textclient, user, "NS_MODE_INVALID", modeStr);
258                             return;
259                         }
260                         carg = (arg == argc ? NULL : argv[arg++]);
261                     } else
262                         carg = NULL;
263                     if((modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_D && isModeSet(chan->modes, modeStr[i]) == add)
264                         break;
265                     if(!isModeAffected(changemodes, modeStr[i])) {
266                         if(!add && (modetype & CHANNEL_MODE_KEY)) {
267                             if(isModeSet(chan->modes, modeStr[i])) {
268                                 char *current_val = getModeValue(chan->modes, modeStr[i]);
269                                 carg = current_val;
270                             }
271                         }
272                         if(parseMode(changemodes, add, modeStr[i], carg)) {
273                             if(carg) {
274                                 if(add && (modetype & CHANNEL_MODE_KEY) && isModeSet(chan->modes, modeStr[i])) {
275                                     char *current_val = getModeValue(chan->modes, modeStr[i]);
276                                     modeBufferSet(modeBuf, 0, modeStr[i], current_val);
277                                     flushModeBuffer(modeBuf);
278                                 }
279                                 if(!add && !isModeSet(chan->modes, modeStr[i])) break;
280                                 modeBufferSet(modeBuf, add, modeStr[i], carg);
281                             } else {
282                                 modeBufferSimpleMode(modeBuf, add, modeStr[i]);
283                             }
284                         } else {
285                             reply(textclient, user, "NS_MODE_INVALID", modeStr);
286                             return;
287                         }
288                     }
289                     break;
290             }
291         }
292     }
293     getFullModeString(changemodes, tmp);
294     freeModeBuffer(modeBuf);
295     if(strcmp(tmp, "+"))
296         reply(textclient, user, "NS_MODE_DONE", tmp);
297     
298     logEvent(event);
299     freeModeNode(modelock);
300     freeModeNode(changemodes);
301 }