home *** CD-ROM | disk | FTP | other *** search
- /*
- * ctcp.c:handles the client-to-client protocol(ctcp).
- *
- * Written By Michael Sandrof
- *
- * Copyright(c) 1990
- *
- * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT
- */
- #ifdef sgi
- # include <stdio.h>
- #endif /* sgi*/
- #include <pwd.h>
- #include "irc.h"
- #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"
-
- #define CTCP_SHUTUP 0
- #define CTCP_VERBOSE 1
- #define CTCP_NOREPLY 2
-
- static char CTCP_Reply_Buffer[BIG_BUFFER_SIZE + 1] = "";
-
- static void do_new_notice_ctcp();
-
- extern void strmcat();
-
- /* forward declarations for the built in CTCP functions */
- static char *do_sed();
- static char *do_version();
- static char *do_clientinfo();
- static char *do_echo();
- static char *do_userinfo();
- static char *do_finger();
- static char *do_time();
- static char *do_atmosphere();
- static char *do_dcc();
- extern char *do_utc();
-
- static CtcpEntry ctcp_cmd[] =
- {
- { "SED", "contains simple_encrypted_data",
- CTCP_SHUTUP | CTCP_NOREPLY, do_sed },
- { "VERSION", "shows client type, version and environment",
- CTCP_VERBOSE, do_version },
- { "CLIENTINFO", "gives information about available CTCP commands",
- CTCP_VERBOSE, do_clientinfo },
- { "USERINFO", "returns user settable information",
- CTCP_VERBOSE, do_userinfo },
- { "ERRMSG", "returns error messages",
- CTCP_VERBOSE, do_echo },
- { "FINGER", "shows real name, login name and idle time of user",
- CTCP_VERBOSE, do_finger },
- { "TIME", "tells you the time on the user's host",
- CTCP_VERBOSE, do_time },
- { "ACTION", "contains action descriptions for atmosphere",
- CTCP_SHUTUP, do_atmosphere },
- { "DCC", "requests a direct_client_connection",
- CTCP_SHUTUP | CTCP_NOREPLY, do_dcc },
- { "UTC", "substitutes the local timezone",
- CTCP_SHUTUP | CTCP_NOREPLY, do_utc },
- { "PING", "returns the arguments it receives",
- CTCP_VERBOSE, do_echo },
- { "ECHO", "returns the arguments it receives",
- CTCP_VERBOSE, do_echo }
- };
-
- char *ctcp_type[] =
- {
- "PRIVMSG",
- "NOTICE"
- };
-
- static char ctcp_buffer[BIG_BUFFER_SIZE + 1];
-
- /*
- * 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;
-
- /*
- * ctcp_last_reply_time: Used to stop flooding... We only send one ctcp
- * reply a second.. if the variable NOCTCP_FLOOD is set... If the current
- * second is still the same, we drop the request, and info the user.
- */
- Long ctcp_last_reply_time = 0;
-
- int ctcp_dropped;
-
- extern char *FromUserHost;
- /*
- * 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.
- */
- char *ctcp_quote_it(str, len)
- char *str;
- int len;
- {
- char buffer[BIG_BUFFER_SIZE + 1];
- char *ptr;
- int i;
-
- ptr = buffer;
- 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;
- default:
- *(ptr++) = str[i];
- break;
- }
- }
- *ptr = null(char); /* null terminate it anyway */
- str = null(char *);
- malloc_strcpy(&str, buffer);
- 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. NOTE: a trailing null is added for
- * convenied, but the returned data may contain nulls!. The len is modified
- * to contain the size of the data returned.
- */
- char *ctcp_unquote_it(str, len)
- char *str;
- int *len;
- {
- char *buffer;
- char *ptr;
- char c;
- int i,
- new_size = 0;
-
- buffer = (char *) new_malloc(sizeof(char) * *len);
- ptr = buffer;
- 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++) = null(char);
- break;
- default:
- *(ptr++) = c;
- break;
- }
- }
- else
- *(ptr++) = c;
- new_size++;
- }
- *ptr = null(char);
- *len = new_size;
- return (buffer);
- }
-
- /*
- * do_sed: Performs the Simple Encrypted 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 char *do_sed(ctcp, from, to, args)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *args;
- {
- char *key,
- *crypt_who;
- char *ret = null(char *);
-
- if (stricmp(to, get_server_nickname(from_server)))
- crypt_who = to;
- else
- crypt_who = from;
- if (key = is_crypted(crypt_who))
- {
- if ((ret = crypt_msg(args, key, 0)) == null(char *))
- malloc_strcpy(&ret, "[ENCRYPTED MESSAGE]");
- }
- else
- malloc_strcpy(&ret, "[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 char *do_clientinfo(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- int i;
-
- if (cmd && *cmd)
- {
- for (i = 0; i < NUMBER_OF_CTCPS; i++)
- {
- if (stricmp(cmd, ctcp_cmd[i].name) == 0)
- {
- send_ctcp_reply(from, ctcp->name, "%s %s",
- ctcp_cmd[i].name, ctcp_cmd[i].desc);
- return (null(char *));
- }
- }
- send_ctcp_reply(from, ctcp_cmd[CTCP_ERRMSG].name,
- "%s: %s is not a valid function",
- ctcp_cmd[CTCP_CLIENTINFO].name, cmd);
- }
- else
- {
- *buffer = null(char);
- for (i = 0; i < NUMBER_OF_CTCPS; i++)
- {
- strmcat(buffer, ctcp_cmd[i].name, BIG_BUFFER_SIZE);
- strmcat(buffer, " ", BIG_BUFFER_SIZE);
- }
- send_ctcp_reply(from, ctcp->name,
- "%s :Use CLIENTINFO <COMMAND> to get more specific \
- information",
- buffer);
- }
- return (null(char *));
- }
-
- /* do_version: does the CTCP VERSION command */
- static char *do_version(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- char *tmp;
-
- send_ctcp_reply(from, ctcp->name, "ircII %s *IX :%s",
- irc_version, (tmp = get_string_var(CLIENTINFO_VAR)) ?
- tmp : IRCII_COMMENT);
- return (null(char *));
- }
-
- /* do_time: does the CTCP TIME command --- done by Veggen */
- static char *do_time(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- send_ctcp_reply(from, ctcp->name, update_clock(GET_TIME));
- return (null(char *));
- }
-
- /* do_userinfo: does the CTCP USERINFO command */
- static char *do_userinfo(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- send_ctcp_reply(from, ctcp->name, get_string_var(USERINFO_VAR));
- return (null(char *));
- }
-
- /*
- * do_echo: does the CTCP ECHO, CTCP ERRMSG and CTCP ECHO commands. Does
- * not send an error for ERRMSG and if the CTCP was sent to a channel.
- */
- static char *do_echo(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- if (!is_channel(to) || strcmp(from, "ERRMSG"))
- send_ctcp_reply(from, ctcp->name, "%s", cmd);
- return (null(char *));
- }
-
- static char *do_finger(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- struct passwd *pwd;
- Long diff;
- int uid;
- 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';
- uid = getuid();
- #ifdef DAEMON_UID
- if (uid != DAEMON_UID)
- {
- #endif /* DAEMON_UID */
- if (pwd = getpwuid(uid))
- {
- char *tmp;
-
- #ifdef GECOS_DELIMITER
- if (tmp = index(pwd->pw_gecos, GECOS_DELIMITER))
- #else
- if (tmp = index(pwd->pw_gecos, ','))
- #endif /* GECOS_DELIMITER */
- *tmp = '\0';
- /* Patched to allow fake ctcp info. */
- send_ctcp_reply(from, ctcp->name,
- "%s (%s@%s) Idle %ld second%c", getenv("IRCNAME"),
- getenv("IRCUSER"), getenv("IRCHOST"), diff, c);
- }
- #ifdef DAEMON_UID
- }
- else
- send_ctcp_reply(from, ctcp->name,
- "IRCII Telnet User (%s) Idle %ld second%c",
- realname, diff, c);
- #endif /* DAEMON_UID */
- return (null(char *));
- }
-
- /*
- * 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
- */
- char *do_ctcp(from, to, str)
- char *from,
- *to,
- *str;
- {
- int i = 0,
- ctcp_flag = 1;
- char *end,
- *cmd,
- *args,
- *ptr;
- char *arg_copy = NULL;
- int flag;
-
- flag = double_ignore(from, FromUserHost, IGNORE_CTCPS);
-
- if (!in_ctcp_flag)
- in_ctcp_flag = 1;
- *ctcp_buffer = null(char);
- ctcp_dropped = 0;
- while (cmd = index(str, CTCP_DELIM_CHAR))
- {
- *(cmd++) = null(char);
- strcat(ctcp_buffer, str);
- if (end = index(cmd, CTCP_DELIM_CHAR))
- {
- if (!ctcp_dropped && (time(NULL) -
- ctcp_last_reply_time) < 2 &&
- get_int_var(NO_CTCP_FLOOD_VAR))
- {
- if (get_int_var(VERBOSE_CTCP_VAR))
- say("CTCP flood from %s", from);
- ctcp_dropped = 1;
- }
- if (flag == IGNORED)
- continue;
- *(end++) = null(char);
- if (args = index(cmd, ' '))
- *(args++) = null(char);
- else
- args = empty_string;
- malloc_strcpy(&arg_copy, args);
- if (!ctcp_dropped)
- for (i = 0; i < NUMBER_OF_CTCPS; i++)
- {
- if (strcmp(cmd, ctcp_cmd[i].name) == 0)
- {
- /*
- * This test here to stop irc operators seeing ctcp replies
- * when using global messages - phone, dec, 1992.
- */
- if (*to != '$' && !(*to == '#' &&
- !lookup_channel(to, from_server, 0)) &&
- (ptr = ctcp_cmd[i].func(&(ctcp_cmd[i]),
- from, to, arg_copy)))
- {
- strcat(ctcp_buffer, ptr);
- new_free(&ptr);
- }
- ctcp_flag = ctcp_cmd[i].flag;
- cmd = ctcp_cmd[i].name;
- break;
- }
- }
- new_free(&arg_copy);
- if (!ctcp_dropped && 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;
-
- lastlog_level = set_lastlog_msg_level(LOG_CTCP);
- message_from(null(char *), LOG_CTCP);
- if (i == NUMBER_OF_CTCPS)
- {
- say("Unknown CTCP %s from %s%s%s",
- cmd, from, *args ? ": " :
- empty_string, args);
- }
- else if (ctcp_flag & CTCP_VERBOSE)
- {
- if (stricmp(to,
- get_server_nickname(from_server)))
- say("CTCP %s from %s to %s: %s",
- cmd, from, to, args);
- else
- say("CTCP %s from %s%s%s", cmd,
- from, *args ? ": " :
- empty_string, args);
- }
- set_lastlog_msg_level(lastlog_level);
- }
- str = end;
- }
- else
- {
- strcat(ctcp_buffer, CTCP_DELIM_STR);
- str = cmd;
- }
- }
- if (in_ctcp_flag == 1)
- in_ctcp_flag = 0;
- if (CTCP_Reply_Buffer && *CTCP_Reply_Buffer)
- ctcp_last_reply_time = time(NULL);
- strcat(ctcp_buffer, str);
- send_to_server("%s", CTCP_Reply_Buffer);
- *CTCP_Reply_Buffer = '\0';
- return (ctcp_buffer);
- }
-
- char *do_notice_ctcp(from, to, str)
- char *from,
- *to,
- *str;
- {
- char *cmd;
-
- in_ctcp_flag = -1;
- *ctcp_buffer = null(char);
- /*
- * 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 = index(str, CTCP_DELIM_CHAR))
- do_new_notice_ctcp(from, to, &str, cmd);
- }
- else
- while (cmd = index(str, CTCP_DELIM_CHAR))
- do_new_notice_ctcp(from, to, &str, cmd);
- in_ctcp_flag = 0;
- strcat(ctcp_buffer, str);
- return (ctcp_buffer);
- }
-
- static void do_new_notice_ctcp(from, to, str, cmd)
- char *from,
- *to,
- **str,
- *cmd;
- {
- char *end,
- *args,
- *ptr,
- *arg_copy = NULL;
- int flags,
- i,
- lastlog_level;
-
- flags = 0;
- *(cmd++) = null(char);
- strcat(ctcp_buffer, *str);
- if (end = index(cmd, CTCP_DELIM_CHAR))
- {
- *(end++) = null(char);
- if (args = index(cmd, ' '))
- *(args++) = null(char);
- malloc_strcpy(&arg_copy, args);
- for (i = 0; i < NUMBER_OF_CTCPS; i++)
- {
- if ((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))
- {
- 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 (!strcmp(cmd, "PING"))
- {
- char buf[20];
- Long timediff,
- currenttime;
-
- currenttime = time(NULL);
- if (args && *args)
- timediff = currenttime-(Long)atoi(args);
- else
- timediff = (Long) 0;
- sprintf(buf, "%d second%s", timediff,
- (timediff == 1) ? "" : "s");
- args = buf;
- }
- lastlog_level = set_lastlog_msg_level(LOG_CTCP);
- message_from(null(char *), LOG_CTCP);
- say("CTCP %s reply from %s: %s", cmd, from,
- args);
- set_lastlog_msg_level(lastlog_level);
- }
- *str = end;
- }
- else
- {
- 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);
- }
-
- /*
- * 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 char *do_atmosphere(ctcp, from, to, cmd)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *cmd;
- {
- if (cmd && *cmd)
- {
- int old;
-
- 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, 0))
- put_it("* %s %s", from, cmd);
- else
- put_it("* %s:%s %s", from, to, cmd);
- }
- }
- else
- {
- message_from(from, LOG_ACTION);
- if (do_hook(ACTION_LIST, "%s %s %s", from, to, cmd))
- put_it("*> %s %s", from, cmd);
- }
- message_from(NULL, LOG_CRAP);
- set_lastlog_msg_level(old);
- }
- return null(char *);
- }
-
- /*
- * 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 char *do_dcc(ctcp, from, to, args)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *args;
- {
- char *type;
- char *description;
- char *inetaddr;
- char *port;
- char *size;
-
- if (stricmp(to, get_server_nickname(from_server)))
- return null(char *);
- 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;
- }
-
- char *do_utc(ctcp, from, to, args)
- CtcpEntry *ctcp;
- char *from,
- *to,
- *args;
- {
- Long tm;
- char *date = NULL;
-
- if (!args || !*args)
- return NULL;
- tm = atol(args);
- malloc_strcpy(&date, ctime(&tm));
- date[strlen(date)-1] = '\0';
- return date;
- }
-
- /* These moved here because they belong here - phone */
-
- /*
- * 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 send_ctcp(type, to, datatag, format, arg0, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8, arg9)
- char *type,
- *to,
- *datatag,
- *format;
- char *arg0,
- *arg1,
- *arg2,
- *arg3,
- *arg4,
- *arg5,
- *arg6,
- *arg7,
- *arg8,
- *arg9;
- {
- char putbuf[BIG_BUFFER_SIZE + 1];
-
- if (in_on_who)
- return; /* Silently drop it on the floor */
- if (format)
- {
- sprintf(putbuf, format, arg0, arg1, arg2, arg3, arg4, arg5,
- arg6, arg7, arg8, arg9);
- send_to_server("%s %s :%c%s %s%c", type, to, CTCP_DELIM_CHAR,
- datatag, putbuf, CTCP_DELIM_CHAR);
- }
- else
- send_to_server("%s %s :%c%s%c", type, to, CTCP_DELIM_CHAR,
- datatag, CTCP_DELIM_CHAR);
- }
-
-
- /*
- * 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 send_ctcp_reply(to, datatag, format, arg0, arg1, arg2, arg3, arg4,
- arg5, arg6, arg7, arg8, arg9)
- char *to,
- *datatag,
- *format;
- char *arg0,
- *arg1,
- *arg2,
- *arg3,
- *arg4,
- *arg5,
- *arg6,
- *arg7,
- *arg8,
- *arg9;
- {
- char putbuf[BIG_BUFFER_SIZE + 1];
-
- if (in_on_who)
- return; /* Silently drop it on the floor */
- if (!*CTCP_Reply_Buffer)
- sprintf(CTCP_Reply_Buffer, "NOTICE %s :", to);
- strmcat(CTCP_Reply_Buffer, "\001", BIG_BUFFER_SIZE);
- strmcat(CTCP_Reply_Buffer, datatag, BIG_BUFFER_SIZE);
- strmcat(CTCP_Reply_Buffer, " ", BIG_BUFFER_SIZE);
- if (format)
- {
- sprintf(putbuf, format, arg0, arg1, arg2, arg3, arg4, arg5,
- arg6, arg7, arg8, arg9);
- strmcat(CTCP_Reply_Buffer, putbuf, BIG_BUFFER_SIZE);
- }
- else
- strmcat(CTCP_Reply_Buffer, putbuf, BIG_BUFFER_SIZE);
- strmcat(CTCP_Reply_Buffer, "\001", BIG_BUFFER_SIZE);
- }
-