1 /*
2 ctrlproxy: A modular IRC proxy
3 admin: module for remote administration.
4
5 (c) 2003-2007 Jelmer Vernooij <jelmer@nl.linux.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "internals.h"
23 #include <string.h>
24 #include <sys/un.h>
25 #include "admin.h"
26 #include "help.h"
27 #include "irc.h"
28
29 help_t *help;
30
31 GList *admin_commands = NULL;
32 guint longest_command = 0;
33
34 static void add_network_helper(admin_handle h, const char *name);
35 static void del_network_helper(admin_handle h, const char *name);
36 static void list_networks_helper(admin_handle h);
37
38 #define ADMIN_USERNAME "ctrlproxy"
39 #define ADMIN_NICK "ctrlproxy"
40
41 /**
42 * Determine the hostmask for the admin user.
43 *
44 * @param n Network for which to generate the hostmask.
45 * @return Host mask, should be freed by the caller.
46 */
admin_hostmask(struct irc_network * n)47 static char *admin_hostmask(struct irc_network *n)
48 {
49 return g_strdup_printf(ADMIN_NICK"!"ADMIN_USERNAME"@%s", n->name);
50 }
51
52 /**
53 * Handle private message sent to the admin user.
54 *
55 * @param h Admin context handle.
56 * @param data Text sent.
57 */
privmsg_admin_out(admin_handle h,const char * data)58 static void privmsg_admin_out(admin_handle h, const char *data)
59 {
60 struct irc_client *c = h->client;
61 char *nick = c->state->me.nick;
62 char *hostmask;
63
64 hostmask = admin_hostmask(c->network);
65 if (c->network->external_state != NULL)
66 nick = c->network->external_state->me.nick;
67 client_send_args_ex(c, hostmask, "NOTICE", nick, data, NULL);
68
69 g_free(hostmask);
70 }
71
72 /**
73 * Sent data to the admin network channel.
74 *
75 * @param h Admin context handle
76 * @param data Text to send.
77 */
network_admin_out(admin_handle h,const char * data)78 static void network_admin_out(admin_handle h, const char *data)
79 {
80 struct irc_client *c = h->client;
81 char *hostmask;
82
83 hostmask = admin_hostmask(c->network);
84 virtual_network_recv_args(c->network, hostmask, "PRIVMSG", ADMIN_CHANNEL,
85 data, NULL);
86
87 g_free(hostmask);
88 }
89
90 /**
91 * Handles the 'help' command.
92 * @param h Admin context handle
93 * @param args Arguments
94 * @param userdata User context data (ignored)
95 */
cmd_help(admin_handle h,const char * const * args,void * userdata)96 static void cmd_help(admin_handle h, const char * const *args, void *userdata)
97 {
98 const char *s;
99
100 s = help_get(help, args[1] != NULL?args[1]:"index");
101
102 if (s == NULL) {
103 if (args[1] == NULL)
104 admin_out(h, "Sorry, help not available");
105 else
106 admin_out(h, "Sorry, no help for %s available", args[1]);
107 return;
108 }
109
110 while (strncmp(s, "%\n", 2) != 0) {
111 char *tmp;
112 admin_out(h, "%s", tmp = g_strndup(s, strchr(s, '\n')-s));
113 g_free(tmp);
114
115 s = strchr(s, '\n')+1;
116 }
117 }
118
119 /**
120 * Return the client handle associated with an admin context.
121 * @param h Admin context handle
122 * @return Client, or NULL if no client is associated.
123 */
admin_get_client(admin_handle h)124 struct irc_client *admin_get_client(admin_handle h)
125 {
126 return h->client;
127 }
128
admin_get_global(admin_handle h)129 struct global *admin_get_global(admin_handle h)
130 {
131 return h->global;
132 }
133
134 /**
135 * Return the network handle associated with an admin context.
136 * @param h Admin context handle
137 * @return Network, or NULL if no network is associated.
138 */
admin_get_network(admin_handle h)139 struct irc_network *admin_get_network(admin_handle h)
140 {
141 return h->network;
142 }
143
144 /**
145 * Send data to the user from the admin user.
146 * @param h Admin context handle
147 * @param fmt Format, printf-style
148 * @param ... Format arguments
149 */
admin_out(admin_handle h,const char * fmt,...)150 void admin_out(admin_handle h, const char *fmt, ...)
151 {
152 va_list ap;
153 char *msg;
154 va_start(ap, fmt);
155 msg = g_strdup_vprintf(fmt, ap);
156 va_end(ap);
157
158 h->send_fn(h, msg);
159
160 g_free(msg);
161 }
162
163 /**
164 * Handles the 'ADDNETWORK' command.
165 *
166 * @param h Admin context handle.
167 * @param args String arguments, argv-style.
168 * @param userdata Optional user data, always NULL.
169 */
cmd_add_network(admin_handle h,const char * const * args,void * userdata)170 static void cmd_add_network (admin_handle h, const char * const *args, void *userdata)
171 {
172 if (args[1] == NULL) {
173 admin_out(h, "No name specified");
174 return;
175 }
176
177 add_network_helper(h, args[1]);
178 }
179
cmd_del_network(admin_handle h,const char * const * args,void * userdata)180 static void cmd_del_network (admin_handle h, const char * const *args, void *userdata)
181 {
182 if (args[1] == NULL) {
183 admin_out(h, "Not enough parameters");
184 return;
185 }
186
187 del_network_helper(h, args[1]);
188 }
189
cmd_add_server(admin_handle h,const char * const * args,void * userdata)190 static void cmd_add_server (admin_handle h, const char * const *args, void *userdata)
191 {
192 struct irc_network *n;
193 struct tcp_server_config *s;
194 struct network_config *nc;
195 char *t;
196
197 if (!args[1] || !args[2]) {
198 admin_out(h, "Not enough parameters");
199 return;
200 }
201
202 n = find_network(admin_get_global(h)->networks, args[1]);
203
204 if (!n) {
205 admin_out(h, "No such network '%s'", args[1]);
206 return;
207 }
208
209 nc = n->private_data;
210
211 if (nc->type != NETWORK_TCP) {
212 admin_out(h, "Not a TCP/IP network!");
213 return;
214 }
215
216 s = g_new0(struct tcp_server_config, 1);
217
218 s->host = g_strdup(args[2]);
219 if ((t = strchr(s->host, ':'))) {
220 *t = '\0';
221 s->port = g_strdup(t+1);
222 } else {
223 s->port = g_strdup(DEFAULT_IRC_PORT);
224 }
225 s->ssl = FALSE;
226 s->password = args[3]?g_strdup(args[3]):NULL;
227
228 nc->type_settings.tcp.servers = g_list_append(nc->type_settings.tcp.servers, s);
229
230 admin_out(h, "Server added to `%s'", args[1]);
231 }
232
cmd_connect_network(admin_handle h,const char * const * args,void * userdata)233 static void cmd_connect_network (admin_handle h, const char * const *args, void *userdata)
234 {
235 struct irc_network *s;
236 if (!args[1]) {
237 admin_out(h, "No network specified");
238 return;
239 }
240
241 s = find_network(admin_get_global(h)->networks, args[1]);
242
243 if (!s) {
244 admin_out(h, "No such network `%s'", args[1]);
245 return;
246 }
247
248 switch (s->connection.state) {
249 case NETWORK_CONNECTION_STATE_NOT_CONNECTED:
250 admin_out(h, "Connecting to `%s'", args[1]);
251 connect_network(s);
252 break;
253 case NETWORK_CONNECTION_STATE_RECONNECT_PENDING:
254 admin_out(h, "Forcing reconnect to `%s'", args[1]);
255 disconnect_network(s);
256 network_select_next_server(s);
257 connect_network(s);
258 break;
259 case NETWORK_CONNECTION_STATE_CONNECTED:
260 case NETWORK_CONNECTION_STATE_CONNECTING:
261 case NETWORK_CONNECTION_STATE_LOGIN_SENT:
262 admin_out(h, "Connect to `%s' already in progress", args[1]);
263 break;
264 case NETWORK_CONNECTION_STATE_MOTD_RECVD:
265 admin_out(h, "Already connected to `%s'", args[1]);
266 break;
267 }
268 }
269
cmd_disconnect_network(admin_handle h,const char * const * args,void * userdata)270 static void cmd_disconnect_network (admin_handle h, const char * const *args, void *userdata)
271 {
272 struct irc_network *n;
273 struct network_config *nc;
274
275 if (args[1] != NULL) {
276 n = find_network(admin_get_global(h)->networks, args[1]);
277 if (!n) {
278 admin_out(h, "Can't find active network with that name");
279 return;
280 }
281 } else {
282 n = admin_get_network(h);
283 if (n == NULL) {
284 admin_out(h, "Usage: DISCONNECT <network>");
285 return;
286 }
287 }
288
289 nc = n->private_data;
290
291 if (n->connection.state == NETWORK_CONNECTION_STATE_NOT_CONNECTED) {
292 admin_out(h, "Already disconnected from `%s'", n->name);
293 } else if (nc->type == NETWORK_VIRTUAL &&
294 n->connection.data.virtual.ops->not_disconnectable) {
295 admin_out(h, "Built-in network `%s' can't be disconnected",
296 n->name);
297 } else {
298 admin_out(h, "Disconnecting from `%s'", n->name);
299 disconnect_network(n);
300 }
301 }
302
cmd_next_server(admin_handle h,const char * const * args,void * userdata)303 static void cmd_next_server (admin_handle h, const char * const *args, void *userdata)
304 {
305 struct irc_network *n;
306 const char *name;
307
308
309 if (args[1] != NULL) {
310 name = args[1];
311 n = find_network(admin_get_global(h)->networks, args[1]);
312 } else {
313 n = admin_get_network(h);
314 if (n == NULL) {
315 admin_out(h, "No current network");
316 return;
317 }
318 name = n->name;
319 }
320 if (!n) {
321 admin_out(h, "%s: Not connected", name);
322 } else {
323 admin_out(h, "%s: Reconnecting", name);
324 disconnect_network(n);
325 network_select_next_server(n);
326 connect_network(n);
327 }
328 }
329
cmd_save_config(admin_handle h,const char * const * args,void * userdata)330 static void cmd_save_config (admin_handle h, const char * const *args, void *userdata)
331 {
332 const char *adm_dir;
333 struct global *global = admin_get_global(h);
334 global_update_config(global);
335 if (global->restricted)
336 adm_dir = global->config->config_dir;
337 else
338 adm_dir = args[1]?args[1]:global->config->config_dir;
339 save_configuration(global->config, adm_dir);
340 admin_out(h, "Configuration saved in %s", adm_dir);
341 }
342
add_network_helper(admin_handle h,const char * name)343 static void add_network_helper(admin_handle h, const char *name)
344 {
345 struct network_config *nc;
346 struct irc_network *n;
347
348 if (find_network(admin_get_global(h)->networks, name) != NULL) {
349 admin_out(h, "Network with name `%s' already exists", name);
350 return;
351 }
352
353 nc = network_config_init(admin_get_global(h)->config);
354 g_free(nc->name); nc->name = g_strdup(name);
355 n = load_network(admin_get_global(h), nc);
356
357 admin_out(h, "Network `%s' added. Use ADDSERVER to add a server to this network.", name);
358 }
359
rename_network_helper(admin_handle h,const char * oldname,const char * newname)360 static void rename_network_helper(admin_handle h, const char *oldname,
361 const char *newname)
362 {
363 struct irc_network *n;
364
365 n = find_network(admin_get_global(h)->networks, oldname);
366
367 if (n == NULL) {
368 admin_out(h, "Network with name `%s' does not exist", oldname);
369 return;
370 }
371
372 if (find_network(admin_get_global(h)->networks, newname) != NULL) {
373 admin_out(h, "Network with name `%s' already exists", newname);
374 return;
375 }
376
377 g_free(n->name);
378 n->name = g_strdup(newname);
379 }
380
del_network_helper(admin_handle h,const char * name)381 static void del_network_helper(admin_handle h, const char *name)
382 {
383 struct irc_network *n = find_network(admin_get_global(h)->networks, name);
384 if (n == NULL) {
385 admin_out(h, "No such network `%s'", name);
386 return;
387 }
388
389 disconnect_network(n);
390
391 unload_network(n);
392
393 config_del_network(admin_get_global(h)->config, name);
394
395 admin_out(h, "Network `%s' deleted", name);
396 }
397
info_network_helper(admin_handle h,const char * name)398 static void info_network_helper(admin_handle h, const char *name)
399 {
400 struct irc_network *n = find_network(admin_get_global(h)->networks, name);
401 struct network_config *nc;
402 if (n == NULL) {
403 admin_out(h, "No such network `%s'", name);
404 return;
405 }
406 nc = n->private_data;
407 if (nc->type == NETWORK_TCP) {
408 GList *gl;
409 admin_out(h, "Servers:");
410 for (gl = nc->type_settings.tcp.servers; gl; gl = gl->next) {
411 struct tcp_server_config *server = gl->data;
412 admin_out(h, " %s:%s%s", server->host, server->port,
413 (server == n->connection.data.tcp.current_server?" (Current)":""));
414 }
415 } else if (nc->type == NETWORK_VIRTUAL) {
416 admin_out(h, "Virtual network");
417 } else if (nc->type == NETWORK_PROGRAM) {
418 admin_out(h, "Interface to %s", nc->type_settings.program_location);
419 }
420
421 if (n->clients == NULL) {
422 admin_out(h, "No clients connected");
423 } else {
424 GList *gl;
425 admin_out(h, "Clients:");
426 for (gl = n->clients; gl; gl = gl->next) {
427 struct irc_client *cl = gl->data;
428 admin_out(h, " %s", cl->description);
429 }
430 }
431 }
432
433
list_networks_helper(admin_handle h)434 static void list_networks_helper(admin_handle h)
435 {
436 GList *gl;
437 for (gl = admin_get_global(h)->networks; gl; gl = gl->next) {
438 struct irc_network *n = gl->data;
439
440 switch (n->connection.state) {
441 case NETWORK_CONNECTION_STATE_CONNECTING:
442 admin_out(h, "%s: Connect in progress", n->name);
443 break;
444 case NETWORK_CONNECTION_STATE_CONNECTED:
445 admin_out(h, "%s: Connected, logging in", n->name);
446 break;
447 case NETWORK_CONNECTION_STATE_NOT_CONNECTED:
448 if (n->connection.data.tcp.last_disconnect_reason)
449 admin_out(h, "%s: Not connected: %s", n->name,
450 n->connection.data.tcp.last_disconnect_reason);
451 else
452 admin_out(h, "%s: Not connected", n->name);
453 break;
454 case NETWORK_CONNECTION_STATE_RECONNECT_PENDING:
455 admin_out(h, "%s: Reconnecting", n->name);
456 break;
457 case NETWORK_CONNECTION_STATE_LOGIN_SENT:
458 case NETWORK_CONNECTION_STATE_MOTD_RECVD:
459 admin_out(h, "%s: connected", n->name);
460 break;
461 }
462 }
463 }
464
465 /* NETWORK LIST */
466 /* NETWORK ADD OFTC */
467 /* NETWORK DEL OFTC */
468 /* NETWORK INFO OFTC */
cmd_network(admin_handle h,const char * const * args,void * userdata)469 static void cmd_network(admin_handle h, const char * const *args, void *userdata)
470 {
471 if (args[1] == NULL)
472 goto usage;
473
474 if (!strcasecmp(args[1], "list")) {
475 list_networks_helper(h);
476 } else if (!strcasecmp(args[1], "add")) {
477 if (args[2] == NULL) {
478 admin_out(h, "Usage: network add <name>");
479 return;
480 }
481 add_network_helper(h, args[2]);
482 } else if (!strcasecmp(args[1], "rename")) {
483 if (args[2] == NULL || args[3] == NULL) {
484 admin_out(h, "Usage: network rename <oldname> <newname>");
485 return;
486 }
487 rename_network_helper(h, args[2], args[3]);
488 } else if (!strcasecmp(args[1], "del") ||
489 !strcasecmp(args[1], "delete") ||
490 !strcasecmp(args[1], "remove")) {
491 if (args[2] == NULL) {
492 admin_out(h, "Usage: network del <name>");
493 return;
494 }
495 del_network_helper(h, args[2]);
496 return;
497 } else if (!strcasecmp(args[1], "info")) {
498 if (args[2] == NULL) {
499 admin_out(h, "Usage: network info <name>");
500 return;
501 }
502 info_network_helper(h, args[2]);
503 return;
504 } else {
505 goto usage;
506 }
507
508 return;
509 usage:
510 admin_out(h, "Usage: network [list|add <name>|del <name>]");
511 }
512
cmd_list_networks(admin_handle h,const char * const * args,void * userdata)513 static void cmd_list_networks(admin_handle h, const char * const *args, void *userdata)
514 {
515 list_networks_helper(h);
516 }
517
cmd_detach(admin_handle h,const char * const * args,void * userdata)518 static void cmd_detach(admin_handle h, const char * const *args, void *userdata)
519 {
520 struct irc_client *c = admin_get_client(h);
521
522 if (c == NULL) {
523 admin_out(h, "No client to detach.");
524 return;
525 }
526
527 client_disconnect(c, "Client exiting");
528 }
529
dump_joined_channels(admin_handle h,const char * const * args,void * userdata)530 static void dump_joined_channels(admin_handle h, const char * const *args, void *userdata)
531 {
532 struct irc_network *n;
533 GList *gl;
534
535 if (args[1] != NULL) {
536 n = find_network(admin_get_global(h)->networks, args[1]);
537 if (n == NULL) {
538 admin_out(h, "Can't find network '%s'", args[1]);
539 return;
540 }
541 } else {
542 n = admin_get_network(h);
543 if (n == NULL) {
544 admin_out(h, "No network specified and no current network.");
545 return;
546 }
547 }
548
549 if (!n->external_state) {
550 admin_out(h, "Network '%s' not connected", n->name);
551 return;
552 }
553
554 for (gl = n->external_state->channels; gl; gl = gl->next) {
555 struct irc_channel_state *ch = (struct irc_channel_state *)gl->data;
556 admin_out(h, "%s", ch->name);
557 }
558 }
559
560 #ifdef DEBUG
cmd_abort(admin_handle h,const char * const * args,void * userdata)561 static void cmd_abort(admin_handle h, const char * const *args, void *userdata)
562 {
563 abort();
564 }
565 #endif
566
cmd_die(admin_handle h,const char * const * args,void * userdata)567 static void cmd_die(admin_handle h, const char * const *args, void *userdata)
568 {
569 exit(0);
570 }
571
572 static GHashTable *markers = NULL;
573
cmd_backlog(admin_handle h,const char * const * args,void * userdata)574 static void cmd_backlog(admin_handle h, const char * const *args, void *userdata)
575 {
576 struct linestack_marker *lm;
577 struct irc_network *n;
578
579 if (admin_get_client(h) == NULL) {
580 admin_out(h, "No client to send backlog to");
581 return;
582 }
583
584 n = admin_get_network(h);
585
586 if (n == NULL) {
587 admin_out(h, "No network connected.");
588 return;
589 }
590
591 lm = g_hash_table_lookup(markers, n);
592
593 if (n->linestack == NULL) {
594 admin_out(h, "No backlog available. Perhaps the connection to the network is down?");
595 return;
596 }
597
598 if (!args[1] || strlen(args[1]) == 0) {
599 admin_out(h, "Sending backlog for network '%s'", n->name);
600
601 linestack_send(n->linestack, lm, NULL, admin_get_client(h),
602 TRUE, n->global->config->report_time != REPORT_TIME_NEVER,
603 n->global->config->report_time_offset);
604
605 g_hash_table_replace(markers, n, linestack_get_marker(n->linestack));
606
607 return;
608 }
609
610 /* Backlog for specific nick/channel */
611 admin_out(h, "Sending backlog for channel %s", args[1]);
612
613 linestack_send_object(n->linestack, args[1], lm, NULL,
614 admin_get_client(h), TRUE,
615 n->global->config->report_time != REPORT_TIME_NEVER,
616 n->global->config->report_time_offset);
617
618 g_hash_table_replace(markers, n, linestack_get_marker(n->linestack));
619 }
620
linestack_get(admin_handle h)621 static char *linestack_get(admin_handle h)
622 {
623 struct global *g = admin_get_global(h);
624
625 return g_strdup(g->config->linestack_backend);
626 }
627
linestack_set(admin_handle h,const char * value)628 static gboolean linestack_set(admin_handle h, const char *value)
629 {
630 struct global *g = admin_get_global(h);
631
632 g_free(g->config->linestack_backend);
633 /* FIXME: Check if this is a valid linestack backend name */
634 g->config->linestack_backend = g_strdup(g->config->linestack_backend);
635
636 return TRUE;
637 }
638
logging_get(admin_handle h)639 static char *logging_get(admin_handle h)
640 {
641 struct global *g = admin_get_global(h);
642
643 if (g->config->log_file == NULL)
644 return g_strdup("none");
645
646 if (g->config->log_file->is_irssi)
647 return g_strdup("irssi");
648
649 return g_strdup("custom");
650 }
651
logging_set(admin_handle h,const char * value)652 static gboolean logging_set(admin_handle h, const char *value)
653 {
654 /* FIXME: Implement */
655
656 return FALSE;
657 }
658
log_level_get(admin_handle h)659 static char *log_level_get(admin_handle h)
660 {
661 extern enum log_level current_log_level;
662
663 return g_strdup_printf("%d", current_log_level);
664 }
665
log_level_set(admin_handle h,const char * value)666 static gboolean log_level_set(admin_handle h, const char *value)
667 {
668 extern enum log_level current_log_level;
669 int x;
670
671 if (value == NULL)
672 return FALSE;
673
674 x = atoi(value);
675 if (x < 0 || x > 5) {
676 admin_out(h, "Invalid log level %d", x);
677 return FALSE;
678 }
679
680 current_log_level = x;
681 admin_out(h, "Log level changed to %d", x);
682 return TRUE;
683 }
684
cmd_charset(admin_handle h,const char * const * args,void * userdata)685 static void cmd_charset(admin_handle h, const char * const *args, void *userdata)
686 {
687 struct irc_client *c;
688
689 if (args[1] == NULL) {
690 admin_out(h, "No charset specified");
691 return;
692 }
693
694 c = admin_get_client(h);
695 if (c == NULL) {
696 admin_out(h, "The CHARSET command can only be used on client connections.");
697 return;
698 }
699
700 if (!client_set_charset(c, args[1])) {
701 admin_out(h, "Error setting charset: %s", args[1]);
702 }
703 }
704
cmd_echo(admin_handle h,const char * const * args,void * userdata)705 static void cmd_echo(admin_handle h, const char * const *args, void *userdata)
706 {
707 admin_out(h, "%s", args[1]);
708 }
709
cmp_cmd(gconstpointer a,gconstpointer b)710 static gint cmp_cmd(gconstpointer a, gconstpointer b)
711 {
712 const struct admin_command *cmda = a, *cmdb = b;
713
714 return g_strcasecmp(cmda->name, cmdb->name);
715 }
716
register_admin_command(const struct admin_command * cmd)717 void register_admin_command(const struct admin_command *cmd)
718 {
719 admin_commands = g_list_insert_sorted(admin_commands, g_memdup(cmd, sizeof(*cmd)), cmp_cmd);
720 if (strlen(cmd->name) > longest_command) longest_command = strlen(cmd->name);
721 }
722
unregister_admin_command(const struct admin_command * cmd)723 void unregister_admin_command(const struct admin_command *cmd)
724 {
725 admin_commands = g_list_remove(admin_commands, cmd);
726 }
727
process_cmd(admin_handle h,const char * cmd)728 gboolean process_cmd(admin_handle h, const char *cmd)
729 {
730 char **args = NULL;
731 GList *gl;
732
733 if (!cmd) {
734 admin_out(h, "Please specify a command. Use the 'help' command to get a list of available commands");
735 return TRUE;
736 }
737
738 args = g_strsplit(cmd, " ", 0);
739
740 if (!args[0]) {
741 admin_out(h, "Please specify a command. Use the 'help' command to get a list of available commands");
742 return TRUE;
743 }
744
745 /* Ok, arguments are processed now. Execute the corresponding command */
746 for (gl = admin_commands; gl; gl = gl->next) {
747 struct admin_command *cmd = (struct admin_command *)gl->data;
748 if (!g_strcasecmp(cmd->name, args[0])) {
749 cmd->handler(h, (const char * const *)args, cmd->userdata);
750 g_strfreev(args);
751 return TRUE;
752 }
753 }
754
755 admin_out(h, "Can't find command '%s'. Type 'help' for a list of available commands. ", args[0]);
756
757 g_strfreev(args);
758
759 return TRUE;
760 }
761
admin_process_command(struct irc_client * c,struct irc_line * l,int cmdoffset)762 gboolean admin_process_command(struct irc_client *c, struct irc_line *l, int cmdoffset)
763 {
764 int i;
765 char *tmp;
766 gboolean ret;
767 struct admin_handle ah;
768
769 if (l->args[cmdoffset] == NULL) {
770 client_send_response(c, ERR_NEEDMOREPARAMS, l->args[0], "Not enough parameters", NULL);
771 return TRUE;
772 }
773
774 tmp = g_strdup(l->args[cmdoffset]);
775
776 /* Add everything after l->args[cmdoffset] to tmp */
777 for(i = cmdoffset+1; l->args[i]; i++) {
778 char *oldtmp = tmp;
779 tmp = g_strdup_printf("%s %s", oldtmp, l->args[i]);
780 g_free(oldtmp);
781 }
782
783 ah.send_fn = privmsg_admin_out;
784 ah.client = c;
785 ah.network = c->network;
786 ah.global = c->network->global;
787 ret = process_cmd(&ah, tmp);
788
789 g_free(tmp);
790
791 return ret;
792 }
793
admin_net_init(struct irc_network * n)794 static gboolean admin_net_init(struct irc_network *n)
795 {
796 char *hostmask;
797 char *nicks;
798
799 hostmask = admin_hostmask(n);
800
801 virtual_network_recv_args(n, n->external_state->me.hostmask, "JOIN", ADMIN_CHANNEL, NULL);
802 virtual_network_recv_response(n, RPL_TOPIC, ADMIN_CHANNEL,
803 "CtrlProxy administration channel | Type `help' for more information",
804 NULL);
805 nicks = g_strdup_printf("@ctrlproxy %s", n->external_state->me.nick);
806
807 virtual_network_recv_response(n, RPL_NAMREPLY, "=", ADMIN_CHANNEL, nicks, NULL);
808 g_free(nicks);
809 virtual_network_recv_response(n, RPL_ENDOFNAMES, ADMIN_CHANNEL, "End of /NAMES list.", NULL);
810
811 g_free(hostmask);
812
813 return TRUE;
814 }
815
admin_to_server(struct irc_network * n,struct irc_client * c,const struct irc_line * l)816 static gboolean admin_to_server (struct irc_network *n, struct irc_client *c, const struct irc_line *l)
817 {
818 if (!g_strcasecmp(l->args[0], "PRIVMSG") ||
819 !g_strcasecmp(l->args[0], "NOTICE")) {
820 struct admin_handle ah;
821
822 if (g_strcasecmp(l->args[0], n->external_state->me.nick) == 0) {
823 virtual_network_recv_args(n, n->external_state->me.hostmask, l->args[0], l->args[1], NULL);
824 return TRUE;
825 }
826
827 if (g_strcasecmp(l->args[1], ADMIN_CHANNEL) &&
828 g_strcasecmp(l->args[1], "ctrlproxy")) {
829 virtual_network_recv_response(n, ERR_NOSUCHNICK, l->args[1], "No such nick/channel", NULL);
830 return TRUE;
831 }
832
833 ah.send_fn = network_admin_out;
834 ah.user_data = NULL;
835 ah.client = c;
836 ah.network = n;
837 ah.global = n->global;
838
839 return process_cmd(&ah, l->args[2]);
840 } else if (!g_strcasecmp(l->args[0], "ISON")) {
841 int i;
842 char *tmp;
843 GList *gl = NULL;
844
845 if (l->args[1] == NULL) {
846 virtual_network_recv_response(n, ERR_NEEDMOREPARAMS, l->args[0], "Not enough params", NULL);
847 return TRUE;
848 }
849
850 for (i = 1; l->args[i]; i++) {
851 if (!g_strcasecmp(l->args[i], "ctrlproxy") ||
852 !g_strcasecmp(l->args[i], n->external_state->me.nick)) {
853 gl = g_list_append(gl, l->args[i]);
854 }
855 }
856 virtual_network_recv_response(n, RPL_ISON, tmp = list_make_string(gl), NULL);
857 g_free(tmp);
858 g_list_free(gl);
859 return TRUE;
860 } else if (!g_strcasecmp(l->args[0], "USERHOST")) {
861 GList *gl = NULL;
862 char *tmp;
863 int i;
864
865 if (l->args[1] == NULL) {
866 virtual_network_recv_response(n, ERR_NEEDMOREPARAMS, l->args[0], "Not enough params", NULL);
867 return TRUE;
868 }
869
870 for (i = 1; l->args[i]; i++) {
871 if (!g_strcasecmp(l->args[i], "ctrlproxy")) {
872 gl = g_list_append(gl, g_strdup_printf("%s=+%s@%s", l->args[i], ADMIN_USERNAME, get_my_hostname()));
873 }
874 if (!g_strcasecmp(l->args[i], n->external_state->me.nick)) {
875 gl = g_list_append(gl, g_strdup_printf("%s=+%s@%s", l->args[i], n->external_state->me.username, n->external_state->me.hostname));
876 }
877 }
878
879 virtual_network_recv_response(n, RPL_USERHOST, tmp = list_make_string(gl), NULL);
880 g_free(tmp);
881 while (gl) {
882 g_free(gl->data);
883 gl = g_list_remove(gl, gl->data);
884 }
885 return TRUE;
886 } else if (!g_strcasecmp(l->args[0], "QUIT")) {
887 return TRUE;
888 } else if (!g_strcasecmp(l->args[0], "MODE")) {
889 /* FIXME: Do something here ? */
890 return TRUE;
891 } else if (!g_strcasecmp(l->args[0], "WHO")) {
892 if (!strcmp(l->args[1], ADMIN_CHANNEL) ||
893 !strcmp(l->args[1], "ctrlproxy")) {
894 virtual_network_recv_response(n, RPL_WHOREPLY, ADMIN_CHANNEL,
895 "ctrlproxy",
896 get_my_hostname(),
897 get_my_hostname(),
898 "ctrlproxy",
899 "H",
900 "0 CtrlProxy user",
901 NULL);
902 }
903 if (!strcmp(l->args[1], ADMIN_CHANNEL) ||
904 !strcmp(l->args[1], n->external_state->me.nick)) {
905 char *fullname = g_strdup_printf("0 %s", n->external_state->me.fullname);
906 virtual_network_recv_response(n, RPL_WHOREPLY, ADMIN_CHANNEL,
907 n->external_state->me.username,
908 n->external_state->me.hostname,
909 get_my_hostname(),
910 n->external_state->me.nick,
911 "H",
912 fullname,
913 NULL);
914 g_free(fullname);
915 }
916
917 virtual_network_recv_response(n, RPL_ENDOFWHO, l->args[1], "End of /WHO list.", NULL);
918
919 return TRUE;
920 } else if (!g_strcasecmp(l->args[0], "JOIN")) {
921 if (strcmp(l->args[1], ADMIN_CHANNEL) != 0) {
922 virtual_network_recv_response(n, ERR_NOSUCHCHANNEL, l->args[1], "No such channel", NULL);
923 }
924 return TRUE;
925 } else if (!g_strcasecmp(l->args[0], "PART")) {
926 if (strcmp(l->args[1], ADMIN_CHANNEL) != 0) {
927 virtual_network_recv_response(n, ERR_NOTONCHANNEL, l->args[1], "You're not on that channel", NULL);
928 } else {
929 virtual_network_recv_args(n, n->external_state->me.hostmask, "PART", l->args[1], NULL);
930 admin_net_init(n);
931 }
932 return TRUE;
933 } else if (!g_strcasecmp(l->args[0], "WHOIS")) {
934 /* FIXME: Send something sensible */
935 virtual_network_recv_response(n, RPL_ENDOFWHOIS, l->args[1],
936 "End of /WHOIS list.", NULL);
937 return TRUE;
938 } else if (!g_strcasecmp(l->args[0], "AWAY")) {
939 if (l->args[1] != NULL && strcmp(l->args[1], "") != 0) {
940 virtual_network_recv_response(n, RPL_NOWAWAY, "You are now marked as being away", NULL);
941 } else {
942 virtual_network_recv_response(n, RPL_UNAWAY, "You are no longer marked as being away", NULL);
943 }
944 return TRUE;
945 } else {
946 virtual_network_recv_response(n, ERR_UNKNOWNCOMMAND, l->args[0], "Unknown command", NULL);
947 log_global(LOG_TRACE, "Unhandled command `%s' to admin network",
948 l->args[0]);
949 return TRUE;
950 }
951 }
952
953 struct virtual_network_ops admin_network = {
954 .name = "admin",
955 .init = admin_net_init,
956 .to_server = admin_to_server,
957 .not_disconnectable = TRUE,
958 };
959
960
admin_log(enum log_level level,const struct irc_network * n,const struct irc_client * c,const char * data)961 void admin_log(enum log_level level, const struct irc_network *n, const struct irc_client *c, const char *data)
962 {
963 extern struct global *my_global;
964 struct irc_line *l;
965 char *tmp, *hostmask;
966 GList *gl;
967 static gboolean entered = FALSE;
968
969 if (!my_global || !my_global->config ||
970 !my_global->config->admin_log)
971 return;
972
973 if (level < LOG_INFO)
974 return;
975
976 if (entered)
977 return; /* Prevent inifinite recursion.. */
978
979 entered = TRUE;
980
981 tmp = g_strdup_printf("%s%s%s%s%s%s",
982 data,
983 n?" (":"",
984 n?n->name:"",
985 c?"/":"",
986 c?c->description:"",
987 n?")":"");
988
989 for (gl = my_global->networks; gl; gl = gl->next) {
990 struct irc_network *network = gl->data;
991
992 if (network->connection.data.virtual.ops != &admin_network)
993 continue;
994
995 hostmask = admin_hostmask(network);
996 l = irc_parse_line_args(hostmask, "PRIVMSG", ADMIN_CHANNEL, tmp, NULL);
997 g_free(hostmask);
998
999 virtual_network_recv_line(network, l);
1000
1001 free_line(l);
1002 }
1003
1004 g_free(tmp);
1005
1006 entered = FALSE;
1007 }
1008
cmd_start_listener(admin_handle h,const char * const * args,void * userdata)1009 static void cmd_start_listener(admin_handle h, const char * const *args, void *userdata)
1010 {
1011 char *b, *p;
1012 struct listener_config *cfg;
1013 struct irc_listener *l;
1014
1015 if (!args[1]) {
1016 admin_out(h, "No port specified");
1017 return;
1018 }
1019
1020 if (!args[2]) {
1021 admin_out(h, "No password specified");
1022 return;
1023 }
1024
1025 cfg = g_new0(struct listener_config, 1);
1026
1027 b = g_strdup(args[1]);
1028 if ((p = strchr(b, ':'))) {
1029 *p = '\0';
1030 p++;
1031 cfg->address = b;
1032 cfg->port = g_strdup(p);
1033 } else {
1034 cfg->port = g_strdup(b);
1035 cfg->address = NULL;
1036 }
1037
1038 if (args[3]) {
1039 cfg->network = g_strdup(args[3]);
1040 if (find_network(admin_get_global(h)->networks, args[3]) == NULL) {
1041 admin_out(h, "No such network `%s`", args[3]);
1042 return;
1043 }
1044 }
1045
1046 cfg->password = g_strdup(args[2]);
1047
1048 l = listener_init(admin_get_global(h), cfg);
1049 l->global = admin_get_global(h);
1050
1051 listener_start_tcp(l, l->config->address, l->config->port);
1052 }
1053
cmd_stop_listener(admin_handle h,const char * const * args,void * userdata)1054 static void cmd_stop_listener(admin_handle h, const char * const *args, void *userdata)
1055 {
1056 GList *gl;
1057 char *b, *p;
1058 int i = 0;
1059
1060 if (args[1] == NULL) {
1061 admin_out(h, "No port specified");
1062 return;
1063 }
1064
1065 b = g_strdup(args[1]);
1066 if ((p = strchr(b, ':'))) {
1067 *p = '\0';
1068 p++;
1069 } else {
1070 p = b;
1071 b = NULL;
1072 }
1073
1074 for (gl = admin_get_global(h)->listeners; gl; gl = gl->next) {
1075 struct irc_listener *l = gl->data;
1076
1077 if (b && l->config->address == NULL)
1078 continue;
1079
1080 if (b && l->config->address != NULL && strcmp(b, l->config->address) != 0)
1081 continue;
1082
1083 if (strcmp(p, l->config->port) != 0)
1084 continue;
1085
1086 listener_stop(l);
1087 free_listener(l);
1088 i++;
1089 }
1090
1091 if (b) g_free(b); else g_free(p);
1092
1093 admin_out(h, "%d listeners stopped", i);
1094 }
1095
cmd_list_listener(admin_handle h,const char * const * args,void * userdata)1096 static void cmd_list_listener(admin_handle h, const char * const *args, void *userdata)
1097 {
1098 GList *gl;
1099
1100 for (gl = admin_get_global(h)->listeners; gl; gl = gl->next) {
1101 struct irc_listener *l = gl->data;
1102
1103 admin_out(h, "%s:%s%s%s%s", l->config->address?l->config->address:"", l->config->port,
1104 l->network?" (":"", l->network?l->network->name:"",
1105 l->network?")":"");
1106 }
1107 }
1108
motd_file_set(admin_handle h,const char * name)1109 static gboolean motd_file_set(admin_handle h, const char *name)
1110 {
1111 struct global *g = admin_get_global(h);
1112
1113 if (!g_file_test(name, G_FILE_TEST_EXISTS)) {
1114 admin_out(h, "%s: file does not exist", name);
1115 return FALSE;
1116 }
1117
1118 g_free(g->config->motd_file);
1119 g->config->motd_file = g_strdup(name);
1120
1121 return TRUE;
1122 }
1123
motd_file_get(admin_handle h)1124 static char *motd_file_get(admin_handle h)
1125 {
1126 struct global *g = admin_get_global(h);
1127
1128 return g_strdup(g->config->motd_file);
1129 }
1130
interpret_boolean(admin_handle h,const char * value,gboolean * ret)1131 static gboolean interpret_boolean(admin_handle h, const char *value,
1132 gboolean *ret)
1133 {
1134 if (value == NULL) {
1135 admin_out(h, "Boolean settings can't be unset.");
1136 return FALSE;
1137 }
1138
1139 if (!g_strcasecmp(value, "true")) {
1140 *ret = TRUE;
1141 return TRUE;
1142 } else if (!g_strcasecmp(value, "false")) {
1143 *ret = FALSE;
1144 return TRUE;
1145 }
1146
1147 admin_out(h, "Invalid boolean value `%s'", value);
1148 return FALSE;
1149 }
1150
1151 #define BOOL_SETTING(name) \
1152 static char *name ## _get(admin_handle h) \
1153 { \
1154 struct global *g = admin_get_global(h); \
1155 return g_strdup(g->config->name?"true":"false"); \
1156 } \
1157 static gboolean name ## _set(admin_handle h, const char *value) \
1158 { \
1159 struct global *g = admin_get_global(h); \
1160 return interpret_boolean(h, value, &g->config->name); \
1161 }
1162
1163 BOOL_SETTING(autosave)
BOOL_SETTING(admin_log)1164 BOOL_SETTING(admin_log)
1165 BOOL_SETTING(learn_network_name)
1166
1167 static char *report_time_get(admin_handle h)
1168 {
1169 struct global *g = admin_get_global(h);
1170
1171 switch (g->config->report_time) {
1172 case REPORT_TIME_ALWAYS:
1173 return g_strdup("always");
1174 case REPORT_TIME_NEVER:
1175 return g_strdup("never");
1176 case REPORT_TIME_REPLICATION:
1177 return g_strdup("replication");
1178 }
1179
1180 return g_strdup("UNKNOWN");
1181 }
1182
report_time_set(admin_handle h,const char * value)1183 static gboolean report_time_set(admin_handle h, const char *value)
1184 {
1185 struct global *g = admin_get_global(h);
1186
1187 if (!g_strcasecmp(value, "never") || !g_strcasecmp(value, "false"))
1188 g->config->report_time = REPORT_TIME_NEVER;
1189 else if (!g_strcasecmp(value, "always"))
1190 g->config->report_time = REPORT_TIME_ALWAYS;
1191 else if (!g_strcasecmp(value, "replication") ||
1192 !g_strcasecmp(value, "true"))
1193 g->config->report_time = REPORT_TIME_REPLICATION;
1194 else {
1195 return FALSE;
1196 }
1197
1198 return TRUE;
1199 }
1200
replication_get(admin_handle h)1201 static char *replication_get(admin_handle h)
1202 {
1203 struct global *g = admin_get_global(h);
1204
1205 return g_strdup(g->config->replication);
1206 }
1207
replication_set(admin_handle h,const char * value)1208 static gboolean replication_set(admin_handle h, const char *value)
1209 {
1210 struct global *g = admin_get_global(h);
1211
1212 if (repl_find_backend(value) == NULL) {
1213 admin_out(h, "No such replication backend `%s'", value);
1214 return FALSE;
1215 }
1216
1217 g_free(g->config->replication);
1218 g->config->replication = g_strdup(value);
1219
1220 return TRUE;
1221 }
1222
admin_user_get(admin_handle h)1223 static char *admin_user_get(admin_handle h)
1224 {
1225 struct global *g = admin_get_global(h);
1226
1227 return g_strdup(g->config->admin_user);
1228 }
1229
admin_user_set(admin_handle h,const char * value)1230 static gboolean admin_user_set(admin_handle h, const char *value)
1231 {
1232 struct global *g = admin_get_global(h);
1233
1234 g_free(g->config->admin_user);
1235 if (strlen(value) == 0) {
1236 /* FIXME: Check whether value is a somewhat valid IRC nick? */
1237 g->config->admin_user = g_strdup(value);
1238 } else
1239 g->config->admin_user = NULL;
1240
1241 return TRUE;
1242 }
1243
BOOL_SETTING(learn_nickserv)1244 BOOL_SETTING(learn_nickserv)
1245
1246 static char *max_who_age_get(admin_handle h)
1247 {
1248 struct global *g = admin_get_global(h);
1249
1250 if (g->config->cache.max_who_age == 0)
1251 return NULL;
1252
1253 return g_strdup_printf("%d", g->config->cache.max_who_age);
1254 }
1255
max_who_age_set(admin_handle h,const char * value)1256 static gboolean max_who_age_set(admin_handle h, const char *value)
1257 {
1258 struct global *g = admin_get_global(h);
1259
1260 g->config->cache.max_who_age = (value == NULL?0:atoi(value));
1261
1262 return TRUE;
1263 }
1264
auto_away_time_get(admin_handle h)1265 static char *auto_away_time_get(admin_handle h)
1266 {
1267 struct global *g = admin_get_global(h);
1268
1269 if (g->config->auto_away.max_idle_time == 0)
1270 return NULL;
1271
1272 return g_strdup_printf("%d", g->config->auto_away.max_idle_time);
1273 }
1274
auto_away_time_set(admin_handle h,const char * value)1275 static gboolean auto_away_time_set(admin_handle h, const char *value)
1276 {
1277 struct global *g = admin_get_global(h);
1278
1279 g->config->auto_away.max_idle_time = (value == NULL?-1:atoi(value));
1280
1281 /* FIXME: Restart auto-away */
1282
1283 return TRUE;
1284 }
1285
auto_away_enable_get(admin_handle h)1286 static char *auto_away_enable_get(admin_handle h)
1287 {
1288 struct global *g = admin_get_global(h);
1289
1290 return g_strdup(g->config->auto_away.enabled?"true":"false");
1291 }
1292
auto_away_enable_set(admin_handle h,const char * value)1293 static gboolean auto_away_enable_set(admin_handle h, const char *value)
1294 {
1295 struct global *g = admin_get_global(h);
1296
1297 return interpret_boolean(h, value, &g->config->auto_away.enabled);
1298 }
1299
auto_away_message_get(admin_handle h)1300 static char *auto_away_message_get(admin_handle h)
1301 {
1302 struct global *g = admin_get_global(h);
1303
1304 return g_strdup(g->config->auto_away.message);
1305 }
1306
auto_away_message_set(admin_handle h,const char * value)1307 static gboolean auto_away_message_set(admin_handle h, const char *value)
1308 {
1309 struct global *g = admin_get_global(h);
1310
1311 g_free(g->config->auto_away.message);
1312
1313 g->config->auto_away.message = g_strdup(value);
1314
1315 return TRUE;
1316 }
1317
auto_away_nick_get(admin_handle h)1318 static char *auto_away_nick_get(admin_handle h)
1319 {
1320 struct global *g = admin_get_global(h);
1321
1322 return g_strdup(g->config->auto_away.nick);
1323 }
1324
auto_away_nick_set(admin_handle h,const char * value)1325 static gboolean auto_away_nick_set(admin_handle h, const char *value)
1326 {
1327 struct global *g = admin_get_global(h);
1328
1329 g_free(g->config->auto_away.nick);
1330
1331 g->config->auto_away.nick = g_strdup(value);
1332
1333 return TRUE;
1334 }
1335
auto_away_client_limit_get(admin_handle h)1336 static char *auto_away_client_limit_get(admin_handle h)
1337 {
1338 struct global *g = admin_get_global(h);
1339
1340 return g_strdup_printf("%d", g->config->auto_away.client_limit);
1341 }
1342
auto_away_client_limit_set(admin_handle h,const char * value)1343 static gboolean auto_away_client_limit_set(admin_handle h, const char *value)
1344 {
1345 struct global *g = admin_get_global(h);
1346
1347 g->config->auto_away.client_limit = (value == NULL?-1:atoi(value));
1348
1349 return TRUE;
1350 }
1351
report_time_offset_get(admin_handle h)1352 static char *report_time_offset_get(admin_handle h)
1353 {
1354 struct global *g = admin_get_global(h);
1355
1356 if (g->config->report_time_offset == 0)
1357 return NULL;
1358
1359 return g_strdup_printf("%d", g->config->report_time_offset);
1360 }
1361
report_time_offset_set(admin_handle h,const char * value)1362 static gboolean report_time_offset_set(admin_handle h, const char *value)
1363 {
1364 struct global *g = admin_get_global(h);
1365
1366 g->config->report_time_offset = (value == NULL?0:atoi(value));
1367
1368 return TRUE;
1369 }
1370
port_get(admin_handle h)1371 static char *port_get(admin_handle h)
1372 {
1373 GList *gl;
1374 struct global *g = admin_get_global(h);
1375
1376 for (gl = g->config->listeners; gl; gl = gl->next) {
1377 struct listener_config *l = gl->data;
1378
1379 if (l->is_default)
1380 return g_strdup_printf("%s", l->port);
1381 }
1382
1383 return NULL;
1384 }
1385
port_set(admin_handle h,const char * value)1386 static gboolean port_set(admin_handle h, const char *value)
1387 {
1388 GList *gl;
1389 struct global *g = admin_get_global(h);
1390
1391 /* FIXME: What if there is no default listener ? */
1392 /* FIXME: Check that value is a valid service name or port number */
1393
1394 for (gl = g->config->listeners; gl; gl = gl->next) {
1395 struct listener_config *l = gl->data;
1396
1397 if (l->is_default) {
1398 l->port = g_strdup(value);
1399 return TRUE;
1400 }
1401 }
1402
1403 return FALSE;
1404 }
1405
bind_get(admin_handle h)1406 static char *bind_get(admin_handle h)
1407 {
1408 GList *gl;
1409 struct global *g = admin_get_global(h);
1410
1411 for (gl = g->config->listeners; gl; gl = gl->next) {
1412 struct listener_config *l = gl->data;
1413
1414 if (l->is_default)
1415 return g_strdup(l->address);
1416 }
1417
1418 return NULL;
1419 }
1420
bind_set(admin_handle h,const char * value)1421 static gboolean bind_set(admin_handle h, const char *value)
1422 {
1423 GList *gl;
1424 struct global *g = admin_get_global(h);
1425
1426 /* FIXME: What if there is no default listener ? */
1427 /* FIXME: Check that value is a valid service name or port number */
1428
1429 for (gl = g->config->listeners; gl; gl = gl->next) {
1430 struct listener_config *l = gl->data;
1431
1432 if (l->is_default) {
1433 l->address = g_strdup(value);
1434 return TRUE;
1435 }
1436 }
1437
1438 return FALSE;
1439 }
1440
password_get(admin_handle h)1441 static char *password_get(admin_handle h)
1442 {
1443 GList *gl;
1444 struct global *g = admin_get_global(h);
1445
1446 for (gl = g->config->listeners; gl; gl = gl->next) {
1447 struct listener_config *l = gl->data;
1448
1449 if (l->is_default)
1450 return g_strdup_printf("%s", l->password);
1451 }
1452
1453 return NULL;
1454 }
1455
password_set(admin_handle h,const char * value)1456 static gboolean password_set(admin_handle h, const char *value)
1457 {
1458 GList *gl;
1459 struct global *g = admin_get_global(h);
1460
1461 /* FIXME: What if there is no default listener ? */
1462 /* FIXME: Check that value is a valid service name or port number */
1463
1464 for (gl = g->config->listeners; gl; gl = gl->next) {
1465 struct listener_config *l = gl->data;
1466
1467 if (l->is_default) {
1468 l->password = g_strdup(value);
1469 return TRUE;
1470 }
1471 }
1472
1473 return FALSE;
1474 }
1475
default_network_get(admin_handle h)1476 static char *default_network_get(admin_handle h)
1477 {
1478 GList *gl;
1479 struct global *g = admin_get_global(h);
1480
1481 for (gl = g->config->listeners; gl; gl = gl->next) {
1482 struct listener_config *l = gl->data;
1483
1484 if (l->is_default)
1485 return g_strdup(l->network);
1486 }
1487
1488 return NULL;
1489 }
1490
default_network_set(admin_handle h,const char * value)1491 static gboolean default_network_set(admin_handle h, const char *value)
1492 {
1493 GList *gl;
1494 struct global *g = admin_get_global(h);
1495
1496 /* FIXME: What if there is no default listener ? */
1497 /* FIXME: Check that value is a valid service name or port number */
1498
1499 for (gl = g->config->listeners; gl; gl = gl->next) {
1500 struct listener_config *l = gl->data;
1501
1502 if (l->is_default) {
1503 l->network = g_strdup(value);
1504 return TRUE;
1505 }
1506 }
1507
1508 return FALSE;
1509 }
1510
default_client_charset_get(admin_handle h)1511 static char *default_client_charset_get(admin_handle h)
1512 {
1513 struct global *g = admin_get_global(h);
1514
1515 return g_strdup(g->config->client_charset);
1516 }
1517
default_client_charset_set(admin_handle h,const char * value)1518 static gboolean default_client_charset_set(admin_handle h, const char *value)
1519 {
1520 struct global *g = admin_get_global(h);
1521
1522 g->config->client_charset = g_strdup(value);
1523
1524 return TRUE;
1525 }
1526
default_nick_get(admin_handle h)1527 static char *default_nick_get(admin_handle h)
1528 {
1529 struct global *g = admin_get_global(h);
1530 return g_strdup(g->config->default_nick);
1531 }
1532
default_nick_set(admin_handle h,const char * value)1533 static gboolean default_nick_set(admin_handle h, const char *value)
1534 {
1535 struct global *g = admin_get_global(h);
1536 if (strlen(value) == 0) {
1537 admin_out(h, "Nick name can't be empty.");
1538 return FALSE;
1539 }
1540 if (strchr(value, ' ') != NULL) {
1541 admin_out(h, "Nick name can't contain spaces.");
1542 return FALSE;
1543 }
1544 if (value[0] == ':') {
1545 admin_out(h, "Nick name can't start with semicolon.");
1546 return FALSE;
1547 }
1548
1549 g_free(g->config->default_nick);
1550 g->config->default_nick = g_strdup(value);
1551 return TRUE;
1552 }
1553
1554
default_username_get(admin_handle h)1555 static char *default_username_get(admin_handle h)
1556 {
1557 struct global *g = admin_get_global(h);
1558 return g_strdup(g->config->default_username);
1559 }
1560
default_username_set(admin_handle h,const char * value)1561 static gboolean default_username_set(admin_handle h, const char *value)
1562 {
1563 struct global *g = admin_get_global(h);
1564 if (strlen(value) == 0) {
1565 admin_out(h, "Username can't be empty.");
1566 return FALSE;
1567 }
1568 if (strchr(value, ' ') != NULL) {
1569 admin_out(h, "Username can't contain spaces.");
1570 return FALSE;
1571 }
1572 if (value[0] == ':') {
1573 admin_out(h, "Username can't start with semicolon.");
1574 return FALSE;
1575 }
1576
1577 g_free(g->config->default_username);
1578 g->config->default_username = g_strdup(value);
1579 return TRUE;
1580 }
1581
1582
default_fullname_get(admin_handle h)1583 static char *default_fullname_get(admin_handle h)
1584 {
1585 struct global *g = admin_get_global(h);
1586 return g_strdup(g->config->default_realname);
1587 }
1588
default_fullname_set(admin_handle h,const char * value)1589 static gboolean default_fullname_set(admin_handle h, const char *value)
1590 {
1591 struct global *g = admin_get_global(h);
1592
1593 if (strlen(value) == 0) {
1594 admin_out(h, "Fullname can't be empty");
1595 return FALSE;
1596 }
1597
1598 g_free(g->config->default_realname);
1599 g->config->default_realname = g_strdup(value);
1600 return TRUE;
1601 }
1602
1603 /**
1604 * Table of administration settings that can be
1605 * viewed and changed using the SET command.
1606 */
1607 static struct admin_setting {
1608 const char *name;
1609 char *(*get) (admin_handle h);
1610 gboolean (*set) (admin_handle h, const char *value);
1611 } settings[] = {
1612 { "admin-log", admin_log_get, admin_log_set },
1613 { "admin-user", admin_user_get, admin_user_set },
1614 { "auto-away-enable", auto_away_enable_get, auto_away_enable_set },
1615 { "auto-away-client-limit", auto_away_client_limit_get, auto_away_client_limit_set },
1616 { "auto-away-message", auto_away_message_get, auto_away_message_set },
1617 { "auto-away-nick", auto_away_nick_get, auto_away_nick_set },
1618 { "auto-away-time", auto_away_time_get, auto_away_time_set },
1619 { "autosave", autosave_get, autosave_set },
1620 { "bind", bind_get, bind_set },
1621 { "default-client-charset", default_client_charset_get, default_client_charset_set },
1622 { "default-network", default_network_get, default_network_set },
1623 { "learn-network-name", learn_network_name_get, learn_network_name_set },
1624 { "learn-nickserv", learn_nickserv_get, learn_nickserv_set },
1625 { "linestack", linestack_get, linestack_set },
1626 { "log_level", log_level_get, log_level_set },
1627 { "logging", logging_get, logging_set },
1628 { "max_who_age", max_who_age_get, max_who_age_set },
1629 { "motd-file", motd_file_get, motd_file_set },
1630 { "password", password_get, password_set },
1631 { "port", port_get, port_set },
1632 { "report-time", report_time_get, report_time_set },
1633 { "report-time-offset", report_time_offset_get, report_time_offset_set },
1634 { "replication", replication_get, replication_set },
1635 { "default-nick", default_nick_get, default_nick_set },
1636 { "default-username", default_username_get, default_username_set },
1637 { "default-fullname", default_fullname_get, default_fullname_set },
1638 { NULL, NULL, NULL }
1639 };
1640
cmd_unset(admin_handle h,const char * const * args,void * userdata)1641 static void cmd_unset(admin_handle h, const char * const *args, void *userdata)
1642 {
1643 int i;
1644 if (args[1] == NULL) {
1645 admin_out(h, "Usage: unset <setting>");
1646 return;
1647 }
1648
1649 for (i = 0; settings[i].name; i++) {
1650 if (!strcasecmp(settings[i].name, args[1])) {
1651 settings[i].set(h, NULL);
1652 }
1653 }
1654 }
1655
cmd_set(admin_handle h,const char * const * args,void * userdata)1656 static void cmd_set(admin_handle h, const char * const *args, void *userdata)
1657 {
1658 int i;
1659 char *tmp;
1660
1661 if (args[1] == NULL) {
1662 for (i = 0; settings[i].name != NULL; i++) {
1663 tmp = settings[i].get(h);
1664 if (tmp == NULL)
1665 admin_out(h, "%s is not set", settings[i]);
1666 else
1667 admin_out(h, "%s = %s", settings[i].name, tmp);
1668 g_free(tmp);
1669 }
1670 } else {
1671 for (i = 0; settings[i].name; i++) {
1672 if (!strcasecmp(settings[i].name, args[1])) {
1673 if (args[2] != NULL) {
1674 settings[i].set(h, args[2]);
1675 } else {
1676 tmp = settings[i].get(h);
1677 admin_out(h, "%s", tmp);
1678 g_free(tmp);
1679 }
1680 return;
1681 }
1682 }
1683
1684 admin_out(h, "Unknown setting `%s'", args[1]);
1685 }
1686 }
1687
1688 const static struct admin_command builtin_commands[] = {
1689 /* Provided for backwards compatibility */
1690 { "ADDNETWORK", cmd_add_network },
1691 { "DELNETWORK", cmd_del_network },
1692 { "LISTNETWORKS", cmd_list_networks },
1693
1694 /* Commands */
1695 { "SET", cmd_set },
1696 { "UNSET", cmd_unset },
1697
1698 { "ADDSERVER", cmd_add_server },
1699 { "BACKLOG", cmd_backlog},
1700 { "CONNECT", cmd_connect_network },
1701 { "DISCONNECT", cmd_disconnect_network },
1702 { "ECHO", cmd_echo },
1703 { "NEXTSERVER", cmd_next_server },
1704 { "CHARSET", cmd_charset },
1705 { "DIE", cmd_die },
1706 { "NETWORK", cmd_network },
1707 { "SAVECONFIG", cmd_save_config },
1708 { "DETACH", cmd_detach },
1709 { "HELP", cmd_help },
1710 { "DUMPJOINEDCHANNELS", dump_joined_channels },
1711 { "STARTLISTENER", cmd_start_listener },
1712 { "STOPLISTENER", cmd_stop_listener },
1713 { "LISTLISTENER", cmd_list_listener },
1714 #ifdef DEBUG
1715 { "ABORT", cmd_abort },
1716 #endif
1717 { NULL }
1718 };
1719
init_admin(void)1720 void init_admin(void)
1721 {
1722 int i;
1723 for(i = 0; builtin_commands[i].name; i++) {
1724 register_admin_command(&builtin_commands[i]);
1725 }
1726
1727 register_virtual_network(&admin_network);
1728
1729 markers = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)linestack_free_marker);
1730 }
1731
iochannel_admin_out(admin_handle h,const char * data)1732 static void iochannel_admin_out(admin_handle h, const char *data)
1733 {
1734 gsize bytes_written;
1735 GError *error = NULL;
1736 GIOStatus status;
1737
1738 status = g_io_channel_write_chars(h->user_data, data, -1, &bytes_written, &error);
1739
1740 status = g_io_channel_write_chars(h->user_data, "\n", -1, &bytes_written, &error);
1741
1742 status = g_io_channel_flush(h->user_data, &error);
1743 }
1744
handle_client_data(GIOChannel * channel,GIOCondition condition,void * _global)1745 static gboolean handle_client_data(GIOChannel *channel,
1746 GIOCondition condition, void *_global)
1747 {
1748 char *raw;
1749 GError *error = NULL;
1750 GIOStatus status;
1751 struct admin_handle ah;
1752 gsize eol;
1753
1754 ah.global = _global;
1755 ah.client = NULL;
1756 ah.user_data = channel;
1757 ah.send_fn = iochannel_admin_out;
1758
1759 if (condition & G_IO_IN) {
1760 status = g_io_channel_read_line(channel, &raw, NULL, &eol, &error);
1761 if (status == G_IO_STATUS_NORMAL) {
1762 raw[eol] = '\0';
1763 process_cmd(&ah, raw);
1764 g_free(raw);
1765 }
1766 }
1767
1768 if (condition & G_IO_HUP) {
1769 return FALSE;
1770 }
1771
1772 if (condition & G_IO_ERR) {
1773 log_global(LOG_WARNING, "Error from admin client");
1774 return FALSE;
1775 }
1776
1777 return TRUE;
1778 }
1779
handle_new_client(GIOChannel * c_server,GIOCondition condition,void * _global)1780 static gboolean handle_new_client(GIOChannel *c_server,
1781 GIOCondition condition, void *_global)
1782 {
1783 GIOChannel *c;
1784 int sock = accept(g_io_channel_unix_get_fd(c_server), NULL, 0);
1785
1786 if (sock < 0) {
1787 log_global(LOG_WARNING, "Error accepting new connection: %s", strerror(errno));
1788 return TRUE;
1789 }
1790
1791 c = g_io_channel_unix_new(sock);
1792
1793 g_io_channel_set_close_on_unref(c, TRUE);
1794 g_io_channel_set_encoding(c, NULL, NULL);
1795 g_io_channel_set_flags(c, G_IO_FLAG_NONBLOCK, NULL);
1796
1797 g_io_add_watch(c, G_IO_IN | G_IO_ERR | G_IO_HUP, handle_client_data, _global);
1798
1799 g_io_channel_unref(c);
1800
1801 return TRUE;
1802 }
1803
start_admin_socket(struct global * global)1804 gboolean start_admin_socket(struct global *global)
1805 {
1806 int sock;
1807 struct sockaddr_un un;
1808
1809 sock = socket(PF_UNIX, SOCK_STREAM, 0);
1810 if (sock < 0) {
1811 log_global(LOG_ERROR, "error creating unix socket: %s", strerror(errno));
1812 return FALSE;
1813 }
1814
1815 un.sun_family = AF_UNIX;
1816 strncpy(un.sun_path, global->config->admin_socket, sizeof(un.sun_path));
1817 unlink(un.sun_path);
1818
1819 if (bind(sock, (struct sockaddr *)&un, sizeof(un)) < 0) {
1820 log_global(LOG_ERROR, "unable to bind to %s: %s", un.sun_path, strerror(errno));
1821 return FALSE;
1822 }
1823
1824 if (listen(sock, 5) < 0) {
1825 log_global(LOG_ERROR, "error listening on socket: %s", strerror(errno));
1826 return FALSE;
1827 }
1828
1829 global->admin_incoming = g_io_channel_unix_new(sock);
1830
1831 if (!global->admin_incoming) {
1832 log_global(LOG_ERROR, "Unable to create GIOChannel for unix server socket");
1833 return FALSE;
1834 }
1835
1836 g_io_channel_set_close_on_unref(global->admin_incoming, TRUE);
1837
1838 g_io_channel_set_encoding(global->admin_incoming, NULL, NULL);
1839 global->admin_incoming_id = g_io_add_watch(global->admin_incoming, G_IO_IN, handle_new_client, global);
1840 g_io_channel_unref(global->admin_incoming);
1841
1842 return TRUE;
1843 }
1844
stop_admin_socket(struct global * global)1845 gboolean stop_admin_socket(struct global *global)
1846 {
1847 if (global->admin_incoming_id > 0)
1848 g_source_remove(global->admin_incoming_id);
1849 unlink(global->config->admin_socket);
1850 return TRUE;
1851 }
1852