Mercurial > pidgin
annotate src/protocols/irc/parse.c @ 11986:bfbb1798535e
[gaim-migrate @ 14279]
Switch to using the unicode character 0x25cf instead of an asterisk as
our password masking character.
In the words of the great Christian Hammond, "By the way, isn't it
about time we replace the asterisk in masked entries with that unicode
character for the round filled circle ("?")? The asterisk is so 1980s."
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Sat, 05 Nov 2005 23:42:35 +0000 |
| parents | 73777ad45562 |
| children | 39734dd473e0 |
| rev | line source |
|---|---|
| 6333 | 1 /** |
| 2 * @file parse.c | |
| 8351 | 3 * |
| 6333 | 4 * gaim |
| 5 * | |
| 6 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> | |
| 8351 | 7 * |
| 6333 | 8 * This program is free software; you can redistribute it and/or modify |
| 9 * it under the terms of the GNU General Public License as published by | |
| 10 * the Free Software Foundation; either version 2 of the License, or | |
| 11 * (at your option) any later version. | |
| 12 * | |
| 13 * This program is distributed in the hope that it will be useful, | |
| 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 16 * GNU General Public License for more details. | |
| 17 * | |
| 18 * You should have received a copy of the GNU General Public License | |
| 19 * along with this program; if not, write to the Free Software | |
| 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 21 */ | |
| 22 | |
| 23 #include "internal.h" | |
| 24 | |
| 25 #include "accountopt.h" | |
| 26 #include "conversation.h" | |
| 27 #include "notify.h" | |
| 28 #include "debug.h" | |
| 10258 | 29 #include "util.h" |
| 9130 | 30 #include "cmds.h" |
| 6333 | 31 #include "irc.h" |
| 32 | |
| 33 #include <stdio.h> | |
| 34 #include <stdlib.h> | |
| 35 #include <ctype.h> | |
| 36 | |
| 37 static char *irc_send_convert(struct irc_conn *irc, const char *string); | |
| 38 static char *irc_recv_convert(struct irc_conn *irc, const char *string); | |
| 39 | |
| 40 static void irc_parse_error_cb(struct irc_conn *irc, char *input); | |
| 41 | |
| 42 static char *irc_mirc_colors[16] = { | |
| 43 "white", "black", "blue", "dark green", "red", "brown", "purple", | |
| 44 "orange", "yellow", "green", "teal", "cyan", "light blue", | |
| 45 "pink", "grey", "light grey" }; | |
| 46 | |
| 47 /*typedef void (*IRCMsgCallback)(struct irc_conn *irc, char *from, char *name, char **args);*/ | |
| 48 static struct _irc_msg { | |
| 49 char *name; | |
| 50 char *format; | |
| 51 void (*cb)(struct irc_conn *irc, const char *name, const char *from, char **args); | |
| 52 } _irc_msgs[] = { | |
| 53 { "301", "nn:", irc_msg_away }, /* User is away */ | |
| 54 { "303", "n:", irc_msg_ison }, /* ISON reply */ | |
| 55 { "311", "nnvvv:", irc_msg_whois }, /* Whois user */ | |
| 56 { "312", "nnv:", irc_msg_whois }, /* Whois server */ | |
| 57 { "313", "nn:", irc_msg_whois }, /* Whois ircop */ | |
| 58 { "317", "nnvv", irc_msg_whois }, /* Whois idle */ | |
| 59 { "318", "nt:", irc_msg_endwhois }, /* End of WHOIS */ | |
| 60 { "319", "nn:", irc_msg_whois }, /* Whois channels */ | |
| 61 { "320", "nn:", irc_msg_whois }, /* Whois (fn ident) */ | |
| 8114 | 62 { "321", "*", irc_msg_list }, /* Start of list */ |
| 63 { "322", "ncv:", irc_msg_list }, /* List. */ | |
| 64 { "323", ":", irc_msg_list }, /* End of list. */ | |
| 6333 | 65 { "324", "ncv:", irc_msg_chanmode }, /* Channel modes */ |
| 66 { "331", "nc:", irc_msg_topic }, /* No channel topic */ | |
| 67 { "332", "nc:", irc_msg_topic }, /* Channel topic */ | |
| 68 { "333", "*", irc_msg_ignore }, /* Topic setter stuff */ | |
| 69 { "353", "nvc:", irc_msg_names }, /* Names list */ | |
| 70 { "366", "nc:", irc_msg_names }, /* End of names */ | |
| 71 { "372", "n:", irc_msg_motd }, /* MOTD */ | |
| 72 { "375", "n:", irc_msg_motd }, /* Start MOTD */ | |
| 73 { "376", "n:", irc_msg_endmotd }, /* End of MOTD */ | |
| 10564 | 74 { "391", "nv:", irc_msg_time }, /* Time reply */ |
| 6333 | 75 { "401", "nt:", irc_msg_nonick }, /* No such nick/chan */ |
| 7877 | 76 { "403", "nc:", irc_msg_nochan }, /* No such channel */ |
| 6333 | 77 { "404", "nt:", irc_msg_nosend }, /* Cannot send to chan */ |
| 78 { "421", "nv:", irc_msg_unknown }, /* Unknown command */ | |
| 6350 | 79 { "422", "nv:", irc_msg_endmotd }, /* No MOTD available */ |
| 10633 | 80 { "432", "vn:", irc_msg_badnick }, /* Erroneous nickname */ |
| 6333 | 81 { "433", "vn:", irc_msg_nickused }, /* Nickname already in use */ |
| 10712 | 82 { "437", "nc:", irc_msg_unavailable }, /* Nick/channel is unavailable */ |
| 6718 | 83 { "438", "nn:", irc_msg_nochangenick }, /* Nick may not change */ |
| 6333 | 84 { "442", "nc:", irc_msg_notinchan }, /* Not in channel */ |
| 85 { "473", "nc:", irc_msg_inviteonly }, /* Tried to join invite-only */ | |
| 86 { "474", "nc:", irc_msg_banned }, /* Banned from channel */ | |
| 10659 | 87 { "478", "nct:", irc_msg_banfull }, /* Banlist is full */ |
| 6333 | 88 { "482", "nc:", irc_msg_notop }, /* Need to be op to do that */ |
| 89 { "501", "n:", irc_msg_badmode }, /* Unknown mode flag */ | |
| 8404 | 90 { "506", "nc:", irc_msg_nosend }, /* Must identify to send */ |
| 6714 | 91 { "515", "nc:", irc_msg_regonly }, /* Registration required */ |
| 6333 | 92 { "invite", "n:", irc_msg_invite }, /* Invited */ |
| 93 { "join", ":", irc_msg_join }, /* Joined a channel */ | |
| 94 { "kick", "cn:", irc_msg_kick }, /* KICK */ | |
| 95 { "mode", "tv:", irc_msg_mode }, /* MODE for channel */ | |
| 96 { "nick", ":", irc_msg_nick }, /* Nick change */ | |
| 97 { "notice", "t:", irc_msg_notice }, /* NOTICE recv */ | |
| 98 { "part", "c:", irc_msg_part }, /* Parted a channel */ | |
| 99 { "ping", ":", irc_msg_ping }, /* Received PING from server */ | |
| 100 { "pong", "v:", irc_msg_pong }, /* Received PONG from server */ | |
| 101 { "privmsg", "t:", irc_msg_privmsg }, /* Received private message */ | |
| 102 { "topic", "c:", irc_msg_topic }, /* TOPIC command */ | |
| 103 { "quit", ":", irc_msg_quit }, /* QUIT notice */ | |
| 104 { "wallops", ":", irc_msg_wallops }, /* WALLOPS command */ | |
| 105 { NULL, NULL, NULL } | |
| 106 }; | |
| 107 | |
| 108 static struct _irc_user_cmd { | |
| 109 char *name; | |
| 110 char *format; | |
| 111 IRCCmdCallback cb; | |
| 9255 | 112 char *help; |
| 6333 | 113 } _irc_cmds[] = { |
| 9255 | 114 { "action", ":", irc_cmd_ctcp_action, N_("action <action to perform>: Perform an action.") }, |
| 115 { "away", ":", irc_cmd_away, N_("away [message]: Set an away message, or use no message to return from being away.") }, | |
| 9258 | 116 { "deop", ":", irc_cmd_op, N_("deop <nick1> [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this.") }, |
| 117 { "devoice", ":", irc_cmd_op, N_("devoice <nick1> [nick2] ...: Remove channel voice status from someone, preventing them from speaking if the channel is moderated (+m). You must be a channel operator to do this.") }, | |
| 118 { "invite", ":", irc_cmd_invite, N_("invite <nick> [room]: Invite someone to join you in the specified channel, or the current channel.") }, | |
| 9266 | 119 { "j", "cv", irc_cmd_join, N_("j <room1>[,room2][,...] [key1[,key2][,...]]: Enter one or more channels, optionally providing a channel key for each if needed.") }, |
| 120 { "join", "cv", irc_cmd_join, N_("join <room1>[,room2][,...] [key1[,key2][,...]]: Enter one or more channels, optionally providing a channel key for each if needed.") }, | |
| 9258 | 121 { "kick", "n:", irc_cmd_kick, N_("kick <nick> [message]: Remove someone from a channel. You must be a channel operator to do this.") }, |
| 122 { "list", ":", irc_cmd_list, N_("list: Display a list of chat rooms on the network. <i>Warning, some servers may disconnect you upon doing this.</i>") }, | |
| 9255 | 123 { "me", ":", irc_cmd_ctcp_action, N_("me <action to perform>: Perform an action.") }, |
| 10609 | 124 { "mode", ":", irc_cmd_mode, N_("mode <+|-><A-Za-z> <nick|channel>: Set or unset a channel or user mode.") }, |
| 9258 | 125 { "msg", "t:", irc_cmd_privmsg, N_("msg <nick> <message>: Send a private message to a user (as opposed to a channel).") }, |
| 126 { "names", "c", irc_cmd_names, N_("names [channel]: List the users currently in a channel.") }, | |
| 9274 | 127 { "nick", "n", irc_cmd_nick, N_("nick <new nickname>: Change your nickname.") }, |
| 9258 | 128 { "op", ":", irc_cmd_op, N_("op <nick1> [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this.") }, |
| 9255 | 129 { "operwall", ":", irc_cmd_wallops, N_("operwall <message>: If you don't know what this is, you probably can't use it.") }, |
| 9258 | 130 { "part", "c:", irc_cmd_part, N_("part [room] [message]: Leave the current channel, or a specified channel, with an optional message.") }, |
| 9255 | 131 { "ping", "n", irc_cmd_ping, N_("ping [nick]: Asks how much lag a user (or the server if no user specified) has.") }, |
| 9258 | 132 { "query", "n:", irc_cmd_query, N_("query <nick> <message>: Send a private message to a user (as opposed to a channel).") }, |
| 9255 | 133 { "quit", ":", irc_cmd_quit, N_("quit [message]: Disconnect from the server, with an optional message.") }, |
| 134 { "quote", "*", irc_cmd_quote, N_("quote [...]: Send a raw command to the server.") }, | |
| 135 { "remove", "n:", irc_cmd_remove, N_("remove <nick> [message]: Remove someone from a room. You must be a channel operator to do this.") }, | |
| 10564 | 136 { "time", "", irc_cmd_time, N_("time: Displays the current local time at the IRC server.") }, |
| 9255 | 137 { "topic", ":", irc_cmd_topic, N_("topic [new topic]: View or change the channel topic.") }, |
| 138 { "umode", ":", irc_cmd_mode, N_("umode <+|-><A-Za-z>: Set or unset a user mode.") }, | |
| 9258 | 139 { "voice", ":", irc_cmd_op, N_("voice <nick1> [nick2] ...: Grant channel voice status to someone. You must be a channel operator to do this.") }, |
| 9255 | 140 { "wallops", ":", irc_cmd_wallops, N_("wallops <message>: If you don't know what this is, you probably can't use it.") }, |
| 10609 | 141 { "whois", "tt", irc_cmd_whois, N_("whois [server] <nick>: Get information on a user.") }, |
| 11318 | 142 { NULL, NULL, NULL, NULL } |
| 6333 | 143 }; |
| 144 | |
| 9130 | 145 static GaimCmdRet irc_parse_gaim_cmd(GaimConversation *conv, const gchar *cmd, |
| 9597 | 146 gchar **args, gchar **error, void *data) |
| 9130 | 147 { |
| 148 GaimConnection *gc; | |
| 149 struct irc_conn *irc; | |
| 150 struct _irc_user_cmd *cmdent; | |
| 151 | |
| 152 gc = gaim_conversation_get_gc(conv); | |
| 153 if (!gc) | |
| 154 return GAIM_CMD_RET_FAILED; | |
| 155 | |
| 156 irc = gc->proto_data; | |
| 157 | |
| 158 if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL) | |
| 159 return GAIM_CMD_RET_FAILED; | |
| 160 | |
| 161 (cmdent->cb)(irc, cmd, gaim_conversation_get_name(conv), (const char **)args); | |
| 162 | |
| 163 return GAIM_CMD_RET_OK; | |
| 164 } | |
| 165 | |
| 166 static void irc_register_command(struct _irc_user_cmd *c) | |
| 167 { | |
| 168 GaimCmdFlag f; | |
| 169 char args[10]; | |
| 170 char *format; | |
| 171 int i; | |
| 172 | |
| 173 f = GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_PRPL_ONLY | |
| 174 | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS; | |
| 175 | |
| 176 format = c->format; | |
| 177 | |
| 178 for (i = 0; (i < (sizeof(args) - 1)) && *format; i++, format++) | |
| 179 switch (*format) { | |
| 180 case 'v': | |
| 181 case 'n': | |
| 182 case 'c': | |
| 183 case 't': | |
| 184 args[i] = 'w'; | |
| 185 break; | |
| 186 case ':': | |
| 187 case '*': | |
| 188 args[i] = 's'; | |
| 189 break; | |
| 190 } | |
| 191 | |
| 192 args[i] = '\0'; | |
| 193 | |
| 9597 | 194 gaim_cmd_register(c->name, args, GAIM_CMD_P_PRPL, f, "prpl-irc", |
| 195 irc_parse_gaim_cmd, _(c->help), NULL); | |
| 9130 | 196 } |
| 197 | |
| 198 void irc_register_commands(void) | |
| 199 { | |
| 200 struct _irc_user_cmd *c; | |
| 201 | |
| 202 for (c = _irc_cmds; c && c->name; c++) | |
| 203 irc_register_command(c); | |
| 204 } | |
| 205 | |
| 6333 | 206 static char *irc_send_convert(struct irc_conn *irc, const char *string) |
| 207 { | |
| 208 char *utf8; | |
| 209 GError *err = NULL; | |
| 10258 | 210 gchar **encodings; |
| 211 const gchar *enclist; | |
| 9644 | 212 |
| 10258 | 213 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); |
| 214 encodings = g_strsplit(enclist, ",", 2); | |
| 215 | |
| 10278 | 216 if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) { |
| 217 g_strfreev(encodings); | |
| 9644 | 218 return g_strdup(string); |
| 10278 | 219 } |
| 9644 | 220 |
| 10258 | 221 utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err); |
| 6333 | 222 if (err) { |
| 9644 | 223 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Send conversion error: %s\n", err->message); |
| 10258 | 224 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Sending as UTF-8 instead of %s\n", encodings[0]); |
| 6333 | 225 utf8 = g_strdup(string); |
| 8954 | 226 g_error_free(err); |
| 6333 | 227 } |
| 10258 | 228 g_strfreev(encodings); |
| 229 | |
| 6333 | 230 return utf8; |
| 231 } | |
| 232 | |
| 233 static char *irc_recv_convert(struct irc_conn *irc, const char *string) | |
| 234 { | |
| 9644 | 235 char *utf8 = NULL; |
| 10258 | 236 const gchar *charset, *enclist; |
| 237 gchar **encodings; | |
| 238 int i; | |
| 9644 | 239 |
| 10258 | 240 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); |
| 241 encodings = g_strsplit(enclist, ",", -1); | |
| 242 | |
| 10504 | 243 if (encodings[0] == NULL) { |
| 244 g_strfreev(encodings); | |
| 10258 | 245 return gaim_utf8_salvage(string); |
| 10504 | 246 } |
| 9644 | 247 |
| 10258 | 248 for (i = 0; encodings[i] != NULL; i++) { |
| 249 charset = encodings[i]; | |
| 250 while (*charset == ' ') | |
| 251 charset++; | |
| 252 | |
| 253 if (!strcasecmp("UTF-8", charset)) { | |
|
11726
6e0986c82bc5
[gaim-migrate @ 14017]
Richard Laager <rlaager@wiktel.com>
parents:
11318
diff
changeset
|
254 if (g_utf8_validate(string, -1, NULL)) |
| 10258 | 255 utf8 = g_strdup(string); |
| 256 } else { | |
|
11726
6e0986c82bc5
[gaim-migrate @ 14017]
Richard Laager <rlaager@wiktel.com>
parents:
11318
diff
changeset
|
257 utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL); |
| 10258 | 258 } |
| 259 | |
| 260 if (utf8) { | |
| 261 g_strfreev(encodings); | |
| 262 return utf8; | |
| 263 } | |
| 9644 | 264 } |
| 10504 | 265 g_strfreev(encodings); |
| 9644 | 266 |
| 10258 | 267 return gaim_utf8_salvage(string); |
| 6333 | 268 } |
| 269 | |
| 270 /* XXX tag closings are not necessarily correctly nested here! If we | |
| 271 * get a ^O or reach the end of the string and there are open | |
| 272 * tags, they are closed in a fixed order ... this means, for | |
| 273 * example, you might see <FONT COLOR="blue">some text <B>with | |
| 274 * various attributes</FONT></B> (notice that B and FONT overlap | |
| 275 * and are not cleanly nested). This is imminently fixable but | |
| 276 * I am not fixing it right now. | |
| 277 */ | |
| 278 char *irc_mirc2html(const char *string) | |
| 279 { | |
| 280 const char *cur, *end; | |
| 281 char fg[3] = "\0\0", bg[3] = "\0\0"; | |
| 282 int fgnum, bgnum; | |
| 6754 | 283 int font = 0, bold = 0, underline = 0; |
| 6333 | 284 GString *decoded = g_string_sized_new(strlen(string)); |
| 285 | |
| 286 cur = string; | |
| 287 do { | |
| 6754 | 288 end = strpbrk(cur, "\002\003\007\017\026\037"); |
| 6333 | 289 |
| 290 decoded = g_string_append_len(decoded, cur, end ? end - cur : strlen(cur)); | |
| 291 cur = end ? end : cur + strlen(cur); | |
| 292 | |
| 293 switch (*cur) { | |
| 294 case '\002': | |
| 295 cur++; | |
| 296 if (!bold) { | |
| 297 decoded = g_string_append(decoded, "<B>"); | |
| 298 bold = TRUE; | |
| 299 } else { | |
| 300 decoded = g_string_append(decoded, "</B>"); | |
| 301 bold = FALSE; | |
| 302 } | |
| 303 break; | |
| 304 case '\003': | |
| 305 cur++; | |
| 306 fg[0] = fg[1] = bg[0] = bg[1] = '\0'; | |
| 307 if (isdigit(*cur)) | |
| 308 fg[0] = *cur++; | |
| 309 if (isdigit(*cur)) | |
| 310 fg[1] = *cur++; | |
| 311 if (*cur == ',') { | |
| 312 cur++; | |
| 313 if (isdigit(*cur)) | |
| 314 bg[0] = *cur++; | |
| 315 if (isdigit(*cur)) | |
| 316 bg[1] = *cur++; | |
| 317 } | |
| 318 if (font) { | |
| 319 decoded = g_string_append(decoded, "</FONT>"); | |
| 320 font = FALSE; | |
| 321 } | |
| 322 | |
| 323 if (fg[0]) { | |
| 324 fgnum = atoi(fg); | |
| 325 if (fgnum < 0 || fgnum > 15) | |
| 326 continue; | |
| 327 font = TRUE; | |
| 328 g_string_append_printf(decoded, "<FONT COLOR=\"%s\"", irc_mirc_colors[fgnum]); | |
| 329 if (bg[0]) { | |
| 330 bgnum = atoi(bg); | |
| 331 if (bgnum >= 0 && bgnum < 16) | |
| 332 g_string_append_printf(decoded, " BACK=\"%s\"", irc_mirc_colors[bgnum]); | |
| 333 } | |
| 334 decoded = g_string_append_c(decoded, '>'); | |
| 335 } | |
| 336 break; | |
| 6754 | 337 case '\037': |
| 338 cur++; | |
| 339 if (!underline) { | |
| 340 decoded = g_string_append(decoded, "<U>"); | |
| 341 underline = TRUE; | |
| 342 } else { | |
| 343 decoded = g_string_append(decoded, "</U>"); | |
| 344 underline = TRUE; | |
| 345 } | |
| 346 break; | |
| 6333 | 347 case '\007': |
| 348 case '\026': | |
| 349 cur++; | |
| 350 break; | |
| 351 case '\017': | |
| 352 cur++; | |
| 353 /* fallthrough */ | |
| 354 case '\000': | |
| 355 if (bold) | |
| 6754 | 356 decoded = g_string_append(decoded, "</B>"); |
| 357 if (underline) | |
| 358 decoded = g_string_append(decoded, "</U>"); | |
| 6333 | 359 if (font) |
| 360 decoded = g_string_append(decoded, "</FONT>"); | |
| 361 break; | |
| 362 default: | |
| 363 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); | |
| 364 } | |
| 365 } while (*cur); | |
| 366 | |
| 367 return g_string_free(decoded, FALSE); | |
| 368 } | |
| 369 | |
| 8529 | 370 char *irc_mirc2txt (const char *string) |
| 371 { | |
| 372 char *result = g_strdup (string); | |
| 373 int i, j; | |
| 374 | |
| 375 for (i = 0, j = 0; result[i]; i++) { | |
| 376 switch (result[i]) { | |
| 377 case '\002': | |
| 378 case '\003': | |
| 379 case '\007': | |
| 380 case '\017': | |
| 381 case '\026': | |
| 382 case '\037': | |
| 383 continue; | |
| 384 default: | |
| 385 result[j++] = result[i]; | |
| 386 } | |
| 387 } | |
| 11136 | 388 result[j] = '\0'; |
| 8529 | 389 return result; |
| 390 } | |
| 391 | |
| 10208 | 392 gboolean irc_ischannel(const char *string) |
| 393 { | |
| 394 return (string[0] == '#' || string[0] == '&'); | |
| 395 } | |
| 396 | |
| 6333 | 397 char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice) |
| 398 { | |
| 399 GaimConnection *gc; | |
| 400 const char *cur = msg + 1; | |
| 401 char *buf, *ctcp; | |
| 402 time_t timestamp; | |
| 403 | |
| 6754 | 404 /* Note that this is NOT correct w.r.t. multiple CTCPs in one |
| 405 * message and low-level quoting ... but if you want that crap, | |
| 406 * use a real IRC client. */ | |
| 407 | |
| 6333 | 408 if (msg[0] != '\001' || msg[strlen(msg) - 1] != '\001') |
| 409 return g_strdup(msg); | |
| 410 | |
| 411 if (!strncmp(cur, "ACTION ", 7)) { | |
| 412 cur += 7; | |
| 413 buf = g_strdup_printf("/me %s", cur); | |
| 414 buf[strlen(buf) - 1] = '\0'; | |
| 415 return buf; | |
| 416 } else if (!strncmp(cur, "PING ", 5)) { | |
| 417 if (notice) { /* reply */ | |
| 11972 | 418 /* TODO: Should this read in the timestamp as a double? */ |
| 6333 | 419 sscanf(cur, "PING %lu", ×tamp); |
| 420 gc = gaim_account_get_connection(irc->account); | |
| 421 if (!gc) | |
| 422 return NULL; | |
| 6350 | 423 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); |
| 6333 | 424 gaim_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf); |
| 425 g_free(buf); | |
| 426 return NULL; | |
| 427 } else { | |
| 428 buf = irc_format(irc, "vt:", "NOTICE", from, msg); | |
| 429 irc_send(irc, buf); | |
| 430 g_free(buf); | |
| 431 gc = gaim_account_get_connection(irc->account); | |
| 432 } | |
| 433 } else if (!strncmp(cur, "VERSION", 7) && !notice) { | |
| 434 buf = irc_format(irc, "vt:", "NOTICE", from, "\001VERSION Gaim IRC\001"); | |
| 435 irc_send(irc, buf); | |
| 436 g_free(buf); | |
| 8351 | 437 } else if (!strncmp(cur, "DCC SEND ", 9)) { |
| 438 irc_dccsend_recv(irc, from, msg + 10); | |
| 439 return NULL; | |
| 6333 | 440 } |
| 441 | |
| 442 ctcp = g_strdup(msg + 1); | |
| 443 ctcp[strlen(ctcp) - 1] = '\0'; | |
| 444 buf = g_strdup_printf("Received CTCP '%s' (to %s) from %s", ctcp, to, from); | |
| 445 g_free(ctcp); | |
| 446 return buf; | |
| 447 } | |
| 448 | |
| 449 void irc_msg_table_build(struct irc_conn *irc) | |
| 450 { | |
| 451 int i; | |
| 452 | |
| 453 if (!irc || !irc->msgs) { | |
| 454 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a message table on a bogus structure\n"); | |
| 455 return; | |
| 456 } | |
| 457 | |
| 458 for (i = 0; _irc_msgs[i].name; i++) { | |
| 459 g_hash_table_insert(irc->msgs, (gpointer)_irc_msgs[i].name, (gpointer)&_irc_msgs[i]); | |
| 460 } | |
| 461 } | |
| 462 | |
| 463 void irc_cmd_table_build(struct irc_conn *irc) | |
| 464 { | |
| 465 int i; | |
| 466 | |
| 467 if (!irc || !irc->cmds) { | |
| 468 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a command table on a bogus structure\n"); | |
| 469 return; | |
| 470 } | |
| 471 | |
| 472 for (i = 0; _irc_cmds[i].name ; i++) { | |
| 473 g_hash_table_insert(irc->cmds, (gpointer)_irc_cmds[i].name, (gpointer)&_irc_cmds[i]); | |
| 474 } | |
| 475 } | |
| 476 | |
| 477 char *irc_format(struct irc_conn *irc, const char *format, ...) | |
| 478 { | |
| 479 GString *string = g_string_new(""); | |
| 480 char *tok, *tmp; | |
| 481 const char *cur; | |
| 482 va_list ap; | |
| 483 | |
| 484 va_start(ap, format); | |
| 485 for (cur = format; *cur; cur++) { | |
| 486 if (cur != format) | |
| 487 g_string_append_c(string, ' '); | |
| 488 | |
| 489 tok = va_arg(ap, char *); | |
| 490 switch (*cur) { | |
| 491 case 'v': | |
| 492 g_string_append(string, tok); | |
| 493 break; | |
| 494 case ':': | |
| 495 g_string_append_c(string, ':'); | |
| 496 /* no break! */ | |
| 497 case 't': | |
| 498 case 'n': | |
| 499 case 'c': | |
| 500 tmp = irc_send_convert(irc, tok); | |
| 501 g_string_append(string, tmp); | |
| 502 g_free(tmp); | |
| 503 break; | |
| 504 default: | |
| 505 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Invalid format character '%c'\n", *cur); | |
| 506 break; | |
| 507 } | |
| 508 } | |
| 509 va_end(ap); | |
| 510 g_string_append(string, "\r\n"); | |
| 511 return (g_string_free(string, FALSE)); | |
| 512 } | |
| 513 | |
| 514 void irc_parse_msg(struct irc_conn *irc, char *input) | |
| 515 { | |
| 516 struct _irc_msg *msgent; | |
| 517 char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg; | |
| 7631 | 518 guint i; |
| 6333 | 519 |
| 520 if (!strncmp(input, "PING ", 5)) { | |
| 521 msg = irc_format(irc, "vv", "PONG", input + 5); | |
| 522 irc_send(irc, msg); | |
| 523 g_free(msg); | |
| 524 return; | |
| 525 } else if (!strncmp(input, "ERROR ", 6)) { | |
| 10154 | 526 if (g_utf8_validate(input, -1, NULL)) { |
| 527 char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input); | |
| 528 gaim_connection_error(gaim_account_get_connection(irc->account), tmp); | |
| 529 g_free(tmp); | |
| 530 } else | |
| 531 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); | |
| 6333 | 532 return; |
| 533 } | |
| 534 | |
| 535 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
| 536 irc_parse_error_cb(irc, input); | |
| 537 return; | |
| 538 } | |
| 539 | |
| 540 from = g_strndup(&input[1], cur - &input[1]); | |
| 541 cur++; | |
| 542 end = strchr(cur, ' '); | |
| 543 if (!end) | |
| 544 end = cur + strlen(cur); | |
| 545 | |
| 546 tmp = g_strndup(cur, end - cur); | |
| 547 msgname = g_ascii_strdown(tmp, -1); | |
| 548 g_free(tmp); | |
| 549 | |
| 550 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
| 551 irc_msg_default(irc, "", from, &input); | |
| 552 g_free(msgname); | |
| 553 g_free(from); | |
| 554 return; | |
| 555 } | |
| 556 g_free(msgname); | |
| 557 | |
| 558 args = g_new0(char *, strlen(msgent->format)); | |
| 559 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 560 switch (fmt[i]) { | |
| 561 case 'v': | |
| 562 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 563 args[i] = g_strndup(cur, end - cur); | |
| 564 cur += end - cur; | |
| 565 break; | |
| 566 case 't': | |
| 567 case 'n': | |
| 568 case 'c': | |
| 569 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 570 tmp = g_strndup(cur, end - cur); | |
| 571 args[i] = irc_recv_convert(irc, tmp); | |
| 572 g_free(tmp); | |
| 573 cur += end - cur; | |
| 574 break; | |
| 575 case ':': | |
| 576 if (*cur == ':') cur++; | |
| 577 args[i] = irc_recv_convert(irc, cur); | |
| 578 cur = cur + strlen(cur); | |
| 579 break; | |
| 580 case '*': | |
| 581 args[i] = g_strdup(cur); | |
| 582 cur = cur + strlen(cur); | |
| 583 break; | |
| 584 default: | |
| 585 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
| 586 break; | |
| 587 } | |
| 588 } | |
| 6970 | 589 tmp = irc_recv_convert(irc, from); |
| 590 (msgent->cb)(irc, msgent->name, tmp, args); | |
| 591 g_free(tmp); | |
| 6333 | 592 for (i = 0; i < strlen(msgent->format); i++) { |
| 593 g_free(args[i]); | |
| 594 } | |
| 595 g_free(args); | |
| 596 g_free(from); | |
| 597 } | |
| 598 | |
| 599 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
| 600 { | |
| 601 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
| 602 } |
