1 /*
2  server-idle.c : irssi
3 
4     Copyright (C) 1999-2000 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 #include "signals.h"
23 
24 #include "irc-servers.h"
25 #include "servers-idle.h"
26 #include "servers-redirect.h"
27 
28 typedef struct {
29 	char *cmd;
30 	char *arg;
31 	int tag;
32 
33 	char *redirect_cmd;
34         int count;
35 	int remote;
36 	char *failure_signal;
37 	GSList *redirects;
38 } SERVER_IDLE_REC;
39 
40 static int idle_tag, idlepos;
41 
42 /* Add new idle command to queue */
43 static SERVER_IDLE_REC *
server_idle_create(const char * cmd,const char * redirect_cmd,int count,const char * arg,int remote,const char * failure_signal,va_list va)44 server_idle_create(const char *cmd, const char *redirect_cmd, int count,
45 		   const char *arg, int remote, const char *failure_signal,
46 		   va_list va)
47 {
48 	SERVER_IDLE_REC *rec;
49 	const char *event, *signal;
50 
51 	g_return_val_if_fail(cmd != NULL, FALSE);
52 
53 	rec = g_new0(SERVER_IDLE_REC, 1);
54 	rec->cmd = g_strdup(cmd);
55 	rec->arg = g_strdup(arg);
56 	rec->tag = ++idlepos;
57 
58         rec->redirect_cmd = g_strdup(redirect_cmd);
59 	rec->count = count;
60 	rec->remote = remote;
61         rec->failure_signal = g_strdup(failure_signal);
62 
63 	while ((event = va_arg(va, const char *)) != NULL) {
64 		signal = va_arg(va, const char *);
65 		if (signal == NULL) {
66 			g_warning("server_idle_create(%s): "
67 				  "signal not specified for event",
68 				  redirect_cmd);
69 			break;
70 		}
71 
72 		rec->redirects =
73 			g_slist_append(rec->redirects, g_strdup(event));
74 		rec->redirects =
75 			g_slist_append(rec->redirects, g_strdup(signal));
76 	}
77 
78 	return rec;
79 }
80 
server_idle_find_rec(IRC_SERVER_REC * server,int tag)81 static SERVER_IDLE_REC *server_idle_find_rec(IRC_SERVER_REC *server, int tag)
82 {
83 	GSList *tmp;
84 
85 	g_return_val_if_fail(server != NULL, FALSE);
86 
87 	for (tmp = server->idles; tmp != NULL; tmp = tmp->next) {
88 		SERVER_IDLE_REC *rec = tmp->data;
89 
90 		if (rec->tag == tag)
91 			return rec;
92 	}
93 
94 	return NULL;
95 }
96 
97 /* Add new idle command to queue */
server_idle_add_redir(IRC_SERVER_REC * server,const char * cmd,const char * redirect_cmd,int count,const char * arg,int remote,const char * failure_signal,...)98 int server_idle_add_redir(IRC_SERVER_REC *server, const char *cmd,
99 			  const char *redirect_cmd, int count, const char *arg,
100 			  int remote, const char *failure_signal, ...)
101 {
102 	va_list va;
103 	SERVER_IDLE_REC *rec;
104 
105 	g_return_val_if_fail(server != NULL, -1);
106 
107 	va_start(va, failure_signal);
108 	rec = server_idle_create(cmd, redirect_cmd, count, arg, remote,
109 				 failure_signal, va);
110 	server->idles = g_slist_append(server->idles, rec);
111 	va_end(va);
112 
113 	return rec->tag;
114 }
115 
116 /* Add new idle command to first of queue */
server_idle_add_first_redir(IRC_SERVER_REC * server,const char * cmd,const char * redirect_cmd,int count,const char * arg,int remote,const char * failure_signal,...)117 int server_idle_add_first_redir(IRC_SERVER_REC *server, const char *cmd,
118 				const char *redirect_cmd, int count,
119 				const char *arg, int remote,
120 				const char *failure_signal, ...)
121 {
122 	va_list va;
123 	SERVER_IDLE_REC *rec;
124 
125 	g_return_val_if_fail(server != NULL, -1);
126 
127 	va_start(va, failure_signal);
128 	rec = server_idle_create(cmd, redirect_cmd, count, arg, remote,
129 				 failure_signal, va);
130 	server->idles = g_slist_prepend(server->idles, rec);
131 	va_end(va);
132 
133 	return rec->tag;
134 }
135 
136 /* Add new idle command to specified position of queue */
server_idle_insert_redir(IRC_SERVER_REC * server,const char * cmd,int tag,const char * redirect_cmd,int count,const char * arg,int remote,const char * failure_signal,...)137 int server_idle_insert_redir(IRC_SERVER_REC *server, const char *cmd, int tag,
138 			     const char *redirect_cmd, int count,
139 			     const char *arg, int remote,
140 			     const char *failure_signal, ...)
141 {
142 	va_list va;
143 	SERVER_IDLE_REC *rec;
144 	int pos;
145 
146 	g_return_val_if_fail(server != NULL, -1);
147 
148 	va_start(va, failure_signal);
149 
150 	/* find the position of tag in idle list */
151 	rec = server_idle_find_rec(server, tag);
152 	pos = g_slist_index(server->idles, rec);
153 
154 	rec = server_idle_create(cmd, redirect_cmd, count, arg, remote,
155 				 failure_signal, va);
156         server->idles = pos < 0 ?
157 		g_slist_append(server->idles, rec) :
158 		g_slist_insert(server->idles, rec, pos);
159 	va_end(va);
160 
161 	return rec->tag;
162 }
163 
server_idle_destroy(IRC_SERVER_REC * server,SERVER_IDLE_REC * rec)164 static void server_idle_destroy(IRC_SERVER_REC *server, SERVER_IDLE_REC *rec)
165 {
166 	g_return_if_fail(server != NULL);
167 
168 	server->idles = g_slist_remove(server->idles, rec);
169 
170         g_slist_foreach(rec->redirects, (GFunc) g_free, NULL);
171 	g_slist_free(rec->redirects);
172 
173 	g_free_not_null(rec->arg);
174         g_free_not_null(rec->redirect_cmd);
175         g_free_not_null(rec->failure_signal);
176 	g_free(rec->cmd);
177 	g_free(rec);
178 }
179 
180 /* Check if record is still in queue */
server_idle_find(IRC_SERVER_REC * server,int tag)181 int server_idle_find(IRC_SERVER_REC *server, int tag)
182 {
183 	return server_idle_find_rec(server, tag) != NULL;
184 }
185 
186 /* Remove record from idle queue */
server_idle_remove(IRC_SERVER_REC * server,int tag)187 int server_idle_remove(IRC_SERVER_REC *server, int tag)
188 {
189 	SERVER_IDLE_REC *rec;
190 
191 	g_return_val_if_fail(server != NULL, FALSE);
192 
193 	rec = server_idle_find_rec(server, tag);
194 	if (rec == NULL)
195 		return FALSE;
196 
197 	server_idle_destroy(server, rec);
198 	return TRUE;
199 }
200 
201 /* Execute next idle command */
server_idle_next(IRC_SERVER_REC * server)202 static void server_idle_next(IRC_SERVER_REC *server)
203 {
204 	SERVER_IDLE_REC *rec;
205 
206 	g_return_if_fail(server != NULL);
207 
208 	if (server->idles == NULL)
209 		return;
210 	rec = server->idles->data;
211 
212 	/* Send command */
213 	if (rec->redirect_cmd != NULL) {
214 		server_redirect_event_list(server, rec->redirect_cmd,
215 					   rec->count, rec->arg,
216 					   rec->remote, rec->failure_signal,
217 					   rec->redirects);
218 	}
219 	irc_send_cmd(server, rec->cmd);
220 
221 	server_idle_destroy(server, rec);
222 }
223 
sig_disconnected(IRC_SERVER_REC * server)224 static void sig_disconnected(IRC_SERVER_REC *server)
225 {
226 	g_return_if_fail(server != NULL);
227 
228 	if (!IS_IRC_SERVER(server))
229 		return;
230 
231 	while (server->idles != NULL)
232 		server_idle_destroy(server, server->idles->data);
233 }
234 
sig_idle_timeout(void)235 static int sig_idle_timeout(void)
236 {
237 	GSList *tmp;
238 
239 	/* Scan through every server */
240 	for (tmp = servers; tmp != NULL; tmp = tmp->next) {
241 		IRC_SERVER_REC *rec = tmp->data;
242 
243 		if (IS_IRC_SERVER(rec) &&
244 		    rec->idles != NULL && rec->cmdcount == 0) {
245 			/* We're idling and we have idle commands to run! */
246 			server_idle_next(rec);
247 		}
248 	}
249 	return 1;
250 }
251 
servers_idle_init(void)252 void servers_idle_init(void)
253 {
254 	idlepos = 0;
255 	idle_tag = g_timeout_add(1000, (GSourceFunc) sig_idle_timeout, NULL);
256 
257 	signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
258 }
259 
servers_idle_deinit(void)260 void servers_idle_deinit(void)
261 {
262 	g_source_remove(idle_tag);
263 	signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
264 }
265