/* * ctcp.c: handles the client-to-client protocol(ctcp). * * Written By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2000 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "irc.h" IRCII_RCSID("@(#)$Id: ctcp.c,v 1.61 2000/07/18 17:12:18 mrg Exp $"); #ifndef _Windows #include #endif /* _Windows */ #ifdef HAVE_UNAME # include #endif /* HAVE_UNAME */ #include "ircaux.h" #include "hook.h" #include "crypt.h" #include "ctcp.h" #include "vars.h" #include "server.h" #include "status.h" #include "lastlog.h" #include "ignore.h" #include "output.h" #include "window.h" #include "dcc.h" #include "names.h" #include "parse.h" static u_char FAR CTCP_Reply_Buffer[BIG_BUFFER_SIZE + 1] = ""; static void do_new_notice_ctcp _((u_char *, u_char *, u_char **, u_char *)); /* forward declarations for the built in CTCP functions */ static u_char *do_crypto _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_version _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_clientinfo _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_echo _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_userinfo _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_finger _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_time _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_atmosphere _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_dcc _((CtcpEntry *, u_char *, u_char *, u_char *)); static u_char *do_utc _((CtcpEntry *, u_char *, u_char *, u_char *)); static CtcpEntry ctcp_cmd[] = { { UP("VERSION"), UP("shows client type, version and environment"), CTCP_VERBOSE, do_version }, { UP("CLIENTINFO"), UP("gives information about available CTCP commands"), CTCP_VERBOSE, do_clientinfo }, { UP("USERINFO"), UP("returns user settable information"), CTCP_VERBOSE, do_userinfo }, #define CTCP_ERRMSG 3 { UP("ERRMSG"), UP("returns error messages"), CTCP_VERBOSE, do_echo }, { UP("FINGER"), UP("shows real name, login name and idle time of user"), CTCP_VERBOSE, do_finger }, { UP("TIME"), UP("tells you the time on the user's host"), CTCP_VERBOSE, do_time }, { UP("ACTION"), UP("contains action descriptions for atmosphere"), CTCP_SHUTUP, do_atmosphere }, { UP("DCC"), UP("requests a direct_client_connection"), CTCP_SHUTUP | CTCP_NOREPLY, do_dcc }, { UP("UTC"), UP("substitutes the local timezone"), CTCP_SHUTUP | CTCP_NOREPLY, do_utc }, { UP("PING"), UP("returns the arguments it receives"), CTCP_VERBOSE, do_echo }, { UP("ECHO"), UP("returns the arguments it receives"), CTCP_VERBOSE, do_echo }, CRYPTO_CTCP_ENTRIES }; #define NUMBER_OF_CTCPS (sizeof(ctcp_cmd) / sizeof(CtcpEntry)) /* XXX */ u_char *ctcp_type[] = { UP("PRIVMSG"), UP("NOTICE") }; static u_char FAR ctcp_buffer[BIG_BUFFER_SIZE + 1] = ""; /* This is set to one if we parsed an SED */ int sed = 0; /* * in_ctcp_flag is set to true when IRCII is handling a CTCP request. This * is used by the ctcp() sending function to force NOTICEs to be used in any * CTCP REPLY */ int in_ctcp_flag = 0; /* * quote_it: This quotes the given string making it sendable via irc. A * pointer to the length of the data is required and the data need not be * null terminated (it can contain nulls). Returned is a malloced, null * terminated string. */ u_char * ctcp_quote_it(str, len) u_char *str; size_t len; { u_char lbuf[BIG_BUFFER_SIZE + 1]; u_char *ptr; int i; ptr = lbuf; for (i = 0; i < len; i++) { switch (str[i]) { case CTCP_DELIM_CHAR: *(ptr++) = CTCP_QUOTE_CHAR; *(ptr++) = 'a'; break; case '\n': *(ptr++) = CTCP_QUOTE_CHAR; *(ptr++) = 'n'; break; case '\r': *(ptr++) = CTCP_QUOTE_CHAR; *(ptr++) = 'r'; break; case CTCP_QUOTE_CHAR: *(ptr++) = CTCP_QUOTE_CHAR; *(ptr++) = CTCP_QUOTE_CHAR; break; case '\0': *(ptr++) = CTCP_QUOTE_CHAR; *(ptr++) = '0'; break; case ':': *(ptr++) = CTCP_QUOTE_CHAR; default: *(ptr++) = str[i]; break; } } *ptr = '\0'; str = (u_char *) 0; malloc_strcpy(&str, lbuf); return (str); } /* * ctcp_unquote_it: This takes a null terminated string that had previously * been quoted using ctcp_quote_it and unquotes it. Returned is a malloced * space pointing to the unquoted string. The len is modified to contain * the size of the data returned. */ u_char * ctcp_unquote_it(str, len) u_char *str; size_t *len; { u_char *lbuf; u_char *ptr; u_char c; int i, new_size = 0; lbuf = (u_char *) new_malloc(sizeof(u_char) * *len); ptr = lbuf; i = 0; while (i < *len) { if ((c = str[i++]) == CTCP_QUOTE_CHAR) { switch (c = str[i++]) { case CTCP_QUOTE_CHAR: *(ptr++) = CTCP_QUOTE_CHAR; break; case 'a': *(ptr++) = CTCP_DELIM_CHAR; break; case 'n': *(ptr++) = '\n'; break; case 'r': *(ptr++) = '\r'; break; case '0': *(ptr++) = '\0'; break; default: *(ptr++) = c; break; } } else *(ptr++) = c; new_size++; } *len = new_size; return (lbuf); } /* * do_crypto: performs the ecrypted data trasfer for ctcp. Returns in a * malloc string the decryped message (if a key is set for that user) or the * text "[ENCRYPTED MESSAGE]" */ static u_char * do_crypto(ctcp, from, to, args) CtcpEntry *ctcp; u_char *from, *to, *args; { crypt_key *key; u_char *crypt_who, *msg; u_char *ret = NULL; if (is_channel(to)) crypt_who = to; else crypt_who = from; if ((key = is_crypted(crypt_who)) && (msg = crypt_msg(args, key, 0))) { /* this doesn't work ... ... when it does, set this to 0 ... */ static int the_youth_of_america_on_elle_esse_dee = 1; malloc_strcpy(&ret, msg); /* * since we are decrypting, run it through do_ctcp() again * to detect embeded CTCP messages, in an encrypted message. * we avoid recusing here more than once. */ if (the_youth_of_america_on_elle_esse_dee++ == 0) ret = do_ctcp(from, to, ret); the_youth_of_america_on_elle_esse_dee--; sed = 1; } else malloc_strcpy(&ret, UP("[ENCRYPTED MESSAGE]")); return (ret); } /* * do_clientinfo: performs the CLIENTINFO CTCP. If cmd is empty, returns the * list of all CTCPs currently recognized by IRCII. If an arg is supplied, * it returns specific information on that CTCP. If a matching CTCP is not * found, an ERRMSG ctcp is returned */ static u_char * do_clientinfo(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { int i; u_char *ucmd = (u_char *) 0; u_char buffer[BIG_BUFFER_SIZE]; if (cmd && *cmd) { malloc_strcpy(&ucmd, cmd); upper(ucmd); for (i = 0; i < NUMBER_OF_CTCPS; i++) { if (my_strcmp(ucmd, ctcp_cmd[i].name) == 0) { send_ctcp_reply(from, ctcp->name, "%s %s", ctcp_cmd[i].name, ctcp_cmd[i].desc); return NULL; } } send_ctcp_reply(from, ctcp_cmd[CTCP_ERRMSG].name, "%s: %s is not a valid function", ctcp->name, cmd); } else { *buffer = '\0'; for (i = 0; i < NUMBER_OF_CTCPS; i++) { my_strmcat(buffer, ctcp_cmd[i].name, BIG_BUFFER_SIZE); my_strmcat(buffer, " ", BIG_BUFFER_SIZE); } send_ctcp_reply(from, ctcp->name, "%s :Use CLIENTINFO to get more specific information", buffer); } return NULL; } /* do_version: does the CTCP VERSION command */ static u_char * do_version(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { #if defined(PARANOID) send_ctcp_reply(from, ctcp->name, "ircII user"); #else u_char *tmp; #if defined(HAVE_UNAME) struct utsname un; u_char *the_unix, *the_version; if (uname(&un) < 0) { the_version = empty_string; the_unix = UP("unknown"); } else { the_version = UP(un.release); the_unix = UP(un.sysname); } send_ctcp_reply(from, ctcp->name, "ircII %s %s %s :%s", irc_version, the_unix, the_version, #else #ifdef _Windows send_ctcp_reply(from, ctcp->name, "ircII %s MS-Windows :%s", irc_version, #else send_ctcp_reply(from, ctcp->name, "ircII %s *IX :%s", irc_version, #endif /* _Windows */ #endif /* HAVE_UNAME */ (tmp = get_string_var(CLIENTINFO_VAR)) ? tmp : UP(IRCII_COMMENT)); #endif /* PARANOID */ return NULL; } /* do_time: does the CTCP TIME command --- done by Veggen */ static u_char * do_time(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { time_t tm = time((time_t *) 0); u_char *s, *t = (u_char *) ctime(&tm); if ((u_char *) 0 != (s = my_index(t, '\n'))) *s = '\0'; send_ctcp_reply(from, ctcp->name, "%s", t); return NULL; } /* do_userinfo: does the CTCP USERINFO command */ static u_char * do_userinfo(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { send_ctcp_reply(from, ctcp->name, "%s", get_string_var(USER_INFO_VAR)); return NULL; } /* * do_echo: does the CTCP ECHO, CTCP ERRMSG and CTCP PING commands. Does * not send an error for ERRMSG and if the CTCP was sent to a channel. */ static u_char * do_echo(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { if (!is_channel(to) || my_strncmp(cmd, "ERRMSG", 6)) send_ctcp_reply(from, ctcp->name, "%s", cmd); return NULL; } static u_char * do_finger(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { #if defined(PARANOID) send_ctcp_reply(from, ctcp->name, "ircII user"); #else struct passwd *pwd; time_t diff; unsigned uid; u_char c; /* * sojge complained that ircII says 'idle 1 seconds' * well, now he won't ever get the chance to see that message again * *grin* ;-) -lynx * * Made this better by saying 'idle 1 second' -phone */ diff = time(0) - idle_time; c = (diff == 1) ? ' ' : 's'; /* XXX - fix me */ # ifdef _Windows send_ctcp_reply(from, ctcp->name, "IRCII For MS-Windows User Idle %d second%c", (int)diff, c); # else uid = getuid(); # ifdef DAEMON_UID if (uid != DAEMON_UID) { # endif /* DAEMON_UID */ if ((pwd = getpwuid(uid)) != NULL) { u_char *tmp; # ifndef GECOS_DELIMITER # define GECOS_DELIMITER ',' # endif /* GECOS_DELIMITER */ if ((tmp = my_index(pwd->pw_gecos, GECOS_DELIMITER)) != NULL) *tmp = '\0'; send_ctcp_reply(from, ctcp->name, "%s (%s@%s) Idle %d second%c", pwd->pw_gecos, pwd->pw_name, hostname, (int)diff, c); } # ifdef DAEMON_UID } else send_ctcp_reply(from, ctcp->name, "IRCII Telnet User (%s) Idle %d second%c", realname, (int)diff, c); # endif /* DAEMON_UID */ # endif /* _Windows */ #endif /* PARANOID */ return NULL; } /* * do_atmosphere: does the CTCP ACTION command --- done by lynX * Changed this to make the default look less offensive to people * who don't like it and added a /on ACTION. This is more in keeping * with the design philosophy behind IRCII */ static u_char * do_atmosphere(ctcp, from, to, cmd) CtcpEntry *ctcp; u_char *from, *to, *cmd; { if (cmd && *cmd) { int old; save_message_from(); old = set_lastlog_msg_level(LOG_ACTION); if (is_channel(to)) { message_from(to, LOG_ACTION); if (do_hook(ACTION_LIST, "%s %s %s", from, to, cmd)) { if (is_current_channel(to, parsing_server_index, 0)) put_it("* %s %s", from, cmd); else put_it("* %s:%s %s", from, to, cmd); } } else { if ('=' == *from) { set_lastlog_msg_level(LOG_DCC); message_from(from+1, LOG_DCC); } else message_from(from, LOG_ACTION); if (do_hook(ACTION_LIST, "%s %s %s", from, to, cmd)) put_it("*> %s %s", from, cmd); } set_lastlog_msg_level(old); restore_message_from(); } return NULL; } /* * do_dcc: Records data on an incoming DCC offer. Makes sure it's a * user->user CTCP, as channel DCCs don't make any sense whatsoever */ static u_char * do_dcc(ctcp, from, to, args) CtcpEntry *ctcp; u_char *from, *to, *args; { u_char *type; u_char *description; u_char *inetaddr; u_char *port; u_char *size; if (my_stricmp(to, get_server_nickname(parsing_server_index))) return NULL; if (!(type = next_arg(args, &args)) || !(description = next_arg(args, &args)) || !(inetaddr = next_arg(args, &args)) || !(port = next_arg(args, &args))) return NULL; size = next_arg(args, &args); register_dcc_offer(from, type, description, inetaddr, port, size); return NULL; } /* * do_utc: converts `UTC ' into a date string, for # inclusion into the normal chat stream. */ static u_char * do_utc(ctcp, from, to, args) CtcpEntry *ctcp; u_char *from, *to, *args; { time_t tm; u_char *date = NULL; if (!args || !*args) return NULL; tm = my_atol(args); malloc_strcpy(&date, UP(ctime(&tm))); date[my_strlen(date)-1] = '\0'; return date; } /* * do_ctcp: handles the client to client protocol embedded in PRIVMSGs. Any * such messages are removed from the original str, so after do_ctcp() * returns, str will be changed */ u_char * do_ctcp(from, to, str) u_char *from, *to, *str; { int i = 0, ctcp_flag = 1; u_char *end, *cmd, *args, *ptr; u_char *arg_copy = NULL; int flag; int messages = 0; time_t curtime = time(NULL); flag = double_ignore(from, FromUserHost, IGNORE_CTCPS); if (!in_ctcp_flag) in_ctcp_flag = 1; *ctcp_buffer = '\0'; while ((cmd = my_index(str, CTCP_DELIM_CHAR)) != NULL) { if (messages > 3) break; *(cmd++) = '\0'; my_strcat(ctcp_buffer, str); if ((end = my_index(cmd, CTCP_DELIM_CHAR)) != NULL) { messages++; if (flag == IGNORED) continue; *(end++) = '\0'; if ((args = my_index(cmd, ' ')) != NULL) *(args++) = '\0'; else args = empty_string; /* Skip leading : for arguments */ if (*args == ':') ++args; malloc_strcpy(&arg_copy, args); for (i = 0; i < NUMBER_OF_CTCPS; i++) { if (my_strcmp(cmd, ctcp_cmd[i].name) == 0) { /* protect against global (oper) messages */ if (*to != '$' && !(*to == '#' && !lookup_channel(to, parsing_server_index, CHAN_NOUNLINK))) { ptr = ctcp_cmd[i].func(&ctcp_cmd[i], from, to, arg_copy); if (ptr) { my_strcat(ctcp_buffer, ptr); new_free(&ptr); } } ctcp_flag = ctcp_cmd[i].flag; cmd = ctcp_cmd[i].name; break; } } new_free(&arg_copy); if (in_ctcp_flag == 1 && do_hook(CTCP_LIST, "%s %s %s %s", from, to, cmd, args) && get_int_var(VERBOSE_CTCP_VAR)) { int lastlog_level; save_message_from(); lastlog_level = set_lastlog_msg_level(LOG_CTCP); message_from((u_char *) 0, LOG_CTCP); if (i == NUMBER_OF_CTCPS) { say("Unknown CTCP %s from %s to %s: %s%s", cmd, from, to, *args ? (u_char *) ": " : empty_string, args); } else if (ctcp_flag & CTCP_VERBOSE) { if (my_stricmp(to, get_server_nickname(parsing_server_index))) say("CTCP %s from %s to %s: %s", cmd, from, to, args); else say("CTCP %s from %s%s%s", cmd, from, *args ? (u_char *) ": " : empty_string, args); } set_lastlog_msg_level(lastlog_level); restore_message_from(); } str = end; } else { my_strcat(ctcp_buffer, CTCP_DELIM_STR); str = cmd; } } if (in_ctcp_flag == 1) in_ctcp_flag = 0; if (CTCP_Reply_Buffer && *CTCP_Reply_Buffer) { #ifdef PARANOID /* * paranoid users don't want to send ctcp replies to * requests send to channels, probably... */ if (is_channel(to)) goto clear_ctcp_reply_buffer; #endif /* * Newave ctcp flood protection : each time you are requested to send * more than CTCP_REPLY_FLOOD_SIZE bytes in CTCP_REPLY_BACKLOG_SECONDS * no ctcp replies will be done for CTCP_REPLY_IGNORE_SECONDS. * Current default is 256 bytes/ 5s/ 10s * This is a sliding window, i.e. you can't get caught sending too much * because of a 5s boundary, and the checking is still active even if * you don't reply anymore. */ if (*from == '=') send_ctcp(ctcp_type[CTCP_NOTICE], from, NULL, "%s", CTCP_Reply_Buffer); else { Server *cur_serv = &server_list[parsing_server_index]; int no_reply, no_flood = get_int_var(NO_CTCP_FLOOD_VAR), delta = cur_serv->ctcp_last_reply_time ? curtime-cur_serv->ctcp_last_reply_time : 0, size = 0, was_ignoring = cur_serv->ctcp_flood_time != 0, crbs = get_int_var(CTCP_REPLY_BACKLOG_SECONDS_VAR), crfs = get_int_var(CTCP_REPLY_FLOOD_SIZE_VAR), cris = get_int_var(CTCP_REPLY_IGNORE_SECONDS_VAR); cur_serv->ctcp_last_reply_time = curtime; if (delta) { for (i = crbs - 1; i >= delta; i--) cur_serv->ctcp_send_size[i] = cur_serv->ctcp_send_size[i - delta]; for (i = 0; i < delta && i < crbs; i++) cur_serv->ctcp_send_size[i] = 0; } cur_serv->ctcp_send_size[0] += my_strlen(CTCP_Reply_Buffer); for (i = 0; i < crbs; i++) size += cur_serv->ctcp_send_size[i]; if (size >= crfs) cur_serv->ctcp_flood_time = curtime; no_reply = cur_serv->ctcp_flood_time && (curtime <= cur_serv->ctcp_flood_time+cris); if (no_flood && get_int_var(VERBOSE_CTCP_VAR)) { save_message_from(); message_from((u_char *) 0, LOG_CTCP); if (no_reply && was_ignoring == 0) say("CTCP flood detected - suspending replies"); else if (no_reply == 0 && was_ignoring) say("CTCP reply suspending time elapsed - replying normally"); restore_message_from(); } if (no_flood == 0 || no_reply == 0) { cur_serv->ctcp_flood_time = 0; send_ctcp(ctcp_type[CTCP_NOTICE], from, NULL, "%s", CTCP_Reply_Buffer); } } #ifdef PARANOID clear_ctcp_reply_buffer: #endif *CTCP_Reply_Buffer = '\0'; } if (*str) my_strcat(ctcp_buffer, str); return (ctcp_buffer); } u_char * do_notice_ctcp(from, to, str) u_char *from, *to, *str; { u_char *cmd; in_ctcp_flag = -1; *ctcp_buffer = '\0'; /* * The following used to say "While". It now says "if" because people * Started using CTCP ERRMSG replies to CTCP bomb. The effect of this * is that IRCII users can only send one CTCP/message if they expect a * reply. This shouldn't be a problem as that is the way IRCII operates * * Changed this behavouir to follow NO_CTCP_FLOOD */ if (get_int_var(NO_CTCP_FLOOD_VAR)) { if ((cmd = my_index(str, CTCP_DELIM_CHAR)) != NULL) do_new_notice_ctcp(from, to, &str, cmd); } else while ((cmd = my_index(str, CTCP_DELIM_CHAR)) != NULL) do_new_notice_ctcp(from, to, &str, cmd); in_ctcp_flag = 0; my_strcat(ctcp_buffer, str); return (ctcp_buffer); } static void do_new_notice_ctcp(from, to, str, cmd) u_char *from, *to, **str, *cmd; { u_char *end, *args, *ptr, *arg_copy = NULL; int flags, i, lastlog_level; flags = 0; *(cmd++) = '\0'; my_strcat(ctcp_buffer, *str); if ((end = my_index(cmd, CTCP_DELIM_CHAR)) != NULL) { *(end++) = '\0'; if ((args = my_index(cmd, ' ')) != NULL) *(args++) = '\0'; malloc_strcpy(&arg_copy, args); for (i = 0; i < NUMBER_OF_CTCPS; i++) { if ((my_strcmp(cmd, ctcp_cmd[i].name) == 0) && ctcp_cmd[i].flag & CTCP_NOREPLY) { if ((ptr = ctcp_cmd[i].func(&(ctcp_cmd[i]), from, to, arg_copy)) != NULL) { my_strcat(ctcp_buffer, ptr); new_free(&ptr); flags = ctcp_cmd[i].flag; } break; } } new_free(&arg_copy); if (!args) args = empty_string; if (do_hook(CTCP_REPLY_LIST, "%s %s %s", from, cmd, args) && !(flags & CTCP_NOREPLY)) { if (!my_strcmp(cmd, "PING")) { u_char buf[20]; time_t timediff, currenttime; currenttime = time(NULL); if (args && *args) timediff = currenttime - (time_t) my_atol(args); else timediff = (time_t) 0; sprintf(CP(buf), "%ld second%s", (long) timediff, (timediff == 1) ? "" : "s"); args = buf; } save_message_from(); lastlog_level = set_lastlog_msg_level(LOG_CTCP); message_from((u_char *) 0, LOG_CTCP); say("CTCP %s reply from %s: %s", cmd, from, args); set_lastlog_msg_level(lastlog_level); restore_message_from(); } *str = end; } else { my_strcat(ctcp_buffer, CTCP_DELIM_STR); *str = cmd; } } /* in_ctcp: simply returns the value of the ctcp flag */ int in_ctcp() { return (in_ctcp_flag); } /* These moved here because they belong here - phone */ /* * send_ctcp: A simply way to send CTCP queries. if the datatag * is NULL, we must have already formatted the ctcp reply (it has the * ctcp delimiters), so don't add them again, etc. */ void #ifdef HAVE_STDARG_H send_ctcp(u_char *type, u_char *to, u_char *datatag, char *format, ...) { va_list vl; #else send_ctcp(type, to, datatag, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) u_char *type, *to, *datatag; char *format; char *arg0, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9; { #endif /* HAVE_STDARG_H */ u_char putbuf[BIG_BUFFER_SIZE + 1], sendbuf[BIG_BUFFER_SIZE + 1]; u_char *sendp; if (in_on_who) return; /* Silently drop it on the floor */ if (format) { #ifdef HAVE_STDARG_H va_start(vl, format); vsprintf(CP(putbuf), format, vl); va_end(vl); #else sprintf(CP(putbuf), format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); #endif /* HAVE_STDARG_H */ if (datatag) { sprintf(CP(sendbuf), "%c%s %s%c", CTCP_DELIM_CHAR, datatag, putbuf, CTCP_DELIM_CHAR); sendp = sendbuf; } else sendp = putbuf; } else { sprintf(CP(sendbuf), "%c%s%c", CTCP_DELIM_CHAR, datatag, CTCP_DELIM_CHAR); sendp = sendbuf; } /* * ugh, special case dcc because we don't want to go through * send_text in it's current state. XXX - fix send_text to * deal with ctcp's as well. */ if (*to == '=') dcc_message_transmit(to + 1, sendp, DCC_CHAT, 0); else send_to_server("%s %s :%s", type, to, sendp); } /* * send_ctcp_notice: A simply way to send CTCP replies. I put this here * rather than in ctcp.c to keep my compiler quiet */ void #ifdef HAVE_STDARG_H send_ctcp_reply(u_char *to, u_char *datatag, char *format, ...) { va_list vl; #else send_ctcp_reply(to, datatag, format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) u_char *to, *datatag; char *format; char *arg0, *arg1, *arg2, *arg3, *arg4, *arg5, *arg6, *arg7, *arg8, *arg9; { #endif /* HAVE_STDARG_H */ u_char putbuf[BIG_BUFFER_SIZE + 1]; if (in_on_who) return; /* Silently drop it on the floor */ if (to && (*to == '=')) return; /* don't allow dcc replies */ my_strmcat(CTCP_Reply_Buffer, "\001", BIG_BUFFER_SIZE); my_strmcat(CTCP_Reply_Buffer, datatag, BIG_BUFFER_SIZE); my_strmcat(CTCP_Reply_Buffer, " ", BIG_BUFFER_SIZE); if (format) { #ifdef HAVE_STDARG_H va_start(vl, format); vsprintf(CP(putbuf), format, vl); va_end(vl); #else sprintf(CP(putbuf), format, arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); #endif /* HAVE_STDARG_H */ my_strmcat(CTCP_Reply_Buffer, putbuf, BIG_BUFFER_SIZE); } else my_strmcat(CTCP_Reply_Buffer, putbuf, BIG_BUFFER_SIZE); my_strmcat(CTCP_Reply_Buffer, "\001", BIG_BUFFER_SIZE); } .