1 /* cmd_neonserv_unvisited.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"
21 struct neonserv_cmd_unvisited_cache {
22 struct ClientSocket *client, *textclient;
23 struct UserNode *user;
24 int duration, unregister_matches;
25 int who_count, matches;
28 static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup);
29 static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches);
30 static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches);
31 static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache);
32 static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel);
33 static TIMEQ_CALLBACK(neonserv_check_unvisited_timer);
35 CMD_BIND(neonserv_cmd_unvisited) {
36 int duration = (argc ? strToTime(user, argv[0]) : 60*60*24*7*3);
37 reply(textclient, user, "NS_SEARCH_HEADER");
39 if(argc > 1 && !stricmp(argv[1], "unregister"))
41 neonserv_check_unvisited(client, textclient, user, duration, unreg);
44 void neonserv_cmd_unvisited_init() {
45 if(!timeq_name_exists("neonserv_unvisited"))
46 timeq_add_name("neonserv_unvisited", 1200, module_id, neonserv_check_unvisited_timer, NULL);
49 static TIMEQ_CALLBACK(neonserv_check_unvisited_timer) {
51 char *modname = get_module_name(module_id);
52 sprintf(tmp, "modules.%s.chan_expire_freq", modname);
53 char *check_freq_str = get_string_field(tmp);
55 if(!check_freq_str || (check_freq = strToTime(NULL, check_freq_str)) < (60*60)) {
56 timeq_add_name("neonserv_unvisited", 1800, module_id, neonserv_check_unvisited_timer, NULL);
59 sprintf(tmp, "modules.%s.chan_expire_delay", modname);
60 char *check_expire_str = get_string_field(tmp);
62 if(!check_expire_str || (duration = strToTime(NULL, check_expire_str)) < 60*60*24*7) return;
63 neonserv_check_unvisited(NULL, NULL, NULL, duration, 1);
66 static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches) {
67 MYSQL_RES *res, *res2;
69 struct ChanNode *channel;
70 struct neonserv_cmd_unvisited_cache *cache = malloc(sizeof(*cache));
72 perror("malloc() failed");
75 cache->client = client;
76 cache->textclient = textclient;
78 cache->duration = duration;
79 cache->unregister_matches = unregister_matches;
80 cache->who_count = 1; /* small fake to prevent the cache to be freed too early */
84 botid = client->botid;
86 botid = NEONSERV_BOTID;
87 printf_mysql_query("SELECT `channel_id`, `channel_name`, `channel_nodelete` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `botid` = '%d'", botid);
89 while ((row = mysql_fetch_row(res)) != NULL) {
90 if(!strcmp(row[2], "1")) continue;
91 printf_mysql_query("SELECT `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%s' AND `chanuser_access` >= 300 ORDER BY `chanuser_seen` DESC LIMIT 1", row[0]);
93 row2 = mysql_fetch_row(res2);
94 if(row2 && atol(row2[0]) > (time(0) - duration)) continue;
95 channel = getChanByName(row[1]);
98 channel->channel_id = atoi(row[0]);
99 get_userlist_with_invisible(channel, module_id, neonserv_cmd_unvisited_userlist_lookup, cache);
102 reply(textclient, user, "%s", row[1]);
103 if(unregister_matches)
104 neonserv_cmd_unvisited_unreg(client, row[1]);
108 cache->who_count--; //see fix on line 78
109 if(cache->who_count == 0) {
110 neonserv_cmd_unvisited_async2(cache);
114 static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup) {
115 struct neonserv_cmd_unvisited_cache *cache = data;
116 if(neonserv_cmd_unvisited_async1(cache->client, cache->textclient, cache->user, chan, cache->duration, cache->unregister_matches))
119 if(cache->who_count == 0) {
120 neonserv_cmd_unvisited_async2(cache);
124 static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches) {
125 struct ChanUser *chanuser;
129 for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) {
130 if(isBot(chanuser->user)) continue;
131 if((chanuser->user->flags & USERFLAG_ISAUTHED)) {
132 printf_mysql_query("SELECT `chanuser_id`, `chanuser_access` FROM `chanusers` LEFT JOIN `users` ON `user_id` = `chanuser_uid` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(chanuser->user->auth));
134 row2 = mysql_fetch_row(res2);
135 if(row2 && atoi(row2[1]) >= 300) {
136 printf_mysql_query("UPDATE `chanusers` SET `chanuser_seen` = UNIX_TIMESTAMP() WHERE `chanuser_id` = '%s'", row2[0]);
143 reply(textclient, user, "%s", chan->name);
144 if(unregister_matches)
145 neonserv_cmd_unvisited_unreg(client, chan->name);
150 static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache) {
151 if(cache->textclient)
152 reply(cache->textclient, cache->user, "NS_TABLE_COUNT", cache->matches);
156 static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel) {
159 int sync_neonspam_unreg = get_int_field("General.sync_neonspam_unreg");
162 botid = client->botid;
164 botid = NEONSERV_BOTID;
165 printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended`, `channels`.`channel_nodelete` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid);
167 if ((row = mysql_fetch_row(res)) == NULL) {
170 int clientid = atoi(row[0]);
171 struct ClientSocket *bot;
172 for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
173 if(bot->clientid == clientid)
176 if(!strcmp(row[3], "1")) return;
177 printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]);
178 if(bot && strcmp(row[2], "1")) {
179 putsock(bot, "PART %s :Channel unregistered.", channel);
181 if(botid == NEONSERV_BOTID && sync_neonspam_unreg) {
182 botid = NEONSPAM_BOTID;
183 printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid);
185 if ((row = mysql_fetch_row(res)) == NULL) {
188 clientid = atoi(row[0]);
189 for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) {
190 if(bot->clientid == clientid)
193 printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]);
194 if(bot && strcmp(row[2], "1")) {
195 putsock(bot, "PART %s :Channel unregistered.", channel);
198 if(client->botid == NEONSERV_BOTID) {
200 sprintf(setting, "modules.%s.auto_backup_unregister", get_module_name(module_id));
201 if(get_int_field(setting))
202 module_global_cmd_unregister_neonbackup(channel);
204 char *alertchan = get_string_field("General.alertchan");
206 struct ChanNode *alertchan_chan = getChanByName(alertchan);
207 struct ClientSocket *alertclient;
208 if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) {
209 putsock(alertclient, "PRIVMSG %s :Unregistered %s (unvisited)", alertchan_chan->name, channel);