1 /* cmd_neonserv_nicklist.c - NeonServ v5.6
2 * Copyright (C) 2011-2012 Philipp Kreil (pk910)
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.
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.
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/>.
18 #include "cmd_neonserv.h"
22 * argv[1] (optional) nick mask
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);
28 #define NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS 0x01
29 #define NEONSERV_CMD_NICKLIST_FLAG_NOWHO 0x02
30 #define NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT 0x04
32 struct neonserv_cmd_nicklist_cache {
33 struct ClientSocket *client, *textclient;
34 struct UserNode *user;
40 CMD_BIND(neonserv_cmd_nicklist) {
43 if(!stricmp(argv[0], "sync")) {
44 if(!checkChannelAccess(user, chan, "channel_canadd", 0)) {
46 event->flags |= CMDFLAG_OPLOG;
48 reply(textclient, user, "NS_ACCESS_DENIED");
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;
63 if(flags & NEONSERV_CMD_NICKLIST_FLAG_NOWHO) {
64 neonserv_cmd_nicklist_async1(client, textclient, user, chan, event, (argc ? argv[0] : NULL), flags);
67 struct neonserv_cmd_nicklist_cache *cache = malloc(sizeof(*cache));
69 printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__);
72 cache->client = client;
73 cache->textclient = textclient;
77 cache->nickmask = strdup(argv[0]);
79 cache->nickmask = NULL;
81 get_userlist_with_invisible(chan, module_id, neonserv_cmd_nicklist_userlist_lookup, cache);
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);
88 free(cache->nickmask);
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,
97 CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
98 CHANUSERFLAG_HALFOPPED,
100 CHANUSERFLAG_INVISIBLE,
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])
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])
113 return stricmp(chanuser_a->user->nick, chanuser_b->user->nick);
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) {
120 MYSQL_ROW row, defaults = NULL;
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;
132 char viscountbuf[50];
136 if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) {
138 content[3] = "VisCount";
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);
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);
148 userlistlen = mysql_num_rows(res);
150 MYSQL_ROW userlist[userlistlen];
151 while ((row = mysql_fetch_row(res)) != NULL) {
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());
161 db_enfops = atoi((row[0] ? row[0] : defaults[0]));
162 db_enfvoice = atoi((row[1] ? row[1] : defaults[1]));
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;
169 qsort(chanusers, chanuser_count, sizeof(struct ChanUser *), neonserv_cmd_nicklist_sort);
171 caccess = getChannelAccess(user, chan);
173 for(i = 0; i < chanuser_count; i++) {
174 chanuser = chanusers[i];
176 content[0] = chanuser->user->nick;
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;
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;
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;
203 accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess);
206 neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
207 accessbufpos = sprintf(accessbuf, "\00309%d\003", uaccess);
211 accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess);
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);
216 accessbufpos = sprintf(accessbuf, "%d", uaccess);
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;
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;
233 accessbufpos = sprintf(accessbuf, "\003040\003");
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);
240 accessbufpos = sprintf(accessbuf, "\003040\003");
244 if(((chanuser->flags & CHANUSERFLAG_OPPED) && db_enfops > uaccess) || ((chanuser->flags & CHANUSERFLAG_VOICED) && db_enfvoice > uaccess))
245 accessbufpos = sprintf(accessbuf, "\003040\003");
247 accessbufpos = sprintf(accessbuf, "0");
250 accessbufpos = sprintf(accessbuf, "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)
258 if(bot && !(bot->flags & SOCKET_FLAG_SECRET_BOT)) {
260 accessbufpos += sprintf(accessbuf+accessbufpos, ", ");
262 accessbufpos += sprintf(accessbuf+accessbufpos, " (");
265 accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_BOT"));
268 if(chanuser->user->flags & USERFLAG_ISIRCOP) {
270 accessbufpos += sprintf(accessbuf+accessbufpos, ", ");
272 accessbufpos += sprintf(accessbuf+accessbufpos, " (");
275 accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_OPER"));
278 accessbufpos += sprintf(accessbuf+accessbufpos, ")");
279 content[2] = accessbuf;
280 if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) {
281 if(chanuser->flags & CHANUSERFLAG_PARTING)
282 sprintf(viscountbuf, "%d (\003P\003 %d)", chanuser->visCount, chanuser->old_visCount);
284 sprintf(viscountbuf, "%d", chanuser->visCount);
285 content[3] = viscountbuf;
287 table_add(table, content);
291 char **table_lines = table_end(table);
292 for(i = 0; i < table->entrys; i++) {
293 reply(textclient, user, table_lines[i]);
295 if(table->length == 1)
296 reply(textclient, user, "NS_TABLE_NONE");
297 reply(textclient, user, "NS_TABLE_COUNT", table->length - 1);
300 if(!(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS))
301 reply(textclient, user, "NS_NICKLIST_SYNC", db_enfops, db_enfvoice);
307 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int caccess) {
308 if(!(user->flags & USERFLAG_ISAUTHED)) return;
312 printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
314 if ((row = mysql_fetch_row(res)) != NULL) {
315 userid = atoi(row[0]);
317 printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth));
318 userid = (int) mysql_insert_id(get_mysql_conn());
320 //check if already added
321 printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid);
323 if ((row = mysql_fetch_row(res)) != NULL) {
325 if(atoi(row[0]) >= caccess) return;
326 printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]);
328 printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess);