Mercurial > pidgin
annotate libgaim/protocols/irc/parse.c @ 14482:a5c7db7be826
[gaim-migrate @ 17201]
Prevent irc accounts from being disconnected for long periods without us noticing. Use the prpl keepalive cb to ping the server if we haven't received anything in at least 60 seconds.
committer: Tailor Script <tailor@pidgin.im>
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Sat, 09 Sep 2006 19:39:31 +0000 |
parents | 60b1bc8dbf37 |
children | bf1f941575be |
rev | line source |
---|---|
14192 | 1 /** |
2 * @file parse.c | |
3 * | |
4 * gaim | |
5 * | |
6 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> | |
7 * | |
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" | |
29 #include "util.h" | |
30 #include "cmds.h" | |
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) */ | |
62 { "321", "*", irc_msg_list }, /* Start of list */ | |
63 { "322", "ncv:", irc_msg_list }, /* List. */ | |
64 { "323", ":", irc_msg_list }, /* End of list. */ | |
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 */ | |
74 { "391", "nv:", irc_msg_time }, /* Time reply */ | |
75 { "401", "nt:", irc_msg_nonick }, /* No such nick/chan */ | |
76 { "403", "nc:", irc_msg_nochan }, /* No such channel */ | |
77 { "404", "nt:", irc_msg_nosend }, /* Cannot send to chan */ | |
78 { "421", "nv:", irc_msg_unknown }, /* Unknown command */ | |
79 { "422", "nv:", irc_msg_endmotd }, /* No MOTD available */ | |
80 { "432", "vn:", irc_msg_badnick }, /* Erroneous nickname */ | |
81 { "433", "vn:", irc_msg_nickused }, /* Nickname already in use */ | |
82 { "437", "nc:", irc_msg_unavailable }, /* Nick/channel is unavailable */ | |
83 { "438", "nn:", irc_msg_nochangenick }, /* Nick may not change */ | |
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 */ | |
87 { "478", "nct:", irc_msg_banfull }, /* Banlist is full */ | |
88 { "482", "nc:", irc_msg_notop }, /* Need to be op to do that */ | |
89 { "501", "n:", irc_msg_badmode }, /* Unknown mode flag */ | |
90 { "506", "nc:", irc_msg_nosend }, /* Must identify to send */ | |
91 { "515", "nc:", irc_msg_regonly }, /* Registration required */ | |
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; | |
112 char *help; | |
113 } _irc_cmds[] = { | |
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.") }, | |
116 { "chanserv", ":", irc_cmd_service, N_("chanserv: Send a command to chanserv") }, | |
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.") }, | |
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.") }, | |
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>") }, | |
124 { "me", ":", irc_cmd_ctcp_action, N_("me <action to perform>: Perform an action.") }, | |
125 { "memoserv", ":", irc_cmd_service, N_("memoserv: Send a command to memoserv") }, | |
126 { "mode", ":", irc_cmd_mode, N_("mode <+|-><A-Za-z> <nick|channel>: Set or unset a channel or user mode.") }, | |
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.") }, | |
129 { "nick", "n", irc_cmd_nick, N_("nick <new nickname>: Change your nickname.") }, | |
130 { "nickserv", ":", irc_cmd_service, N_("nickserv: Send a command to nickserv") }, | |
131 { "op", ":", irc_cmd_op, N_("op <nick1> [nick2] ...: Grant channel operator status to someone. You must be a channel operator to do this.") }, | |
132 { "operwall", ":", irc_cmd_wallops, N_("operwall <message>: If you don't know what this is, you probably can't use it.") }, | |
133 { "operserv", ":", irc_cmd_service, N_("operserv: Send a command to operserv") }, | |
134 { "part", "c:", irc_cmd_part, N_("part [room] [message]: Leave the current channel, or a specified channel, with an optional message.") }, | |
135 { "ping", "n", irc_cmd_ping, N_("ping [nick]: Asks how much lag a user (or the server if no user specified) has.") }, | |
136 { "query", "n:", irc_cmd_query, N_("query <nick> <message>: Send a private message to a user (as opposed to a channel).") }, | |
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.") }, | |
140 { "time", "", irc_cmd_time, N_("time: Displays the current local time at the IRC server.") }, | |
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.") }, | |
143 { "version", ":", irc_cmd_ctcp_version, N_("version [nick]: send CTCP VERSION request to a user") }, | |
144 { "voice", ":", irc_cmd_op, N_("voice <nick1> [nick2] ...: Grant channel voice status to someone. You must be a channel operator to do this.") }, | |
145 { "wallops", ":", irc_cmd_wallops, N_("wallops <message>: If you don't know what this is, you probably can't use it.") }, | |
146 { "whois", "tt", irc_cmd_whois, N_("whois [server] <nick>: Get information on a user.") }, | |
147 { NULL, NULL, NULL, NULL } | |
148 }; | |
149 | |
150 static GaimCmdRet irc_parse_gaim_cmd(GaimConversation *conv, const gchar *cmd, | |
151 gchar **args, gchar **error, void *data) | |
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; | |
176 size_t i; | |
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 | |
199 gaim_cmd_register(c->name, args, GAIM_CMD_P_PRPL, f, "prpl-irc", | |
200 irc_parse_gaim_cmd, _(c->help), NULL); | |
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 | |
211 static char *irc_send_convert(struct irc_conn *irc, const char *string) | |
212 { | |
213 char *utf8; | |
214 GError *err = NULL; | |
215 gchar **encodings; | |
216 const gchar *enclist; | |
217 | |
218 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); | |
219 encodings = g_strsplit(enclist, ",", 2); | |
220 | |
221 if (encodings[0] == NULL || !strcasecmp("UTF-8", encodings[0])) { | |
222 g_strfreev(encodings); | |
223 return g_strdup(string); | |
224 } | |
225 | |
226 utf8 = g_convert(string, strlen(string), encodings[0], "UTF-8", NULL, NULL, &err); | |
227 if (err) { | |
228 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Send conversion error: %s\n", err->message); | |
229 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Sending as UTF-8 instead of %s\n", encodings[0]); | |
230 utf8 = g_strdup(string); | |
231 g_error_free(err); | |
232 } | |
233 g_strfreev(encodings); | |
234 | |
235 return utf8; | |
236 } | |
237 | |
238 static char *irc_recv_convert(struct irc_conn *irc, const char *string) | |
239 { | |
240 char *utf8 = NULL; | |
241 const gchar *charset, *enclist; | |
242 gchar **encodings; | |
243 int i; | |
244 | |
245 enclist = gaim_account_get_string(irc->account, "encoding", IRC_DEFAULT_CHARSET); | |
246 encodings = g_strsplit(enclist, ",", -1); | |
247 | |
248 if (encodings[0] == NULL) { | |
249 g_strfreev(encodings); | |
250 return gaim_utf8_salvage(string); | |
251 } | |
252 | |
253 for (i = 0; encodings[i] != NULL; i++) { | |
254 charset = encodings[i]; | |
255 while (*charset == ' ') | |
256 charset++; | |
257 | |
258 if (!strcasecmp("UTF-8", charset)) { | |
259 if (g_utf8_validate(string, -1, NULL)) | |
260 utf8 = g_strdup(string); | |
261 } else { | |
262 utf8 = g_convert(string, -1, "UTF-8", charset, NULL, NULL, NULL); | |
263 } | |
264 | |
265 if (utf8) { | |
266 g_strfreev(encodings); | |
267 return utf8; | |
268 } | |
269 } | |
270 g_strfreev(encodings); | |
271 | |
272 return gaim_utf8_salvage(string); | |
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; | |
288 int font = 0, bold = 0, underline = 0, italic = 0; | |
289 GString *decoded = g_string_sized_new(strlen(string)); | |
290 | |
291 cur = string; | |
292 do { | |
293 end = strpbrk(cur, "\002\003\007\017\026\037"); | |
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; | |
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; | |
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>"); | |
359 underline = FALSE; | |
360 } | |
361 break; | |
362 case '\007': | |
363 case '\026': | |
364 cur++; | |
365 break; | |
366 case '\017': | |
367 cur++; | |
368 /* fallthrough */ | |
369 case '\000': | |
370 if (bold) | |
371 decoded = g_string_append(decoded, "</B>"); | |
372 if (italic) | |
373 decoded = g_string_append(decoded, "</I>"); | |
374 if (underline) | |
375 decoded = g_string_append(decoded, "</U>"); | |
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 | |
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 } | |
405 result[j] = '\0'; | |
406 return result; | |
407 } | |
408 | |
409 gboolean irc_ischannel(const char *string) | |
410 { | |
411 return (string[0] == '#' || string[0] == '&'); | |
412 } | |
413 | |
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 | |
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 | |
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 */ | |
435 /* TODO: Should this read in the timestamp as a double? */ | |
436 sscanf(cur, "PING %lu", ×tamp); | |
437 gc = gaim_account_get_connection(irc->account); | |
438 if (!gc) | |
439 return NULL; | |
440 buf = g_strdup_printf(_("Reply time from %s: %lu seconds"), from, time(NULL) - timestamp); | |
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); | |
453 } else if (!strncmp(cur, "DCC SEND ", 9)) { | |
454 irc_dccsend_recv(irc, from, msg + 10); | |
455 return NULL; | |
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; | |
534 guint i; | |
535 | |
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
536 irc->recv_time = time(NULL); |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
537 |
14192 | 538 if (!strncmp(input, "PING ", 5)) { |
539 msg = irc_format(irc, "vv", "PONG", input + 5); | |
540 irc_send(irc, msg); | |
541 g_free(msg); | |
542 return; | |
543 } else if (!strncmp(input, "ERROR ", 6)) { | |
544 if (g_utf8_validate(input, -1, NULL)) { | |
545 char *tmp = g_strdup_printf("%s\n%s", _("Disconnected."), input); | |
546 gaim_connection_error(gaim_account_get_connection(irc->account), tmp); | |
547 g_free(tmp); | |
548 } else | |
549 gaim_connection_error(gaim_account_get_connection(irc->account), _("Disconnected.")); | |
550 return; | |
551 } | |
552 | |
553 if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) { | |
554 irc_parse_error_cb(irc, input); | |
555 return; | |
556 } | |
557 | |
558 from = g_strndup(&input[1], cur - &input[1]); | |
559 cur++; | |
560 end = strchr(cur, ' '); | |
561 if (!end) | |
562 end = cur + strlen(cur); | |
563 | |
564 tmp = g_strndup(cur, end - cur); | |
565 msgname = g_ascii_strdown(tmp, -1); | |
566 g_free(tmp); | |
567 | |
568 if ((msgent = g_hash_table_lookup(irc->msgs, msgname)) == NULL) { | |
569 irc_msg_default(irc, "", from, &input); | |
570 g_free(msgname); | |
571 g_free(from); | |
572 return; | |
573 } | |
574 g_free(msgname); | |
575 | |
576 args = g_new0(char *, strlen(msgent->format)); | |
577 for (cur = end, fmt = msgent->format, i = 0; fmt[i] && *cur++; i++) { | |
578 switch (fmt[i]) { | |
579 case 'v': | |
580 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
581 args[i] = g_strndup(cur, end - cur); | |
582 cur += end - cur; | |
583 break; | |
584 case 't': | |
585 case 'n': | |
586 case 'c': | |
587 if (!(end = strchr(cur, ' '))) end = cur + strlen(cur); | |
588 tmp = g_strndup(cur, end - cur); | |
589 args[i] = irc_recv_convert(irc, tmp); | |
590 g_free(tmp); | |
591 cur += end - cur; | |
592 break; | |
593 case ':': | |
594 if (*cur == ':') cur++; | |
595 args[i] = irc_recv_convert(irc, cur); | |
596 cur = cur + strlen(cur); | |
597 break; | |
598 case '*': | |
599 args[i] = g_strdup(cur); | |
600 cur = cur + strlen(cur); | |
601 break; | |
602 default: | |
603 gaim_debug(GAIM_DEBUG_ERROR, "irc", "invalid message format character '%c'\n", fmt[i]); | |
604 break; | |
605 } | |
606 } | |
607 tmp = irc_recv_convert(irc, from); | |
608 (msgent->cb)(irc, msgent->name, tmp, args); | |
609 g_free(tmp); | |
610 for (i = 0; i < strlen(msgent->format); i++) { | |
611 g_free(args[i]); | |
612 } | |
613 g_free(args); | |
614 g_free(from); | |
615 } | |
616 | |
617 static void irc_parse_error_cb(struct irc_conn *irc, char *input) | |
618 { | |
619 gaim_debug(GAIM_DEBUG_WARNING, "irc", "Unrecognized string: %s\n", input); | |
620 } |