Mercurial > pidgin
annotate src/protocols/irc/parse.c @ 9175:3e2ea5b69605
[gaim-migrate @ 9970]
W and S are now implemented for /cmds in core.
This means you can do /me with colors again.
This was probably the hardest part of cmds that was left to do. So the rest
should be fairly easy. Hopefully there's no major bugs in this. There's
some inconsist use of g_utf8_isspace vs strchr(s, ' ') I want to clean up
yet that will cause some oddness if you use a tab instead of a space as
your argument separater.
committer: Tailor Script <tailor@pidgin.im>
| author | Tim Ringenbach <marv@pidgin.im> |
|---|---|
| date | Sat, 05 Jun 2004 07:33:58 +0000 |
| parents | 933a19e3a6b3 |
| children | 6430b09ccc71 |
| 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" | |
| 9130 | 29 #include "cmds.h" |
| 6333 | 30 #include "irc.h" |
| 31 | |
| 32 #include <stdio.h> | |
| 33 #include <stdlib.h> | |
| 34 #include <ctype.h> | |
| 35 | |
| 36 static char *irc_send_convert(struct irc_conn *irc, const char *string); | |
| 37 static char *irc_recv_convert(struct irc_conn *irc, const char *string); | |
| 38 | |
| 39 static void irc_parse_error_cb(struct irc_conn *irc, char *input); | |
| 40 | |
| 41 static char *irc_mirc_colors[16] = { | |
| 42 "white", "black", "blue", "dark green", "red", "brown", "purple", | |
| 43 "orange", "yellow", "green", "teal", "cyan", "light blue", | |
| 44 "pink", "grey", "light grey" }; | |
| 45 | |
| 46 /*typedef void (*IRCMsgCallback)(struct irc_conn *irc, char *from, char *name, char **args);*/ | |
| 47 static struct _irc_msg { | |
| 48 char *name; | |
| 49 char *format; | |
| 50 void (*cb)(struct irc_conn *irc, const char *name, const char *from, char **args); | |
| 51 } _irc_msgs[] = { | |
| 52 { "301", "nn:", irc_msg_away }, /* User is away */ | |
| 53 { "303", "n:", irc_msg_ison }, /* ISON reply */ | |
| 54 { "311", "nnvvv:", irc_msg_whois }, /* Whois user */ | |
| 55 { "312", "nnv:", irc_msg_whois }, /* Whois server */ | |
| 56 { "313", "nn:", irc_msg_whois }, /* Whois ircop */ | |
| 57 { "317", "nnvv", irc_msg_whois }, /* Whois idle */ | |
| 58 { "318", "nt:", irc_msg_endwhois }, /* End of WHOIS */ | |
| 59 { "319", "nn:", irc_msg_whois }, /* Whois channels */ | |
| 60 { "320", "nn:", irc_msg_whois }, /* Whois (fn ident) */ | |
| 8114 | 61 { "321", "*", irc_msg_list }, /* Start of list */ |
| 62 { "322", "ncv:", irc_msg_list }, /* List. */ | |
| 63 { "323", ":", irc_msg_list }, /* End of list. */ | |
| 6333 | 64 { "324", "ncv:", irc_msg_chanmode }, /* Channel modes */ |
| 65 { "331", "nc:", irc_msg_topic }, /* No channel topic */ | |
| 66 { "332", "nc:", irc_msg_topic }, /* Channel topic */ | |
| 67 { "333", "*", irc_msg_ignore }, /* Topic setter stuff */ | |
| 68 { "353", "nvc:", irc_msg_names }, /* Names list */ | |
| 69 { "366", "nc:", irc_msg_names }, /* End of names */ | |
| 70 { "372", "n:", irc_msg_motd }, /* MOTD */ | |
| 71 { "375", "n:", irc_msg_motd }, /* Start MOTD */ | |
| 72 { "376", "n:", irc_msg_endmotd }, /* End of MOTD */ | |
| 73 { "401", "nt:", irc_msg_nonick }, /* No such nick/chan */ | |
| 7877 | 74 { "403", "nc:", irc_msg_nochan }, /* No such channel */ |
| 6333 | 75 { "404", "nt:", irc_msg_nosend }, /* Cannot send to chan */ |
| 76 { "421", "nv:", irc_msg_unknown }, /* Unknown command */ | |
| 6350 | 77 { "422", "nv:", irc_msg_endmotd }, /* No MOTD available */ |
| 6333 | 78 { "433", "vn:", irc_msg_nickused }, /* Nickname already in use */ |
| 6718 | 79 { "438", "nn:", irc_msg_nochangenick }, /* Nick may not change */ |
| 6333 | 80 { "442", "nc:", irc_msg_notinchan }, /* Not in channel */ |
| 81 { "473", "nc:", irc_msg_inviteonly }, /* Tried to join invite-only */ | |
| 82 { "474", "nc:", irc_msg_banned }, /* Banned from channel */ | |
| 83 { "482", "nc:", irc_msg_notop }, /* Need to be op to do that */ | |
| 84 { "501", "n:", irc_msg_badmode }, /* Unknown mode flag */ | |
| 8404 | 85 { "506", "nc:", irc_msg_nosend }, /* Must identify to send */ |
| 6714 | 86 { "515", "nc:", irc_msg_regonly }, /* Registration required */ |
| 6333 | 87 { "invite", "n:", irc_msg_invite }, /* Invited */ |
| 88 { "join", ":", irc_msg_join }, /* Joined a channel */ | |
| 89 { "kick", "cn:", irc_msg_kick }, /* KICK */ | |
| 90 { "mode", "tv:", irc_msg_mode }, /* MODE for channel */ | |
| 91 { "nick", ":", irc_msg_nick }, /* Nick change */ | |
| 92 { "notice", "t:", irc_msg_notice }, /* NOTICE recv */ | |
| 93 { "part", "c:", irc_msg_part }, /* Parted a channel */ | |
| 94 { "ping", ":", irc_msg_ping }, /* Received PING from server */ | |
| 95 { "pong", "v:", irc_msg_pong }, /* Received PONG from server */ | |
| 96 { "privmsg", "t:", irc_msg_privmsg }, /* Received private message */ | |
| 97 { "topic", "c:", irc_msg_topic }, /* TOPIC command */ | |
| 98 { "quit", ":", irc_msg_quit }, /* QUIT notice */ | |
| 99 { "wallops", ":", irc_msg_wallops }, /* WALLOPS command */ | |
| 100 { NULL, NULL, NULL } | |
| 101 }; | |
| 102 | |
| 103 static struct _irc_user_cmd { | |
| 104 char *name; | |
| 105 char *format; | |
| 106 IRCCmdCallback cb; | |
| 107 } _irc_cmds[] = { | |
| 8627 | 108 { "action", ":", irc_cmd_ctcp_action }, |
| 6333 | 109 { "away", ":", irc_cmd_away }, |
| 110 { "deop", ":", irc_cmd_op }, | |
| 111 { "devoice", ":", irc_cmd_op }, | |
|
6415
e3be6b9744b7
[gaim-migrate @ 6922]
Christian Hammond <chipx86@chipx86.com>
parents:
6350
diff
changeset
|
112 { "help", "v", irc_cmd_help }, |
| 6333 | 113 { "invite", ":", irc_cmd_invite }, |
| 114 { "j", "cv", irc_cmd_join }, | |
| 115 { "join", "cv", irc_cmd_join }, | |
| 116 { "kick", "n:", irc_cmd_kick }, | |
| 8114 | 117 { "list", ":", irc_cmd_list }, |
| 6333 | 118 { "me", ":", irc_cmd_ctcp_action }, |
| 119 { "mode", ":", irc_cmd_mode }, | |
| 120 { "msg", "t:", irc_cmd_privmsg }, | |
| 121 { "names", "c", irc_cmd_names }, | |
| 122 { "nick", "n", irc_cmd_nick }, | |
| 123 { "op", ":", irc_cmd_op }, | |
| 124 { "operwall", ":", irc_cmd_wallops }, | |
| 125 { "part", "c:", irc_cmd_part }, | |
| 126 { "ping", "n", irc_cmd_ping }, | |
| 127 { "query", "n:", irc_cmd_query }, | |
| 128 { "quit", ":", irc_cmd_quit }, | |
| 129 { "quote", "*", irc_cmd_quote }, | |
| 130 { "remove", "n:", irc_cmd_remove }, | |
| 131 { "topic", ":", irc_cmd_topic }, | |
| 132 { "umode", ":", irc_cmd_mode }, | |
| 133 { "voice", ":", irc_cmd_op }, | |
| 134 { "wallops", ":", irc_cmd_wallops }, | |
| 135 { "whois", "n", irc_cmd_whois }, | |
| 7631 | 136 { NULL, NULL, NULL } |
| 6333 | 137 }; |
| 138 | |
| 9130 | 139 static GaimCmdRet irc_parse_gaim_cmd(GaimConversation *conv, const gchar *cmd, |
| 140 gchar **args, gchar **error) | |
| 141 { | |
| 142 GaimConnection *gc; | |
| 143 struct irc_conn *irc; | |
| 144 struct _irc_user_cmd *cmdent; | |
| 145 | |
| 146 gc = gaim_conversation_get_gc(conv); | |
| 147 if (!gc) | |
| 148 return GAIM_CMD_RET_FAILED; | |
| 149 | |
| 150 irc = gc->proto_data; | |
| 151 | |
| 152 if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL) | |
| 153 return GAIM_CMD_RET_FAILED; | |
| 154 | |
| 155 (cmdent->cb)(irc, cmd, gaim_conversation_get_name(conv), (const char **)args); | |
| 156 | |
| 157 return GAIM_CMD_RET_OK; | |
| 158 } | |
| 159 | |
| 160 static void irc_register_command(struct _irc_user_cmd *c) | |
| 161 { | |
| 162 GaimCmdFlag f; | |
| 163 char args[10]; | |
| 164 char *format; | |
| 165 int i; | |
| 166 | |
| 167 f = GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_PRPL_ONLY | |
| 168 | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS; | |
| 169 | |
| 170 format = c->format; | |
| 171 | |
| 172 for (i = 0; (i < (sizeof(args) - 1)) && *format; i++, format++) | |
| 173 switch (*format) { | |
| 174 case 'v': | |
| 175 case 'n': | |
| 176 case 'c': | |
| 177 case 't': | |
| 178 args[i] = 'w'; | |
| 179 break; | |
| 180 case ':': | |
| 181 case '*': | |
| 182 args[i] = 's'; | |
| 183 break; | |
| 184 } | |
| 185 | |
| 186 args[i] = '\0'; | |
| 187 | |
| 188 gaim_cmd_register(c->name, args, GAIM_CMD_P_PRPL, f, "prpl-irc", irc_parse_gaim_cmd, | |
| 189 _("No help is available at this time for this command.")); | |
| 190 } | |
| 191 | |
| 192 void irc_register_commands(void) | |
| 193 { | |
| 194 struct _irc_user_cmd *c; | |
| 195 | |
| 196 for (c = _irc_cmds; c && c->name; c++) | |
| 197 irc_register_command(c); | |
| 198 } | |
| 199 | |
| 6333 | 200 static char *irc_send_convert(struct irc_conn *irc, const char *string) |
| 201 { | |
| 202 char *utf8; | |
| 203 GError *err = NULL; | |
| 204 | |
| 205 utf8 = g_convert(string, strlen(string), | |
| 206 gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET), | |
| 207 "UTF-8", NULL, NULL, &err); | |
| 208 if (err) { | |
| 209 gaim_debug(GAIM_DEBUG_ERROR, "irc", "send conversion error: %s\n", err->message); | |
| 210 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Sending raw, which probably isn't right\n"); | |
| 211 utf8 = g_strdup(string); | |
| 8954 | 212 g_error_free(err); |
| 6333 | 213 } |
| 214 | |
| 215 return utf8; | |
| 216 } | |
| 217 | |
| 218 static char *irc_recv_convert(struct irc_conn *irc, const char *string) | |
| 219 { | |
| 220 char *utf8; | |
| 221 GError *err = NULL; | |
| 222 | |
| 223 utf8 = g_convert(string, strlen(string), "UTF-8", | |
| 224 gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET), | |
| 225 NULL, NULL, &err); | |
| 226 if (err) { | |
| 227 gaim_debug(GAIM_DEBUG_ERROR, "irc", "recv conversion error: %s\n", err->message); | |
| 228 utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)")); | |
| 8954 | 229 g_error_free(err); |
| 6333 | 230 } |
| 231 | |
| 232 return utf8; | |
| 233 } | |
| 234 | |
| 235 /* XXX tag closings are not necessarily correctly nested here! If we | |
| 236 * get a ^O or reach the end of the string and there are open | |
| 237 * tags, they are closed in a fixed order ... this means, for | |
| 238 * example, you might see <FONT COLOR="blue">some text <B>with | |
| 239 * various attributes</FONT></B> (notice that B and FONT overlap | |
| 240 * and are not cleanly nested). This is imminently fixable but | |
| 241 * I am not fixing it right now. | |
| 242 */ | |
| 243 char *irc_mirc2html(const char *string) | |
| 244 { | |
| 245 const char *cur, *end; | |
| 246 char fg[3] = "\0\0", bg[3] = "\0\0"; | |
| 247 int fgnum, bgnum; | |
| 6754 | 248 int font = 0, bold = 0, underline = 0; |
| 6333 | 249 GString *decoded = g_string_sized_new(strlen(string)); |
| 250 | |
| 251 cur = string; | |
| 252 do { | |
| 6754 | 253 end = strpbrk(cur, "\002\003\007\017\026\037"); |
| 6333 | 254 |
| 255 decoded = g_string_append_len(decoded, cur, end ? end - cur : strlen(cur)); | |
| 256 cur = end ? end : cur + strlen(cur); | |
| 257 | |
| 258 switch (*cur) { | |
| 259 case '\002': | |
| 260 cur++; | |
| 261 if (!bold) { | |
| 262 decoded = g_string_append(decoded, "<B>"); | |
| 263 bold = TRUE; | |
| 264 } else { | |
| 265 decoded = g_string_append(decoded, "</B>"); | |
| 266 bold = FALSE; | |
| 267 } | |
| 268 break; | |
| 269 case '\003': | |
| 270 cur++; | |
| 271 fg[0] = fg[1] = bg[0] = bg[1] = '\0'; | |
| 272 if (isdigit(*cur)) | |
| 273 fg[0] = *cur++; | |
| 274 if (isdigit(*cur)) | |
| 275 fg[1] = *cur++; | |
| 276 if (*cur == ',') { | |
| 277 cur++; | |
| 278 if (isdigit(*cur)) | |
| 279 bg[0] = *cur++; | |
| 280 if (isdigit(*cur)) | |
| 281 bg[1] = *cur++; | |
| 282 } | |
| 283 if (font) { | |
| 284 decoded = g_string_append(decoded, "</FONT>"); | |
| 285 font = FALSE; | |
| 286 } | |
| 287 | |
| 288 if (fg[0]) { | |
| 289 fgnum = atoi(fg); | |
| 290 if (fgnum < 0 || fgnum > 15) | |
| 291 continue; | |
| 292 font = TRUE; | |
| 293 g_string_append_printf(decoded, "<FONT COLOR=\"%s\"", irc_mirc_colors[fgnum]); | |
| 294 if (bg[0]) { | |
| 295 bgnum = atoi(bg); | |
| 296 if (bgnum >= 0 && bgnum < 16) | |
| 297 g_string_append_printf(decoded, " BACK=\"%s\"", irc_mirc_colors[bgnum]); | |
| 298 } | |
| 299 decoded = g_string_append_c(decoded, '>'); | |
| 300 } | |
| 301 break; | |
| 6754 | 302 case '\037': |
| 303 cur++; | |
| 304 if (!underline) { | |
| 305 decoded = g_string_append(decoded, "<U>"); | |
| 306 underline = TRUE; | |
| 307 } else { | |
| 308 decoded = g_string_append(decoded, "</U>"); | |
| 309 underline = TRUE; | |
| 310 } | |
| 311 break; | |
| 6333 | 312 case '\007': |
| 313 case '\026': | |
| 314 cur++; | |
| 315 break; | |
| 316 case '\017': | |
| 317 cur++; | |
| 318 /* fallthrough */ | |
| 319 case '\000': | |
| 320 if (bold) | |
| 6754 | 321 decoded = g_string_append(decoded, "</B>"); |
| 322 if (underline) | |
| 323 decoded = g_string_append(decoded, "</U>"); | |
| 6333 | 324 if (font) |
| 325 decoded = g_string_append(decoded, "</FONT>"); | |
| 326 break; | |
| 327 default: | |
| 328 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); | |
| 329 } | |
| 330 } while (*cur); | |
| 331 | |
| 332 return g_string_free(decoded, FALSE); | |
| 333 } | |
| 334 | |
| 8529 | 335 char *irc_mirc2txt (const char *string) |
| 336 { | |
| 337 char *result = g_strdup (string); | |
| 338 int i, j; | |
| 339 | |
| 340 for (i = 0, j = 0; result[i]; i++) { | |
| 341 switch (result[i]) { | |
| 342 case '\002': | |
| 343 case '\003': | |
| 344 case '\007': | |
| 345 case '\017': | |
| 346 case '\026': | |
| 347 case '\037': | |
| 348 continue; | |
| 349 default: | |
| 350 result[j++] = result[i]; | |
| 351 } | |
| 352 } | |
| 353 result[i] = '\0'; | |
| 354 return result; | |
| 355 } | |
| 356 | |
| 6333 | 357 char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice) |
| 358 { | |
| 359 GaimConnection *gc; | |
| 360 const char *cur = msg + 1; | |
| 361 char *buf, *ctcp; | |
| 362 time_t timestamp; | |
| 363 | |
| 6754 | 364 /* Note that this is NOT correct w.r.t. multiple CTCPs in one |
| 365 * message and low-level quoting ... but if you want that crap, | |
| 366 * use a real IRC client. */ | |
| 367 | |
| 6333 | 368 if (msg[0] != '\001' || msg[strlen(msg) - 1] != '\001') |
| 369 return g_strdup(msg); | |
| 370 | |
| 371 if (!strncmp(cur, "ACTION ", 7)) { | |
| 372 cur += 7; | |
| 373 buf = g_strdup_printf("/me %s", cur); | |
| 374 buf[strlen(buf) - 1] = '\0'; | |
| 375 return buf; | |
| 376 } else if (!strncmp(cur, "PING ", 5)) { | |
| 377 if (notice) { /* reply */ | |
| 378 sscanf(cur, "PING %lu", ×tamp); | |
| 379 gc = gaim_account_get_connection(irc->account); | |
| 380 if (!gc) | |
| 381 return NULL; | |
| 6350 | 382 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); |
| 6333 | 383 gaim_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf); |
| 384 g_free(buf); | |
| 385 return NULL; | |
| 386 } else { | |
| 387 buf = irc_format(irc, "vt:", "NOTICE", from, msg); | |
| 388 irc_send(irc, buf); | |
| 389 g_free(buf); | |
| 390 gc = gaim_account_get_connection(irc->account); | |
| 391 } | |
| 392 } else if (!strncmp(cur, "VERSION", 7) && !notice) { | |
| 393 buf = irc_format(irc, "vt:", "NOTICE", from, "\001VERSION Gaim IRC\001"); | |
| 394 irc_send(irc, buf); | |
| 395 g_free(buf); | |
| 8351 | 396 } else if (!strncmp(cur, "DCC SEND ", 9)) { |
| 397 irc_dccsend_recv(irc, from, msg + 10); | |
| 398 return NULL; | |
| 6333 | 399 } |
| 400 | |
| 401 ctcp = g_strdup(msg + 1); | |
| 402 ctcp[strlen(ctcp) - 1] = '\0'; | |
| 403 buf = g_strdup_printf("Received CTCP '%s' (to %s) from %s", ctcp, to, from); | |
| 404 g_free(ctcp); | |
| 405 return buf; | |
| 406 } | |
| 407 | |
| 408 void irc_msg_table_build(struct irc_conn *irc) | |
| 409 { | |
| 410 int i; | |
| 411 | |
| 412 if (!irc || !irc->msgs) { | |
| 413 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a message table on a bogus structure\n"); | |
| 414 return; | |
| 415 } | |
| 416 | |
| 417 for (i = 0; _irc_msgs[i].name; i++) { | |
| 418 g_hash_table_insert(irc->msgs, (gpointer)_irc_msgs[i].name, (gpointer)&_irc_msgs[i]); | |
| 419 } | |
| 420 } | |
| 421 | |
| 422 void irc_cmd_table_build(struct irc_conn *irc) | |
| 423 { | |
| 424 int i; | |
| 425 | |
| 426 if (!irc || !irc->cmds) { | |
| 427 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a command table on a bogus structure\n"); | |
| 428 return; | |
| 429 } | |
| 430 | |
| 431 for (i = 0; _irc_cmds[i].name ; i++) { | |
| 432 g_hash_table_insert(irc->cmds, (gpointer)_irc_cmds[i].name, (gpointer)&_irc_cmds[i]); | |
| 433 } | |
| 434 } | |
| 435 | |
| 436 char *irc_format(struct irc_conn *irc, const char *format, ...) | |
| 437 { | |
| 438 GString *string = g_string_new(""); | |
| 439 char *tok, *tmp; | |
| 440 const char *cur; | |
| 441 va_list ap; | |
| 442 | |
| 443 va_start(ap, format); | |
| 444 for (cur = format; *cur; cur++) { | |
| 445 if (cur != format) | |
| 446 g_string_append_c(string, ' '); | |
| 447 | |
| 448 tok = va_arg(ap, char *); | |
| 449 switch (*cur) { | |
| 450 case 'v': | |
| 451 g_string_append(string, tok); | |
| 452 break; | |
| 453 case ':': | |
| 454 g_string_append_c(string, ':'); | |
| 455 /* no break! */ | |
| 456 case 't': | |
| 457 case 'n': | |
| 458 case 'c': | |
| 459 tmp = irc_send_convert(irc, tok); | |
| 460 g_string_append(string, tmp); | |
| 461 g_free(tmp); | |
| 462 break; | |
| 463 default: | |
| 464 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Invalid format character '%c'\n", *cur); | |
| 465 break; | |
| 466 } | |
| 467 } | |
| 468 va_end(ap); | |
| 469 g_string_append(string, "\r\n"); | |
| 470 return (g_string_free(string, FALSE)); | |
| 471 } | |
| 472 | |
| 473 void irc_parse_msg(struct irc_conn *irc, char *input) | |
| 474 { | |
| 475 struct _irc_msg *msgent; | |
| 476 char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg; | |
| 7631 | 477 guint i; |
| 6333 | 478 |
| 479 if (!strncmp(input, "PING ", 5)) { | |
| 480 msg = irc_format(irc, "vv", "PONG", input + 5); | |
| 481 irc_send(irc, msg); | |
| 482 g_free(msg); | |
| 483 return; | |
| 484 } else if (!strncmp(input, "ERROR ", 6)) { | |
| 7981 | 485 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); |
| 6333 | 486 return; |
| 487 } | |
| 488 | |
| 489 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
| 490 irc_parse_error_cb(irc, input); | |
| 491 return; | |
| 492 } | |
| 493 | |
| 494 from = g_strndup(&input[1], cur - &input[1]); | |
| 495 cur++; | |
| 496 end = strchr(cur, ' '); | |
| 497 if (!end) | |
| 498 end = cur + strlen(cur); | |
| 499 | |
| 500 tmp = g_strndup(cur, end - cur); | |
| 501 msgname = g_ascii_strdown(tmp, -1); | |
| 502 g_free(tmp); | |
| 503 | |
| 504 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
| 505 irc_msg_default(irc, "", from, &input); | |
| 506 g_free(msgname); | |
| 507 g_free(from); | |
| 508 return; | |
| 509 } | |
| 510 g_free(msgname); | |
| 511 | |
| 512 args = g_new0(char *, strlen(msgent->format)); | |
| 513 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 514 switch (fmt[i]) { | |
| 515 case 'v': | |
| 516 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 517 args[i] = g_strndup(cur, end - cur); | |
| 518 cur += end - cur; | |
| 519 break; | |
| 520 case 't': | |
| 521 case 'n': | |
| 522 case 'c': | |
| 523 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 524 tmp = g_strndup(cur, end - cur); | |
| 525 args[i] = irc_recv_convert(irc, tmp); | |
| 526 g_free(tmp); | |
| 527 cur += end - cur; | |
| 528 break; | |
| 529 case ':': | |
| 530 if (*cur == ':') cur++; | |
| 531 args[i] = irc_recv_convert(irc, cur); | |
| 532 cur = cur + strlen(cur); | |
| 533 break; | |
| 534 case '*': | |
| 535 args[i] = g_strdup(cur); | |
| 536 cur = cur + strlen(cur); | |
| 537 break; | |
| 538 default: | |
| 539 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
| 540 break; | |
| 541 } | |
| 542 } | |
| 6970 | 543 tmp = irc_recv_convert(irc, from); |
| 544 (msgent->cb)(irc, msgent->name, tmp, args); | |
| 545 g_free(tmp); | |
| 6333 | 546 for (i = 0; i < strlen(msgent->format); i++) { |
| 547 g_free(args[i]); | |
| 548 } | |
| 549 g_free(args); | |
| 550 g_free(from); | |
| 551 } | |
| 552 | |
| 553 int irc_parse_cmd(struct irc_conn *irc, const char *target, const char *cmdstr) | |
| 554 { | |
| 555 const char *cur, *end, *fmt; | |
| 556 char *tmp, *cmd, **args; | |
| 557 struct _irc_user_cmd *cmdent; | |
| 7631 | 558 guint i; |
| 559 int ret; | |
| 6333 | 560 |
| 561 cur = cmdstr; | |
| 562 end = strchr(cmdstr, ' '); | |
| 563 if (!end) | |
| 564 end = cur + strlen(cur); | |
| 565 | |
| 566 tmp = g_strndup(cur, end - cur); | |
| 567 cmd = g_utf8_strdown(tmp, -1); | |
| 568 g_free(tmp); | |
| 569 | |
| 570 if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL) { | |
| 571 ret = irc_cmd_default(irc, cmd, target, &cmdstr); | |
| 572 g_free(cmd); | |
| 573 return ret; | |
| 574 } | |
| 575 | |
| 576 args = g_new0(char *, strlen(cmdent->format)); | |
| 577 for (cur = end, fmt = cmdent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 578 switch (fmt[i]) { | |
| 579 case 'v': | |
| 580 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 581 args[i] = g_strndup(cur, end - cur); | |
| 582 cur += end - cur; | |
| 583 break; | |
| 584 case 't': | |
| 585 case 'n': | |
| 586 case 'c': | |
| 587 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 588 args[i] = g_strndup(cur, end - cur); | |
| 589 cur += end - cur; | |
| 590 break; | |
| 591 case ':': | |
| 592 case '*': | |
| 593 args[i] = g_strdup(cur); | |
| 594 cur = cur + strlen(cur); | |
| 595 break; | |
| 596 default: | |
| 597 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid command format character '%c'\n", fmt[i]); | |
| 598 break; | |
| 599 } | |
| 600 } | |
| 601 ret = (cmdent->cb)(irc, cmd, target, (const char **)args); | |
| 602 for (i = 0; i < strlen(cmdent->format); i++) | |
| 603 g_free(args[i]); | |
| 604 g_free(args); | |
| 605 | |
| 606 g_free(cmd); | |
| 607 return ret; | |
| 608 } | |
| 609 | |
| 610 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
| 611 { | |
| 612 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
| 613 } |
