Mercurial > pidgin
annotate libgaim/protocols/irc/parse.c @ 15113:4a8c368df4ea
[gaim-migrate @ 17899]
Some touchups:
* If one of the parallel connection attempts fails immediately (i.e.
does not time out) then don't cancel the other one.
* Make sure we don't continue on to step 2 of the peer connection
process after we kick off the parallel gaim_proxy_connects(). It
looks like this would happen most of the time, because the
connect_timeout_timer would be added for the verified ip, so it
would NOT be added for the client ip, and so we wouldn't hit the
"return" call because it happens to be in the same block as the
second gaim_timeout_add() call.
* Add the connection timeout timer even if the gaim_proxy_connect() to
the verified ip returns NULL for some crazy reason.
I didn't actually test any of this. I should probably do that when
I get home.
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Wed, 06 Dec 2006 01:29:59 +0000 |
| parents | 4504a21b1d8f |
| children |
| rev | line source |
|---|---|
| 14192 | 1 /** |
| 2 * @file parse.c | |
| 3 * | |
| 4 * gaim | |
| 5 * | |
| 6 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> | |
| 7 * | |
| 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" | |
| 29 #include "util.h" | |
| 30 #include "cmds.h" | |
| 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 | |
| 14621 | 47 extern GaimPlugin *_irc_plugin; |
| 48 | |
| 14192 | 49 /*typedef void (*IRCMsgCallback)(struct irc_conn *irc, char *from, char *name, char **args);*/ |
| 50 static struct _irc_msg { | |
| 51 char *name; | |
| 52 char *format; | |
| 53 void (*cb)(struct irc_conn *irc, const char *name, const char *from, char **args); | |
| 54 } _irc_msgs[] = { | |
| 55 { "301", "nn:", irc_msg_away }, /* User is away */ | |
| 56 { "303", "n:", irc_msg_ison }, /* ISON reply */ | |
| 57 { "311", "nnvvv:", irc_msg_whois }, /* Whois user */ | |
| 58 { "312", "nnv:", irc_msg_whois }, /* Whois server */ | |
| 59 { "313", "nn:", irc_msg_whois }, /* Whois ircop */ | |
| 60 { "317", "nnvv", irc_msg_whois }, /* Whois idle */ | |
| 61 { "318", "nt:", irc_msg_endwhois }, /* End of WHOIS */ | |
| 62 { "319", "nn:", irc_msg_whois }, /* Whois channels */ | |
| 63 { "320", "nn:", irc_msg_whois }, /* Whois (fn ident) */ | |
| 64 { "321", "*", irc_msg_list }, /* Start of list */ | |
| 65 { "322", "ncv:", irc_msg_list }, /* List. */ | |
| 66 { "323", ":", irc_msg_list }, /* End of list. */ | |
| 67 { "324", "ncv:", irc_msg_chanmode }, /* Channel modes */ | |
| 68 { "331", "nc:", irc_msg_topic }, /* No channel topic */ | |
| 69 { "332", "nc:", irc_msg_topic }, /* Channel topic */ | |
| 70 { "333", "*", irc_msg_ignore }, /* Topic setter stuff */ | |
| 71 { "353", "nvc:", irc_msg_names }, /* Names list */ | |
| 72 { "366", "nc:", irc_msg_names }, /* End of names */ | |
| 73 { "372", "n:", irc_msg_motd }, /* MOTD */ | |
| 74 { "375", "n:", irc_msg_motd }, /* Start MOTD */ | |
| 75 { "376", "n:", irc_msg_endmotd }, /* End of MOTD */ | |
| 76 { "391", "nv:", irc_msg_time }, /* Time reply */ | |
| 77 { "401", "nt:", irc_msg_nonick }, /* No such nick/chan */ | |
| 78 { "403", "nc:", irc_msg_nochan }, /* No such channel */ | |
| 79 { "404", "nt:", irc_msg_nosend }, /* Cannot send to chan */ | |
| 80 { "421", "nv:", irc_msg_unknown }, /* Unknown command */ | |
| 81 { "422", "nv:", irc_msg_endmotd }, /* No MOTD available */ | |
| 82 { "432", "vn:", irc_msg_badnick }, /* Erroneous nickname */ | |
| 83 { "433", "vn:", irc_msg_nickused }, /* Nickname already in use */ | |
| 84 { "437", "nc:", irc_msg_unavailable }, /* Nick/channel is unavailable */ | |
| 85 { "438", "nn:", irc_msg_nochangenick }, /* Nick may not change */ | |
| 86 { "442", "nc:", irc_msg_notinchan }, /* Not in channel */ | |
| 87 { "473", "nc:", irc_msg_inviteonly }, /* Tried to join invite-only */ | |
| 88 { "474", "nc:", irc_msg_banned }, /* Banned from channel */ | |
| 89 { "478", "nct:", irc_msg_banfull }, /* Banlist is full */ | |
| 90 { "482", "nc:", irc_msg_notop }, /* Need to be op to do that */ | |
| 91 { "501", "n:", irc_msg_badmode }, /* Unknown mode flag */ | |
| 92 { "506", "nc:", irc_msg_nosend }, /* Must identify to send */ | |
| 93 { "515", "nc:", irc_msg_regonly }, /* Registration required */ | |
| 94 { "invite", "n:", irc_msg_invite }, /* Invited */ | |
| 95 { "join", ":", irc_msg_join }, /* Joined a channel */ | |
| 96 { "kick", "cn:", irc_msg_kick }, /* KICK */ | |
| 97 { "mode", "tv:", irc_msg_mode }, /* MODE for channel */ | |
| 98 { "nick", ":", irc_msg_nick }, /* Nick change */ | |
| 99 { "notice", "t:", irc_msg_notice }, /* NOTICE recv */ | |
| 100 { "part", "c:", irc_msg_part }, /* Parted a channel */ | |
| 101 { "ping", ":", irc_msg_ping }, /* Received PING from server */ | |
| 102 { "pong", "v:", irc_msg_pong }, /* Received PONG from server */ | |
| 103 { "privmsg", "t:", irc_msg_privmsg }, /* Received private message */ | |
| 104 { "topic", "c:", irc_msg_topic }, /* TOPIC command */ | |
| 105 { "quit", ":", irc_msg_quit }, /* QUIT notice */ | |
| 106 { "wallops", ":", irc_msg_wallops }, /* WALLOPS command */ | |
| 107 { NULL, NULL, NULL } | |
| 108 }; | |
| 109 | |
| 110 static struct _irc_user_cmd { | |
| 111 char *name; | |
| 112 char *format; | |
| 113 IRCCmdCallback cb; | |
| 114 char *help; | |
| 115 } _irc_cmds[] = { | |
| 116 { "action", ":", irc_cmd_ctcp_action, N_("action <action to perform>: Perform an action.") }, | |
| 117 { "away", ":", irc_cmd_away, N_("away [message]: Set an away message, or use no message to return from being away.") }, | |
| 118 { "chanserv", ":", irc_cmd_service, N_("chanserv: Send a command to chanserv") }, | |
| 119 { "deop", ":", irc_cmd_op, N_("deop <nick1> [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this.") }, | |
| 120 { "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.") }, | |
| 121 { "invite", ":", irc_cmd_invite, N_("invite <nick> [room]: Invite someone to join you in the specified channel, or the current channel.") }, | |
| 122 { "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.") }, | |
| 123 { "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.") }, | |
| 124 { "kick", "n:", irc_cmd_kick, N_("kick <nick> [message]: Remove someone from a channel. You must be a channel operator to do this.") }, | |
| 125 { "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>") }, | |
| 126 { "me", ":", irc_cmd_ctcp_action, N_("me <action to perform>: Perform an action.") }, | |
| 127 { "memoserv", ":", irc_cmd_service, N_("memoserv: Send a command to memoserv") }, | |
| 128 { "mode", ":", irc_cmd_mode, N_("mode <+|-><A-Za-z> <nick|channel>: Set or unset a channel or user mode.") }, | |
| 129 { "msg", "t:", irc_cmd_privmsg, N_("msg <nick> <message>: Send a private message to a user (as opposed to a channel).") }, | |
| 130 { "names", "c", irc_cmd_names, N_("names [channel]: List the users currently in a channel.") }, | |
| 131 { "nick", "n", irc_cmd_nick, N_("nick <new nickname>: Change your nickname.") }, | |
| 132 { "nickserv", ":", irc_cmd_service, N_("nickserv: Send a command to nickserv") }, | |
| 133 { "op", ":", irc_cmd_op, N_("op <nick1> [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this.") }, | |
| 134 { "operwall", ":", irc_cmd_wallops, N_("operwall <message>: If you don't know what this is, you probably can't use it.") }, | |
| 135 { "operserv", ":", irc_cmd_service, N_("operserv: Send a command to operserv") }, | |
| 136 { "part", "c:", irc_cmd_part, N_("part [room] [message]: Leave the current channel, or a specified channel, with an optional message.") }, | |
| 137 { "ping", "n", irc_cmd_ping, N_("ping [nick]: Asks how much lag a user (or the server if no user specified) has.") }, | |
| 138 { "query", "n:", irc_cmd_query, N_("query <nick> <message>: Send a private message to a user (as opposed to a channel).") }, | |
| 139 { "quit", ":", irc_cmd_quit, N_("quit [message]: Disconnect from the server, with an optional message.") }, | |
| 140 { "quote", "*", irc_cmd_quote, N_("quote [...]: Send a raw command to the server.") }, | |
| 141 { "remove", "n:", irc_cmd_remove, N_("remove <nick> [message]: Remove someone from a room. You must be a channel operator to do this.") }, | |
| 142 { "time", "", irc_cmd_time, N_("time: Displays the current local time at the IRC server.") }, | |
| 143 { "topic", ":", irc_cmd_topic, N_("topic [new topic]: View or change the channel topic.") }, | |
| 144 { "umode", ":", irc_cmd_mode, N_("umode <+|-><A-Za-z>: Set or unset a user mode.") }, | |
| 145 { "version", ":", irc_cmd_ctcp_version, N_("version [nick]: send CTCP VERSION request to a user") }, | |
| 146 { "voice", ":", irc_cmd_op, N_("voice <nick1> [nick2] ...: Grant channel voice status to someone. You must be a channel operator to do this.") }, | |
| 147 { "wallops", ":", irc_cmd_wallops, N_("wallops <message>: If you don't know what this is, you probably can't use it.") }, | |
| 148 { "whois", "tt", irc_cmd_whois, N_("whois [server] <nick>: Get information on a user.") }, | |
| 149 { NULL, NULL, NULL, NULL } | |
| 150 }; | |
| 151 | |
| 152 static GaimCmdRet irc_parse_gaim_cmd(GaimConversation *conv, const gchar *cmd, | |
| 153 gchar **args, gchar **error, void *data) | |
| 154 { | |
| 155 GaimConnection *gc; | |
| 156 struct irc_conn *irc; | |
| 157 struct _irc_user_cmd *cmdent; | |
| 158 | |
| 159 gc = gaim_conversation_get_gc(conv); | |
| 160 if (!gc) | |
| 161 return GAIM_CMD_RET_FAILED; | |
| 162 | |
| 163 irc = gc->proto_data; | |
| 164 | |
| 165 if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL) | |
| 166 return GAIM_CMD_RET_FAILED; | |
| 167 | |
| 168 (cmdent->cb)(irc, cmd, gaim_conversation_get_name(conv), (const char **)args); | |
| 169 | |
| 170 return GAIM_CMD_RET_OK; | |
| 171 } | |
| 172 | |
| 173 static void irc_register_command(struct _irc_user_cmd *c) | |
| 174 { | |
| 175 GaimCmdFlag f; | |
| 176 char args[10]; | |
| 177 char *format; | |
| 178 size_t i; | |
| 179 | |
| 180 f = GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_PRPL_ONLY | |
| 181 | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS; | |
| 182 | |
| 183 format = c->format; | |
| 184 | |
| 185 for (i = 0; (i < (sizeof(args) - 1)) && *format; i++, format++) | |
| 186 switch (*format) { | |
| 187 case 'v': | |
| 188 case 'n': | |
| 189 case 'c': | |
| 190 case 't': | |
| 191 args[i] = 'w'; | |
| 192 break; | |
| 193 case ':': | |
| 194 case '*': | |
| 195 args[i] = 's'; | |
| 196 break; | |
| 197 } | |
| 198 | |
| 199 args[i] = '\0'; | |
| 200 | |
| 201 gaim_cmd_register(c->name, args, GAIM_CMD_P_PRPL, f, "prpl-irc", | |
| 202 irc_parse_gaim_cmd, _(c->help), NULL); | |
| 203 } | |
| 204 | |
| 205 void irc_register_commands(void) | |
| 206 { | |
| 207 struct _irc_user_cmd *c; | |
| 208 | |
| 209 for (c = _irc_cmds; c && c->name; c++) | |
| 210 irc_register_command(c); | |
| 211 } | |
| 212 | |
| 213 static char *irc_send_convert(struct irc_conn *irc, const char *string) | |
| 214 { | |
| 215 char *utf8; | |
| 216 GError *err = NULL; | |
| 217 gchar **encodings; | |
| 218 const gchar *enclist; | |
| 219 | |
| 220 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); | |
| 221 encodings = g_strsplit(enclist, ",", 2); | |
| 222 | |
| 223 if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) { | |
| 224 g_strfreev(encodings); | |
| 225 return g_strdup(string); | |
| 226 } | |
| 227 | |
| 228 utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err); | |
| 229 if (err) { | |
| 230 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Send conversion error: %s\n", err->message); | |
| 231 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Sending as UTF-8 instead of %s\n", encodings[0]); | |
| 232 utf8 = g_strdup(string); | |
| 233 g_error_free(err); | |
| 234 } | |
| 235 g_strfreev(encodings); | |
| 236 | |
| 237 return utf8; | |
| 238 } | |
| 239 | |
| 240 static char *irc_recv_convert(struct irc_conn *irc, const char *string) | |
| 241 { | |
| 242 char *utf8 = NULL; | |
| 243 const gchar *charset, *enclist; | |
| 244 gchar **encodings; | |
| 245 int i; | |
| 246 | |
| 247 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); | |
| 248 encodings = g_strsplit(enclist, ",", -1); | |
| 249 | |
| 250 if (encodings[0] == NULL) { | |
| 251 g_strfreev(encodings); | |
| 252 return gaim_utf8_salvage(string); | |
| 253 } | |
| 254 | |
| 255 for (i = 0; encodings[i] != NULL; i++) { | |
| 256 charset = encodings[i]; | |
| 257 while (*charset == ' ') | |
| 258 charset++; | |
| 259 | |
| 260 if (!strcasecmp("UTF-8", charset)) { | |
| 261 if (g_utf8_validate(string, -1, NULL)) | |
| 262 utf8 = g_strdup(string); | |
| 263 } else { | |
| 264 utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL); | |
| 265 } | |
| 266 | |
| 267 if (utf8) { | |
| 268 g_strfreev(encodings); | |
| 269 return utf8; | |
| 270 } | |
| 271 } | |
| 272 g_strfreev(encodings); | |
| 273 | |
| 274 return gaim_utf8_salvage(string); | |
| 275 } | |
| 276 | |
| 277 /* XXX tag closings are not necessarily correctly nested here! If we | |
| 278 * get a ^O or reach the end of the string and there are open | |
| 279 * tags, they are closed in a fixed order ... this means, for | |
| 280 * example, you might see <FONT COLOR="blue">some text <B>with | |
| 281 * various attributes</FONT></B> (notice that B and FONT overlap | |
| 282 * and are not cleanly nested). This is imminently fixable but | |
| 283 * I am not fixing it right now. | |
| 284 */ | |
| 285 char *irc_mirc2html(const char *string) | |
| 286 { | |
| 287 const char *cur, *end; | |
| 288 char fg[3] = "\0\0", bg[3] = "\0\0"; | |
| 289 int fgnum, bgnum; | |
| 290 int font = 0, bold = 0, underline = 0, italic = 0; | |
| 291 GString *decoded = g_string_sized_new(strlen(string)); | |
| 292 | |
| 293 cur = string; | |
| 294 do { | |
| 295 end = strpbrk(cur, "\002\003\007\017\026\037"); | |
| 296 | |
| 297 decoded = g_string_append_len(decoded, cur, end ? end - cur : strlen(cur)); | |
| 298 cur = end ? end : cur + strlen(cur); | |
| 299 | |
| 300 switch (*cur) { | |
| 301 case '\002': | |
| 302 cur++; | |
| 303 if (!bold) { | |
| 304 decoded = g_string_append(decoded, "<B>"); | |
| 305 bold = TRUE; | |
| 306 } else { | |
| 307 decoded = g_string_append(decoded, "</B>"); | |
| 308 bold = FALSE; | |
| 309 } | |
| 310 break; | |
| 311 case '\003': | |
| 312 cur++; | |
| 313 fg[0] = fg[1] = bg[0] = bg[1] = '\0'; | |
| 314 if (isdigit(*cur)) | |
| 315 fg[0] = *cur++; | |
| 316 if (isdigit(*cur)) | |
| 317 fg[1] = *cur++; | |
| 318 if (*cur == ',') { | |
| 319 cur++; | |
| 320 if (isdigit(*cur)) | |
| 321 bg[0] = *cur++; | |
| 322 if (isdigit(*cur)) | |
| 323 bg[1] = *cur++; | |
| 324 } | |
| 325 if (font) { | |
| 326 decoded = g_string_append(decoded, "</FONT>"); | |
| 327 font = FALSE; | |
| 328 } | |
| 329 | |
| 330 if (fg[0]) { | |
| 331 fgnum = atoi(fg); | |
| 332 if (fgnum < 0 || fgnum > 15) | |
| 333 continue; | |
| 334 font = TRUE; | |
| 335 g_string_append_printf(decoded, "<FONT COLOR=\"%s\"", irc_mirc_colors[fgnum]); | |
| 336 if (bg[0]) { | |
| 337 bgnum = atoi(bg); | |
| 338 if (bgnum >= 0 && bgnum < 16) | |
| 339 g_string_append_printf(decoded, " BACK=\"%s\"", irc_mirc_colors[bgnum]); | |
| 340 } | |
| 341 decoded = g_string_append_c(decoded, '>'); | |
| 342 } | |
| 343 break; | |
| 344 case '\011': | |
| 345 cur++; | |
| 346 if (!italic) { | |
| 347 decoded = g_string_append(decoded, "<I>"); | |
| 348 italic = TRUE; | |
| 349 } else { | |
| 350 decoded = g_string_append(decoded, "</I>"); | |
| 351 italic = FALSE; | |
| 352 } | |
| 353 break; | |
| 354 case '\037': | |
| 355 cur++; | |
| 356 if (!underline) { | |
| 357 decoded = g_string_append(decoded, "<U>"); | |
| 358 underline = TRUE; | |
| 359 } else { | |
| 360 decoded = g_string_append(decoded, "</U>"); | |
| 361 underline = FALSE; | |
| 362 } | |
| 363 break; | |
| 364 case '\007': | |
| 365 case '\026': | |
| 366 cur++; | |
| 367 break; | |
| 368 case '\017': | |
| 369 cur++; | |
| 370 /* fallthrough */ | |
| 371 case '\000': | |
| 372 if (bold) | |
| 373 decoded = g_string_append(decoded, "</B>"); | |
| 374 if (italic) | |
| 375 decoded = g_string_append(decoded, "</I>"); | |
| 376 if (underline) | |
| 377 decoded = g_string_append(decoded, "</U>"); | |
| 378 if (font) | |
| 379 decoded = g_string_append(decoded, "</FONT>"); | |
| 380 break; | |
| 381 default: | |
| 382 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); | |
| 383 } | |
| 384 } while (*cur); | |
| 385 | |
| 386 return g_string_free(decoded, FALSE); | |
| 387 } | |
| 388 | |
| 389 char *irc_mirc2txt (const char *string) | |
| 390 { | |
| 391 char *result = g_strdup (string); | |
| 392 int i, j; | |
| 393 | |
| 394 for (i = 0, j = 0; result[i]; i++) { | |
| 395 switch (result[i]) { | |
| 396 case '\002': | |
| 397 case '\003': | |
| 398 case '\007': | |
| 399 case '\017': | |
| 400 case '\026': | |
| 401 case '\037': | |
| 402 continue; | |
| 403 default: | |
| 404 result[j++] = result[i]; | |
| 405 } | |
| 406 } | |
| 407 result[j] = '\0'; | |
| 408 return result; | |
| 409 } | |
| 410 | |
| 411 gboolean irc_ischannel(const char *string) | |
| 412 { | |
| 413 return (string[0] == '#' || string[0] == '&'); | |
| 414 } | |
| 415 | |
| 416 char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice) | |
| 417 { | |
| 418 GaimConnection *gc; | |
| 419 const char *cur = msg + 1; | |
| 420 char *buf, *ctcp; | |
| 421 time_t timestamp; | |
| 422 | |
| 423 /* Note that this is NOT correct w.r.t. multiple CTCPs in one | |
| 424 * message and low-level quoting ... but if you want that crap, | |
| 425 * use a real IRC client. */ | |
| 426 | |
| 427 if (msg[0] != '\001' || msg[strlen(msg) - 1] != '\001') | |
| 428 return g_strdup(msg); | |
| 429 | |
| 430 if (!strncmp(cur, "ACTION ", 7)) { | |
| 431 cur += 7; | |
| 432 buf = g_strdup_printf("/me %s", cur); | |
| 433 buf[strlen(buf) - 1] = '\0'; | |
| 434 return buf; | |
| 435 } else if (!strncmp(cur, "PING ", 5)) { | |
| 436 if (notice) { /* reply */ | |
| 437 /* TODO: Should this read in the timestamp as a double? */ | |
| 438 sscanf(cur, "PING %lu", ×tamp); | |
| 439 gc = gaim_account_get_connection(irc->account); | |
| 440 if (!gc) | |
| 441 return NULL; | |
| 442 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); | |
| 443 gaim_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf); | |
| 444 g_free(buf); | |
| 445 return NULL; | |
| 446 } else { | |
| 447 buf = irc_format(irc, "vt:", "NOTICE", from, msg); | |
| 448 irc_send(irc, buf); | |
| 449 g_free(buf); | |
| 450 } | |
| 451 } else if (!strncmp(cur, "VERSION", 7) && !notice) { | |
| 452 buf = irc_format(irc, "vt:", "NOTICE", from, "\001VERSION Gaim IRC\001"); | |
| 453 irc_send(irc, buf); | |
| 454 g_free(buf); | |
| 455 } else if (!strncmp(cur, "DCC SEND ", 9)) { | |
| 456 irc_dccsend_recv(irc, from, msg + 10); | |
| 457 return NULL; | |
| 458 } | |
| 459 | |
| 460 ctcp = g_strdup(msg + 1); | |
| 461 ctcp[strlen(ctcp) - 1] = '\0'; | |
| 462 buf = g_strdup_printf("Received CTCP '%s' (to %s) from %s", ctcp, to, from); | |
| 463 g_free(ctcp); | |
| 464 return buf; | |
| 465 } | |
| 466 | |
| 467 void irc_msg_table_build(struct irc_conn *irc) | |
| 468 { | |
| 469 int i; | |
| 470 | |
| 471 if (!irc || !irc->msgs) { | |
| 472 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a message table on a bogus structure\n"); | |
| 473 return; | |
| 474 } | |
| 475 | |
| 476 for (i = 0; _irc_msgs[i].name; i++) { | |
| 477 g_hash_table_insert(irc->msgs, (gpointer)_irc_msgs[i].name, (gpointer)&_irc_msgs[i]); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 void irc_cmd_table_build(struct irc_conn *irc) | |
| 482 { | |
| 483 int i; | |
| 484 | |
| 485 if (!irc || !irc->cmds) { | |
| 486 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a command table on a bogus structure\n"); | |
| 487 return; | |
| 488 } | |
| 489 | |
| 490 for (i = 0; _irc_cmds[i].name ; i++) { | |
| 491 g_hash_table_insert(irc->cmds, (gpointer)_irc_cmds[i].name, (gpointer)&_irc_cmds[i]); | |
| 492 } | |
| 493 } | |
| 494 | |
| 495 char *irc_format(struct irc_conn *irc, const char *format, ...) | |
| 496 { | |
| 497 GString *string = g_string_new(""); | |
| 498 char *tok, *tmp; | |
| 499 const char *cur; | |
| 500 va_list ap; | |
| 501 | |
| 502 va_start(ap, format); | |
| 503 for (cur = format; *cur; cur++) { | |
| 504 if (cur != format) | |
| 505 g_string_append_c(string, ' '); | |
| 506 | |
| 507 tok = va_arg(ap, char *); | |
| 508 switch (*cur) { | |
| 509 case 'v': | |
| 510 g_string_append(string, tok); | |
| 511 break; | |
| 512 case ':': | |
| 513 g_string_append_c(string, ':'); | |
| 514 /* no break! */ | |
| 515 case 't': | |
| 516 case 'n': | |
| 517 case 'c': | |
| 518 tmp = irc_send_convert(irc, tok); | |
| 519 g_string_append(string, tmp); | |
| 520 g_free(tmp); | |
| 521 break; | |
| 522 default: | |
| 523 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Invalid format character '%c'\n", *cur); | |
| 524 break; | |
| 525 } | |
| 526 } | |
| 527 va_end(ap); | |
| 528 g_string_append(string, "\r\n"); | |
| 529 return (g_string_free(string, FALSE)); | |
| 530 } | |
| 531 | |
| 532 void irc_parse_msg(struct irc_conn *irc, char *input) | |
| 533 { | |
| 534 struct _irc_msg *msgent; | |
| 535 char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg; | |
| 536 guint i; | |
| 537 | |
|
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
538 irc->recv_time = time(NULL); |
| 14922 | 539 |
| 540 /* | |
| 14934 | 541 * The data passed to irc-receiving-text is the raw protocol data. |
| 14933 | 542 * TODO: It should be passed as an array of bytes and a length |
| 543 * instead of a null terminated string. | |
| 14922 | 544 */ |
| 14621 | 545 gaim_signal_emit(_irc_plugin, "irc-receiving-text", gaim_account_get_connection(irc->account), &input); |
| 546 | |
| 14192 | 547 if (!strncmp(input, "PING ", 5)) { |
| 548 msg = irc_format(irc, "vv", "PONG", input + 5); | |
| 549 irc_send(irc, msg); | |
| 550 g_free(msg); | |
| 551 return; | |
| 552 } else if (!strncmp(input, "ERROR ", 6)) { | |
| 553 if (g_utf8_validate(input, -1, NULL)) { | |
| 554 char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input); | |
| 555 gaim_connection_error(gaim_account_get_connection(irc->account), tmp); | |
| 556 g_free(tmp); | |
| 557 } else | |
| 558 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); | |
| 559 return; | |
| 560 } | |
| 561 | |
| 562 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
| 563 irc_parse_error_cb(irc, input); | |
| 564 return; | |
| 565 } | |
| 566 | |
| 567 from = g_strndup(&input[1], cur - &input[1]); | |
| 568 cur++; | |
| 569 end = strchr(cur, ' '); | |
| 570 if (!end) | |
| 571 end = cur + strlen(cur); | |
| 572 | |
| 573 tmp = g_strndup(cur, end - cur); | |
| 574 msgname = g_ascii_strdown(tmp, -1); | |
| 575 g_free(tmp); | |
| 576 | |
| 577 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
| 578 irc_msg_default(irc, "", from, &input); | |
| 579 g_free(msgname); | |
| 580 g_free(from); | |
| 581 return; | |
| 582 } | |
| 583 g_free(msgname); | |
| 584 | |
| 585 args = g_new0(char *, strlen(msgent->format)); | |
| 586 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 587 switch (fmt[i]) { | |
| 588 case 'v': | |
| 589 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 590 args[i] = g_strndup(cur, end - cur); | |
| 591 cur += end - cur; | |
| 592 break; | |
| 593 case 't': | |
| 594 case 'n': | |
| 595 case 'c': | |
| 596 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 597 tmp = g_strndup(cur, end - cur); | |
| 598 args[i] = irc_recv_convert(irc, tmp); | |
| 599 g_free(tmp); | |
| 600 cur += end - cur; | |
| 601 break; | |
| 602 case ':': | |
| 603 if (*cur == ':') cur++; | |
| 604 args[i] = irc_recv_convert(irc, cur); | |
| 605 cur = cur + strlen(cur); | |
| 606 break; | |
| 607 case '*': | |
| 608 args[i] = g_strdup(cur); | |
| 609 cur = cur + strlen(cur); | |
| 610 break; | |
| 611 default: | |
| 612 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
| 613 break; | |
| 614 } | |
| 615 } | |
| 616 tmp = irc_recv_convert(irc, from); | |
| 617 (msgent->cb)(irc, msgent->name, tmp, args); | |
| 618 g_free(tmp); | |
| 619 for (i = 0; i < strlen(msgent->format); i++) { | |
| 620 g_free(args[i]); | |
| 621 } | |
| 622 g_free(args); | |
| 623 g_free(from); | |
| 624 } | |
| 625 | |
| 626 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
| 627 { | |
| 628 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
| 629 } |
