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