Mercurial > pidgin
annotate src/protocols/irc/parse.c @ 13253:87a7c3077c19
[gaim-migrate @ 15619]
More cleaning up of oscar. Renamed some functions to be more clear.
Got rid of some stuff that wasn't used. Inlined some small things
in conn.c that were only used once.
The goals of all this are
1. Non-blocking I/O for all connections
2. p2p stuff won't use the same struct as oscar connections, because
that's stupid
3. The oscar PRPL should be less scary
committer: Tailor Script <tailor@pidgin.im>
| author | Mark Doliner <mark@kingant.net> |
|---|---|
| date | Sun, 12 Feb 2006 21:27:04 +0000 |
| parents | 3c6de63fbfe8 |
| children | 5d5e249c488e |
| 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.") }, | |
| 12013 | 116 { "chanserv", ":", irc_cmd_service, N_("chanserv: Send a command to chanserv") }, |
| 9258 | 117 { "deop", ":", irc_cmd_op, N_("deop <nick1> [nick2] ...: Remove channel operator status from someone. You must be a channel operator to do this.") }, |
| 118 { "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.") }, | |
| 119 { "invite", ":", irc_cmd_invite, N_("invite <nick> [room]: Invite someone to join you in the specified channel, or the current channel.") }, | |
| 9266 | 120 { "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.") }, |
| 121 { "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 | 122 { "kick", "n:", irc_cmd_kick, N_("kick <nick> [message]: Remove someone from a channel. You must be a channel operator to do this.") }, |
| 123 { "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 | 124 { "me", ":", irc_cmd_ctcp_action, N_("me <action to perform>: Perform an action.") }, |
| 12013 | 125 { "memoserv", ":", irc_cmd_service, N_("memoserv: Send a command to memoserv") }, |
| 10609 | 126 { "mode", ":", irc_cmd_mode, N_("mode <+|-><A-Za-z> <nick|channel>: Set or unset a channel or user mode.") }, |
| 9258 | 127 { "msg", "t:", irc_cmd_privmsg, N_("msg <nick> <message>: Send a private message to a user (as opposed to a channel).") }, |
| 128 { "names", "c", irc_cmd_names, N_("names [channel]: List the users currently in a channel.") }, | |
| 9274 | 129 { "nick", "n", irc_cmd_nick, N_("nick <new nickname>: Change your nickname.") }, |
| 12013 | 130 { "nickserv", ":", irc_cmd_service, N_("nickserv: Send a command to nickserv") }, |
| 9258 | 131 { "op", ":", irc_cmd_op, N_("op <nick1> [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this.") }, |
| 9255 | 132 { "operwall", ":", irc_cmd_wallops, N_("operwall <message>: If you don't know what this is, you probably can't use it.") }, |
| 12013 | 133 { "operserv", ":", irc_cmd_service, N_("operserv: Send a command to operserv") }, |
| 9258 | 134 { "part", "c:", irc_cmd_part, N_("part [room] [message]: Leave the current channel, or a specified channel, with an optional message.") }, |
| 9255 | 135 { "ping", "n", irc_cmd_ping, N_("ping [nick]: Asks how much lag a user (or the server if no user specified) has.") }, |
| 9258 | 136 { "query", "n:", irc_cmd_query, N_("query <nick> <message>: Send a private message to a user (as opposed to a channel).") }, |
| 9255 | 137 { "quit", ":", irc_cmd_quit, N_("quit [message]: Disconnect from the server, with an optional message.") }, |
| 138 { "quote", "*", irc_cmd_quote, N_("quote [...]: Send a raw command to the server.") }, | |
| 139 { "remove", "n:", irc_cmd_remove, N_("remove <nick> [message]: Remove someone from a room. You must be a channel operator to do this.") }, | |
| 10564 | 140 { "time", "", irc_cmd_time, N_("time: Displays the current local time at the IRC server.") }, |
| 9255 | 141 { "topic", ":", irc_cmd_topic, N_("topic [new topic]: View or change the channel topic.") }, |
| 142 { "umode", ":", irc_cmd_mode, N_("umode <+|-><A-Za-z>: Set or unset a user mode.") }, | |
| 9258 | 143 { "voice", ":", irc_cmd_op, N_("voice <nick1> [nick2] ...: Grant channel voice status to someone. You must be a channel operator to do this.") }, |
| 9255 | 144 { "wallops", ":", irc_cmd_wallops, N_("wallops <message>: If you don't know what this is, you probably can't use it.") }, |
| 10609 | 145 { "whois", "tt", irc_cmd_whois, N_("whois [server] <nick>: Get information on a user.") }, |
| 11318 | 146 { NULL, NULL, NULL, NULL } |
| 6333 | 147 }; |
| 148 | |
| 9130 | 149 static GaimCmdRet irc_parse_gaim_cmd(GaimConversation *conv, const gchar *cmd, |
| 9597 | 150 gchar **args, gchar **error, void *data) |
| 9130 | 151 { |
| 152 GaimConnection *gc; | |
| 153 struct irc_conn *irc; | |
| 154 struct _irc_user_cmd *cmdent; | |
| 155 | |
| 156 gc = gaim_conversation_get_gc(conv); | |
| 157 if (!gc) | |
| 158 return GAIM_CMD_RET_FAILED; | |
| 159 | |
| 160 irc = gc->proto_data; | |
| 161 | |
| 162 if ((cmdent = g_hash_table_lookup(irc->cmds, cmd)) == NULL) | |
| 163 return GAIM_CMD_RET_FAILED; | |
| 164 | |
| 165 (cmdent->cb)(irc, cmd, gaim_conversation_get_name(conv), (const char **)args); | |
| 166 | |
| 167 return GAIM_CMD_RET_OK; | |
| 168 } | |
| 169 | |
| 170 static void irc_register_command(struct _irc_user_cmd *c) | |
| 171 { | |
| 172 GaimCmdFlag f; | |
| 173 char args[10]; | |
| 174 char *format; | |
|
12316
d5088e83fe0c
[gaim-migrate @ 14620]
Richard Laager <rlaager@wiktel.com>
parents:
12282
diff
changeset
|
175 size_t i; |
| 9130 | 176 |
| 177 f = GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_PRPL_ONLY | |
| 178 | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS; | |
| 179 | |
| 180 format = c->format; | |
| 181 | |
| 182 for (i = 0; (i < (sizeof(args) - 1)) && *format; i++, format++) | |
| 183 switch (*format) { | |
| 184 case 'v': | |
| 185 case 'n': | |
| 186 case 'c': | |
| 187 case 't': | |
| 188 args[i] = 'w'; | |
| 189 break; | |
| 190 case ':': | |
| 191 case '*': | |
| 192 args[i] = 's'; | |
| 193 break; | |
| 194 } | |
| 195 | |
| 196 args[i] = '\0'; | |
| 197 | |
| 9597 | 198 gaim_cmd_register(c->name, args, GAIM_CMD_P_PRPL, f, "prpl-irc", |
| 199 irc_parse_gaim_cmd, _(c->help), NULL); | |
| 9130 | 200 } |
| 201 | |
| 202 void irc_register_commands(void) | |
| 203 { | |
| 204 struct _irc_user_cmd *c; | |
| 205 | |
| 206 for (c = _irc_cmds; c && c->name; c++) | |
| 207 irc_register_command(c); | |
| 208 } | |
| 209 | |
| 6333 | 210 static char *irc_send_convert(struct irc_conn *irc, const char *string) |
| 211 { | |
| 212 char *utf8; | |
| 213 GError *err = NULL; | |
| 10258 | 214 gchar **encodings; |
| 215 const gchar *enclist; | |
| 9644 | 216 |
| 10258 | 217 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); |
| 218 encodings = g_strsplit(enclist, ",", 2); | |
| 219 | |
| 10278 | 220 if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) { |
| 221 g_strfreev(encodings); | |
| 9644 | 222 return g_strdup(string); |
| 10278 | 223 } |
| 9644 | 224 |
| 10258 | 225 utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err); |
| 6333 | 226 if (err) { |
| 9644 | 227 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Send conversion error: %s\n", err->message); |
| 10258 | 228 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Sending as UTF-8 instead of %s\n", encodings[0]); |
| 6333 | 229 utf8 = g_strdup(string); |
| 8954 | 230 g_error_free(err); |
| 6333 | 231 } |
| 10258 | 232 g_strfreev(encodings); |
| 233 | |
| 6333 | 234 return utf8; |
| 235 } | |
| 236 | |
| 237 static char *irc_recv_convert(struct irc_conn *irc, const char *string) | |
| 238 { | |
| 9644 | 239 char *utf8 = NULL; |
| 10258 | 240 const gchar *charset, *enclist; |
| 241 gchar **encodings; | |
| 242 int i; | |
| 9644 | 243 |
| 10258 | 244 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); |
| 245 encodings = g_strsplit(enclist, ",", -1); | |
| 246 | |
| 10504 | 247 if (encodings[0] == NULL) { |
| 248 g_strfreev(encodings); | |
| 10258 | 249 return gaim_utf8_salvage(string); |
| 10504 | 250 } |
| 9644 | 251 |
| 10258 | 252 for (i = 0; encodings[i] != NULL; i++) { |
| 253 charset = encodings[i]; | |
| 254 while (*charset == ' ') | |
| 255 charset++; | |
| 256 | |
| 257 if (!strcasecmp("UTF-8", charset)) { | |
|
11726
6e0986c82bc5
[gaim-migrate @ 14017]
Richard Laager <rlaager@wiktel.com>
parents:
11318
diff
changeset
|
258 if (g_utf8_validate(string, -1, NULL)) |
| 10258 | 259 utf8 = g_strdup(string); |
| 260 } else { | |
|
11726
6e0986c82bc5
[gaim-migrate @ 14017]
Richard Laager <rlaager@wiktel.com>
parents:
11318
diff
changeset
|
261 utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL); |
| 10258 | 262 } |
| 263 | |
| 264 if (utf8) { | |
| 265 g_strfreev(encodings); | |
| 266 return utf8; | |
| 267 } | |
| 9644 | 268 } |
| 10504 | 269 g_strfreev(encodings); |
| 9644 | 270 |
| 10258 | 271 return gaim_utf8_salvage(string); |
| 6333 | 272 } |
| 273 | |
| 274 /* XXX tag closings are not necessarily correctly nested here! If we | |
| 275 * get a ^O or reach the end of the string and there are open | |
| 276 * tags, they are closed in a fixed order ... this means, for | |
| 277 * example, you might see <FONT COLOR="blue">some text <B>with | |
| 278 * various attributes</FONT></B> (notice that B and FONT overlap | |
| 279 * and are not cleanly nested). This is imminently fixable but | |
| 280 * I am not fixing it right now. | |
| 281 */ | |
| 282 char *irc_mirc2html(const char *string) | |
| 283 { | |
| 284 const char *cur, *end; | |
| 285 char fg[3] = "\0\0", bg[3] = "\0\0"; | |
| 286 int fgnum, bgnum; | |
| 12158 | 287 int font = 0, bold = 0, underline = 0, italic = 0; |
| 6333 | 288 GString *decoded = g_string_sized_new(strlen(string)); |
| 289 | |
| 290 cur = string; | |
| 291 do { | |
| 6754 | 292 end = strpbrk(cur, "\002\003\007\017\026\037"); |
| 6333 | 293 |
| 294 decoded = g_string_append_len(decoded, cur, end ? end - cur : strlen(cur)); | |
| 295 cur = end ? end : cur + strlen(cur); | |
| 296 | |
| 297 switch (*cur) { | |
| 298 case '\002': | |
| 299 cur++; | |
| 300 if (!bold) { | |
| 301 decoded = g_string_append(decoded, "<B>"); | |
| 302 bold = TRUE; | |
| 303 } else { | |
| 304 decoded = g_string_append(decoded, "</B>"); | |
| 305 bold = FALSE; | |
| 306 } | |
| 307 break; | |
| 308 case '\003': | |
| 309 cur++; | |
| 310 fg[0] = fg[1] = bg[0] = bg[1] = '\0'; | |
| 311 if (isdigit(*cur)) | |
| 312 fg[0] = *cur++; | |
| 313 if (isdigit(*cur)) | |
| 314 fg[1] = *cur++; | |
| 315 if (*cur == ',') { | |
| 316 cur++; | |
| 317 if (isdigit(*cur)) | |
| 318 bg[0] = *cur++; | |
| 319 if (isdigit(*cur)) | |
| 320 bg[1] = *cur++; | |
| 321 } | |
| 322 if (font) { | |
| 323 decoded = g_string_append(decoded, "</FONT>"); | |
| 324 font = FALSE; | |
| 325 } | |
| 326 | |
| 327 if (fg[0]) { | |
| 328 fgnum = atoi(fg); | |
| 329 if (fgnum < 0 || fgnum > 15) | |
| 330 continue; | |
| 331 font = TRUE; | |
| 332 g_string_append_printf(decoded, "<FONT COLOR=\"%s\"", irc_mirc_colors[fgnum]); | |
| 333 if (bg[0]) { | |
| 334 bgnum = atoi(bg); | |
| 335 if (bgnum >= 0 && bgnum < 16) | |
| 336 g_string_append_printf(decoded, " BACK=\"%s\"", irc_mirc_colors[bgnum]); | |
| 337 } | |
| 338 decoded = g_string_append_c(decoded, '>'); | |
| 339 } | |
| 340 break; | |
| 12158 | 341 case '\011': |
| 342 cur++; | |
| 343 if (!italic) { | |
| 344 decoded = g_string_append(decoded, "<I>"); | |
| 345 italic = TRUE; | |
| 346 } else { | |
| 347 decoded = g_string_append(decoded, "</I>"); | |
| 348 italic = FALSE; | |
| 349 } | |
| 350 break; | |
| 6754 | 351 case '\037': |
| 352 cur++; | |
| 353 if (!underline) { | |
| 354 decoded = g_string_append(decoded, "<U>"); | |
| 355 underline = TRUE; | |
| 356 } else { | |
| 357 decoded = g_string_append(decoded, "</U>"); | |
|
12282
de27f2831309
[gaim-migrate @ 14586]
Etan Reisner <pidgin@unreliablesource.net>
parents:
12158
diff
changeset
|
358 underline = FALSE; |
| 6754 | 359 } |
| 360 break; | |
| 6333 | 361 case '\007': |
| 362 case '\026': | |
| 363 cur++; | |
| 364 break; | |
| 365 case '\017': | |
| 366 cur++; | |
| 367 /* fallthrough */ | |
| 368 case '\000': | |
| 369 if (bold) | |
| 6754 | 370 decoded = g_string_append(decoded, "</B>"); |
| 12158 | 371 if (italic) |
| 372 decoded = g_string_append(decoded, "</I>"); | |
| 6754 | 373 if (underline) |
| 374 decoded = g_string_append(decoded, "</U>"); | |
| 6333 | 375 if (font) |
| 376 decoded = g_string_append(decoded, "</FONT>"); | |
| 377 break; | |
| 378 default: | |
| 379 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); | |
| 380 } | |
| 381 } while (*cur); | |
| 382 | |
| 383 return g_string_free(decoded, FALSE); | |
| 384 } | |
| 385 | |
| 8529 | 386 char *irc_mirc2txt (const char *string) |
| 387 { | |
| 388 char *result = g_strdup (string); | |
| 389 int i, j; | |
| 390 | |
| 391 for (i = 0, j = 0; result[i]; i++) { | |
| 392 switch (result[i]) { | |
| 393 case '\002': | |
| 394 case '\003': | |
| 395 case '\007': | |
| 396 case '\017': | |
| 397 case '\026': | |
| 398 case '\037': | |
| 399 continue; | |
| 400 default: | |
| 401 result[j++] = result[i]; | |
| 402 } | |
| 403 } | |
| 11136 | 404 result[j] = '\0'; |
| 8529 | 405 return result; |
| 406 } | |
| 407 | |
| 10208 | 408 gboolean irc_ischannel(const char *string) |
| 409 { | |
| 410 return (string[0] == '#' || string[0] == '&'); | |
| 411 } | |
| 412 | |
| 6333 | 413 char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice) |
| 414 { | |
| 415 GaimConnection *gc; | |
| 416 const char *cur = msg + 1; | |
| 417 char *buf, *ctcp; | |
| 418 time_t timestamp; | |
| 419 | |
| 6754 | 420 /* Note that this is NOT correct w.r.t. multiple CTCPs in one |
| 421 * message and low-level quoting ... but if you want that crap, | |
| 422 * use a real IRC client. */ | |
| 423 | |
| 6333 | 424 if (msg[0] != '\001' || msg[strlen(msg) - 1] != '\001') |
| 425 return g_strdup(msg); | |
| 426 | |
| 427 if (!strncmp(cur, "ACTION ", 7)) { | |
| 428 cur += 7; | |
| 429 buf = g_strdup_printf("/me %s", cur); | |
| 430 buf[strlen(buf) - 1] = '\0'; | |
| 431 return buf; | |
| 432 } else if (!strncmp(cur, "PING ", 5)) { | |
| 433 if (notice) { /* reply */ | |
| 11972 | 434 /* TODO: Should this read in the timestamp as a double? */ |
| 6333 | 435 sscanf(cur, "PING %lu", ×tamp); |
| 436 gc = gaim_account_get_connection(irc->account); | |
| 437 if (!gc) | |
| 438 return NULL; | |
| 6350 | 439 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); |
| 6333 | 440 gaim_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf); |
| 441 g_free(buf); | |
| 442 return NULL; | |
| 443 } else { | |
| 444 buf = irc_format(irc, "vt:", "NOTICE", from, msg); | |
| 445 irc_send(irc, buf); | |
| 446 g_free(buf); | |
| 447 } | |
| 448 } else if (!strncmp(cur, "VERSION", 7) && !notice) { | |
| 449 buf = irc_format(irc, "vt:", "NOTICE", from, "\001VERSION Gaim IRC\001"); | |
| 450 irc_send(irc, buf); | |
| 451 g_free(buf); | |
| 8351 | 452 } else if (!strncmp(cur, "DCC SEND ", 9)) { |
| 453 irc_dccsend_recv(irc, from, msg + 10); | |
| 454 return NULL; | |
| 6333 | 455 } |
| 456 | |
| 457 ctcp = g_strdup(msg + 1); | |
| 458 ctcp[strlen(ctcp) - 1] = '\0'; | |
| 459 buf = g_strdup_printf("Received CTCP '%s' (to %s) from %s", ctcp, to, from); | |
| 460 g_free(ctcp); | |
| 461 return buf; | |
| 462 } | |
| 463 | |
| 464 void irc_msg_table_build(struct irc_conn *irc) | |
| 465 { | |
| 466 int i; | |
| 467 | |
| 468 if (!irc || !irc->msgs) { | |
| 469 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a message table on a bogus structure\n"); | |
| 470 return; | |
| 471 } | |
| 472 | |
| 473 for (i = 0; _irc_msgs[i].name; i++) { | |
| 474 g_hash_table_insert(irc->msgs, (gpointer)_irc_msgs[i].name, (gpointer)&_irc_msgs[i]); | |
| 475 } | |
| 476 } | |
| 477 | |
| 478 void irc_cmd_table_build(struct irc_conn *irc) | |
| 479 { | |
| 480 int i; | |
| 481 | |
| 482 if (!irc || !irc->cmds) { | |
| 483 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a command table on a bogus structure\n"); | |
| 484 return; | |
| 485 } | |
| 486 | |
| 487 for (i = 0; _irc_cmds[i].name ; i++) { | |
| 488 g_hash_table_insert(irc->cmds, (gpointer)_irc_cmds[i].name, (gpointer)&_irc_cmds[i]); | |
| 489 } | |
| 490 } | |
| 491 | |
| 492 char *irc_format(struct irc_conn *irc, const char *format, ...) | |
| 493 { | |
| 494 GString *string = g_string_new(""); | |
| 495 char *tok, *tmp; | |
| 496 const char *cur; | |
| 497 va_list ap; | |
| 498 | |
| 499 va_start(ap, format); | |
| 500 for (cur = format; *cur; cur++) { | |
| 501 if (cur != format) | |
| 502 g_string_append_c(string, ' '); | |
| 503 | |
| 504 tok = va_arg(ap, char *); | |
| 505 switch (*cur) { | |
| 506 case 'v': | |
| 507 g_string_append(string, tok); | |
| 508 break; | |
| 509 case ':': | |
| 510 g_string_append_c(string, ':'); | |
| 511 /* no break! */ | |
| 512 case 't': | |
| 513 case 'n': | |
| 514 case 'c': | |
| 515 tmp = irc_send_convert(irc, tok); | |
| 516 g_string_append(string, tmp); | |
| 517 g_free(tmp); | |
| 518 break; | |
| 519 default: | |
| 520 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Invalid format character '%c'\n", *cur); | |
| 521 break; | |
| 522 } | |
| 523 } | |
| 524 va_end(ap); | |
| 525 g_string_append(string, "\r\n"); | |
| 526 return (g_string_free(string, FALSE)); | |
| 527 } | |
| 528 | |
| 529 void irc_parse_msg(struct irc_conn *irc, char *input) | |
| 530 { | |
| 531 struct _irc_msg *msgent; | |
| 532 char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg; | |
| 7631 | 533 guint i; |
| 6333 | 534 |
| 535 if (!strncmp(input, "PING ", 5)) { | |
| 536 msg = irc_format(irc, "vv", "PONG", input + 5); | |
| 537 irc_send(irc, msg); | |
| 538 g_free(msg); | |
| 539 return; | |
| 540 } else if (!strncmp(input, "ERROR ", 6)) { | |
| 10154 | 541 if (g_utf8_validate(input, -1, NULL)) { |
| 542 char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input); | |
| 543 gaim_connection_error(gaim_account_get_connection(irc->account), tmp); | |
| 544 g_free(tmp); | |
| 545 } else | |
| 546 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); | |
| 6333 | 547 return; |
| 548 } | |
| 549 | |
| 550 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
| 551 irc_parse_error_cb(irc, input); | |
| 552 return; | |
| 553 } | |
| 554 | |
| 555 from = g_strndup(&input[1], cur - &input[1]); | |
| 556 cur++; | |
| 557 end = strchr(cur, ' '); | |
| 558 if (!end) | |
| 559 end = cur + strlen(cur); | |
| 560 | |
| 561 tmp = g_strndup(cur, end - cur); | |
| 562 msgname = g_ascii_strdown(tmp, -1); | |
| 563 g_free(tmp); | |
| 564 | |
| 565 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
| 566 irc_msg_default(irc, "", from, &input); | |
| 567 g_free(msgname); | |
| 568 g_free(from); | |
| 569 return; | |
| 570 } | |
| 571 g_free(msgname); | |
| 572 | |
| 573 args = g_new0(char *, strlen(msgent->format)); | |
| 574 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 575 switch (fmt[i]) { | |
| 576 case 'v': | |
| 577 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 578 args[i] = g_strndup(cur, end - cur); | |
| 579 cur += end - cur; | |
| 580 break; | |
| 581 case 't': | |
| 582 case 'n': | |
| 583 case 'c': | |
| 584 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 585 tmp = g_strndup(cur, end - cur); | |
| 586 args[i] = irc_recv_convert(irc, tmp); | |
| 587 g_free(tmp); | |
| 588 cur += end - cur; | |
| 589 break; | |
| 590 case ':': | |
| 591 if (*cur == ':') cur++; | |
| 592 args[i] = irc_recv_convert(irc, cur); | |
| 593 cur = cur + strlen(cur); | |
| 594 break; | |
| 595 case '*': | |
| 596 args[i] = g_strdup(cur); | |
| 597 cur = cur + strlen(cur); | |
| 598 break; | |
| 599 default: | |
| 600 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
| 601 break; | |
| 602 } | |
| 603 } | |
| 6970 | 604 tmp = irc_recv_convert(irc, from); |
| 605 (msgent->cb)(irc, msgent->name, tmp, args); | |
| 606 g_free(tmp); | |
| 6333 | 607 for (i = 0; i < strlen(msgent->format); i++) { |
| 608 g_free(args[i]); | |
| 609 } | |
| 610 g_free(args); | |
| 611 g_free(from); | |
| 612 } | |
| 613 | |
| 614 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
| 615 { | |
| 616 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
| 617 } |
