comparison irccdctl/irccdctl.c @ 1037:8f8ce47aba8a

make: switch to GNU make
author David Demelier <markand@malikania.fr>
date Tue, 27 Apr 2021 09:22:16 +0200
parents
children c857bc879669
comparison
equal deleted inserted replaced
1036:bafb5943cd35 1037:8f8ce47aba8a
1 /*
2 * main.c -- irccdctl(1) main file
3 *
4 * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/socket.h>
20 #include <sys/time.h>
21 #include <sys/un.h>
22 #include <assert.h>
23 #include <ctype.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdnoreturn.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <ketopt.h>
35
36 #include <irccd/limits.h>
37 #include <irccd/util.h>
38
39 static int verbose;
40 static int sock;
41 static struct sockaddr_un sockaddr = {
42 .sun_family = PF_LOCAL,
43 .sun_path = "/tmp/irccd.sock"
44 };
45 static char in[IRC_BUF_LEN];
46 static char out[IRC_BUF_LEN];
47
48 static char *
49 poll(void)
50 {
51 static char ret[IRC_BUF_LEN];
52 char *nl;
53
54 while (!(nl = strstr(in, "\n"))) {
55 char buf[IRC_BUF_LEN] = {0};
56 ssize_t nr;
57
58 if ((nr = recv(sock, buf, sizeof (buf) - 1, 0)) <= 0)
59 errc(1, nr == 0 ? ECONNRESET : errno, "abort");
60 if (strlcat(in, buf, sizeof (in)) >= sizeof (in))
61 errc(1, EMSGSIZE, "abort");
62 }
63
64 *nl = '\0';
65 strlcpy(ret, in, sizeof (ret));
66 memmove(in, nl + 1, sizeof (in) - (nl - in) - 1);
67
68 return ret;
69 }
70
71 static void
72 dial(void)
73 {
74 const struct timeval tv = {
75 .tv_sec = 30
76 };
77
78 if ((sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
79 err(1, "socket");
80 if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) < 0 ||
81 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0)
82 err(1, "setsockopt");
83 if (connect(sock, (const struct sockaddr *)&sockaddr, SUN_LEN(&sockaddr)) < 0)
84 err(1, "connect");
85 }
86
87 static void
88 check(void)
89 {
90 /* Ensure we're talking to irccd. */
91 int major, minor, patch;
92
93 if ((sscanf(poll(), "IRCCD %d.%d.%d", &major, &minor, &patch) != 3))
94 errx(1, "abort: not irccd instance");
95 if (verbose)
96 printf("connected to irccd %d.%d.%d\n", major, minor, patch);
97 }
98
99 static void
100 req(const char *fmt, ...)
101 {
102 char buf[IRC_BUF_LEN];
103 va_list ap;
104
105 va_start(ap, fmt);
106 vsnprintf(buf, sizeof (buf), fmt, ap);
107 va_end(ap);
108
109 if (strlcat(out, buf, sizeof (out)) >= sizeof (out) ||
110 strlcat(out, "\n", sizeof (out)) >= sizeof (out))
111 errc(1, EMSGSIZE, "abort");
112
113 while (out[0]) {
114 ssize_t ns, len;
115
116 len = strlen(out);
117
118 if ((ns = send(sock, out, len, MSG_NOSIGNAL)) <= 0)
119 err(1, "send");
120
121 if (ns >= len)
122 memset(out, 0, sizeof (out));
123 else
124 memmove(out, out + ns, sizeof (out) - ns);
125 }
126 }
127
128 static char *
129 ok(void)
130 {
131 char *response = poll();
132
133 if (strncmp(response, "OK", 2) != 0)
134 errx(1, "abort: %s", response);
135
136 /* Skip "OK". */
137 response += 2;
138
139 while (*response && isspace(*response))
140 response++;
141
142 return response;
143 }
144
145 static void
146 show_connect(char *line)
147 {
148 const char *args[2] = {0};
149
150 if (irc_util_split(line, args, 2, ' ') == 2) {
151 printf("%-16s: %s\n", "event", "onConnect");
152 printf("%-16s: %s\n", "server", args[0]);
153 }
154 }
155
156 static void
157 show_disconnect(char *line)
158 {
159 const char *args[2] = {0};
160
161 if (irc_util_split(line, args, 2, ' ') == 2) {
162 printf("%-16s: %s\n", "event", "onDisonnect");
163 printf("%-16s: %s\n", "server", args[0]);
164 }
165 }
166
167 static void
168 show_invite(char *line)
169 {
170 const char *args[5] = {0};
171
172 if (irc_util_split(line, args, 5, ' ') == 5) {
173 printf("%-16s: %s\n", "event", "onInvite");
174 printf("%-16s: %s\n", "server", args[1]);
175 printf("%-16s: %s\n", "origin", args[2]);
176 printf("%-16s: %s\n", "channel", args[3]);
177 printf("%-16s: %s\n", "nickname", args[4]);
178 }
179 }
180
181 static void
182 show_join(char *line)
183 {
184 const char *args[4] = {0};
185
186 if (irc_util_split(line, args, 4, ' ') == 4) {
187 printf("%-16s: %s\n", "event", "onJoin");
188 printf("%-16s: %s\n", "server", args[1]);
189 printf("%-16s: %s\n", "origin", args[2]);
190 printf("%-16s: %s\n", "channel", args[3]);
191 }
192 }
193
194 static void
195 show_kick(char *line)
196 {
197 const char *args[6] = {0};
198
199 if (irc_util_split(line, args, 6, ' ') >= 5) {
200 printf("%-16s: %s\n", "event", "onKick");
201 printf("%-16s: %s\n", "server", args[1]);
202 printf("%-16s: %s\n", "origin", args[2]);
203 printf("%-16s: %s\n", "channel", args[3]);
204 printf("%-16s: %s\n", "target", args[4]);
205 printf("%-16s: %s\n", "reason", args[5] ? args[5] : "");
206 }
207 }
208
209 static void
210 show_me(char *line)
211 {
212 const char *args[5] = {0};
213
214 if (irc_util_split(line, args, 5, ' ') == 5) {
215 printf("%-16s: %s\n", "event", "onMe");
216 printf("%-16s: %s\n", "server", args[1]);
217 printf("%-16s: %s\n", "origin", args[2]);
218 printf("%-16s: %s\n", "channel", args[3]);
219 printf("%-16s: %s\n", "message", args[4]);
220 }
221 }
222
223 static void
224 show_message(char *line)
225 {
226 const char *args[5] = {0};
227
228 if (irc_util_split(line, args, 5, ' ') == 5) {
229 printf("%-16s: %s\n", "event", "onMessage");
230 printf("%-16s: %s\n", "server", args[1]);
231 printf("%-16s: %s\n", "origin", args[2]);
232 printf("%-16s: %s\n", "channel", args[3]);
233 printf("%-16s: %s\n", "message", args[4]);
234 }
235 }
236
237 static void
238 show_mode(char *line)
239 {
240 const char *args[8] = {0};
241
242 if (irc_util_split(line, args, 8, ' ') >= 5) {
243 printf("%-16s: %s\n", "event", "onMode");
244 printf("%-16s: %s\n", "server", args[1]);
245 printf("%-16s: %s\n", "origin", args[2]);
246 printf("%-16s: %s\n", "channel", args[3]);
247 printf("%-16s: %s\n", "mode", args[4]);
248 printf("%-16s: %s\n", "limit", (args[5] ? args[5] : ""));
249 printf("%-16s: %s\n", "user", (args[6] ? args[6] : ""));
250 printf("%-16s: %s\n", "mask", (args[7] ? args[7] : ""));
251 }
252 }
253
254 static void
255 show_nick(char *line)
256 {
257 const char *args[4] = {0};
258
259 if (irc_util_split(line, args, 4, ' ') == 4) {
260 printf("%-16s: %s\n", "event", "onNick");
261 printf("%-16s: %s\n", "server", args[1]);
262 printf("%-16s: %s\n", "origin", args[2]);
263 printf("%-16s: %s\n", "nickname", args[3]);
264 }
265 }
266
267 static void
268 show_notice(char *line)
269 {
270 const char *args[5] = {0};
271
272 if (irc_util_split(line, args, 5, ' ') == 5) {
273 printf("%-16s: %s\n", "event", "onNotice");
274 printf("%-16s: %s\n", "server", args[1]);
275 printf("%-16s: %s\n", "origin", args[2]);
276 printf("%-16s: %s\n", "channel", args[3]);
277 printf("%-16s: %s\n", "message", args[4]);
278 }
279 }
280
281 static void
282 show_part(char *line)
283 {
284 const char *args[5] = {0};
285
286 if (irc_util_split(line, args, 5, ' ') >= 4) {
287 printf("%-16s: %s\n", "event", "onPart");
288 printf("%-16s: %s\n", "server", args[1]);
289 printf("%-16s: %s\n", "origin", args[2]);
290 printf("%-16s: %s\n", "channel", args[3]);
291 printf("%-16s: %s\n", "reason", (args[4] ? args[4] : ""));
292 }
293 }
294
295 static void
296 show_topic(char *line)
297 {
298 const char *args[5] = {0};
299
300 if (irc_util_split(line, args, 5, ' ') >= 4) {
301 printf("%-16s: %s\n", "event", "onTopic");
302 printf("%-16s: %s\n", "server", args[1]);
303 printf("%-16s: %s\n", "origin", args[2]);
304 printf("%-16s: %s\n", "channel", args[3]);
305 printf("%-16s: %s\n", "topic", args[4]);
306 }
307 }
308
309 static void
310 show_whois(char *line)
311 {
312 const char *args[6] = {0};
313 //char *p, *token;
314
315 if (irc_util_split(line, args, 6, ' ') >= 4) {
316 printf("%-16s: %s\n", "event", "onWhois");
317 printf("%-16s: %s\n", "server", args[1]);
318 printf("%-16s: %s\n", "nickname", args[2]);
319 printf("%-16s: %s\n", "username", args[3]);
320 printf("%-16s: %s\n", "hostname", args[4]);
321 printf("%-16s: %s\n", "username", args[5]);
322 //printf("channels: %s\n", args[6]);
323 }
324 }
325
326 static const struct {
327 const char *event;
328 void (*show)(char *);
329 } watchtable[] = {
330 { "EVENT-CONNECT", show_connect },
331 { "EVENT-DISCONNECT", show_disconnect },
332 { "EVENT-INVITE", show_invite },
333 { "EVENT-JOIN", show_join },
334 { "EVENT-KICK", show_kick },
335 { "EVENT-MESSAGE", show_message },
336 { "EVENT-ME", show_me },
337 { "EVENT-MODE", show_mode },
338 { "EVENT-NICK", show_nick },
339 { "EVENT-NOTICE", show_notice },
340 { "EVENT-PART", show_part },
341 { "EVENT-TOPIC", show_topic },
342 { "EVENT-WHOIS", show_whois }
343 };
344
345 static void
346 show(char *ev)
347 {
348 for (size_t i = 0; i < IRC_UTIL_SIZE(watchtable); ++i) {
349 if (strncmp(watchtable[i].event, ev, strlen(watchtable[i].event)) == 0) {
350 watchtable[i].show(ev);
351 printf("\n");
352 break;
353 }
354 }
355 }
356
357 static void
358 plugin_list_set(int argc, char **argv, const char *cmd)
359 {
360 char *line, *p;
361 size_t num = 0;
362
363 if (argc == 3) {
364 req("%s %s %s %s", cmd, argv[0], argv[1], argv[2]);
365 ok();
366 return;
367 }
368
369 if (argc == 2)
370 req("%s %s %s", cmd, argv[0], argv[1]);
371 else
372 req("%s %s", cmd, argv[0]);
373
374 if (sscanf(line = ok(), "%zu", &num) != 1)
375 errx(1, "could not retrieve list");
376
377 if (argc == 2)
378 puts(poll());
379 else {
380 while (num-- != 0 && (line = poll())) {
381 if (!(p = strchr(line, '=')))
382 continue;
383
384 *p = '\0';
385 printf("%-16s: %s\n", line, p + 1);
386 }
387 }
388 }
389
390 static void
391 response_list(const char *cmd)
392 {
393 char *list;
394
395 req(cmd);
396
397 if (strncmp(list = poll(), "OK ", 3) != 0)
398 errx(1, "failed to retrieve plugin list");
399
400 list += 3;
401
402 for (char *p; (p = strchr(list, ' ')); )
403 *p = '\n';
404
405 if (*list)
406 puts(list);
407 }
408
409 static void
410 cmd_hook_add(int argc, char **argv)
411 {
412 (void)argc;
413
414 req("HOOK-ADD %s %s", argv[0], argv[1]);
415 ok();
416 }
417
418 static void
419 cmd_hook_list(int argc, char **argv)
420 {
421 (void)argc;
422 (void)argv;
423
424 response_list("HOOK-LIST");
425 }
426
427 static void
428 cmd_hook_remove(int argc, char **argv)
429 {
430 (void)argc;
431
432 req("HOOK-REMOVE %s", argv[0]);
433 ok();
434 }
435
436 static void
437 cmd_plugin_config(int argc, char **argv)
438 {
439 return plugin_list_set(argc, argv, "PLUGIN-CONFIG");
440 }
441
442 /*
443 * Response:
444 *
445 * OK name
446 * summary
447 * version
448 * license
449 * author
450 */
451 static void
452 cmd_plugin_info(int argc, char **argv)
453 {
454 (void)argc;
455
456 const char *response;
457
458 req("PLUGIN-INFO %s", argv[0]);
459
460 if (strncmp((response = poll()), "OK ", 3) != 0)
461 errx(1, "failed to retrieve plugin information");
462
463 printf("%-16s: %s\n", "name", response + 3);
464 printf("%-16s: %s\n", "summary", poll());
465 printf("%-16s: %s\n", "version", poll());
466 printf("%-16s: %s\n", "license", poll());
467 printf("%-16s: %s\n", "author", poll());
468 }
469
470 static void
471 cmd_plugin_list(int argc, char **argv)
472 {
473 (void)argc;
474 (void)argv;
475
476 response_list("PLUGIN-LIST");
477 }
478
479 static void
480 cmd_plugin_load(int argc, char **argv)
481 {
482 (void)argc;
483
484 req("PLUGIN-LOAD %s", argv[0]);
485 ok();
486 }
487
488 static void
489 cmd_plugin_path(int argc, char **argv)
490 {
491 return plugin_list_set(argc, argv, "PLUGIN-PATH");
492 }
493
494 static void
495 cmd_plugin_reload(int argc, char **argv)
496 {
497 if (argc == 1)
498 req("PLUGIN-RELOAD %s", argv[0]);
499 else
500 req("PLUGIN-RELOAD");
501
502 ok();
503 }
504
505 static void
506 cmd_plugin_template(int argc, char **argv)
507 {
508 return plugin_list_set(argc, argv, "PLUGIN-TEMPLATE");
509 }
510
511 static void
512 cmd_plugin_unload(int argc, char **argv)
513 {
514 if (argc == 1)
515 req("PLUGIN-UNLOAD %s", argv[0]);
516 else
517 req("PLUGIN-UNLOAD");
518
519 ok();
520 }
521
522 static void
523 cmd_rule_add(int argc, char **argv)
524 {
525 ketopt_t ko = KETOPT_INIT;
526 FILE *fp;
527 char out[IRC_BUF_LEN];
528
529 if (!(fp = fmemopen(out, sizeof (out) - 1, "w")))
530 err(1, "fmemopen");
531
532 /* TODO: invalid option. */
533 for (int ch; (ch = ketopt(&ko, argc, argv, 0, "c:e:i:o:p:s:", NULL)) != -1; )
534 fprintf(fp, "%c=%s ", ch, ko.arg);
535
536 argc -= ko.ind;
537 argv += ko.ind;
538
539 if (argc < 1)
540 errx(1, "missing accept or drop rule action");
541
542 fprintf(fp, "%s", argv[0]);
543
544 if (ferror(fp) || feof(fp))
545 err(1, "fprintf");
546
547 fclose(fp);
548 req("RULE-ADD %s %s", argv[0], out);
549 ok();
550 }
551
552 static void
553 cmd_rule_edit(int argc, char **argv)
554 {
555 ketopt_t ko = KETOPT_INIT;
556 FILE *fp;
557 char out[IRC_BUF_LEN];
558
559 if (!(fp = fmemopen(out, sizeof (out) - 1, "w")))
560 err(1, "fmemopen");
561
562 /* TODO: invalid option. */
563 for (int ch; (ch = ketopt(&ko, argc, argv, 0, "a:C:c:E:e:O:o:P:p:S:s:", NULL)) != -1; ) {
564 if (ch == 'a')
565 fprintf(fp, "a=%s ", ko.arg);
566 else
567 fprintf(fp, "%c%c%s ", tolower(ch), isupper(ch) ? '-' : '+', ko.arg);
568 }
569
570 argc -= ko.ind;
571 argv += ko.ind;
572
573 if (argc < 1)
574 errx(1, "missing rule index");
575
576 if (ferror(fp) || feof(fp))
577 err(1, "fprintf");
578
579 fclose(fp);
580 req("RULE-EDIT %s %s", argv[0], out);
581 ok();
582 }
583
584 /*
585 * Response:
586 *
587 * OK <n>
588 * accept
589 * server1 server2 server3 ...
590 * channel1 channel2 channel3 ...
591 * origin1 origin2 origin3 ...
592 * plugin1 plugin2 plugin3 ...
593 * event1 event2 plugin3 ...
594 * (repeat for every rule in <n>)
595 */
596 static void
597 cmd_rule_list(int argc, char **argv)
598 {
599 (void)argc;
600 (void)argv;
601
602 size_t num = 0;
603
604 req("RULE-LIST");
605
606 if (sscanf(ok(), "%zu", &num) != 1)
607 errx(1, "could not retrieve rule list");
608
609 for (size_t i = 0; i < num; ++i) {
610 printf("%-16s: %zu\n", "index", i);
611 printf("%-16s: %s\n", "action", poll());
612 printf("%-16s: %s\n", "servers", poll());
613 printf("%-16s: %s\n", "channels", poll());
614 printf("%-16s: %s\n", "origins", poll());
615 printf("%-16s: %s\n", "plugins", poll());
616 printf("%-16s: %s\n", "events", poll());
617 printf("\n");
618 }
619 }
620
621 static void
622 cmd_rule_move(int argc, char **argv)
623 {
624 (void)argc;
625
626 long long from, to;
627 const char *errstr;
628
629 if ((from = strtonum(argv[0], 0, LLONG_MAX, &errstr)) == 0 && errstr)
630 err(1, "%s", argv[0]);
631 if ((to = strtonum(argv[1], 0, LLONG_MAX, &errstr)) == 0 && errstr)
632 err(1, "%s", argv[1]);
633
634 req("RULE-MOVE %lld %lld", from, to);
635 ok();
636 }
637
638 static void
639 cmd_rule_remove(int argc, char **argv)
640 {
641 (void)argc;
642
643 req("RULE-REMOVE %s", argv[0]);
644 ok();
645 }
646
647 static void
648 cmd_server_connect(int argc, char **argv)
649 {
650 ketopt_t ko = KETOPT_INIT;
651 int ssl = 0;
652 const char *nickname = "irccd",
653 *username = "irccd",
654 *realname = "IRC Client Daemon",
655 *port = "6667";
656
657 for (int ch; (ch = ketopt(&ko, argc, argv, 0, "sn:r:u:p:", NULL)) != -1; ) {
658 switch (ch) {
659 case 's':
660 ssl = 1;
661 break;
662 case 'n':
663 nickname = ko.arg;
664 break;
665 case 'r':
666 realname = ko.arg;
667 break;
668 case 'u':
669 username = ko.arg;
670 break;
671 case 'p':
672 port = ko.arg;
673 break;
674 default:
675 break;
676 }
677 }
678
679 argc -= ko.ind;
680 argv += ko.ind;
681
682 if (argc < 2)
683 errx(1, "missing id and/or host");
684
685 req("SERVER-CONNECT %s %s %s%s %s %s %s", argv[0], argv[1], (ssl ? "+" : ""),
686 port, nickname, username, realname);
687 ok();
688 }
689
690 static void
691 cmd_server_disconnect(int argc, char **argv)
692 {
693 if (argc == 1)
694 req("SERVER-DISCONNECT %s", argv[0]);
695 else
696 req("SERVER-DISCONNECT");
697
698 ok();
699 }
700
701 /*
702 * Response:
703 *
704 * OK name
705 * hostname port [ssl]
706 * nickname username realname
707 * chan1 chan2 chanN
708 */
709 static void
710 cmd_server_info(int argc, char **argv)
711 {
712 (void)argc;
713
714 char *list;
715 const char *args[16] = {0};
716
717 req("SERVER-INFO %s", argv[0]);
718
719 if (strncmp(list = poll(), "OK ", 3) != 0)
720 errx(1, "failed to retrieve server information");
721
722 printf("%-16s: %s\n", "name", list + 3);
723
724 if (irc_util_split((list = poll()), args, 3, ' ') < 2)
725 errx(1, "malformed server connection");
726
727 printf("%-16s: %s\n", "hostname", args[0]);
728 printf("%-16s: %s\n", "port", args[1]);
729
730 if (args[2])
731 printf("%-16s: %s\n", "ssl", "true");
732
733 if (irc_util_split((list = poll()), args, 3, ' ') != 3)
734 errx(1, "malformed server ident");
735
736 printf("%-16s: %s\n", "nickname", args[0]);
737 printf("%-16s: %s\n", "username", args[0]);
738 printf("%-16s: %s\n", "realname", args[0]);
739 printf("%-16s: %s\n", "channels", poll());
740 }
741
742 static void
743 cmd_server_join(int argc, char **argv)
744 {
745 if (argc >= 3)
746 req("SERVER-JOIN %s %s %s", argv[0], argv[1], argv[2]);
747 else
748 req("SERVER-JOIN %s %s", argv[0], argv[1]);
749
750 ok();
751 }
752
753 static void
754 cmd_server_list(int argc, char **argv)
755 {
756 (void)argc;
757 (void)argv;
758
759 response_list("SERVER-LIST");
760 }
761
762 static void
763 cmd_server_message(int argc, char **argv)
764 {
765 (void)argc;
766
767 req("SERVER-MESSAGE %s %s %s", argv[0], argv[1], argv[2]);
768 ok();
769 }
770
771 static void
772 cmd_server_me(int argc, char **argv)
773 {
774 (void)argc;
775
776 req("SERVER-ME %s %s %s", argv[0], argv[1], argv[2]);
777 ok();
778 }
779
780 static void
781 cmd_server_mode(int argc, char **argv)
782 {
783 req("SERVER-MODE %s %s %s%c%s", argv[0], argv[1], argv[2],
784 argc >= 4 ? ' ' : '\0',
785 argc >= 4 ? argv[3] : "");
786 ok();
787 }
788
789 static void
790 cmd_server_nick(int argc, char **argv)
791 {
792 (void)argc;
793
794 req("SERVER-NICK %s %s", argv[0], argv[1]);
795 ok();
796 }
797
798 static void
799 cmd_server_notice(int argc, char **argv)
800 {
801 (void)argc;
802
803 req("SERVER-NOTICE %s %s %s", argv[0], argv[1], argv[2]);
804 ok();
805 }
806
807 static void
808 cmd_server_part(int argc, char **argv)
809 {
810 (void)argc;
811
812 /* Let's advertise irccd a bit. */
813 req("SERVER-PART %s %s %s", argv[0], argv[1],
814 argc >= 3 ? argv[2] : "irccd is shutting down");
815 ok();
816 }
817
818 static void
819 cmd_server_topic(int argc, char **argv)
820 {
821 (void)argc;
822
823 req("SERVER-TOPIC %s %s %s", argv[0], argv[1], argv[2]);
824 ok();
825 }
826
827 static void
828 cmd_watch(int argc, char **argv)
829 {
830 (void)argc;
831 (void)argv;
832
833 struct timeval tv = {0};
834 char *ev;
835
836 /* Enable watch. */
837 req("WATCH");
838 ok();
839
840 /* Turn off timeout to receive indefinitely. */
841 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) < 0)
842 err(1, "setsockopt");
843
844 while ((ev = poll()))
845 show(ev);
846 }
847
848 static const struct cmd {
849 const char *name;
850 int minargs;
851 int maxargs;
852 void (*exec)(int, char **);
853 } cmds[] = {
854 /* name min max exec */
855 { "hook-add", 2, 2, cmd_hook_add },
856 { "hook-list", 0, 0, cmd_hook_list },
857 { "hook-remove", 1, 1, cmd_hook_remove },
858 { "plugin-config", 1, 3, cmd_plugin_config },
859 { "plugin-info", 1, 1, cmd_plugin_info },
860 { "plugin-list", 0, 0, cmd_plugin_list },
861 { "plugin-load", 1, 1, cmd_plugin_load },
862 { "plugin-path", 0, 3, cmd_plugin_path },
863 { "plugin-reload", 0, 1, cmd_plugin_reload },
864 { "plugin-template", 1, 3, cmd_plugin_template },
865 { "plugin-unload", 0, 1, cmd_plugin_unload },
866 { "rule-add", -1, -1, cmd_rule_add },
867 { "rule-edit", -1, -1, cmd_rule_edit },
868 { "rule-list", 0, 0, cmd_rule_list },
869 { "rule-move", 2, 2, cmd_rule_move },
870 { "rule-remove", 1, 1, cmd_rule_remove },
871 { "server-connect", -1, -1, cmd_server_connect },
872 { "server-disconnect", 0, 1, cmd_server_disconnect },
873 { "server-info", 1, 1, cmd_server_info },
874 { "server-join", 2, 3, cmd_server_join },
875 { "server-list", 0, 0, cmd_server_list },
876 { "server-me", 3, 3, cmd_server_me },
877 { "server-message", 3, 3, cmd_server_message },
878 { "server-mode", 3, 4, cmd_server_mode },
879 { "server-nick", 2, 2, cmd_server_nick },
880 { "server-notice", 3, 3, cmd_server_notice },
881 { "server-part", 3, 3, cmd_server_part },
882 { "server-topic", 3, 3, cmd_server_topic },
883 { "watch", 0, 0, cmd_watch }
884 };
885
886 static int
887 cmp_cmd(const void *d1, const void *d2)
888 {
889 return strcmp(d1, ((const struct cmd *)d2)->name);
890 }
891
892 static const struct cmd *
893 find_cmd(const char *name)
894 {
895 return bsearch(name, cmds, IRC_UTIL_SIZE(cmds), sizeof (cmds[0]), cmp_cmd);
896 }
897
898 static void
899 run(int argc, char **argv)
900 {
901 const struct cmd *c;
902
903 if (!(c = find_cmd(argv[0])))
904 errx(1, "abort: command not found");
905
906 --argc;
907 ++argv;
908
909 if ((c->minargs != -1 && argc < c->minargs) || (c->minargs != -1 && argc > c->maxargs))
910 errx(1, "abort: invalid number of arguments");
911
912 c->exec(argc, argv);
913 }
914
915 noreturn static void
916 usage(void)
917 {
918 fprintf(stderr, "usage: %s [-v] [-s sock] command [options...] [arguments...]\n", getprogname());
919 exit(1);
920 }
921
922 noreturn static void
923 help(void)
924 {
925 fprintf(stderr, "usage: %s hook-add name path\n", getprogname());
926 fprintf(stderr, " %s hook-list\n", getprogname());
927 fprintf(stderr, " %s hook-remove id\n", getprogname());
928 fprintf(stderr, " %s plugin-config id [variable [value]]\n", getprogname());
929 fprintf(stderr, " %s plugin-info id\n", getprogname());
930 fprintf(stderr, " %s plugin-list\n", getprogname());
931 fprintf(stderr, " %s plugin-load name\n", getprogname());
932 fprintf(stderr, " %s plugin-path [variable [value]]\n", getprogname());
933 fprintf(stderr, " %s plugin-template [variable [value]]\n", getprogname());
934 fprintf(stderr, " %s plugin-reload [plugin]\n", getprogname());
935 fprintf(stderr, " %s plugin-unload [plugin]\n", getprogname());
936 fprintf(stderr, " %s rule-add [-c channel] [-e event] [-i index] [-o origin] [-p plugin] [-s server] accept|drop\n", getprogname());
937 fprintf(stderr, " %s rule-edit [-a accept|drop] [-c|C channel] [-e|E event] [-o|O origin] [-s|S server] index\n", getprogname());
938 fprintf(stderr, " %s rule-list\n", getprogname());
939 fprintf(stderr, " %s rule-move from to\n", getprogname());
940 fprintf(stderr, " %s rule-remove index\n", getprogname());
941 fprintf(stderr, " %s server-connect [-n nickname] [-r realname] [-u username] [-p port] id hostname\n", getprogname());
942 fprintf(stderr, " %s server-disconnect [server]\n", getprogname());
943 fprintf(stderr, " %s server-info server\n", getprogname());
944 fprintf(stderr, " %s server-invite server target channel\n", getprogname());
945 fprintf(stderr, " %s server-join server channel [password]\n", getprogname());
946 fprintf(stderr, " %s server-kick server target channel [reason]\n", getprogname());
947 fprintf(stderr, " %s server-list\n", getprogname());
948 fprintf(stderr, " %s server-me server target message\n", getprogname());
949 fprintf(stderr, " %s server-message server target message\n", getprogname());
950 fprintf(stderr, " %s server-mode server target mode [args]\n", getprogname());
951 fprintf(stderr, " %s server-nick server nickname\n", getprogname());
952 fprintf(stderr, " %s server-notice server target message\n", getprogname());
953 fprintf(stderr, " %s server-part server channel [reason]\n", getprogname());
954 fprintf(stderr, " %s server-reconnect [server]\n", getprogname());
955 fprintf(stderr, " %s server-topic server channel topic\n", getprogname());
956 fprintf(stderr, " %s watch\n", getprogname());
957 exit(1);
958 }
959
960 int
961 main(int argc, char **argv)
962 {
963 ketopt_t ko = KETOPT_INIT;
964
965 setprogname("irccdctl");
966
967 --argc;
968 ++argv;
969
970 for (int ch; (ch = ketopt(&ko, argc, argv, 0, "s:v", NULL)) != -1; ) {
971 switch (ch) {
972 case 's':
973 strlcpy(sockaddr.sun_path, ko.arg, sizeof (sockaddr.sun_path));
974 break;
975 case 'v':
976 verbose = 1;
977 break;
978 default:
979 usage();
980 break;
981 }
982 }
983
984 argc -= ko.ind;
985 argv += ko.ind;
986
987 if (argc < 1)
988 usage();
989 else if (strcmp(argv[0], "help") == 0)
990 help();
991
992 dial();
993 check();
994 run(argc, argv);
995 }