fixed colors in cmd_neonserv_nicklist.c
[NeonServV5.git] / src / modules / NeonServ.mod / cmd_neonserv_nicklist.c
1 /* cmd_neonserv_nicklist.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]    "force"
22 * argv[1]    (optional) nick mask
23 */
24 static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup);
25 static void neonserv_cmd_nicklist_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask, int flags);
26 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int access);
27
28 #define NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS  0x01
29 #define NEONSERV_CMD_NICKLIST_FLAG_NOWHO      0x02
30 #define NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT   0x04
31
32 struct neonserv_cmd_nicklist_cache {
33     struct ClientSocket *client, *textclient;
34     struct UserNode *user;
35     struct Event *event;
36     char *nickmask;
37     int flags;
38 };
39
40 CMD_BIND(neonserv_cmd_nicklist) {
41     int flags = 0;
42     while(argc) {
43         if(!stricmp(argv[0], "sync")) {
44             if(!checkChannelAccess(user, chan, "channel_canadd", 0)) {
45                 if(isGodMode(user)) {
46                     event->flags |= CMDFLAG_OPLOG;
47                 } else {
48                     reply(textclient, user, "NS_ACCESS_DENIED");
49                     return;
50                 }
51             }
52             flags |= NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS;
53             event->flags |= CMDFLAG_LOG;
54         } else if(argc && !stricmp(argv[0], "nowho") && isGodMode(user)) {
55             flags |= NEONSERV_CMD_NICKLIST_FLAG_NOWHO;
56         } else if(argc && !stricmp(argv[0], "viscount") && isGodMode(user)) {
57             flags |= NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT;
58         } else
59             break;
60         argv++;
61         argc--;
62     }
63     if(flags & NEONSERV_CMD_NICKLIST_FLAG_NOWHO) {
64         neonserv_cmd_nicklist_async1(client, textclient, user, chan, event, (argc ? argv[0] : NULL), flags);
65         return;
66     }
67     struct neonserv_cmd_nicklist_cache *cache = malloc(sizeof(*cache));
68     if (!cache) {
69         perror("malloc() failed");
70         return;
71     }
72     cache->client = client;
73     cache->textclient = textclient;
74     cache->user = user;
75     cache->event = event;
76     if(argc) {
77         cache->nickmask = strdup(argv[0]);
78     } else
79         cache->nickmask = NULL;
80     cache->flags = flags;
81     get_userlist_with_invisible(chan, module_id, neonserv_cmd_nicklist_userlist_lookup, cache);
82 }
83
84 static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup) {
85     struct neonserv_cmd_nicklist_cache *cache = data;
86     neonserv_cmd_nicklist_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask, cache->flags);
87     if(cache->nickmask)
88         free(cache->nickmask);
89     free(cache);
90 }
91
92 static int neonserv_cmd_nicklist_sort_flags[] = { 
93     CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
94     CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED,
95     CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED,
96     CHANUSERFLAG_OPPED,
97     CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
98     CHANUSERFLAG_HALFOPPED,
99     CHANUSERFLAG_VOICED,
100     CHANUSERFLAG_INVISIBLE,
101     0
102 };
103
104 static int neonserv_cmd_nicklist_sort(const void *a, const void *b) {
105     const struct ChanUser *chanuser_a = *((struct ChanUser * const *) a);
106     const struct ChanUser *chanuser_b = *((struct ChanUser * const *) b); 
107     int i_a = 0, i_b = 0;
108     while((chanuser_a->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_a] && neonserv_cmd_nicklist_sort_flags[i_a])
109         i_a++;
110     while((chanuser_b->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_b] && neonserv_cmd_nicklist_sort_flags[i_b])
111         i_b++;
112     if(i_a == i_b) {
113         return stricmp(chanuser_a->user->nick, chanuser_b->user->nick);
114     } else
115         return i_a - i_b;
116 }
117
118 static void neonserv_cmd_nicklist_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask, int flags) {
119     MYSQL_RES *res;
120     MYSQL_ROW row, defaults = NULL;
121     struct Table *table;
122     char *content[4];
123     int userlistlen, i, j;
124     int db_enfops, db_enfvoice;
125     int caccess, synced_user, accessbufpos;
126     struct ChanUser *chanusers[chan->usercount];
127     struct ChanUser *chanuser;
128     struct ClientSocket *bot;
129     int chanuser_count;
130     char statebuf[5];
131     char accessbuf[50];
132     char viscountbuf[5];
133     int uaccess;
134     
135     i = 3;
136     if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) {
137         i++;
138         content[3] = "VisCount";
139     }
140     table = table_init(i, chan->usercount + 1, 0);
141     content[0] = get_language_string(user, "NS_NICKLIST_NICK");
142     content[1] = get_language_string(user, "NS_NICKLIST_STATE");
143     content[2] = get_language_string(user, "NS_NICKLIST_ACCESS");
144     table_add(table, content);
145     
146     printf_mysql_query("SELECT `chanuser_access`, `user_user`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d'", chan->channel_id);
147     res = mysql_use();
148     userlistlen = mysql_num_rows(res);
149     i = 0;
150     MYSQL_ROW userlist[userlistlen];
151     while ((row = mysql_fetch_row(res)) != NULL) {
152         userlist[i++] = row;
153     }
154     
155     printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
156     row = mysql_fetch_row(mysql_use());
157     if(row[0] == NULL || row[1] == NULL) {
158         printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'");
159         defaults = mysql_fetch_row(mysql_use());
160     }
161     db_enfops = atoi((row[0] ? row[0] : defaults[0]));
162     db_enfvoice = atoi((row[1] ? row[1] : defaults[1]));
163     
164     chanuser_count = 0;
165     for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
166         if(nickmask && match(nickmask, chanuser->user->nick)) continue;
167         chanusers[chanuser_count++] = chanuser;
168     }
169     qsort(chanusers, chanuser_count, sizeof(struct ChanUser *), neonserv_cmd_nicklist_sort);
170     
171     caccess = getChannelAccess(user, chan);
172     synced_user = 0;
173     for(i = 0; i < chanuser_count; i++) {
174         chanuser = chanusers[i];
175         
176         content[0] = chanuser->user->nick;
177         
178         j = 0;
179         if((chanuser->flags & CHANUSERFLAG_INVISIBLE)) statebuf[j++] = '<';
180         if((chanuser->flags & CHANUSERFLAG_OPPED)) statebuf[j++] = '@';
181         if((chanuser->flags & CHANUSERFLAG_HALFOPPED)) statebuf[j++] = '%';
182         if((chanuser->flags & CHANUSERFLAG_VOICED)) statebuf[j++] = '+';
183         statebuf[j++] = '\0';
184         content[1] = statebuf;
185         
186         uaccess = 0;
187         if(chanuser->user->flags & USERFLAG_ISAUTHED) {
188             for(j = 0; j < userlistlen; j++) {
189                 if(!stricmp(chanuser->user->auth, userlist[j][1])) {
190                     uaccess = atoi(userlist[j][0]);
191                     if((((chanuser->flags & CHANUSERFLAG_OPPED) && uaccess < db_enfops) || ((chanuser->flags & CHANUSERFLAG_VOICED) && uaccess < db_enfvoice)) && !isNetworkService(chanuser->user)) {
192                         if(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS) {
193                             if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
194                                 if(db_enfops >= caccess)
195                                     event->flags |= CMDFLAG_OPLOG;
196                                 uaccess = db_enfops;
197                             } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (caccess < db_enfvoice || isGodMode(user))) {
198                                 if(db_enfvoice >= caccess)
199                                     event->flags |= CMDFLAG_OPLOG;
200                                 uaccess = db_enfvoice;
201                             } else {
202                                 //fail...
203                                 accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess);
204                                 break;
205                             }
206                             neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
207                             accessbufpos = sprintf(accessbuf, "\00309%d\003", uaccess);
208                             synced_user = 1;
209                         } else {
210                             synced_user = 1;
211                             accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess);
212                         }
213                     } else if((uaccess >= db_enfops && !(chanuser->flags & CHANUSERFLAG_OPPED)) || (uaccess >= db_enfvoice && !(chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED)))
214                         accessbufpos = sprintf(accessbuf, "\00303%d\003", uaccess);
215                     else
216                         accessbufpos = sprintf(accessbuf, "%d", uaccess);
217                     break;
218                 }
219             }
220         }
221         if(!uaccess && (chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED) && !isNetworkService(chanuser->user)) {
222             if(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS) {
223                 if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
224                     if(db_enfops >= caccess)
225                         event->flags |= CMDFLAG_OPLOG;
226                     uaccess = db_enfops;
227                 } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (db_enfvoice < caccess || isGodMode(user))) {
228                     if(db_enfvoice >= caccess)
229                         event->flags |= CMDFLAG_OPLOG;
230                     uaccess = db_enfvoice;
231                 } else {
232                     uaccess = 0;
233                     accessbufpos = sprintf(accessbuf, "\003040\003");
234                 }
235                 if(uaccess && (chanuser->user->flags & USERFLAG_ISAUTHED)) {
236                     neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
237                     accessbufpos = sprintf(accessbuf, "\00309%d\003", uaccess);
238                     synced_user = 1;
239                 } else if(uaccess) {
240                     accessbufpos = sprintf(accessbuf, "\003040\003");
241                 }
242             } else {
243                 synced_user = 1;
244                 if(((chanuser->flags & CHANUSERFLAG_OPPED) && db_enfops > caccess) || ((chanuser->flags & CHANUSERFLAG_VOICED) && db_enfvoice > caccess))
245                     accessbufpos = sprintf(accessbuf, "\003040\003");
246                 else
247                     accessbufpos = sprintf(accessbuf, "0");
248             }
249         } else if(!uaccess)
250             accessbufpos = sprintf(accessbuf, "0");
251         j = 0;
252         if(isBot(chanuser->user)) {
253             //check if bot is secret
254             for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
255                 if(bot->user == chanuser->user)
256                     break;
257             }
258             if(bot && !(bot->flags & SOCKET_FLAG_SECRET_BOT)) {
259                 if(j)
260                     accessbufpos += sprintf(accessbuf+accessbufpos, ", ");
261                 else {
262                     accessbufpos += sprintf(accessbuf+accessbufpos, " (");
263                     j = 1;
264                 }
265                 accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_BOT"));
266             }
267         }
268         if(chanuser->user->flags & USERFLAG_ISIRCOP) {
269             if(j)
270                 accessbufpos += sprintf(accessbuf+accessbufpos, ", ");
271             else {
272                 accessbufpos += sprintf(accessbuf+accessbufpos, " (");
273                 j = 1;
274             }
275             accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_OPER"));
276         }
277         if(j)
278             accessbufpos += sprintf(accessbuf+accessbufpos, ")");
279         content[2] = accessbuf;
280         if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) {
281             sprintf(viscountbuf, "%d", chanuser->visCount);
282             content[3] = viscountbuf;
283         }
284         table_add(table, content);
285     }
286     
287     //send the table
288     char **table_lines = table_end(table);
289     for(i = 0; i < table->entrys; i++) {
290         reply(textclient, user, table_lines[i]);
291     }
292     if(table->length == 1)
293         reply(textclient, user, "NS_TABLE_NONE");
294     reply(textclient, user, "NS_TABLE_COUNT", table->length - 1);
295     table_free(table);
296     if(synced_user) {
297         if(!(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS))
298             reply(textclient, user, "NS_NICKLIST_SYNC", db_enfops, db_enfvoice);
299         else
300             logEvent(event);
301     }
302 }
303
304 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int caccess) {
305     if(!(user->flags & USERFLAG_ISAUTHED)) return;
306     MYSQL_RES *res;
307     MYSQL_ROW row;
308     int userid;
309     printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
310     res = mysql_use();
311     if ((row = mysql_fetch_row(res)) != NULL) {
312         userid = atoi(row[0]);
313     } else {
314         printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth));
315         userid = (int) mysql_insert_id(get_mysql_conn());
316     }
317     //check if already added
318     printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid);
319     res = mysql_use();
320     if ((row = mysql_fetch_row(res)) != NULL) {
321         //clvl
322         if(atoi(row[0]) >= caccess) return;
323         printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]);
324     } else 
325         printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess);
326 }