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