added basic halfop support
[NeonServV5.git] / src / cmd_neonserv_nicklist.c
1 /* cmd_neonserv_nicklist.c - NeonServ v5.2
2  * Copyright (C) 2011  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 syncusers);
26 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int access);
27
28 struct neonserv_cmd_nicklist_cache {
29     struct ClientSocket *client, *textclient;
30     struct UserNode *user;
31     struct Event *event;
32     char *nickmask;
33     int syncusers;
34 };
35
36 CMD_BIND(neonserv_cmd_nicklist) {
37     int syncusers = 0;
38     if(argc && !stricmp(argv[0], "sync")) {
39         if(!checkChannelAccess(user, chan, "channel_canadd", 0)) {
40             if(isGodMode(user)) {
41                 event->flags |= CMDFLAG_OPLOG;
42             } else {
43                 reply(getTextBot(), user, "NS_ACCESS_DENIED");
44                 return;
45             }
46         }
47         argv++;
48         argc--;
49         syncusers = 1;
50         event->flags |= CMDFLAG_LOG;
51     }
52     struct neonserv_cmd_nicklist_cache *cache = malloc(sizeof(*cache));
53     if (!cache) {
54         perror("malloc() failed");
55         return;
56     }
57     cache->client = client;
58     cache->textclient = getTextBot();
59     cache->user = user;
60     cache->event = event;
61     if(argc) {
62         cache->nickmask = strdup(argv[0]);
63     } else
64         cache->nickmask = NULL;
65     cache->syncusers = syncusers;
66     get_userlist_with_invisible(chan, neonserv_cmd_nicklist_userlist_lookup, cache);
67 }
68
69 static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup) {
70     struct neonserv_cmd_nicklist_cache *cache = data;
71     neonserv_cmd_nicklist_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask, cache->syncusers);
72     if(cache->nickmask)
73         free(cache->nickmask);
74     free(cache);
75 }
76
77 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) {
78     MYSQL_RES *res;
79     MYSQL_ROW row, defaults = NULL;
80     struct Table *table;
81     table = table_init(3, chan->usercount + 1, 0);
82     char *content[3];
83     content[0] = get_language_string(user, "NS_NICKLIST_NICK");
84     content[1] = get_language_string(user, "NS_NICKLIST_STATE");
85     content[2] = get_language_string(user, "NS_NICKLIST_ACCESS");
86     table_add(table, content);
87     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);
88     res = mysql_use();
89     int userlistlen = mysql_num_rows(res);
90     int i = 0;
91     MYSQL_ROW userlist[userlistlen];
92     while ((row = mysql_fetch_row(res)) != NULL) {
93         userlist[i++] = row;
94     }
95     int db_enfops, db_enfvoice;
96     printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id);
97     row = mysql_fetch_row(mysql_use());
98     if(row[0] == NULL || row[1] == NULL) {
99         printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'");
100         defaults = mysql_fetch_row(mysql_use());
101     }
102     db_enfops = atoi((row[0] ? row[0] : defaults[0]));
103     db_enfvoice = atoi((row[1] ? row[1] : defaults[1]));
104     int caccess = getChannelAccess(user, chan);
105     int synced_user = 0;
106     struct ChanUser *chanuser;
107     int sort_nicklist[] = { 
108             CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
109             CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED,
110             CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED,
111             CHANUSERFLAG_OPPED,
112             CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED,
113             CHANUSERFLAG_HALFOPPED,
114             CHANUSERFLAG_VOICED,
115             CHANUSERFLAG_INVISIBLE,
116             0
117         };
118     int *sort_pos = sort_nicklist;
119     int sort_flags;
120     do {
121         sort_flags = *(sort_pos++);
122         char statebuf[5];
123         char accessbuf[9];
124         int uaccess;
125         int stateset = 0;
126         for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
127             if((chanuser->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != sort_flags) continue;
128             if(nickmask && match(nickmask, chanuser->user->nick)) continue;
129             if(!stateset) {
130                 if((chanuser->flags & CHANUSERFLAG_INVISIBLE)) statebuf[stateset++] = '<';
131                 if((chanuser->flags & CHANUSERFLAG_OPPED)) statebuf[stateset++] = '@';
132                 if((chanuser->flags & CHANUSERFLAG_HALFOPPED)) statebuf[stateset++] = '%';
133                 if((chanuser->flags & CHANUSERFLAG_VOICED)) statebuf[stateset++] = '+';
134                 statebuf[stateset++] = '\0';
135             }
136             content[0] = chanuser->user->nick;
137             content[1] = statebuf;
138             uaccess = 0;
139             if(chanuser->user->flags & USERFLAG_ISAUTHED) {
140                 for(i = 0; i < userlistlen; i++) {
141                     if(!stricmp(chanuser->user->auth, userlist[i][1])) {
142                         uaccess = atoi(userlist[i][0]);
143                         if((((chanuser->flags & CHANUSERFLAG_OPPED) && uaccess < db_enfops) || ((chanuser->flags & CHANUSERFLAG_VOICED) && uaccess < db_enfvoice)) && !isNetworkService(chanuser->user)) {
144                             if(syncusers) {
145                                 if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
146                                     if(db_enfops >= caccess)
147                                         event->flags |= CMDFLAG_OPLOG;
148                                     uaccess = db_enfops;
149                                 } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (caccess < db_enfvoice || isGodMode(user))) {
150                                     if(db_enfvoice >= caccess)
151                                         event->flags |= CMDFLAG_OPLOG;
152                                     uaccess = db_enfvoice;
153                                 } else {
154                                     //fail...
155                                     sprintf(accessbuf, "\00307%d\003", uaccess);
156                                     break;
157                                 }
158                                 neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
159                                 sprintf(accessbuf, "\00309%d\003", uaccess);
160                                 synced_user = 1;
161                             } else {
162                                 synced_user = 1;
163                                 sprintf(accessbuf, "\00307%d\003", uaccess);
164                             }
165                         } else if((uaccess >= db_enfops && !(chanuser->flags & CHANUSERFLAG_OPPED)) || (uaccess >= db_enfvoice && !(chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED)))
166                             sprintf(accessbuf, "\00303%d\003", uaccess);
167                         else
168                             sprintf(accessbuf, "%d", uaccess);
169                         break;
170                     }
171                 }
172             }
173             if(!uaccess && (chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED) && !isNetworkService(chanuser->user)) {
174                 if(syncusers) {
175                     if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) {
176                         if(db_enfops >= caccess)
177                             event->flags |= CMDFLAG_OPLOG;
178                         uaccess = db_enfops;
179                     } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (db_enfvoice < caccess || isGodMode(user))) {
180                         if(db_enfvoice >= caccess)
181                             event->flags |= CMDFLAG_OPLOG;
182                         uaccess = db_enfvoice;
183                     } else {
184                         uaccess = 0;
185                         sprintf(accessbuf, "\003040\003");
186                     }
187                     if(uaccess && (chanuser->user->flags & USERFLAG_ISAUTHED)) {
188                         neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess);
189                         sprintf(accessbuf, "\00309%d\003", uaccess);
190                         synced_user = 1;
191                     } else if(uaccess) {
192                         sprintf(accessbuf, "\003040\003");
193                     }
194                 } else {
195                     synced_user = 1;
196                     sprintf(accessbuf, "\003040\003");
197                 }
198             } else if(!uaccess)
199                 sprintf(accessbuf, "0");
200             content[2] = accessbuf;
201             table_add(table, content);
202         }
203     } while(sort_flags != 0);
204     //send the table
205     char **table_lines = table_end(table);
206     for(i = 0; i < table->entrys; i++) {
207         reply(textclient, user, table_lines[i]);
208     }
209     if(table->length == 1)
210         reply(textclient, user, "NS_TABLE_NONE");
211     reply(textclient, user, "NS_TABLE_COUNT", table->length - 1);
212     table_free(table);
213     if(synced_user) {
214         if(!syncusers)
215             reply(textclient, user, "NS_NICKLIST_SYNC");
216         else
217             logEvent(event);
218     }
219 }
220
221 static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int caccess) {
222     if(!(user->flags & USERFLAG_ISAUTHED)) return;
223     MYSQL_RES *res;
224     MYSQL_ROW row;
225     int userid;
226     printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth));
227     res = mysql_use();
228     if ((row = mysql_fetch_row(res)) != NULL) {
229         userid = atoi(row[0]);
230     } else {
231         printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth));
232         userid = (int) mysql_insert_id(mysql_conn);
233     }
234     //check if already added
235     printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid);
236     res = mysql_use();
237     if ((row = mysql_fetch_row(res)) != NULL) {
238         //clvl
239         if(atoi(row[0]) >= caccess) return;
240         printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]);
241     } else 
242         printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess);
243 }