Mercurial > pidgin
annotate src/protocols/irc/parse.c @ 12645:fc28451f5d96
[gaim-migrate @ 14983]
SF Patch #1314512 from Sadrul (who has a patch for everything)
"This patch introduces a flag for protocol plugins that
support offline messages (like Y!M and ICQ). This was
encouraged by the following conversation:
<sadrul> should offline buddies be listed/enabled in
the send-to menu?
<rekkanoryo> i would think only for protocols that
support offline messaging, if it's indicated that the
buddy is offline
-- <snip> --
<Bleeter> sadrul: personally, I'd like to see a
'supports offline' flag of some description
<Bleeter> one could then redirect (via plugins) through
email or alternative methods
<Bleeter> just a thought
<Paco-Paco> yeah, that sounds like a reasonble thing to have
This patch uses this flag to disable the buddies in the
send-to menu who are offline and the protocol doesn't
support offline messages."
I made this make the label insensitive instead of the whole menuitem. This
should address SimGuy's concerns about inconsistency (i.e. you could create a
conversation with someone via the buddy list that you couldn't create via the
Send To menu). I also hacked up some voodoo to show the label as sensitive when
moused-over, as that looks better (given the label-insensitive thing is itself a
hack). I think this works quite well.
BUG NOTE:
This makes more obvious an existing bug. The Send To menu isn't updated when
buddies sign on or off or change status (at least under some circumstances).
We need to fix that anyway, so I'm not going to let it hold up this commit.
Switching tabs will clear it up. I'm thinking we just might want to build the
contents of that menu when it is selected. That would save us a mess of
inefficient signal callbacks that update the Send To menus in open windows all
the time.
AIM NOTE:
This assumes that AIM can't offline message. That's not strictly true. You can
message invisible users on AIM. However, by design, we can't tell when a user
is invisible without resorting to dirty hackery. In practice, this isn't a
problem, as you can still select the AIM user from the menu. And really, how
often will you be choosing the Invisible contact, rather than the user going
Invisible in the middle of a conversation or IMing you while they're Invisible?
JABBER NOTE:
This assumes that Jabber can always offline message. This isn't strictly true.
Sadrul said:
I have updated Jabber according to this link which seems to
talk about how to determine the existence offline-message
support in a server:
http://www.jabber.org/jeps/jep-0013.html#discover
However, jabber.org doesn't seem to send the required
info. So I am not sure about it.
He later said:
I talked to Nathan and he said offline message support is
mostly assumed for most jabber servers. GTalk doesn't yet
support it, but they are working on it. So I have made
jabber to always return TRUE.
If there is truly no way to detect offline messaging capability, then this is
an acceptable solution. We could special case Google Talk because of its
popularity, and remove that later. It's probably not worth it though.
MSN NOTE:
This assumes that MSN can never offline message. That's effectively true, but
to be technically correct, MSN can offline message if there's already a
switchboard conversation open with a user. We could write an offline_message
function in the MSN prpl to detect that, but it'd be of limited usefulness,
especially given that under most circumstances (where this might matter), the
switchboard connection will be closed almost immediately.
CVS NOTE:
I'm writing to share a tragic little story.
I have a PC that I use for Gaim development. One day, I was writing a commit
message on it, when all of a suddent it went berserk. The screen started
flashing, and the whole commit message just disappeared. All of it. And it was
a good commit message! I had to cram and rewrite it really quickly. Needless to
say, my rushed commit message wasn't nearly as good, and I blame the PC for that.
Seriously, though, what kind of version control system loses your commit
message on a broken connection to the server? Stupid!
committer: Tailor Script <tailor@pidgin.im>
| author | Richard Laager <rlaager@wiktel.com> |
|---|---|
| date | Fri, 23 Dec 2005 19:26:04 +0000 |
| parents | d5088e83fe0c |
| children | 3c6de63fbfe8 |
| 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 gc = gaim_account_get_connection(irc->account); | |
| 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 } |
