Added SILC Thread Queue API
[crypto.git] / apps / irssi / src / fe-common / core / fe-queries.c
1 /*
2  fe-queries.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
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "module-formats.h"
23 #include "modules.h"
24 #include "signals.h"
25 #include "commands.h"
26 #include "levels.h"
27 #include "settings.h"
28
29 #include "chat-protocols.h"
30 #include "servers.h"
31 #include "queries.h"
32
33 #include "fe-core-commands.h"
34 #include "fe-windows.h"
35 #include "window-items.h"
36 #include "printtext.h"
37
38 static int queryclose_tag, query_auto_close, querycreate_level;
39
40 /* Return query where to put the private message. */
41 QUERY_REC *privmsg_get_query(SERVER_REC *server, const char *nick,
42                              int own, int level)
43 {
44         QUERY_REC *query;
45
46         g_return_val_if_fail(IS_SERVER(server), NULL);
47         g_return_val_if_fail(nick != NULL, NULL);
48
49         query = query_find(server, nick);
50         if (query == NULL && !command_hide_output &&
51             (querycreate_level & level) != 0 &&
52             (!own || settings_get_bool("autocreate_own_query"))) {
53                 query = CHAT_PROTOCOL(server)->
54                         query_create(server->tag, nick, TRUE);
55         }
56
57         return query;
58 }
59
60 static void signal_query_created(QUERY_REC *query, gpointer automatic)
61 {
62         TEXT_DEST_REC dest;
63
64         g_return_if_fail(IS_QUERY(query));
65
66         if (window_item_window(query) == NULL) {
67                 window_item_create((WI_ITEM_REC *) query,
68                                    GPOINTER_TO_INT(automatic));
69         }
70
71         format_create_dest_tag(&dest, query->server, query->server_tag,
72                                query->name, MSGLEVEL_CLIENTNOTICE, NULL);
73         printformat_dest(&dest, TXT_QUERY_START,
74                          query->name, query->server_tag);
75 }
76
77 static void signal_query_created_curwin(QUERY_REC *query)
78 {
79         g_return_if_fail(IS_QUERY(query));
80
81         window_item_add(active_win, (WI_ITEM_REC *) query, FALSE);
82 }
83
84 static void signal_query_destroyed(QUERY_REC *query)
85 {
86         WINDOW_REC *window;
87         TEXT_DEST_REC dest;
88
89         g_return_if_fail(IS_QUERY(query));
90
91         window = window_item_window((WI_ITEM_REC *) query);
92         if (window == NULL)
93                 return;
94
95         format_create_dest_tag(&dest, query->server, query->server_tag,
96                                query->name, MSGLEVEL_CLIENTNOTICE, NULL);
97         printformat_dest(&dest, TXT_QUERY_STOP, query->name);
98
99         window_item_destroy((WI_ITEM_REC *) query);
100
101         if (!query->unwanted)
102                 window_auto_destroy(window);
103         else {
104                 /* eg. connection lost to dcc chat */
105                 window_bind_add(window, query->server_tag, query->name);
106         }
107 }
108
109 static void signal_query_server_changed(QUERY_REC *query)
110 {
111         WINDOW_REC *window;
112
113         g_return_if_fail(query != NULL);
114
115         window = window_item_window((WI_ITEM_REC *) query);
116         if (window->active == (WI_ITEM_REC *) query)
117                 window_change_server(window, query->server);
118 }
119
120 static void signal_query_nick_changed(QUERY_REC *query, const char *oldnick)
121 {
122         TEXT_DEST_REC dest;
123
124         g_return_if_fail(query != NULL);
125
126         format_create_dest_tag(&dest, query->server, query->server_tag,
127                                query->name, MSGLEVEL_CLIENTNOTICE, NULL);
128
129         /* don't print the nick change message if only the case was changed */
130         if (g_strcasecmp(query->name, oldnick) != 0) {
131                 printformat_dest(&dest,  TXT_NICK_CHANGED, oldnick,
132                                  query->name, query->name,
133                                  query->address == NULL ? "" : query->address);
134         }
135
136         signal_emit("window item changed", 2,
137                     window_item_window((WI_ITEM_REC *) query), query);
138 }
139
140 static void signal_window_item_server_changed(WINDOW_REC *window,
141                                               QUERY_REC *query)
142 {
143         if (IS_QUERY(query)) {
144                 g_free_and_null(query->server_tag);
145                 if (query->server != NULL)
146                         query->server_tag = g_strdup(query->server->tag);
147         }
148 }
149
150 static void sig_server_connected(SERVER_REC *server)
151 {
152         GSList *tmp;
153
154         if (!IS_SERVER(server))
155                 return;
156
157         /* check if there's any queries without server */
158         for (tmp = queries; tmp != NULL; tmp = tmp->next) {
159                 QUERY_REC *rec = tmp->data;
160
161                 if (rec->server == NULL &&
162                     (rec->server_tag == NULL ||
163                      g_strcasecmp(rec->server_tag, server->tag) == 0)) {
164                         window_item_change_server((WI_ITEM_REC *) rec, server);
165                         server->queries = g_slist_append(server->queries, rec);
166                 }
167         }
168 }
169
170 static void cmd_window_server(const char *data)
171 {
172         SERVER_REC *server;
173         QUERY_REC *query;
174         TEXT_DEST_REC dest;
175
176         g_return_if_fail(data != NULL);
177
178         server = server_find_tag(data);
179         query = QUERY(active_win->active);
180         if (server == NULL || query == NULL)
181                 return;
182
183         /* /WINDOW SERVER used in a query window */
184         format_create_dest_tag(&dest, query->server, query->server_tag,
185                                query->name, MSGLEVEL_CLIENTNOTICE, NULL);
186         printformat_dest(&dest, TXT_QUERY_SERVER_CHANGED,
187                          query->name, server->tag);
188
189         query_change_server(query, server);
190         signal_stop();
191 }
192
193 /* SYNTAX: UNQUERY [<nick>] */
194 static void cmd_unquery(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
195 {
196         QUERY_REC *query;
197         char *nick;
198         void *free_arg;
199
200         g_return_if_fail(data != NULL);
201
202         if (!cmd_get_params(data, &free_arg, 1, &nick))
203                 return;
204
205         if (*nick == '\0') {
206                 /* remove current query */
207                 query = QUERY(item);
208         } else {
209                 query = query_find(server, nick);
210                 if (query == NULL) {
211                         printformat(server, NULL, MSGLEVEL_CLIENTERROR,
212                                     TXT_NO_QUERY, nick);
213                 }
214         }
215
216         if (query != NULL)
217                 query_destroy(query);
218
219         cmd_params_free(free_arg);
220 }
221
222 /* SYNTAX: QUERY [-window] [-<server tag>] <nick> [<message>] */
223 static void cmd_query(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
224 {
225         GHashTable *optlist;
226         QUERY_REC *query;
227         char *nick, *msg;
228         void *free_arg;
229
230         g_return_if_fail(data != NULL);
231
232         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_GETREST |
233                             PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS,
234                             "query", &optlist, &nick, &msg))
235                 return;
236
237         if (*nick == '\0') {
238                 /* remove current query */
239                 cmd_unquery("", server, item);
240                 cmd_params_free(free_arg);
241                 return;
242         }
243
244         server = cmd_options_get_server("query", optlist, server);
245         if (server == NULL) {
246                 cmd_params_free(free_arg);
247                 return;
248         }
249
250         if (*nick != '=' && (server == NULL || !server->connected))
251                 cmd_param_error(CMDERR_NOT_CONNECTED);
252
253         if (g_hash_table_lookup(optlist, "window") != NULL) {
254                 signal_add("query created",
255                            (SIGNAL_FUNC) signal_query_created_curwin);
256         }
257
258         query = query_find(server, nick);
259         if (query == NULL)
260                 query = CHAT_PROTOCOL(server)->
261                         query_create(server->tag, nick, FALSE);
262         else {
263                 /* query already exists, set it active */
264                 WINDOW_REC *window = window_item_window(query);
265
266                 if (window != active_win)
267                         window_set_active(window);
268                 window_item_set_active(active_win, (WI_ITEM_REC *) query);
269         }
270
271         if (g_hash_table_lookup(optlist, "window") != NULL) {
272                 signal_remove("query created",
273                               (SIGNAL_FUNC) signal_query_created_curwin);
274         }
275
276         if (*msg != '\0') {
277                 msg = g_strdup_printf("-nick %s %s", nick, msg);
278                 signal_emit("command msg", 3, msg, server, query);
279                 g_free(msg);
280         }
281
282         cmd_params_free(free_arg);
283 }
284
285 static void window_reset_query_timestamps(WINDOW_REC *window)
286 {
287         GSList *tmp;
288
289         if (window == NULL)
290                 return;
291
292         for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
293                 QUERY_REC *query = QUERY(tmp->data);
294
295                 if (query != NULL)
296                         query->last_unread_msg = time(NULL);
297         }
298 }
299
300 static void sig_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
301 {
302         /* reset the queries last_unread_msg so query doesn't get closed
303            immediately after switched to the window, or after changed to
304            some other window from it */
305         window_reset_query_timestamps(window);
306         window_reset_query_timestamps(old_window);
307 }
308
309 static int sig_query_autoclose(void)
310 {
311         WINDOW_REC *window;
312         GSList *tmp, *next;
313         time_t now;
314
315         now = time(NULL);
316         for (tmp = queries; tmp != NULL; tmp = next) {
317                 QUERY_REC *rec = tmp->data;
318
319                 next = tmp->next;
320                 window = window_item_window((WI_ITEM_REC *) rec);
321                 if (window != active_win && rec->data_level < DATA_LEVEL_MSG &&
322                     now-rec->last_unread_msg > query_auto_close)
323                         query_destroy(rec);
324         }
325      return 1;
326 }
327
328 static void sig_message_private(SERVER_REC *server, const char *msg,
329                                 const char *nick, const char *address)
330 {
331         QUERY_REC *query;
332
333         /* create query window if needed */
334         query = privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
335
336         /* reset the query's last_unread_msg timestamp */
337         if (query != NULL)
338                 query->last_unread_msg = time(NULL);
339 }
340
341 static void read_settings(void)
342 {
343         querycreate_level = settings_get_level("autocreate_query_level");
344         query_auto_close = settings_get_time("autoclose_query")/1000;
345         if (query_auto_close > 0 && queryclose_tag == -1)
346                 queryclose_tag = g_timeout_add(5000, (GSourceFunc) sig_query_autoclose, NULL);
347         else if (query_auto_close <= 0 && queryclose_tag != -1) {
348                 g_source_remove(queryclose_tag);
349                 queryclose_tag = -1;
350         }
351 }
352
353 void fe_queries_init(void)
354 {
355         settings_add_level("lookandfeel", "autocreate_query_level", "MSGS DCCMSGS");
356         settings_add_bool("lookandfeel", "autocreate_own_query", TRUE);
357         settings_add_time("lookandfeel", "autoclose_query", "0");
358
359         queryclose_tag = -1;
360         read_settings();
361
362         signal_add("query created", (SIGNAL_FUNC) signal_query_created);
363         signal_add("query destroyed", (SIGNAL_FUNC) signal_query_destroyed);
364         signal_add("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
365         signal_add("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
366         signal_add("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
367         signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
368         signal_add("window changed", (SIGNAL_FUNC) sig_window_changed);
369         signal_add_first("message private", (SIGNAL_FUNC) sig_message_private);
370         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
371
372         command_bind("query", NULL, (SIGNAL_FUNC) cmd_query);
373         command_bind("unquery", NULL, (SIGNAL_FUNC) cmd_unquery);
374         command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server);
375
376         command_set_options("query", "window");
377 }
378
379 void fe_queries_deinit(void)
380 {
381         if (queryclose_tag != -1) g_source_remove(queryclose_tag);
382
383         signal_remove("query created", (SIGNAL_FUNC) signal_query_created);
384         signal_remove("query destroyed", (SIGNAL_FUNC) signal_query_destroyed);
385         signal_remove("query server changed", (SIGNAL_FUNC) signal_query_server_changed);
386         signal_remove("query nick changed", (SIGNAL_FUNC) signal_query_nick_changed);
387         signal_remove("window item server changed", (SIGNAL_FUNC) signal_window_item_server_changed);
388         signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
389         signal_remove("window changed", (SIGNAL_FUNC) sig_window_changed);
390         signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
391         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
392
393         command_unbind("query", (SIGNAL_FUNC) cmd_query);
394         command_unbind("unquery", (SIGNAL_FUNC) cmd_unquery);
395         command_unbind("window server", (SIGNAL_FUNC) cmd_window_server);
396 }