Mercurial > pidgin
annotate src/protocols/irc/parse.c @ 12024:e67993da8a22
[gaim-migrate @ 14317]
I strongly suspect CruiseControl is going to yell at me for this.
A voice chat API, GUI + mediastreamer. This is what I'm using for Google Talk.
This doesn't actually do anything at all. There's no code in the Jabber plugin yet
to use this API (although it Works For Me). All it will do is compile and link.
If you're lucky.
To build this, you should install oRTP from Linphone, Speex and iLBC (also
from linphone, I believe). To not build this, ./configure --disable-vv.
Most of the configure.ac and Makefile.am hackery was lifted right out of
Linphone with a few modifications. It seems to work if you have everything
installed or if you --disable-vv. I haven't really tested not having
everything installed and not --disabling-vv.
It's kinda funky to include all of mediastreamer in the source tree like this,
but linphone doesn't build it as a separate library. I'll probably wind up
writing them a patch to build it as a .so so we can link it dynamically instead.
This code certainly isn't finished. It'll adapt as I progress on the Google code,
but it's certainly of more use here in CVS than in my personal tree.
committer: Tailor Script <tailor@pidgin.im>
| author | Sean Egan <seanegan@gmail.com> |
|---|---|
| date | Wed, 09 Nov 2005 08:07:20 +0000 |
| parents | 39734dd473e0 |
| children | d4725469b653 |
| 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; | |
| 175 int i; | |
| 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; | |
| 6754 | 287 int font = 0, bold = 0, underline = 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; | |
| 6754 | 341 case '\037': |
| 342 cur++; | |
| 343 if (!underline) { | |
| 344 decoded = g_string_append(decoded, "<U>"); | |
| 345 underline = TRUE; | |
| 346 } else { | |
| 347 decoded = g_string_append(decoded, "</U>"); | |
| 348 underline = TRUE; | |
| 349 } | |
| 350 break; | |
| 6333 | 351 case '\007': |
| 352 case '\026': | |
| 353 cur++; | |
| 354 break; | |
| 355 case '\017': | |
| 356 cur++; | |
| 357 /* fallthrough */ | |
| 358 case '\000': | |
| 359 if (bold) | |
| 6754 | 360 decoded = g_string_append(decoded, "</B>"); |
| 361 if (underline) | |
| 362 decoded = g_string_append(decoded, "</U>"); | |
| 6333 | 363 if (font) |
| 364 decoded = g_string_append(decoded, "</FONT>"); | |
| 365 break; | |
| 366 default: | |
| 367 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Unexpected mIRC formatting character %d\n", *cur); | |
| 368 } | |
| 369 } while (*cur); | |
| 370 | |
| 371 return g_string_free(decoded, FALSE); | |
| 372 } | |
| 373 | |
| 8529 | 374 char *irc_mirc2txt (const char *string) |
| 375 { | |
| 376 char *result = g_strdup (string); | |
| 377 int i, j; | |
| 378 | |
| 379 for (i = 0, j = 0; result[i]; i++) { | |
| 380 switch (result[i]) { | |
| 381 case '\002': | |
| 382 case '\003': | |
| 383 case '\007': | |
| 384 case '\017': | |
| 385 case '\026': | |
| 386 case '\037': | |
| 387 continue; | |
| 388 default: | |
| 389 result[j++] = result[i]; | |
| 390 } | |
| 391 } | |
| 11136 | 392 result[j] = '\0'; |
| 8529 | 393 return result; |
| 394 } | |
| 395 | |
| 10208 | 396 gboolean irc_ischannel(const char *string) |
| 397 { | |
| 398 return (string[0] == '#' || string[0] == '&'); | |
| 399 } | |
| 400 | |
| 6333 | 401 char *irc_parse_ctcp(struct irc_conn *irc, const char *from, const char *to, const char *msg, int notice) |
| 402 { | |
| 403 GaimConnection *gc; | |
| 404 const char *cur = msg + 1; | |
| 405 char *buf, *ctcp; | |
| 406 time_t timestamp; | |
| 407 | |
| 6754 | 408 /* Note that this is NOT correct w.r.t. multiple CTCPs in one |
| 409 * message and low-level quoting ... but if you want that crap, | |
| 410 * use a real IRC client. */ | |
| 411 | |
| 6333 | 412 if (msg[0] != '\001' || msg[strlen(msg) - 1] != '\001') |
| 413 return g_strdup(msg); | |
| 414 | |
| 415 if (!strncmp(cur, "ACTION ", 7)) { | |
| 416 cur += 7; | |
| 417 buf = g_strdup_printf("/me %s", cur); | |
| 418 buf[strlen(buf) - 1] = '\0'; | |
| 419 return buf; | |
| 420 } else if (!strncmp(cur, "PING ", 5)) { | |
| 421 if (notice) { /* reply */ | |
| 11972 | 422 /* TODO: Should this read in the timestamp as a double? */ |
| 6333 | 423 sscanf(cur, "PING %lu", ×tamp); |
| 424 gc = gaim_account_get_connection(irc->account); | |
| 425 if (!gc) | |
| 426 return NULL; | |
| 6350 | 427 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); |
| 6333 | 428 gaim_notify_info(gc, _("PONG"), _("CTCP PING reply"), buf); |
| 429 g_free(buf); | |
| 430 return NULL; | |
| 431 } else { | |
| 432 buf = irc_format(irc, "vt:", "NOTICE", from, msg); | |
| 433 irc_send(irc, buf); | |
| 434 g_free(buf); | |
| 435 gc = gaim_account_get_connection(irc->account); | |
| 436 } | |
| 437 } else if (!strncmp(cur, "VERSION", 7) && !notice) { | |
| 438 buf = irc_format(irc, "vt:", "NOTICE", from, "\001VERSION Gaim IRC\001"); | |
| 439 irc_send(irc, buf); | |
| 440 g_free(buf); | |
| 8351 | 441 } else if (!strncmp(cur, "DCC SEND ", 9)) { |
| 442 irc_dccsend_recv(irc, from, msg + 10); | |
| 443 return NULL; | |
| 6333 | 444 } |
| 445 | |
| 446 ctcp = g_strdup(msg + 1); | |
| 447 ctcp[strlen(ctcp) - 1] = '\0'; | |
| 448 buf = g_strdup_printf("Received CTCP '%s' (to %s) from %s", ctcp, to, from); | |
| 449 g_free(ctcp); | |
| 450 return buf; | |
| 451 } | |
| 452 | |
| 453 void irc_msg_table_build(struct irc_conn *irc) | |
| 454 { | |
| 455 int i; | |
| 456 | |
| 457 if (!irc || !irc->msgs) { | |
| 458 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a message table on a bogus structure\n"); | |
| 459 return; | |
| 460 } | |
| 461 | |
| 462 for (i = 0; _irc_msgs[i].name; i++) { | |
| 463 g_hash_table_insert(irc->msgs, (gpointer)_irc_msgs[i].name, (gpointer)&_irc_msgs[i]); | |
| 464 } | |
| 465 } | |
| 466 | |
| 467 void irc_cmd_table_build(struct irc_conn *irc) | |
| 468 { | |
| 469 int i; | |
| 470 | |
| 471 if (!irc || !irc->cmds) { | |
| 472 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Attempt to build a command table on a bogus structure\n"); | |
| 473 return; | |
| 474 } | |
| 475 | |
| 476 for (i = 0; _irc_cmds[i].name ; i++) { | |
| 477 g_hash_table_insert(irc->cmds, (gpointer)_irc_cmds[i].name, (gpointer)&_irc_cmds[i]); | |
| 478 } | |
| 479 } | |
| 480 | |
| 481 char *irc_format(struct irc_conn *irc, const char *format, ...) | |
| 482 { | |
| 483 GString *string = g_string_new(""); | |
| 484 char *tok, *tmp; | |
| 485 const char *cur; | |
| 486 va_list ap; | |
| 487 | |
| 488 va_start(ap, format); | |
| 489 for (cur = format; *cur; cur++) { | |
| 490 if (cur != format) | |
| 491 g_string_append_c(string, ' '); | |
| 492 | |
| 493 tok = va_arg(ap, char *); | |
| 494 switch (*cur) { | |
| 495 case 'v': | |
| 496 g_string_append(string, tok); | |
| 497 break; | |
| 498 case ':': | |
| 499 g_string_append_c(string, ':'); | |
| 500 /* no break! */ | |
| 501 case 't': | |
| 502 case 'n': | |
| 503 case 'c': | |
| 504 tmp = irc_send_convert(irc, tok); | |
| 505 g_string_append(string, tmp); | |
| 506 g_free(tmp); | |
| 507 break; | |
| 508 default: | |
| 509 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Invalid format character '%c'\n", *cur); | |
| 510 break; | |
| 511 } | |
| 512 } | |
| 513 va_end(ap); | |
| 514 g_string_append(string, "\r\n"); | |
| 515 return (g_string_free(string, FALSE)); | |
| 516 } | |
| 517 | |
| 518 void irc_parse_msg(struct irc_conn *irc, char *input) | |
| 519 { | |
| 520 struct _irc_msg *msgent; | |
| 521 char *cur, *end, *tmp, *from, *msgname, *fmt, **args, *msg; | |
| 7631 | 522 guint i; |
| 6333 | 523 |
| 524 if (!strncmp(input, "PING ", 5)) { | |
| 525 msg = irc_format(irc, "vv", "PONG", input + 5); | |
| 526 irc_send(irc, msg); | |
| 527 g_free(msg); | |
| 528 return; | |
| 529 } else if (!strncmp(input, "ERROR ", 6)) { | |
| 10154 | 530 if (g_utf8_validate(input, -1, NULL)) { |
| 531 char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input); | |
| 532 gaim_connection_error(gaim_account_get_connection(irc->account), tmp); | |
| 533 g_free(tmp); | |
| 534 } else | |
| 535 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); | |
| 6333 | 536 return; |
| 537 } | |
| 538 | |
| 539 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
| 540 irc_parse_error_cb(irc, input); | |
| 541 return; | |
| 542 } | |
| 543 | |
| 544 from = g_strndup(&input[1], cur - &input[1]); | |
| 545 cur++; | |
| 546 end = strchr(cur, ' '); | |
| 547 if (!end) | |
| 548 end = cur + strlen(cur); | |
| 549 | |
| 550 tmp = g_strndup(cur, end - cur); | |
| 551 msgname = g_ascii_strdown(tmp, -1); | |
| 552 g_free(tmp); | |
| 553 | |
| 554 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
| 555 irc_msg_default(irc, "", from, &input); | |
| 556 g_free(msgname); | |
| 557 g_free(from); | |
| 558 return; | |
| 559 } | |
| 560 g_free(msgname); | |
| 561 | |
| 562 args = g_new0(char *, strlen(msgent->format)); | |
| 563 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
| 564 switch (fmt[i]) { | |
| 565 case 'v': | |
| 566 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 567 args[i] = g_strndup(cur, end - cur); | |
| 568 cur += end - cur; | |
| 569 break; | |
| 570 case 't': | |
| 571 case 'n': | |
| 572 case 'c': | |
| 573 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
| 574 tmp = g_strndup(cur, end - cur); | |
| 575 args[i] = irc_recv_convert(irc, tmp); | |
| 576 g_free(tmp); | |
| 577 cur += end - cur; | |
| 578 break; | |
| 579 case ':': | |
| 580 if (*cur == ':') cur++; | |
| 581 args[i] = irc_recv_convert(irc, cur); | |
| 582 cur = cur + strlen(cur); | |
| 583 break; | |
| 584 case '*': | |
| 585 args[i] = g_strdup(cur); | |
| 586 cur = cur + strlen(cur); | |
| 587 break; | |
| 588 default: | |
| 589 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
| 590 break; | |
| 591 } | |
| 592 } | |
| 6970 | 593 tmp = irc_recv_convert(irc, from); |
| 594 (msgent->cb)(irc, msgent->name, tmp, args); | |
| 595 g_free(tmp); | |
| 6333 | 596 for (i = 0; i < strlen(msgent->format); i++) { |
| 597 g_free(args[i]); | |
| 598 } | |
| 599 g_free(args); | |
| 600 g_free(from); | |
| 601 } | |
| 602 | |
| 603 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
| 604 { | |
| 605 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
| 606 } |
