Mercurial > irccd
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 } |