1 /*
2 * icb.c - handles icb connections.
3 *
4 * written by matthew green
5 *
6 * copyright (C) 1995-2021. Matthew R. Green.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include "irc.h"
34 IRCII_RCSID("@(#)$eterna: icb.c,v 2.101 2021/02/27 08:00:42 mrg Exp $");
35
36 /*
37 * ICB protocol is weird. everything is separated by ASCII 001. there
38 * are fortunately few protocol message types (unlike IRC).
39 *
40 * each message is formatted like:
41 * CTdata
42 *
43 * where C is one byte interpreted as a message length, including this
44 * byte. this makes the max length size 255. the T is the packet type,
45 * making 256 types of messages available (not many are used). and data
46 * is interpreted on a per-T basis. normally, within T, there are
47 * different fields separtaed by ASCII 001. most data types have a
48 * specific number of fields (perhaps variable). see each function below
49 * for a description of how these work.
50 */
51
52 /*
53 *** error: [ERROR] 104: Invalid packet type "P" (PRIVMSG blah :?DCC CHAT chat 3406786305 49527?)
54 */
55
56 #include "ircaux.h"
57 #include "names.h"
58 #include "server.h"
59 #include "output.h"
60 #include "vars.h"
61 #include "ircterm.h"
62 #include "names.h"
63 #include "parse.h"
64 #include "screen.h"
65 #include "server.h"
66 #include "icb.h"
67 #include "hook.h"
68 #include "ignore.h"
69 #include "flood.h"
70 #include "whois.h"
71 #include "notice.h"
72
73 /*
74 * private data for ICB.
75 */
76 struct icb_server_status {
77 /*
78 * some grossness to prime the $chanusers() list. if this variable
79 * is non-zero we are currently expecting Members: or more output.
80 */
81 int icb_got_group_waiting_for_members;
82 };
83 static void icb_close_callback(void **);
84
85 /* sender functions */
86 static void icb_put_login(u_char *, u_char *, u_char *, u_char *, u_char *);
87
88 /* hooks out of "icbcmd" */
89 static void icb_put_msg(u_char *);
90 static void icb_put_beep(u_char *);
91 static void icb_put_boot(u_char *);
92 static void icb_put_cancel(u_char *);
93 static void icb_put_echo(u_char *);
94 static void icb_put_pass(u_char *);
95 static void icb_put_status(u_char *);
96 static void icb_put_pong(u_char *);
97 static void icb_put_command(u_char *);
98
99 /* receiver functions */
100 static void icb_got_login(u_char *);
101 static void icb_got_public(u_char *);
102 static void icb_got_msg(u_char *);
103 static void icb_got_status(u_char *);
104 static void icb_got_error(u_char *);
105 static void icb_got_important(u_char *);
106 static void icb_got_exit(u_char *);
107 static void icb_got_cmdout(u_char *);
108 static void icb_got_proto(u_char *);
109 static void icb_got_beep(u_char *);
110 static void icb_got_ping(u_char *);
111 static void icb_got_something(int, u_char *);
112
113 /* misc. helper functions */
114 static int icb_split_msg(u_char *, u_char **, int);
115 static void icb_set_fromuserhost(u_char *);
116 static void icb_got_who(u_char *);
117 static u_char *icb_who_idle(u_char *);
118 static u_char *icb_who_signon(u_char *);
119
120 /* icb state variables */
121 static u_char *icb_initial_status = UP("iml");
122
123 static int icb_port_local = ICB_PORT; /* port of icbd */
124
125 static void
icb_close_callback(void ** ptrptr)126 icb_close_callback(void **ptrptr)
127 {
128 new_free(ptrptr);
129 }
130
131 /*
132 * these are the functions that interpret incoming ICB packets.
133 */
134
135 /*
136 * login packets fromt the server should be a single field "a"
137 */
138 static void
icb_got_login(u_char * line)139 icb_got_login(u_char *line)
140 {
141 u_char *server = server_get_name(parsing_server());
142 struct icb_server_status *status;
143
144 status = new_malloc(sizeof *status);
145 status->icb_got_group_waiting_for_members = 0;
146
147 server_set_version_string(parsing_server(), UP("ICB"));
148 server_set_itsname(parsing_server(), server);
149 server_set_server_private(parsing_server(), status, icb_close_callback);
150 maybe_load_ircrc();
151 update_all_status();
152 do_hook(CONNECT_LIST, "%s %d", server, server_get_port(parsing_server()));
153 }
154
155 /*
156 * public messages have two fields
157 * sender
158 * text
159 */
160 static void
icb_got_public(u_char * line)161 icb_got_public(u_char *line)
162 {
163 u_char *ap[ICB_GET_PUBLIC_MAXFIELD];
164 int ac, level;
165 u_char *high;
166
167 ac = icb_split_msg(line, ap, ICB_GET_PUBLIC_MAXFIELD);
168 if (ac != ICB_GET_PUBLIC_MAXFIELD)
169 return;
170
171 save_message_from();
172 level = set_lastlog_msg_level(LOG_PUBLIC);
173 message_from(server_get_icbgroup(parsing_server()), LOG_PUBLIC);
174
175 switch (double_ignore(ap[0], from_user_host(), IGNORE_PUBLIC))
176 {
177 case IGNORED:
178 goto out;
179 case HIGHLIGHTED:
180 high = highlight_str();
181 break;
182 default:
183 high = empty_string();
184 break;
185 }
186
187 if (check_flooding(ap[0], server_get_nickname(parsing_server()), PUBLIC_FLOOD, ap[1]))
188 {
189 if (do_hook(PUBLIC_LIST, "%s %s %s", ap[0],
190 server_get_icbgroup(parsing_server()), ap[1]))
191 put_it("%s<%s>%s %s", high, ap[0], high, ap[1]);
192 if (do_beep_on_level(LOG_PUBLIC))
193 beep_em(1);
194 }
195 out:
196 set_lastlog_msg_level(level);
197 restore_message_from();
198 }
199
200 static void
icb_got_msg(u_char * line)201 icb_got_msg(u_char *line)
202 {
203 u_char *ap[ICB_GET_MSG_MAXFIELD];
204 int ac, level;
205 u_char *high;
206
207 ac = icb_split_msg(line, ap, ICB_GET_MSG_MAXFIELD);
208 if (ac != ICB_GET_MSG_MAXFIELD)
209 return;
210 set_recv_nick(ap[0]);
211 if (is_away_set())
212 beep_em(get_int_var(BEEP_WHEN_AWAY_VAR));
213 save_message_from();
214 message_from(ap[0], LOG_MSG);
215 level = set_lastlog_msg_level(LOG_MSG);
216
217 switch (double_ignore(ap[0], from_user_host(), IGNORE_MSGS))
218 {
219 case IGNORED:
220 goto out;
221 case HIGHLIGHTED:
222 high = highlight_str();
223 break;
224 default:
225 high = empty_string();
226 break;
227 }
228
229 if (check_flooding(ap[0], server_get_nickname(parsing_server()), MSG_FLOOD, ap[1]))
230 {
231 if (do_hook(MSG_LIST, "%s %s", ap[0], ap[1]))
232 {
233 if (is_away_set())
234 {
235 time_t t;
236 u_char *msg = NULL;
237 size_t len = my_strlen(ap[1]) + 20;
238
239 t = time(NULL);
240 msg = new_malloc(len);
241 snprintf(CP(msg), len, "%s <%.16s>", ap[1], ctime(&t));
242 put_it("%s*%s*%s %s", high, ap[0], high, msg);
243 new_free(&msg);
244 }
245 else
246 put_it("%s*%s*%s %s", high, ap[0], high, ap[1]);
247 }
248 if (do_beep_on_level(LOG_MSG))
249 beep_em(1);
250 }
251 out:
252 set_lastlog_msg_level(level);
253 restore_message_from();
254 }
255
256 static const u_char status_match[] = "You are now in group ";
257 static const u_char signoff_match[] = "Your group moderator signed off.";
258 static const u_char change_match[] = "Group is now named ";
259 static const u_char change_match2[] = "renamed group to ";
260 static const u_char rsvp_match[] = "You are invited to group ";
261 static const u_char topic_match[] = "changed the topic to \"";
262 static const u_char nick_match[] = "changed nickname to ";
263
264 static void
icb_got_status(u_char * line)265 icb_got_status(u_char *line)
266 {
267 u_char *ap[ICB_GET_STATUS_MAXFIELD], *group, *space, save;
268 int ac, do_say = 1, level;
269
270 /* use these a few times */
271 #define KILL_SPACE(what) \
272 do { \
273 if ((space = my_index(what, ' ')) != NULL) { \
274 save = *space; \
275 *space = 0; \
276 } \
277 } while (0)
278 #define RESTORE_SPACE \
279 do { \
280 if (space) \
281 *space = save; \
282 } while (0)
283
284 save = 0;
285 space = NULL;
286 save_message_from();
287 level = set_lastlog_msg_level(LOG_CRAP);
288 ac = icb_split_msg(line, ap, ICB_GET_STATUS_MAXFIELD);
289 if (ac != ICB_GET_STATUS_MAXFIELD)
290 goto out;
291 if (my_stricmp(ap[0], UP("status")) == 0)
292 {
293 if (my_strnicmp(ap[1], status_match, sizeof(status_match) - 1) == 0)
294 {
295 group = ap[1] + sizeof(status_match) - 1;
296 KILL_SPACE(group);
297 clear_channel_list(parsing_server());
298 add_channel(group, 0, parsing_server(), CHAN_JOINED, 0);
299 server_set_icbgroup(parsing_server(), group);
300 icb_set_fromuserhost(empty_string());
301 message_from(group, LOG_CRAP);
302 if (do_hook(JOIN_LIST, "%s %s %s", server_get_nickname(parsing_server()), group, empty_string()) == 0)
303 do_say = 0;
304 if (get_int_var(SHOW_CHANNEL_NAMES_VAR))
305 icb_put_funny_stuff(UP("NAMES"), group, NULL);
306 RESTORE_SPACE;
307 }
308 /* leave do_say set */
309 }
310 else
311 if (my_stricmp(ap[0], UP("sign-on")) == 0)
312 {
313 KILL_SPACE(ap[1]);
314 icb_set_fromuserhost(space+1);
315 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
316 if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
317 do_hook(JOIN_LIST, "%s %s %s", ap[1], server_get_icbgroup(parsing_server()), empty_string()) == 0)
318 do_say = 0;
319 add_to_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server(), 0, 0);
320 RESTORE_SPACE;
321 }
322 else
323 if (my_stricmp(ap[0], UP("sign-off")) == 0)
324 {
325 if (my_strnicmp(ap[1], signoff_match, sizeof(signoff_match) - 1) != 0)
326 {
327 u_char *s = server_get_icbgroup(parsing_server());
328
329 KILL_SPACE(ap[1]);
330 icb_set_fromuserhost(space+1);
331 message_from(s, LOG_CRAP);
332 if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
333 (do_hook(CHANNEL_SIGNOFF_LIST, "%s %s %s", s, ap[1], s) == 0 ||
334 do_hook(SIGNOFF_LIST, "%s %s", ap[1], s) == 0))
335 do_say = 0;
336 remove_from_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server());
337 RESTORE_SPACE;
338 }
339 /* leave do_say set */
340 }
341 else
342 if (my_stricmp(ap[0], UP("arrive")) == 0)
343 {
344 KILL_SPACE(ap[1]);
345 icb_set_fromuserhost(space+1);
346 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
347 if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
348 do_hook(JOIN_LIST, "%s %s %s", ap[1], server_get_icbgroup(parsing_server()), empty_string()) == 0)
349 do_say = 0;
350 add_to_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server(), 0, 0);
351 RESTORE_SPACE;
352 }
353 else
354 if (my_stricmp(ap[0], UP("depart")) == 0)
355 {
356 KILL_SPACE(ap[1]);
357 icb_set_fromuserhost(space+1);
358 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
359 if (double_ignore(ap[1], from_user_host(), IGNORE_CRAP) != IGNORED &&
360 do_hook(LEAVE_LIST, "%s %s", ap[1], server_get_icbgroup(parsing_server())) == 0)
361 do_say = 0;
362 remove_from_channel(server_get_icbgroup(parsing_server()), ap[1], parsing_server());
363 RESTORE_SPACE;
364 }
365 else
366 if (my_stricmp(ap[0], UP("change")) == 0)
367 {
368 int match2;
369 int len;
370
371 group = 0;
372 KILL_SPACE(ap[1]);
373 match2 = my_strnicmp(space+1, change_match2, sizeof(change_match2) - 1);
374 RESTORE_SPACE;
375 if (match2 == 0)
376 group = space+1 + sizeof(change_match2) - 1;
377 else
378 if (my_strnicmp(ap[1], change_match, sizeof(change_match) - 1) == 0)
379 group = ap[1] + sizeof(change_match) - 1;
380
381 if (group)
382 {
383 len = my_strlen(group);
384 if (group[len - 1] == '.')
385 group[len - 1] = 0; /* kill the period */
386
387 message_from(group, LOG_CRAP);
388 rename_channel(server_get_icbgroup(parsing_server()), group, parsing_server());
389 server_set_icbgroup(parsing_server(), group);
390 icb_set_fromuserhost(empty_string());
391 }
392 /* leave do_say set for all cases */
393 }
394 else
395 if (my_stricmp(ap[0], UP("RSVP")) == 0)
396 {
397 if (my_strnicmp(ap[1], rsvp_match, sizeof(rsvp_match) - 1) == 0)
398 {
399 group = ap[1] + sizeof(rsvp_match) - 1;
400 KILL_SPACE(group);
401 set_invite_channel(group);
402 RESTORE_SPACE;
403 }
404 /* leave do_say set */
405 }
406 else
407 if (my_stricmp(ap[0], UP("topic")) == 0)
408 {
409 KILL_SPACE(ap[1]);
410 if (my_strnicmp(space + 1, topic_match, sizeof(topic_match) - 1) == 0)
411 {
412 u_char *topic, *lbuf = NULL;
413
414 group = server_get_icbgroup(parsing_server());
415 message_from(group, LOG_CRAP);
416 topic = space + 1 + sizeof(topic_match) - 1;
417 /* trim the final " */
418 malloc_strcpy(&lbuf, topic);
419 lbuf[my_strlen(lbuf) - 1] = '\0';
420 if (do_hook(TOPIC_LIST, "%s %s %s", ap[1], group, lbuf) == 0)
421 do_say = 0;
422 new_free(&lbuf);
423 }
424 RESTORE_SPACE;
425 /* leave do_say set */
426 }
427 else
428 if (my_stricmp(ap[0], UP("name")) == 0)
429 {
430 KILL_SPACE(ap[1]);
431 if (my_strnicmp(space + 1, nick_match, sizeof(nick_match) - 1) == 0)
432 {
433 u_char *new_nick;
434
435 group = server_get_icbgroup(parsing_server());
436 message_from(group, LOG_CRAP);
437 new_nick = space + 1 + sizeof(nick_match) - 1;
438 if (do_hook(CHANNEL_NICK_LIST, "%s %s %s", group, ap[1], new_nick) &&
439 do_hook(NICKNAME_LIST, "%s %s", ap[1], new_nick) == 0)
440 do_say = 0;
441 rename_nick(ap[1], new_nick, parsing_server());
442 }
443 RESTORE_SPACE;
444 /* leave do_say set */
445 }
446 /* make default info messages go to the current channel */
447 else
448 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
449
450 #if 0
451 /* these need to be match for /ignore to work */
452 *** info Sign-on: nick (user@domain.com) entered group
453 *** info Sign-off: nick (user@domain.com) has signed off.
454 #endif
455
456 if (do_say && do_hook(ICB_STATUS_LIST, "%s %s", ap[0], ap[1]))
457 say("info %s: %s", ap[0], ap[1]);
458 out:
459 set_lastlog_msg_level(level);
460 restore_message_from();
461 }
462
463 static void
icb_set_fromuserhost(u_char * what)464 icb_set_fromuserhost(u_char *what)
465 {
466 static u_char *icb_fromuserhost = NULL;
467 u_char *righty;
468
469 if (!what || !*what)
470 what = empty_string();
471 else if (*what == '(')
472 what++;
473 malloc_strcpy(&icb_fromuserhost, what); /* ( for below */
474 if ((righty = my_index(icb_fromuserhost, ')')))
475 *righty = 0;
476 set_from_user_host(icb_fromuserhost);
477 }
478
479 static void
icb_got_error(u_char * line)480 icb_got_error(u_char *line)
481 {
482 int level;
483
484 save_message_from();
485 level = set_lastlog_msg_level(LOG_CRAP);
486 message_from(NULL, LOG_CRAP);
487 if (do_hook(ICB_ERROR_LIST, "%s", line))
488 say("error: %s", line);
489 set_lastlog_msg_level(level);
490 restore_message_from();
491 }
492
493 static void
icb_got_important(u_char * line)494 icb_got_important(u_char *line)
495 {
496 u_char *ap[ICB_GET_IMPORTANT_MAXFIELD];
497 int ac, level;
498
499 ac = icb_split_msg(line, ap, ICB_GET_IMPORTANT_MAXFIELD);
500 if (ac != ICB_GET_IMPORTANT_MAXFIELD)
501 return;
502 save_message_from();
503 level = set_lastlog_msg_level(LOG_CRAP);
504 message_from(NULL, LOG_CRAP);
505 if (do_hook(SERVER_NOTICE_LIST, "%s *** %s", ap[0], ap[1]))
506 say("Important Message %s: %s", ap[0], ap[1]);
507 set_lastlog_msg_level(level);
508 restore_message_from();
509 }
510
511 static void
icb_got_exit(u_char * line)512 icb_got_exit(u_char *line)
513 {
514 int level;
515
516 level = set_lastlog_msg_level(LOG_CRAP);
517 say("ICB server disconnecting us");
518 close_server(parsing_server(), empty_string());
519 set_lastlog_msg_level(level);
520 }
521
522 /*
523 * command output: we split this up a little ...
524 */
525
526 static u_char *
icb_who_idle(u_char * line)527 icb_who_idle(u_char *line)
528 {
529 time_t idle = (time_t)my_atoi(line);
530 static u_char lbuf[16];
531
532 if (idle > 99 * 60)
533 snprintf(CP(lbuf), sizeof lbuf, "%5dm", (int)idle / 60);
534 else
535 if (idle < 60)
536 snprintf(CP(lbuf), sizeof lbuf, "%d sec", (int)idle);
537 else
538 snprintf(CP(lbuf), sizeof lbuf, "%d:%02ds", (int)idle / 60, (int)idle % 60);
539 /* only has 6 chars */
540 lbuf[6] = 0;
541 return (lbuf);
542 }
543
544 static u_char *
icb_who_signon(u_char * line)545 icb_who_signon(u_char *line)
546 {
547 time_t their_time = (time_t)my_atoi(line);
548 u_char *s = UP(asctime(localtime(&their_time)));
549
550 s[16] = '\0';
551 /* Tue Mar 2 05:10:10 1999J */
552 /* <----------> */
553 return (s + 4);
554 }
555
556 static void
icb_got_who(u_char * line)557 icb_got_who(u_char *line)
558 {
559 u_char *ap[ICB_GET_WHOOUT_MAXFIELD], *arg0;
560 int ac, level;
561
562 /* just get the command */
563 ac = icb_split_msg(line, ap, ICB_GET_WHOOUT_MAXFIELD);
564 if (ac != ICB_GET_WHOOUT_MAXFIELD)
565 {
566 yell("--- icb_got_who: split ac(%d) not 8", ac);
567 return;
568 }
569 /* ap[3] is always "0" */
570 /* these are: mod?, nick, idle, signon, user, [@]host, status? */
571 /* keep format in sync with below */
572 save_message_from();
573 level = set_lastlog_msg_level(LOG_CRAP);
574 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
575 arg0 = UP(ap[0][0] == 'm' ? "*" : " ");
576 if (do_hook(ICB_WHO_LIST, "%s%s %s %s %s %s %s", arg0,
577 ap[1], ap[2], ap[4],
578 ap[5], ap[6], ap[7]))
579 say("%s%-13s %6s %-12s %s@%s %s", arg0, ap[1],
580 icb_who_idle(ap[2]),
581 icb_who_signon(ap[4]),
582 ap[5], ap[6], ap[7]);
583 set_lastlog_msg_level(level);
584 restore_message_from();
585 }
586
587 static const u_char group_match[] = "Group: ";
588 static const u_char members_match[] = " Members: ";
589
590 static void icb_cmdout_check_members(u_char *);
591 static void
icb_cmdout_check_members(u_char * line)592 icb_cmdout_check_members(u_char *line)
593 {
594 u_char *group, *nick_list = NULL;
595 struct icb_server_status *status;
596
597 status = server_get_server_private(parsing_server());
598
599 if (status->icb_got_group_waiting_for_members)
600 status->icb_got_group_waiting_for_members++;
601 else if (my_strnicmp(line, UP(members_match),
602 sizeof(members_match) - 1) == 0)
603 {
604 status->icb_got_group_waiting_for_members = 2;
605 line += sizeof(members_match) - 1;
606 }
607 else
608 return;
609
610 /* parse list of nicks, calling add_to_channel() */
611 group = server_get_icbgroup(parsing_server());
612 malloc_strcpy(&nick_list, line);
613 line = nick_list;
614 while (line && *line)
615 {
616 u_char *next;
617
618 next = my_index(line, ',');
619 if (next)
620 {
621 *next++ = '\0';
622 if (*next == ' ')
623 next++;
624 }
625 add_to_channel(group, line, parsing_server(), 0, 0);
626 line = next;
627 }
628 new_free(&nick_list);
629 }
630
631 static void
icb_got_cmdout(u_char * line)632 icb_got_cmdout(u_char *line)
633 {
634 u_char *ap[2];
635 int ac, level;
636
637 /* just get the command */
638 ac = icb_split_msg(line, ap, -2);
639
640 save_message_from();
641 level = set_lastlog_msg_level(LOG_CRAP);
642 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
643 if (ac == 2)
644 {
645 if (my_stricmp(ap[0], UP("co")) == 0)
646 {
647 if (my_strnicmp(ap[1], UP(group_match),
648 sizeof(group_match) - 1) == 0) {
649 u_char *s = ap[1] + sizeof(group_match) - 1;
650
651 /* skip "*name*<spaces>" */
652 while (!isspace(*s))
653 s++;
654 while (isspace(*s))
655 s++;
656 if (*s == '(' && *(s+4) == ')') {
657 u_char mode[4];
658
659 mode[0] = s[1];
660 mode[1] = s[2];
661 mode[2] = s[3];
662 mode[3] = 0;
663 server_set_icbmode(parsing_server(), mode);
664 }
665 }
666 else
667 icb_cmdout_check_members(ap[1]);
668
669 if (do_hook(ICB_CMDOUT_LIST, "%s", ap[1]))
670 say("%s", ap[1]);
671 }
672 else
673 /* who list */
674 if (my_stricmp(ap[0], UP("wl")) == 0)
675 icb_got_who(ap[1]);
676 else
677 put_it("[unknown command output %s] %s", ap[0], ap[1]);
678 }
679 else
680 /* head of who list */
681 if (my_stricmp(ap[0], UP("wh")) == 0 &&
682 do_hook(ICB_WHO_LIST, "%s %s %s %s %s %s",
683 "Nickname", "Idle", "Sign-On", "Account",
684 empty_string(), empty_string()))
685 /* keep format in sync with above. */
686 say(" %-13s %6s %-12s %s", "Nickname", "Idle",
687 "Sign-On", "Account");
688 set_lastlog_msg_level(level);
689 restore_message_from();
690 }
691
692 /*
693 * we send our login to the server now... it has told us we are
694 * actually there, time to login.
695 */
696 static void
icb_got_proto(u_char * line)697 icb_got_proto(u_char *line)
698 {
699 int server = parsing_server();
700 u_char *chan = server_get_icbgroup(server);
701 u_char *mode = server_get_icbmode(server);
702
703 if (!chan)
704 chan = empty_string();
705 if (!mode || !*mode)
706 mode = icb_initial_status;
707 say("You are wasting time.");
708 server_is_connected(server, 1);
709 icb_put_login(my_username(),
710 server_get_nickname(server),
711 chan,
712 server_get_password(server),
713 mode);
714 do_hook(CONNECT_LIST, "%s %d",
715 server_get_name(server), server_get_port(server));
716 }
717
718 /*
719 * if we beep, we beep. tell the user about the annoy packet
720 * anyway.
721 */
722 static void
icb_got_beep(u_char * line)723 icb_got_beep(u_char *line)
724 {
725 int level;
726
727 if (get_int_var(BEEP_VAR))
728 term_beep();
729 save_message_from();
730 level = set_lastlog_msg_level(LOG_CRAP);
731 message_from(server_get_icbgroup(parsing_server()), LOG_CRAP);
732 say("%s wants to annoy you.", line);
733 set_lastlog_msg_level(level);
734 restore_message_from();
735 }
736
737 /*
738 * if we get a ping, send a pong
739 */
740 static void
icb_got_ping(u_char * line)741 icb_got_ping(u_char *line)
742 {
743 icb_put_pong(line);
744 }
745
746 /*
747 * eek.
748 */
749 static void
icb_got_something(int type,u_char * line)750 icb_got_something(int type, u_char *line)
751 {
752 say("unknown: packet type %d, ignored", type);
753 }
754
755 /*
756 * below are functions to send messages to the icb server.
757 */
758
759 /*
760 * for hooks perhaps we should have a "icb <command> <args>"
761 * hook, that does 'everything'. dunno, there are enough
762 * irc-like ones to afford just adding a few icb specifics..
763 */
764
765 /*
766 * login packets have from 5 to 7 fields:
767 * username
768 * nickname
769 * default group
770 * login command
771 * password
772 * default group status (optional)
773 * protocol level (optional, deprecated).
774 *
775 * the login command must be either "login" or "w", to either login
776 * to ICB or see who is on.
777 */
778 static void
icb_put_login(u_char * user,u_char * nick,u_char * group,u_char * pass,u_char * status)779 icb_put_login(u_char *user, u_char *nick, u_char *group, u_char *pass, u_char *status)
780 {
781 u_char *mode, *prefix;
782
783 mode = server_get_icbmode(parsing_server());
784 if (my_strcmp(group, "..") == 0 &&
785 mode && my_strlen(mode) > 1 && mode[1] == 'i') {
786 /* save previous status */
787 prefix = UP("..");
788 } else
789 prefix = UP("");
790
791 send_to_server("%c%s%c%s%c%s%s%c%s%c%s%c%s", ICB_LOGIN,
792 user, ICB_SEP,
793 nick, ICB_SEP,
794 prefix, group, ICB_SEP,
795 "login", ICB_SEP,
796 pass ? pass : (u_char *) "", ICB_SEP,
797 status);
798 }
799
800 /*
801 * public packets have 1 field:
802 * text
803 */
804 void
icb_put_public(u_char * line)805 icb_put_public(u_char *line)
806 {
807 int level;
808 size_t len, remain;
809 const int from_server = get_from_server();
810
811 if (get_display())
812 {
813 save_message_from();
814 level = set_lastlog_msg_level(LOG_PUBLIC);
815 message_from(server_get_icbgroup(from_server), LOG_PUBLIC);
816 if (do_hook(SEND_PUBLIC_LIST, "%s %s", server_get_icbgroup(from_server), line))
817 put_it("> %s", line);
818 set_lastlog_msg_level(level);
819 restore_message_from();
820 }
821
822 /*
823 * deal with ICB 255 length limits. we have 255 bytes
824 * maximum to deal with. as public messages are send
825 * out with our nickname, we must take away that much,
826 * one for the space after our nick, one for the public
827 * message token, one for the trailing nul, one for the
828 * ^A, and one more for good measure, totalling 5.
829 */
830 remain = 250 - my_strlen(server_get_nickname(from_server));
831 while (*line) {
832 u_char b[256], *s;
833 size_t copylen;
834
835 len = my_strlen(line);
836 copylen = remain;
837 if (len > remain)
838 {
839 int i;
840
841 /*
842 * try to find a space in the previous
843 * 10 characters, and split there instead.
844 */
845 for (i = 1; i < 11 && i < len; i++)
846 if (isspace(line[remain - i]))
847 {
848 copylen -= i;
849 break;
850 }
851 my_strncpy(b, line, copylen);
852 b[copylen] = 0;
853 s = b;
854 }
855 else
856 s = line;
857 send_to_server("%c%s", ICB_PUBLIC, s);
858 line += len > copylen ? copylen : len;
859 }
860 }
861
862 /*
863 * msg packets have 2 fields:
864 * to - recipient of message
865 * text
866 */
867 static void
icb_put_msg(u_char * line)868 icb_put_msg(u_char *line)
869 {
870 u_char *to;
871
872 if ((to = next_arg(line, &line)) == NULL)
873 line = empty_string();
874 icb_put_msg2(to, line);
875 }
876
877 void
icb_put_msg2(u_char * to,u_char * line)878 icb_put_msg2(u_char *to, u_char *line)
879 {
880 int level;
881 size_t nlen, mlen, len, remain;
882
883 if (get_display())
884 {
885 save_message_from();
886 level = set_lastlog_msg_level(LOG_MSG);
887 message_from(to, LOG_MSG);
888 if (do_hook(SEND_MSG_LIST, "%s %s", to, line))
889 put_it("-> *%s* %s", to, line);
890 set_lastlog_msg_level(level);
891 restore_message_from();
892 }
893
894 /*
895 * deal with ICB 255 length limits. we have 255 bytes maximum to
896 * deal with. as private messages are send out with our nickname,
897 * but are sent in with the target's nickname, we must take away
898 * the larger length of these, plus one for the space after our
899 * nick, one for the command tag, one for the private message
900 * token, one for the separator, one for the trailing nul, one
901 * for the ^A, and one more for good measure, totalling 7.
902 */
903 nlen = my_strlen(to);
904 mlen = my_strlen(server_get_nickname(get_from_server()));
905 if (nlen > mlen)
906 remain = 248 - nlen;
907 else
908 remain = 248 - mlen;
909 while (*line)
910 {
911 u_char b[256], *s;
912
913 len = my_strlen(line);
914 if (len > remain)
915 {
916 my_strncpy(b, line, remain);
917 b[remain] = 0;
918 s = b;
919 }
920 else
921 s = line;
922 send_to_server("%cm%c%s %s", ICB_COMMAND, ICB_SEP, to, s);
923 line += len > remain ? remain : len;
924 }
925 }
926
927 static void
icb_put_beep(u_char * line)928 icb_put_beep(u_char *line)
929 {
930 send_to_server("%cbeep%c%s", ICB_COMMAND, ICB_SEP, line);
931 }
932
933 static void
icb_put_pong(u_char * line)934 icb_put_pong(u_char *line)
935 {
936 send_to_server("%c", ICB_PONG);
937 }
938
939 static void
icb_put_boot(u_char * line)940 icb_put_boot(u_char *line)
941 {
942 send_to_server("%cboot%c%s", ICB_COMMAND, ICB_SEP, line);
943 }
944
945 static void
icb_put_cancel(u_char * line)946 icb_put_cancel(u_char *line)
947 {
948 send_to_server("%ccancel%c%s", ICB_COMMAND, ICB_SEP, line);
949 }
950
951 static void
icb_put_echo(u_char * line)952 icb_put_echo(u_char *line)
953 {
954 send_to_server("%cechoback%c%s", ICB_COMMAND, ICB_SEP, line);
955 }
956
957 void
icb_put_group(u_char * line)958 icb_put_group(u_char *line)
959 {
960 send_to_server("%cg%c%s", ICB_COMMAND, ICB_SEP, line);
961 }
962
963 void
icb_put_invite(u_char * line)964 icb_put_invite(u_char *line)
965 {
966 send_to_server("%cinvite%c%s", ICB_COMMAND, ICB_SEP, line);
967 }
968
969 void
icb_put_motd(u_char * line)970 icb_put_motd(u_char *line)
971 {
972 send_to_server("%cmotd%c", ICB_COMMAND, ICB_SEP);
973 }
974
975 void
icb_put_nick(u_char * line)976 icb_put_nick(u_char *line)
977 {
978 send_to_server("%cname%c%s", ICB_COMMAND, ICB_SEP, line);
979 server_set_nickname(get_window_server(0), line);
980 }
981
982 static void
icb_put_pass(u_char * line)983 icb_put_pass(u_char *line)
984 {
985 send_to_server("%cpass%c%s", ICB_COMMAND, ICB_SEP, line);
986 }
987
988 static void
icb_put_status(u_char * line)989 icb_put_status(u_char *line)
990 {
991 send_to_server("%cstatus%c%s", ICB_COMMAND, ICB_SEP, line);
992 }
993
994 void
icb_put_topic(u_char * line)995 icb_put_topic(u_char *line)
996 {
997 send_to_server("%ctopic%c%s", ICB_COMMAND, ICB_SEP, line);
998 }
999
1000 void
icb_put_version(u_char * line)1001 icb_put_version(u_char *line)
1002 {
1003 send_to_server("%cv%c", ICB_COMMAND, ICB_SEP);
1004 }
1005
1006 /*
1007 * /names & /list support:
1008 *
1009 * a bare /names is ICB /who -s
1010 * a bare /list is ICB /who -g
1011 * any arguments are just passed, though a "*" (ircII "this channel") is
1012 * converted into a "." (icb "this channel").
1013 */
1014 void
icb_put_funny_stuff(u_char * command,u_char * args,u_char * subargs)1015 icb_put_funny_stuff(u_char *command, u_char *args, u_char *subargs)
1016 {
1017 u_char *arg, *arg1;
1018
1019 if (my_strcmp(command, "NAMES") == 0)
1020 {
1021 arg1 = UP("-s");
1022 }
1023 else
1024 if (my_strcmp(command, "LIST") == 0)
1025 {
1026 arg1 = UP("-g");
1027 }
1028 else
1029 {
1030 yell("--- icb_put_funny_stuff: not NAMES or LIST.");
1031 return;
1032 }
1033 if (args && *args)
1034 {
1035 u_char lbuf[255];
1036
1037 if ((arg = my_index(args, '*')) != NULL && (arg[1] == '\0' || isspace(arg[1])))
1038 arg[0] = '.';
1039 /* XXX */
1040 if (my_strlen(args) > 251)
1041 args[251] = 0;
1042 snprintf(CP(lbuf), sizeof lbuf, "%s %s", arg1, args);
1043 icb_put_who(lbuf);
1044 }
1045 else
1046 icb_put_who(arg1);
1047
1048 }
1049
1050 void
icb_put_who(u_char * line)1051 icb_put_who(u_char *line)
1052 {
1053 send_to_server("%cw%c%s", ICB_COMMAND, ICB_SEP, line);
1054 }
1055
1056 static void
icb_put_command(u_char * line)1057 icb_put_command(u_char *line)
1058 {
1059 yell("--- icb_put_command not implemented yet");
1060 }
1061
1062 void
icb_put_action(u_char * target,u_char * text)1063 icb_put_action(u_char *target, u_char *text)
1064 {
1065 u_char *s;
1066 unsigned display;
1067
1068 s = new_malloc(2 + my_strlen(text) + 2 + 1);
1069 strcpy(CP(s), "*");
1070 strcat(CP(s), CP(text));
1071 strcat(CP(s), "*");
1072 display = set_display_off();
1073 if (is_current_channel(target, get_from_server(), 0))
1074 icb_put_public(s);
1075 else
1076 icb_put_msg2(target, s);
1077 set_display(display);
1078 new_free(&s);
1079 }
1080
1081 /*
1082 * icb_split_msg(): split up `msg' into (upto) `ac' parts separate parts,
1083 * delimited by ICB_SEP, returning each part if to `ap', which points to
1084 * an array of u_char *'s `ac' long. if ac is positive, and if there is
1085 * not enough room, this function returns -1, otherwise the number of
1086 * parts is returned. if ac is negative, it is an indication that we
1087 * might have more fields, but we only want to split abs(ac);
1088 */
1089 static int
icb_split_msg(u_char * msg,u_char ** ap,int ac)1090 icb_split_msg(u_char *msg, u_char **ap, int ac)
1091 {
1092 int myac = 0, shortok = 0;
1093 u_char *s;
1094
1095 if (ac == 0)
1096 return (-1);
1097 else if (ac < 0)
1098 {
1099 shortok = 1;
1100 ac = -ac;
1101 }
1102 ap[myac++] = msg;
1103 while ((s = my_index(msg, ICB_SEP)))
1104 {
1105 if (myac >= ac)
1106 {
1107 if (shortok)
1108 return (myac);
1109 else
1110 return (-1);
1111 }
1112 *s = '\0';
1113 msg = s + 1;
1114 ap[myac++] = msg;
1115 }
1116 return (myac);
1117 }
1118
1119 /*
1120 * external hooks
1121 */
1122
1123 /*
1124 * this function handles connecting to a server, and is called from
1125 * the guts of the server code. we could send a login packet here
1126 * but we defer that until we get a protocol packet.
1127 */
1128 void
icb_login_to_server(int server)1129 icb_login_to_server(int server)
1130 {
1131
1132 }
1133
1134 /*
1135 * handle the icb protocol.
1136 */
1137 void
icb_parse_server(u_char * line)1138 icb_parse_server(u_char *line)
1139 {
1140 int len = (int)(u_char)*line++;
1141 int type = (int)*line++;
1142 struct icb_server_status *status;
1143
1144 status = server_get_server_private(parsing_server());
1145
1146 (void)len;
1147 icb_set_fromuserhost(NULL);
1148 switch (type)
1149 {
1150 case ICB_LOGIN:
1151 icb_got_login(line);
1152 break;
1153 case ICB_PUBLIC:
1154 icb_got_public(line);
1155 break;
1156 case ICB_PERSONAL:
1157 icb_got_msg(line);
1158 break;
1159 case ICB_STATUS:
1160 icb_got_status(line);
1161 break;
1162 case ICB_ERROR:
1163 icb_got_error(line);
1164 break;
1165 case ICB_IMPORTANT:
1166 icb_got_important(line);
1167 break;
1168 case ICB_EXIT:
1169 icb_got_exit(line);
1170 break;
1171 case ICB_CMDOUT:
1172 icb_got_cmdout(line);
1173 break;
1174 case ICB_PROTO:
1175 icb_got_proto(line);
1176 break;
1177 case ICB_BEEP:
1178 icb_got_beep(line);
1179 break;
1180 case ICB_PING:
1181 icb_got_ping(line);
1182 break;
1183 default:
1184 icb_got_something(type, line);
1185 break;
1186 }
1187 if (status && status->icb_got_group_waiting_for_members)
1188 status->icb_got_group_waiting_for_members--;
1189 }
1190
1191 /*
1192 * hook for user to send anything so the icb server; we only
1193 * provide one of these entry points for the user. let the
1194 * rest be in scripts, or via some other IRC-like command
1195 * eg, send_text() will need to call into public/msg.
1196 *
1197 * format of this:
1198 * type arg1 arg2 arg3 arg4 ...
1199 */
1200 void
icbcmd(u_char * command,u_char * args,u_char * subargs)1201 icbcmd(u_char *command, u_char *args, u_char *subargs)
1202 {
1203 u_char *type;
1204 size_t len;
1205
1206 if ((type = next_arg(args, &args)) != NULL)
1207 {
1208 len = my_strlen(type);
1209 upper(type);
1210
1211 if (my_strncmp(type, "PUBLIC", len) == 0)
1212 icb_put_public(args);
1213 else
1214 if (my_strncmp(type, "MSG", len) == 0)
1215 icb_put_msg(args);
1216 else
1217 if (my_strncmp(type, "BEEP", len) == 0)
1218 icb_put_beep(args);
1219 else
1220 if (my_strncmp(type, "BOOT", len) == 0)
1221 icb_put_boot(args);
1222 else
1223 if (my_strncmp(type, "CANCEL", len) == 0)
1224 icb_put_cancel(args);
1225 else
1226 if (my_strncmp(type, "ECHO", len) == 0)
1227 icb_put_echo(args);
1228 else
1229 if (my_strncmp(type, "GROUP", len) == 0)
1230 icb_put_group(args);
1231 else
1232 if (my_strncmp(type, "INVITE", len) == 0)
1233 icb_put_invite(args);
1234 else
1235 if (my_strncmp(type, "MOTD", len) == 0)
1236 icb_put_motd(args);
1237 else
1238 if (my_strncmp(type, "NICK", len) == 0)
1239 icb_put_nick(args);
1240 else
1241 if (my_strncmp(type, "PASS", len) == 0)
1242 icb_put_pass(args);
1243 else
1244 if (my_strncmp(type, "STATUS", len) == 0)
1245 icb_put_status(args);
1246 else
1247 if (my_strncmp(type, "TOPIC", len) == 0)
1248 icb_put_topic(args);
1249 else
1250 if (my_strncmp(type, "VERSION", len) == 0)
1251 icb_put_version(args);
1252 else
1253 if (my_strncmp(type, "WHO", len) == 0)
1254 icb_put_who(args);
1255 else
1256 if (my_strncmp(type, "COMMAND", len) == 0)
1257 icb_put_command(args);
1258 else
1259 say("No such ICB command %s", type);
1260 }
1261 }
1262
1263 int
icb_port(void)1264 icb_port(void)
1265 {
1266 return icb_port_local;
1267 }
1268
1269 void
set_icb_port(int port)1270 set_icb_port(int port)
1271 {
1272 icb_port_local = port;
1273 }
1274