1 /* cmd_neonserv_nicklist.c - NeonServ v5.5
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 syncusers);
26 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int access);
28 struct neonserv_cmd_nicklist_cache {
29 struct ClientSocket *client, *textclient;
30 struct UserNode *user;
36 CMD_BIND(neonserv_cmd_nicklist) {
38 if(argc && !stricmp(argv[0], "sync")) {
39 if(!checkChannelAccess(user, chan, "channel_canadd", 0)) {
41 event->flags |= CMDFLAG_OPLOG;
43 reply(textclient, user, "NS_ACCESS_DENIED");
50 event->flags |= CMDFLAG_LOG;
52 if(argc && !stricmp(argv[0], "nowho") && isGodMode(user)) {
55 neonserv_cmd_nicklist_async1(client, textclient, user, chan, event, (argc ? argv[0] : NULL), syncusers);
58 struct neonserv_cmd_nicklist_cache *cache = malloc(sizeof(*cache));
60 perror("malloc() failed");
63 cache->client = client;
64 cache->textclient = textclient;
68 cache->nickmask = strdup(argv[0]);
70 cache->nickmask = NULL;
71 cache->syncusers = syncusers;
72 get_userlist_with_invisible(chan, module_id, neonserv_cmd_nicklist_userlist_lookup, cache);
75 static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup) {
76 struct neonserv_cmd_nicklist_cache *cache = data;
77 neonserv_cmd_nicklist_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask, cache->syncusers);
79 free(cache->nickmask);
83 static int neonserv_cmd_nicklist_sort_flags[] = {
84 CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
85 CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED,
86 CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED,
88 CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
89 CHANUSERFLAG_HALFOPPED,
91 CHANUSERFLAG_INVISIBLE,
95 static int neonserv_cmd_nicklist_sort(const void *a, const void *b) {
96 const struct ChanUser *chanuser_a = *((struct ChanUser * const *) a);
97 const struct ChanUser *chanuser_b = *((struct ChanUser * const *) b);
99 while((chanuser_a->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_a] && neonserv_cmd_nicklist_sort_flags[i_a])
101 while((chanuser_b->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_b] && neonserv_cmd_nicklist_sort_flags[i_b])
104 return stricmp(chanuser_a->user->nick, chanuser_b->user->nick);
109 static void neonserv_cmd_nicklist_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask, int syncusers) {
111 MYSQL_ROW row, defaults = NULL;
114 int userlistlen, i, j;
115 int db_enfops, db_enfvoice;
116 int caccess, synced_user;
117 struct ChanUser *chanusers[chan->usercount];
118 struct ChanUser *chanuser;
124 table = table_init(3, chan->usercount + 1, 0);
125 content[0] = get_language_string(user, "NS_NICKLIST_NICK");
126 content[1] = get_language_string(user, "NS_NICKLIST_STATE");
127 content[2] = get_language_string(user, "NS_NICKLIST_ACCESS");
128 table_add(table, content);
130 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);
132 userlistlen = mysql_num_rows(res);
134 MYSQL_ROW userlist[userlistlen];
135 while ((row = mysql_fetch_row(res)) != NULL) {
139 printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
140 row = mysql_fetch_row(mysql_use());
141 if(row[0] == NULL || row[1] == NULL) {
142 printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'");
143 defaults = mysql_fetch_row(mysql_use());
145 db_enfops = atoi((row[0] ? row[0] : defaults[0]));
146 db_enfvoice = atoi((row[1] ? row[1] : defaults[1]));
149 for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
150 if(nickmask && match(nickmask, chanuser->user->nick)) continue;
151 chanusers[chanuser_count++] = chanuser;
153 qsort(chanusers, chanuser_count, sizeof(struct ChanUser *), neonserv_cmd_nicklist_sort);
155 caccess = getChannelAccess(user, chan);
157 for(i = 0; i < chanuser_count; i++) {
158 chanuser = chanusers[i];
160 content[0] = chanuser->user->nick;
163 if((chanuser->flags & CHANUSERFLAG_INVISIBLE)) statebuf[j++] = '<';
164 if((chanuser->flags & CHANUSERFLAG_OPPED)) statebuf[j++] = '@';
165 if((chanuser->flags & CHANUSERFLAG_HALFOPPED)) statebuf[j++] = '%';
166 if((chanuser->flags & CHANUSERFLAG_VOICED)) statebuf[j++] = '+';
167 statebuf[j++] = '\0';
168 content[1] = statebuf;
171 if(chanuser->user->flags & USERFLAG_ISAUTHED) {
172 for(j = 0; j < userlistlen; j++) {
173 if(!stricmp(chanuser->user->auth, userlist[j][1])) {
174 uaccess = atoi(userlist[j][0]);
175 if((((chanuser->flags & CHANUSERFLAG_OPPED) && uaccess < db_enfops) || ((chanuser->flags & CHANUSERFLAG_VOICED) && uaccess < db_enfvoice)) && !isNetworkService(chanuser->user)) {
177 if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
178 if(db_enfops >= caccess)
179 event->flags |= CMDFLAG_OPLOG;
181 } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (caccess < db_enfvoice || isGodMode(user))) {
182 if(db_enfvoice >= caccess)
183 event->flags |= CMDFLAG_OPLOG;
184 uaccess = db_enfvoice;
187 sprintf(accessbuf, "\00307%d\003", uaccess);
190 neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
191 sprintf(accessbuf, "\00309%d\003", uaccess);
195 sprintf(accessbuf, "\00307%d\003", uaccess);
197 } else if((uaccess >= db_enfops && !(chanuser->flags & CHANUSERFLAG_OPPED)) || (uaccess >= db_enfvoice && !(chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED)))
198 sprintf(accessbuf, "\00303%d\003", uaccess);
200 sprintf(accessbuf, "%d", uaccess);
205 if(!uaccess && (chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED) && !isNetworkService(chanuser->user)) {
207 if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
208 if(db_enfops >= caccess)
209 event->flags |= CMDFLAG_OPLOG;
211 } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (db_enfvoice < caccess || isGodMode(user))) {
212 if(db_enfvoice >= caccess)
213 event->flags |= CMDFLAG_OPLOG;
214 uaccess = db_enfvoice;
217 sprintf(accessbuf, "\003040\003");
219 if(uaccess && (chanuser->user->flags & USERFLAG_ISAUTHED)) {
220 neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
221 sprintf(accessbuf, "\00309%d\003", uaccess);
224 sprintf(accessbuf, "\003040\003");
228 sprintf(accessbuf, "\003040\003");
231 sprintf(accessbuf, "0");
232 content[2] = accessbuf;
234 table_add(table, content);
238 char **table_lines = table_end(table);
239 for(i = 0; i < table->entrys; i++) {
240 reply(textclient, user, table_lines[i]);
242 if(table->length == 1)
243 reply(textclient, user, "NS_TABLE_NONE");
244 reply(textclient, user, "NS_TABLE_COUNT", table->length - 1);
248 reply(textclient, user, "NS_NICKLIST_SYNC", db_enfops, db_enfvoice);
254 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int caccess) {
255 if(!(user->flags & USERFLAG_ISAUTHED)) return;
259 printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
261 if ((row = mysql_fetch_row(res)) != NULL) {
262 userid = atoi(row[0]);
264 printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth));
265 userid = (int) mysql_insert_id(get_mysql_conn());
267 //check if already added
268 printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid);
270 if ((row = mysql_fetch_row(res)) != NULL) {
272 if(atoi(row[0]) >= caccess) return;
273 printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]);
275 printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess);