41e0455e11d50fe003358c867bc2b72e6a89e5f2
[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         for(i = 0; i < strlen(modeStr); i++) {
96             switch(modeStr[i]) {
97                 case '+':
98                     add = 1;
99                     break;
100                 case '-':
101                     add = 0;
102                     break;
103                 case 'o':
104                 case 'v':
105                     if(arg == argc) {
106                         reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
107                         return;
108                     }
109                     carg = argv[arg++];
110                     if(modeStr[i] == 'o') {
111                         if(uaccess < db_canop) {
112                             reply(textclient, user, "NS_MODE_ENFOPS", chan->name);
113                             db_canop = -1;
114                             break;
115                         }
116                         if(db_canop == -1) break;
117                     } else {
118                         if(uaccess < db_canvoice) {
119                             reply(textclient, user, "NS_MODE_ENFVOICE", chan->name);
120                             db_canvoice = -1;
121                             break;
122                         }
123                         if(db_canvoice == -1) break;
124                     }
125                     cuser = searchUserByNick(carg);
126                     if(!cuser) {
127                         //check for an invisible user
128                         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
129                             if(!stricmp(chanuser->user->nick, carg)) {
130                                 cuser = chanuser->user;
131                                 break;
132                             }
133                         }
134                         if(!cuser) break;
135                     } else {
136                         chanuser = getChanUser(cuser, chan);
137                         if(!chanuser) break;
138                     }
139                     if(!(add ^ (chanuser->flags & (modeStr[i] == 'o' ? CHANUSERFLAG_OPPED : CHANUSERFLAG_VOICED)))) break;
140                     if(!add) {
141                         //check protection
142                         if(modeStr[i] == 'o' && isNetworkService(cuser)) {
143                             reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick);
144                             break;
145                         }
146                         if(isUserProtected(chan, cuser, user)) {
147                             reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
148                             break;
149                         }
150                     }
151                     modeBufferSet(modeBuf, add, modeStr[i], carg);
152                     break;
153                 case 'b':
154                     if(arg == argc) {
155                         reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
156                         return;
157                     }
158                     carg = argv[arg++];
159                     if(uaccess < db_canban) {
160                         reply(textclient, user, "NS_MODE_CANBAN", chan->name);
161                         db_canban = -1;
162                         break;
163                     }
164                     if(db_canban == -1) break;
165                     char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3];
166                     char usermask[NICKLEN+USERLEN+HOSTLEN+3];
167                     struct BanNode *ban;
168                     int match_count = 0;
169                     carg = make_banmask(carg, hostmask_buffer);
170                     if(add) {
171                         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
172                             cuser = chanuser->user;
173                             sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host);
174                             if(!match(carg, usermask)) {
175                                 if(isNetworkService(chanuser->user)) {
176                                     reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick);
177                                     skip = 1;
178                                     break;
179                                 }
180                                 if(isUserProtected(chan, cuser, user)) {
181                                     reply(textclient, user, "NS_USER_PROTECTED", cuser->nick);
182                                     skip = 1;
183                                     break;
184                                 }
185                                 match_count++;
186                                 if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) {
187                                     skip = 1;
188                                     reply(textclient, user, "NS_LAME_MASK", carg);
189                                     break;
190                                 }
191                             }
192                         }
193                     } else {
194                         skip = 1;
195                         for(ban = chan->bans; ban; ban = ban->next) {
196                             if(!match(carg, ban->mask)) {
197                                 skip = 0;
198                                 break;
199                             }
200                         }
201                     }
202                     if(!skip) {
203                         modeBufferSet(modeBuf, add, 'b', carg);
204                     }
205                     break;
206                 default:
207                     modetype = getModeType(modelock, modeStr[i]);
208                     if(modetype == 0) {
209                         reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
210                         return;
211                     }
212                     if(isModeAffected(modelock, modeStr[i]) && add == !isModeSet(modelock, modeStr[i]) && uaccess < db_enfmodes) {
213                         if(isGodMode(user))
214                             event->flags |= CMDFLAG_OPLOG;
215                         else {
216                             getFullModeString(modelock, tmp);
217                             reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
218                             return;
219                         }
220                     }
221                     if(add && (modetype & CHANNEL_MODE_TYPE) != CHANNEL_MODE_TYPE_D) {
222                         if(arg == argc) {
223                             reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
224                             return;
225                         }
226                         carg = argv[arg++];
227                         if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
228                             char *modelock_val = getModeValue(modelock, modeStr[i]);
229                             if(stricmp(carg, modelock_val)) {
230                                 if(isGodMode(user))
231                                     event->flags |= CMDFLAG_OPLOG;
232                                 else {
233                                     getFullModeString(modelock, tmp);
234                                     reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
235                                     return;
236                                 }
237                             }
238                         }
239                         if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) {
240                             int *modelock_val = getModeValue(modelock, modeStr[i]);
241                             if(atoi(carg) != *modelock_val) {
242                                 if(isGodMode(user))
243                                     event->flags |= CMDFLAG_OPLOG;
244                                 else {
245                                     getFullModeString(modelock, tmp);
246                                     reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name);
247                                     return;
248                                 }
249                             }
250                         }
251                     } else if(!add && (modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_B) {
252                         if(arg == argc && !(modetype & CHANNEL_MODE_KEY)) {
253                             reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
254                             return;
255                         }
256                         carg = (arg == argc ? NULL : argv[arg++]);
257                     } else
258                         carg = NULL;
259                     if((modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_D && isModeSet(chan->modes, modeStr[i]) == add)
260                         break;
261                     if(!isModeAffected(changemodes, modeStr[i])) {
262                         if(!add && (modetype & CHANNEL_MODE_KEY)) {
263                             if(isModeSet(chan->modes, modeStr[i])) {
264                                 char *current_val = getModeValue(chan->modes, modeStr[i]);
265                                 carg = current_val;
266                             }
267                         }
268                         if(parseMode(changemodes, add, modeStr[i], carg)) {
269                             if(carg) {
270                                 if(add && (modetype & CHANNEL_MODE_KEY) && isModeSet(chan->modes, modeStr[i])) {
271                                     char *current_val = getModeValue(chan->modes, modeStr[i]);
272                                     modeBufferSet(modeBuf, 0, modeStr[i], current_val);
273                                     flushModeBuffer(modeBuf);
274                                 }
275                                 if(!add && !isModeSet(chan->modes, modeStr[i])) break;
276                                 modeBufferSet(modeBuf, add, modeStr[i], carg);
277                             } else {
278                                 modeBufferSimpleMode(modeBuf, add, modeStr[i]);
279                             }
280                         } else {
281                             reply(textclient, user, "NS_MODE_INVALID", modeStr[i]);
282                             return;
283                         }
284                     }
285                     break;
286             }
287         }
288     }
289     getFullModeString(changemodes, tmp);
290     freeModeBuffer(modeBuf);
291     if(strcmp(tmp, "+"))
292         reply(textclient, user, "NS_MODE_DONE", tmp);
293     
294     logEvent(event);
295     freeModeNode(modelock);
296     freeModeNode(changemodes);
297 }