unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
message.c (19072B)
1 /* 2 * Unreal Internet Relay Chat Daemon, src/modules/message.c 3 * (C) 2000-2001 Carsten V. Munk and the UnrealIRCd Team 4 * Moved to modules by Fish (Justin Hammond) 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 1, or (at your option) 9 * any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19 */ 20 21 #include "unrealircd.h" 22 23 /* Forward declarations */ 24 const char *_StripColors(const char *text); 25 int ban_version(Client *client, const char *text); 26 CMD_FUNC(cmd_private); 27 CMD_FUNC(cmd_notice); 28 CMD_FUNC(cmd_tagmsg); 29 void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype); 30 int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype); 31 int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype); 32 33 /* Variables */ 34 long CAP_MESSAGE_TAGS = 0; /**< Looked up at MOD_LOAD, may stay 0 if message-tags support is absent */ 35 36 ModuleHeader MOD_HEADER 37 = { 38 "message", /* Name of module */ 39 "6.0.2", /* Version */ 40 "private message and notice", /* Short description of module */ 41 "UnrealIRCd Team", 42 "unrealircd-6", 43 }; 44 45 MOD_TEST() 46 { 47 MARK_AS_OFFICIAL_MODULE(modinfo); 48 EfunctionAddConstString(modinfo->handle, EFUNC_STRIPCOLORS, _StripColors); 49 EfunctionAdd(modinfo->handle, EFUNC_CAN_SEND_TO_CHANNEL, _can_send_to_channel); 50 return MOD_SUCCESS; 51 } 52 53 /* This is called on module init, before Server Ready */ 54 MOD_INIT() 55 { 56 CommandAdd(modinfo->handle, "PRIVMSG", cmd_private, 2, CMD_USER|CMD_SERVER|CMD_RESETIDLE|CMD_VIRUS); 57 CommandAdd(modinfo->handle, "NOTICE", cmd_notice, 2, CMD_USER|CMD_SERVER); 58 CommandAdd(modinfo->handle, "TAGMSG", cmd_tagmsg, 1, CMD_USER|CMD_SERVER); 59 MARK_AS_OFFICIAL_MODULE(modinfo); 60 return MOD_SUCCESS; 61 } 62 63 /* Is first run when server is 100% ready */ 64 MOD_LOAD() 65 { 66 CAP_MESSAGE_TAGS = ClientCapabilityBit("message-tags"); 67 68 return MOD_SUCCESS; 69 } 70 71 /* Called when module is unloaded */ 72 MOD_UNLOAD() 73 { 74 return MOD_SUCCESS; 75 } 76 77 #define CANPRIVMSG_CONTINUE 100 78 #define CANPRIVMSG_SEND 101 79 /** Check if PRIVMSG's are permitted from a person to another person. 80 * client: source client 81 * target: target client 82 * sendtype: One of SEND_TYPE_* 83 * text: Pointer to a pointer to a text [in, out] 84 * cmd: Pointer to a pointer which contains the command to use [in, out] 85 */ 86 int can_send_to_user(Client *client, Client *target, const char **msgtext, const char **errmsg, SendType sendtype) 87 { 88 int ret; 89 Hook *h; 90 int n; 91 static char errbuf[256]; 92 93 *errmsg = NULL; 94 95 if (IsVirus(client)) 96 { 97 ircsnprintf(errbuf, sizeof(errbuf), "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN); 98 *errmsg = errbuf; 99 return 0; 100 } 101 102 if (MyUser(client) && target_limit_exceeded(client, target, target->name)) 103 { 104 /* target_limit_exceeded() is an exception, in the sense that 105 * it will send a different numeric. So we don't set errmsg. 106 */ 107 return 0; 108 } 109 110 if (is_silenced(client, target)) 111 { 112 RunHook(HOOKTYPE_SILENCED, client, target, sendtype); 113 /* Silently discarded, no error message */ 114 return 0; 115 } 116 117 // Possible FIXME: make match_spamfilter also use errmsg, or via a wrapper? or use same numeric? 118 if (MyUser(client)) 119 { 120 int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_USERNOTICE : SPAMF_USERMSG); 121 const char *cmd = sendtype_to_cmd(sendtype); 122 123 if (match_spamfilter(client, *msgtext, spamtype, cmd, target->name, 0, NULL)) 124 return 0; 125 } 126 127 n = HOOK_CONTINUE; 128 for (h = Hooks[HOOKTYPE_CAN_SEND_TO_USER]; h; h = h->next) 129 { 130 n = (*(h->func.intfunc))(client, target, msgtext, errmsg, sendtype); 131 if (n == HOOK_DENY) 132 { 133 if (!*errmsg) 134 { 135 unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_USER_NO_ERRMSG", client, 136 "[BUG] Module $module did not set errmsg!!!", 137 log_data_string("module", h->owner->header->name)); 138 abort(); 139 } 140 return 0; 141 } 142 if (!*msgtext || !**msgtext) 143 { 144 if (sendtype != SEND_TYPE_TAGMSG) 145 return 0; 146 else 147 *msgtext = ""; 148 } 149 } 150 151 return 1; 152 } 153 154 /** Check if user is allowed to send to a prefix (eg: @#channel). 155 * @param client The client (sender) 156 * @param channel The target channel 157 * @param mode The member mode to send to (eg: 'o') 158 */ 159 int can_send_to_member_mode(Client *client, Channel *channel, char mode) 160 { 161 Membership *lp; 162 163 if (op_can_override("channel:override:message:prefix",client,channel,NULL)) 164 return 1; 165 166 lp = find_membership_link(client->user->channel, channel); 167 168 /* Check if user is allowed to send. RULES: 169 * Need at least voice (+) in order to send to +,% or @ 170 * Need at least ops (@) in order to send to & or ~ 171 */ 172 if (!lp || !check_channel_access_membership(lp, "vhoaq")) 173 { 174 sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); 175 return 0; 176 } 177 178 #if 0 179 if (!(prefix & PREFIX_OP) && ((prefix & PREFIX_OWNER) || (prefix & PREFIX_ADMIN)) && 180 !check_channel_access_membership(lp, "oaq")) 181 { 182 sendnumeric(client, ERR_CHANOPRIVSNEEDED, channel->name); 183 return 0; 184 } 185 #endif 186 187 return 1; 188 } 189 190 int has_client_mtags(MessageTag *mtags) 191 { 192 MessageTag *m; 193 194 for (m = mtags; m; m = m->next) 195 if (*m->name == '+') 196 return 1; 197 return 0; 198 } 199 200 /* General message handler to users and channels. Used by PRIVMSG, NOTICE, etc. 201 */ 202 void cmd_message(Client *client, MessageTag *recv_mtags, int parc, const char *parv[], SendType sendtype) 203 { 204 Client *target; 205 Channel *channel; 206 char targets[BUFSIZE]; 207 char *targetstr, *p, *p2, *pc; 208 const char *text, *errmsg; 209 int ret; 210 int ntargets = 0; 211 const char *cmd = sendtype_to_cmd(sendtype); 212 int maxtargets = max_targets_for_command(cmd); 213 Hook *h; 214 MessageTag *mtags; 215 int sendflags; 216 217 /* Force a labeled-response, even if we don't send anything 218 * and the request was sent to other servers (which won't 219 * reply either :D). 220 */ 221 labeled_response_force = 1; 222 223 if (parc < 2 || *parv[1] == '\0') 224 { 225 sendnumeric(client, ERR_NORECIPIENT, cmd); 226 return; 227 } 228 229 if ((sendtype != SEND_TYPE_TAGMSG) && (parc < 3 || *parv[2] == '\0')) 230 { 231 sendnumeric(client, ERR_NOTEXTTOSEND); 232 return; 233 } 234 235 if (MyConnect(client)) 236 parv[1] = (char *)canonize(parv[1]); 237 238 strlcpy(targets, parv[1], sizeof(targets)); 239 for (p = NULL, targetstr = strtoken(&p, targets, ","); targetstr; targetstr = strtoken(&p, NULL, ",")) 240 { 241 if (MyUser(client) && (++ntargets > maxtargets)) 242 { 243 sendnumeric(client, ERR_TOOMANYTARGETS, targetstr, maxtargets, cmd); 244 break; 245 } 246 247 /* The nicks "ircd" and "irc" are special (and reserved) */ 248 if (!strcasecmp(targetstr, "ircd") && MyUser(client)) 249 return; 250 251 if (!strcasecmp(targetstr, "irc") && MyUser(client)) 252 { 253 /* When ban version { } is enabled the IRCd sends a CTCP VERSION request 254 * from the "IRC" nick. So we need to handle CTCP VERSION replies to "IRC". 255 */ 256 if (!strncmp(parv[2], "\1VERSION ", 9)) 257 ban_version(client, parv[2] + 9); 258 else if (!strncmp(parv[2], "\1SCRIPT ", 8)) 259 ban_version(client, parv[2] + 8); 260 return; 261 } 262 263 p2 = strchr(targetstr, '#'); 264 265 /* Message to channel */ 266 if (p2 && (channel = find_channel(p2))) 267 { 268 char pfixchan[CHANNELLEN + 4]; 269 int replaced = 0; 270 char member_modes_tmp[2]; 271 char *member_modes = NULL; 272 if (p2 - targetstr > 0) 273 { 274 /* There is (posssibly) a prefix involved... */ 275 char prefix_tmp[32]; 276 char prefix; 277 strlncpy(prefix_tmp, targetstr, sizeof(prefix_tmp), p2 - targetstr); 278 prefix = lowest_ranking_prefix(prefix_tmp); 279 if (prefix) 280 { 281 /* Rewrite the target. Eg: @&~#chan becomes @#chan */ 282 snprintf(pfixchan, sizeof(pfixchan), "%c%s", prefix, channel->name); 283 targetstr = pfixchan; 284 replaced = 1; 285 /* And set 'member_modes' */ 286 member_modes_tmp[0] = prefix_to_mode(prefix); 287 member_modes_tmp[1] = '\0'; 288 member_modes = member_modes_tmp; 289 /* Oh, and some access check */ 290 if (MyUser(client) && !can_send_to_member_mode(client, channel, *member_modes)) 291 continue; 292 } 293 } 294 if (!replaced) 295 { 296 /* Replace target so the privmsg always goes to the "official" channel name */ 297 strlcpy(pfixchan, channel->name, sizeof(pfixchan)); 298 targetstr = pfixchan; 299 } 300 301 if (IsVirus(client) && strcasecmp(channel->name, SPAMFILTER_VIRUSCHAN)) 302 { 303 sendnotice(client, "You are only allowed to talk in '%s'", SPAMFILTER_VIRUSCHAN); 304 continue; 305 } 306 307 text = parv[2]; 308 errmsg = NULL; 309 if (MyUser(client) && !IsULine(client)) 310 { 311 if (!can_send_to_channel(client, channel, &text, &errmsg, sendtype)) 312 { 313 /* Send the error message, but only if: 314 * 1) The user has not been killed 315 * 2) It is not a NOTICE 316 */ 317 if (IsDead(client)) 318 return; 319 if (!IsDead(client) && (sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg)) 320 sendnumeric(client, ERR_CANNOTSENDTOCHAN, channel->name, errmsg, p2); 321 continue; /* skip delivery to this target */ 322 } 323 } 324 mtags = NULL; 325 sendflags = SEND_ALL; 326 327 if (!strchr(CHANCMDPFX,parv[2][0])) 328 sendflags |= SKIP_DEAF; 329 330 if ((*parv[2] == '\001') && strncmp(&parv[2][1], "ACTION ", 7)) 331 sendflags |= SKIP_CTCP; 332 333 if (MyUser(client)) 334 { 335 int spamtype = (sendtype == SEND_TYPE_NOTICE ? SPAMF_CHANNOTICE : SPAMF_CHANMSG); 336 337 if (match_spamfilter(client, text, spamtype, cmd, channel->name, 0, NULL)) 338 return; 339 } 340 341 new_message(client, recv_mtags, &mtags); 342 343 RunHook(HOOKTYPE_PRE_CHANMSG, client, channel, mtags, text, sendtype); 344 345 if (!text) 346 { 347 free_message_tags(mtags); 348 continue; 349 } 350 351 if (sendtype != SEND_TYPE_TAGMSG) 352 { 353 /* PRIVMSG or NOTICE */ 354 sendto_channel(channel, client, client->direction, 355 member_modes, 0, sendflags, mtags, 356 ":%s %s %s :%s", 357 client->name, cmd, targetstr, text); 358 } else { 359 /* TAGMSG: 360 * Only send if the message includes any user message tags 361 * and if the 'message-tags' module is loaded. 362 * Do not allow empty and useless TAGMSG. 363 */ 364 if (!CAP_MESSAGE_TAGS || !has_client_mtags(mtags)) 365 { 366 free_message_tags(mtags); 367 continue; 368 } 369 sendto_channel(channel, client, client->direction, 370 member_modes, CAP_MESSAGE_TAGS, sendflags, mtags, 371 ":%s TAGMSG %s", 372 client->name, targetstr); 373 } 374 375 RunHook(HOOKTYPE_CHANMSG, client, channel, sendflags, member_modes, targetstr, mtags, text, sendtype); 376 377 free_message_tags(mtags); 378 379 continue; 380 } 381 else if (p2) 382 { 383 sendnumeric(client, ERR_NOSUCHNICK, p2); 384 continue; 385 } 386 387 388 /* Message to $servermask */ 389 if (*targetstr == '$') 390 { 391 MessageTag *mtags = NULL; 392 393 if (!ValidatePermissionsForPath("chat:notice:global", client, NULL, NULL, NULL)) 394 { 395 /* Apparently no other IRCd does this, but I think it's confusing not to 396 * send an error message, especially with our new privilege system. 397 * Error message could be more descriptive perhaps. 398 */ 399 sendnumeric(client, ERR_NOPRIVILEGES); 400 continue; 401 } 402 new_message(client, recv_mtags, &mtags); 403 sendto_match_butone(IsServer(client->direction) ? client->direction : NULL, 404 client, targetstr + 1, 405 (*targetstr == '#') ? MATCH_HOST : 406 MATCH_SERVER, 407 mtags, 408 ":%s %s %s :%s", client->name, cmd, targetstr, parv[2]); 409 free_message_tags(mtags); 410 continue; 411 } 412 413 /* nickname addressed? */ 414 target = hash_find_nickatserver(targetstr, NULL); 415 if (target) 416 { 417 const char *errmsg = NULL; 418 text = parv[2]; 419 if (!can_send_to_user(client, target, &text, &errmsg, sendtype)) 420 { 421 /* Message is discarded */ 422 if (IsDead(client)) 423 return; 424 if ((sendtype != SEND_TYPE_NOTICE) && !BadPtr(errmsg)) 425 sendnumeric(client, ERR_CANTSENDTOUSER, target->name, errmsg); 426 } else 427 { 428 /* We may send the message */ 429 MessageTag *mtags = NULL; 430 431 /* Inform sender that recipient is away, if this is so */ 432 if ((sendtype == SEND_TYPE_PRIVMSG) && MyConnect(client) && target->user && target->user->away) 433 sendnumeric(client, RPL_AWAY, target->name, target->user->away); 434 435 new_message(client, recv_mtags, &mtags); 436 if ((sendtype == SEND_TYPE_TAGMSG) && !has_client_mtags(mtags)) 437 { 438 free_message_tags(mtags); 439 continue; 440 } 441 labeled_response_inhibit = 1; 442 if (MyUser(target)) 443 { 444 /* Deliver to end-user */ 445 if (sendtype == SEND_TYPE_TAGMSG) 446 { 447 if (HasCapability(target, "message-tags")) 448 { 449 sendto_prefix_one(target, client, mtags, ":%s %s %s", 450 client->name, cmd, target->name); 451 } 452 } else { 453 sendto_prefix_one(target, client, mtags, ":%s %s %s :%s", 454 client->name, cmd, target->name, text); 455 } 456 } else { 457 /* Send to another server */ 458 if (sendtype == SEND_TYPE_TAGMSG) 459 { 460 sendto_prefix_one(target, client, mtags, ":%s %s %s", 461 client->id, cmd, target->id); 462 } else { 463 sendto_prefix_one(target, client, mtags, ":%s %s %s :%s", 464 client->id, cmd, target->id, text); 465 } 466 } 467 labeled_response_inhibit = 0; 468 RunHook(HOOKTYPE_USERMSG, client, target, mtags, text, sendtype); 469 free_message_tags(mtags); 470 continue; 471 } 472 continue; /* Message has been delivered or rejected, continue with next target */ 473 } 474 475 /* If nick@server -and- the @server portion was set::services-server then send a special message */ 476 if (!target && SERVICES_NAME) 477 { 478 char *server = strchr(targetstr, '@'); 479 if (server && strncasecmp(server + 1, SERVICES_NAME, strlen(SERVICES_NAME)) == 0) 480 { 481 sendnumeric(client, ERR_SERVICESDOWN, targetstr); 482 continue; 483 } 484 } 485 486 /* nothing, nada, not anything found */ 487 sendnumeric(client, ERR_NOSUCHNICK, targetstr); 488 continue; 489 } 490 } 491 492 /* 493 ** cmd_private 494 ** parv[1] = receiver list 495 ** parv[2] = message text 496 */ 497 CMD_FUNC(cmd_private) 498 { 499 cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_PRIVMSG); 500 } 501 502 /* 503 ** cmd_notice 504 ** parv[1] = receiver list 505 ** parv[2] = notice text 506 */ 507 CMD_FUNC(cmd_notice) 508 { 509 cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_NOTICE); 510 } 511 512 /* 513 ** cmd_tagmsg 514 ** parv[1] = receiver list 515 */ 516 CMD_FUNC(cmd_tagmsg) 517 { 518 /* compatibility hack */ 519 parv[2] = ""; 520 parv[3] = NULL; 521 cmd_message(client, recv_mtags, parc, parv, SEND_TYPE_TAGMSG); 522 } 523 524 /* Taken from xchat by Peter Zelezny 525 * changed very slightly by codemastr 526 * RGB color stripping support added -- codemastr 527 * 528 * NOTE: if you change/update/enhance StripColors() then consider changing 529 * the StripControlCodes() function as well (in misc.c) !! 530 */ 531 const char *_StripColors(const char *text) 532 { 533 int i = 0, len = strlen(text), save_len=0; 534 char nc = 0, col = 0, rgb = 0; 535 const char *save_text=NULL; 536 static char new_str[4096]; 537 538 while (len > 0) 539 { 540 if ((col && isdigit(*text) && nc < 2) || 541 ((col == 1) && (*text == ',') && isdigit(text[1]) && (nc > 0) && (nc < 3))) 542 { 543 nc++; 544 if (*text == ',') 545 { 546 nc = 0; 547 col++; 548 } 549 } 550 /* Syntax for RGB is ^DHHHHHH where H is a hex digit. 551 * If < 6 hex digits are specified, the code is displayed 552 * as text 553 */ 554 else if ((rgb && isxdigit(*text) && nc < 6) || (rgb && *text == ',' && nc < 7)) 555 { 556 nc++; 557 if (*text == ',') 558 nc = 0; 559 } 560 else 561 { 562 if (col) 563 col = 0; 564 if (rgb) 565 { 566 if (nc != 6) 567 { 568 text = save_text+1; 569 len = save_len-1; 570 rgb = 0; 571 continue; 572 } 573 rgb = 0; 574 } 575 if (*text == '\003') 576 { 577 col = 1; 578 nc = 0; 579 } 580 else if (*text == '\004') 581 { 582 save_text = text; 583 save_len = len; 584 rgb = 1; 585 nc = 0; 586 } 587 else if (*text != '\026') /* (strip reverse too) */ 588 { 589 new_str[i] = *text; 590 i++; 591 } 592 } 593 text++; 594 len--; 595 } 596 new_str[i] = 0; 597 if (new_str[0] == '\0') 598 return NULL; 599 return new_str; 600 } 601 602 /** Check ban version { } blocks, returns 1 if banned and 0 if not. */ 603 int ban_version(Client *client, const char *text) 604 { 605 int len; 606 ConfigItem_ban *ban; 607 char ctcp_reply[BUFSIZE]; 608 609 strlcpy(ctcp_reply, text, sizeof(ctcp_reply)); 610 len = strlen(ctcp_reply); 611 if (!len) 612 return 0; 613 614 if (ctcp_reply[len-1] == '\1') 615 ctcp_reply[len-1] = '\0'; /* remove CTCP REPLY terminator (ASCII 1) */ 616 617 if ((ban = find_ban(NULL, ctcp_reply, CONF_BAN_VERSION))) 618 { 619 if (IsSoftBanAction(ban->action) && IsLoggedIn(client)) 620 return 0; /* soft ban does not apply to us, we are logged in */ 621 622 if (find_tkl_exception(TKL_BAN_VERSION, client)) 623 return 0; /* we are exempt */ 624 625 place_host_ban(client, ban->action, ban->reason, BAN_VERSION_TKL_TIME); 626 return 1; 627 } 628 629 return 0; 630 } 631 632 /** Can user send a message to this channel? 633 * @param client The client 634 * @param channel The channel 635 * @param msgtext The message to send (MAY be changed, even if user is allowed to send) 636 * @param errmsg The error message (will be filled in) 637 * @param sendtype One of SEND_TYPE_* 638 * @returns Returns 1 if the user is allowed to send, otherwise 0. 639 * (note that this behavior was reversed in UnrealIRCd versions <5.x. 640 */ 641 int _can_send_to_channel(Client *client, Channel *channel, const char **msgtext, const char **errmsg, SendType sendtype) 642 { 643 Membership *lp; 644 int member, i = 0; 645 Hook *h; 646 647 if (!MyUser(client)) 648 return 1; 649 650 *errmsg = NULL; 651 652 member = IsMember(client, channel); 653 654 lp = find_membership_link(client->user->channel, channel); 655 656 /* Modules can plug in as well */ 657 for (h = Hooks[HOOKTYPE_CAN_SEND_TO_CHANNEL]; h; h = h->next) 658 { 659 i = (*(h->func.intfunc))(client, channel, lp, msgtext, errmsg, sendtype); 660 if (i != HOOK_CONTINUE) 661 { 662 if (!*errmsg) 663 { 664 unreal_log(ULOG_ERROR, "main", "BUG_CAN_SEND_TO_CHANNEL_NO_ERRMSG", client, 665 "[BUG] Module $module did not set errmsg!!!", 666 log_data_string("module", h->owner->header->name)); 667 abort(); 668 } 669 break; 670 } 671 if (!*msgtext || !**msgtext) 672 { 673 if (sendtype != SEND_TYPE_TAGMSG) 674 return 0; 675 else 676 *msgtext = ""; 677 } 678 } 679 680 if (i != HOOK_CONTINUE) 681 { 682 if (!*errmsg) 683 *errmsg = "You are banned"; 684 /* Don't send message if the user was previously a member 685 * and isn't anymore, so if the user is KICK'ed, eg by floodprot. 686 */ 687 if (member && !IsDead(client) && !find_membership_link(client->user->channel, channel)) 688 *errmsg = NULL; 689 return 0; 690 } 691 692 /* Now we are going to check bans */ 693 694 /* ..but first: exempt ircops */ 695 if (op_can_override("channel:override:message:ban",client,channel,NULL)) 696 return 1; 697 698 /* If local client is banned and not +vhoaq... */ 699 if (MyUser(client) && 700 !check_channel_access_membership(lp, "vhoaq") && 701 is_banned(client, channel, BANCHK_MSG, msgtext, errmsg)) 702 { 703 /* Modules can set 'errmsg', otherwise we default to this: */ 704 if (!*errmsg) 705 *errmsg = "You are banned"; 706 return 0; 707 } 708 709 return 1; 710 }