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