Mercurial > pidgin.yaz
comparison src/protocols/irc/irc.c @ 6333:e06e04e44914
[gaim-migrate @ 6832]
(20:48:32) Robot101: new IRC plugin y'all
(20:48:51) Paco-Paco: The IRC Protocol Plugin that Sucks Less (TM)
(20:49:18) Paco-Paco: I think that's what the prpl description field says
(20:50:09) LSchiere2: :-)
committer: Tailor Script <tailor@pidgin.im>
author | Luke Schierer <lschiere@pidgin.im> |
---|---|
date | Wed, 30 Jul 2003 00:50:29 +0000 |
parents | 3613007cbb6e |
children | 34c07f5f34a0 |
comparison
equal
deleted
inserted
replaced
6332:392f771bbeb3 | 6333:e06e04e44914 |
---|---|
1 /* | 1 /** |
2 * gaim - IRC Protocol Plugin | 2 * @file irc.c |
3 * | |
4 * gaim | |
3 * | 5 * |
4 * Copyright (C) 2000-2001, Rob Flynn <rob@tgflinux.com> | 6 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu> |
7 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com> | |
5 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> | 8 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> |
6 * | |
7 * A large portion of this was copied more or less directly from X-Chat, | |
8 * the world's most rocking IRC client. http://www.xchat.org/ | |
9 * | 9 * |
10 * This program is free software; you can redistribute it and/or modify | 10 * This program is free software; you can redistribute it and/or modify |
11 * it under the terms of the GNU General Public License as published by | 11 * it under the terms of the GNU General Public License as published by |
12 * the Free Software Foundation; either version 2 of the License, or | 12 * the Free Software Foundation; either version 2 of the License, or |
13 * (at your option) any later version. | 13 * (at your option) any later version. |
18 * GNU General Public License for more details. | 18 * GNU General Public License for more details. |
19 * | 19 * |
20 * You should have received a copy of the GNU General Public License | 20 * You should have received a copy of the GNU General Public License |
21 * along with this program; if not, write to the Free Software | 21 * along with this program; if not, write to the Free Software |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
23 * | |
24 */ | 23 */ |
24 | |
25 #include "internal.h" | 25 #include "internal.h" |
26 | 26 |
27 #include "account.h" | 27 #include "plugin.h" |
28 #include "accountopt.h" | 28 #include "accountopt.h" |
29 #include "multi.h" | |
30 #include "prpl.h" | |
29 #include "conversation.h" | 31 #include "conversation.h" |
30 #include "core.h" | |
31 #include "debug.h" | 32 #include "debug.h" |
32 #include "ft.h" | 33 #include "blist.h" |
33 #include "multi.h" | 34 #include "irc.h" |
34 #include "notify.h" | 35 |
35 #include "proxy.h" | 36 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string); |
36 #include "prpl.h" | 37 |
37 #include "request.h" | 38 static const char *irc_blist_icon(GaimAccount *a, struct buddy *b); |
38 #include "server.h" | 39 static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne); |
39 #include "util.h" | 40 static GList *irc_away_states(GaimConnection *gc); |
40 | 41 /* static GList *irc_chat_info(GaimConnection *gc); */ |
41 /* XXX for g_show_info_text(), WEBSITE, etc. */ | 42 static void irc_login(GaimAccount *account); |
42 #include "gaim.h" | 43 static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond); |
43 | 44 static void irc_close(GaimConnection *gc); |
44 #define IRC_BUF_LEN 4096 | 45 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags); |
45 #define PDIWORDS 32 | 46 static int irc_chat_send(GaimConnection *gc, int id, const char *what); |
46 | 47 static void irc_chat_join (GaimConnection *gc, GHashTable *data); |
47 #define DEFAULT_SERVER "irc.freenode.net" | 48 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond); |
48 | 49 |
49 static GaimPlugin *my_protocol = NULL; | 50 static guint irc_nick_hash(const char *nick); |
50 | 51 static gboolean irc_nick_equal(const char *nick1, const char *nick2); |
51 #ifndef INET6_ADDRSTRLEN | 52 static void irc_buddy_free(struct irc_buddy *ib); |
52 #define INET6_ADDRSTRLEN 46 | 53 |
53 #endif | 54 static GaimPlugin *_irc_plugin = NULL; |
54 | 55 |
55 /* Datastructs */ | 56 int irc_send(struct irc_conn *irc, const char *buf) |
56 struct dcc_chat | 57 { |
57 { | 58 if (irc->fd < 0) |
58 GaimConnection *gc; | 59 return -1; |
59 char ip_address[INET6_ADDRSTRLEN]; | 60 |
60 int port; | 61 /* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent: %s", buf); */ |
61 int fd; | 62 return write(irc->fd, buf, strlen(buf)); |
62 int inpa; | 63 } |
63 char nick[80]; | 64 |
64 }; | 65 /* XXX I don't like messing directly with these buddies */ |
65 | 66 gboolean irc_blist_timeout(struct irc_conn *irc) |
66 struct irc_xfer_data | 67 { |
67 { | 68 GString *string = g_string_sized_new(512); |
68 char *ip; | 69 char *list, *buf; |
69 int port; | 70 |
70 | 71 g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string); |
71 struct irc_data *idata; | 72 |
72 }; | 73 list = g_string_free(string, FALSE); |
73 | 74 if (!list || !strlen(list)) { |
74 struct irc_data { | 75 g_free(list); |
75 int fd; | 76 return TRUE; |
76 gboolean online; | 77 } |
77 guint32 timer; | 78 |
78 | 79 buf = irc_format(irc, "v:", "ISON", list); |
79 char *server; | 80 g_free(list); |
80 | 81 irc_send(irc, buf); |
81 char *rxqueue; | 82 g_free(buf); |
82 int rxlen; | 83 |
83 | 84 return TRUE; |
84 GString *str; | 85 } |
85 int bc; | 86 |
86 | 87 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string) |
87 char *chantypes; | 88 { |
88 char *chanmodes; | 89 ib->flag = FALSE; |
89 char *nickmodes; | 90 g_string_append_printf(string, "%s ", name); |
90 gboolean six_modes; | 91 } |
91 | 92 |
92 gboolean in_whois; | 93 static const char *irc_blist_icon(GaimAccount *a, struct buddy *b) |
93 gboolean in_list; | 94 { |
94 GString *liststr; | 95 return "irc"; |
95 GSList *file_transfers; | 96 } |
96 }; | 97 |
97 | 98 static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) |
98 /* Prototypes */ | 99 { |
99 static void irc_start_chat(GaimConnection *gc, const char *who); | 100 if (b->present == GAIM_BUDDY_OFFLINE) |
100 static void irc_ctcp_clientinfo(GaimConnection *gc, const char *who); | 101 *se = "offline"; |
101 static void irc_ctcp_userinfo(GaimConnection *gc, const char *who); | 102 } |
102 static void irc_ctcp_version(GaimConnection *gc, const char *who); | 103 |
103 static void irc_ctcp_ping(GaimConnection *gc, const char *who); | 104 static GList *irc_away_states(GaimConnection *gc) |
104 | 105 { |
105 static void irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment); | 106 return g_list_append(NULL, (gpointer)GAIM_AWAY_CUSTOM); |
106 static void irc_send_notice(GaimConnection *gc, char *who, char *what); | 107 } |
107 | 108 |
108 static char *irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done); | 109 static GList *irc_buddy_menu(GaimConnection *gc, const char *who) |
109 static char *irc_recv_convert(GaimConnection *gc, char *string); | 110 { |
110 static void irc_parse_notice(GaimConnection *gc, char *nick, char *ex, | 111 struct irc_conn *irc = gc->proto_data; |
111 char *word[], char *word_eol[]); | 112 struct proto_buddy_menu *pbm; |
112 static void irc_parse_join(GaimConnection *gc, char *nick, | 113 |
113 char *word[], char *word_eol[]); | |
114 static gboolean irc_parse_part(GaimConnection *gc, char *nick, char *cmd, | |
115 char *word[], char *word_eol[]); | |
116 static void irc_parse_topic(GaimConnection *gc, char *nick, | |
117 char *word[], char *word_eol[]); | |
118 | |
119 static void dcc_chat_cancel(struct dcc_chat *); | |
120 | |
121 /* Global variables */ | |
122 GSList *dcc_chat_list = NULL; | |
123 | |
124 struct dcc_chat * | |
125 find_dcc_chat (GaimConnection *gc, const char *nick) | |
126 { | |
127 GSList *tmp; | |
128 struct dcc_chat *data; | |
129 tmp = dcc_chat_list; | |
130 while (tmp != NULL) | |
131 { | |
132 data = (struct dcc_chat *) (tmp)->data; | |
133 if (data | |
134 && data->nick | |
135 && strcmp (nick, data->nick) == 0 | |
136 && gc == data->gc) | |
137 { | |
138 return data; | |
139 } | |
140 tmp = tmp->next; | |
141 } | |
142 return NULL; | 114 return NULL; |
143 } | 115 } |
144 | 116 |
145 static int | 117 static GList *irc_chat_join_info(GaimConnection *gc) |
146 irc_write(int fd, char *data, int len) | |
147 { | |
148 gaim_debug(GAIM_DEBUG_MISC, "irc", "C: %s", data); | |
149 return write(fd, data, len); | |
150 } | |
151 | |
152 static char * | |
153 irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done) | |
154 { | |
155 char *converted = g_malloc(maxlen + 1); | |
156 gchar *inptr = (gchar*)string, *outptr = converted; | |
157 int inleft = strlen(string), outleft = maxlen; | |
158 GIConv conv; | |
159 | |
160 /* XXX - I think the below line is leaking */ | |
161 conv = g_iconv_open(gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , "UTF-8"); | |
162 if (g_iconv(conv, &inptr, &inleft, &outptr, &outleft) == -1) { | |
163 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Charset conversion error\n"); | |
164 gaim_debug(GAIM_DEBUG_ERROR, "irc", | |
165 "Sending as UTF-8 (this is a hack!)\n"); | |
166 g_free(converted); | |
167 *done = maxlen; | |
168 return(g_strndup(string, maxlen)); | |
169 } | |
170 | |
171 *done = strlen(string) - inleft; | |
172 *outptr = '\0'; | |
173 return(converted); | |
174 } | |
175 | |
176 static char * | |
177 irc_recv_convert(GaimConnection *gc, char *string) | |
178 { | |
179 char *utf8; | |
180 GError *err = NULL; | |
181 | |
182 utf8 = g_convert(string, strlen(string), "UTF-8", | |
183 gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , NULL, NULL, &err); | |
184 if (err) { | |
185 gaim_debug(GAIM_DEBUG_ERROR, "irc", | |
186 "recv conversion error: %s\n", err->message); | |
187 utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)")); | |
188 } | |
189 | |
190 return (utf8); | |
191 } | |
192 | |
193 static GaimConversation * | |
194 irc_find_chat(GaimConnection *gc, const char *name) | |
195 { | |
196 GSList *bcs = gc->buddy_chats; | |
197 | |
198 while (bcs) { | |
199 GaimConversation *b = bcs->data; | |
200 if (!gaim_utf8_strcasecmp(b->name, name)) | |
201 return b; | |
202 bcs = bcs->next; | |
203 } | |
204 return NULL; | |
205 } | |
206 | |
207 static void | |
208 process_data_init(char *buf, char *cmd, char *word[], char *eol[], gboolean quotes) | |
209 { | |
210 int wordcount = 2; | |
211 gboolean space = FALSE; | |
212 gboolean quote = FALSE; | |
213 int j = 0; | |
214 | |
215 word[1] = cmd; | |
216 eol[1] = buf; | |
217 | |
218 while (TRUE) { | |
219 switch (*cmd) { | |
220 case 0: | |
221 buf[j] = 0; | |
222 for (j = wordcount; j < PDIWORDS; j++) { | |
223 word[j] = "\000\000"; | |
224 eol[j] = "\000\000"; | |
225 } | |
226 return; | |
227 case '"': | |
228 if (!quotes) { | |
229 space = FALSE; | |
230 buf[j++] = *cmd; | |
231 break; | |
232 } | |
233 quote = !quote; | |
234 break; | |
235 case ' ': | |
236 if (quote) { | |
237 space = FALSE; | |
238 buf[j++] = *cmd; | |
239 break; | |
240 } | |
241 if (space) | |
242 break; | |
243 buf[j++] = 0; | |
244 word[wordcount] = &buf[j]; | |
245 eol[wordcount++] = cmd + 1; | |
246 if (wordcount == PDIWORDS - 1) { | |
247 buf[j] = 0; | |
248 return; | |
249 } | |
250 space = TRUE; | |
251 break; | |
252 default: | |
253 space = FALSE; | |
254 buf[j++] = *cmd; | |
255 } | |
256 cmd++; | |
257 } | |
258 } | |
259 | |
260 static void | |
261 handle_005(GaimConnection *gc, char *word[], char *word_eol[]) | |
262 { | |
263 int w = 4; | |
264 struct irc_data *id = gc->proto_data; | |
265 | |
266 while (w < PDIWORDS && *word[w]) { | |
267 if (!strncmp(word[w], "MODES=", 5)) { | |
268 if (atoi(word[w] + 6) >= 6) | |
269 id->six_modes = TRUE; | |
270 } else if (!strncmp(word[w], "CHANTYPES=", 10)) { | |
271 g_free(id->chantypes); | |
272 id->chantypes = g_strdup(word[w] + 10); | |
273 } else if (!strncmp(word[w], "CHANMODES=", 10)) { | |
274 g_free(id->chanmodes); | |
275 id->chanmodes = g_strdup(word[w] + 10); | |
276 } else if (!strncmp(word[w], "PREFIX=", 7)) { | |
277 char *pre = strchr(word[w] + 7, ')'); | |
278 if (pre) { | |
279 *pre = 0; | |
280 g_free(id->nickmodes); | |
281 id->nickmodes = g_strdup(word[w] + 8); | |
282 } | |
283 } | |
284 w++; | |
285 } | |
286 } | |
287 | |
288 static const char *irc_colors[] = { | |
289 "#000000", "#ffffff", "#000066", "#006600", | |
290 "#ff0000", "#660000", "#660066", "#666600", | |
291 "#cccc00", "#33cc33", "#00acac", "#00ccac", | |
292 "#0000ff", "#cc00cc", "#666666", "#00ccac" | |
293 }; | |
294 | |
295 #define int_to_col(c) (irc_colors[(((c)<0 || (c)> 15)?0:c)]) | |
296 | |
297 static GString * | |
298 encode_html(char *msg) | |
299 { | |
300 GString *str = g_string_new(""); | |
301 char *cur = msg, *end = msg; | |
302 gboolean bold = FALSE, underline = FALSE, italics = FALSE; | |
303 | |
304 while ((end = strchr(cur, '<'))) { | |
305 *end = 0; | |
306 str = g_string_append(str, cur); | |
307 cur = ++end; | |
308 if (!g_ascii_strncasecmp(cur, "B>", 2)) { | |
309 if (!bold) { | |
310 bold = TRUE; | |
311 str = g_string_append_c(str, '\2'); | |
312 } | |
313 cur = cur + 2; | |
314 } else if (!g_ascii_strncasecmp(cur, "I>", 2)) { /* use bold for italics too */ | |
315 if (!italics) { | |
316 italics = TRUE; | |
317 str = g_string_append_c(str, '\2'); | |
318 } | |
319 cur = cur + 2; | |
320 } else if (!g_ascii_strncasecmp(cur, "U>", 2)) { | |
321 if (!underline) { | |
322 underline = TRUE; | |
323 str = g_string_append_c(str, '\37'); | |
324 } | |
325 cur = cur + 2; | |
326 } else if (!g_ascii_strncasecmp(cur, "/B>", 3)) { | |
327 if (bold) { | |
328 bold = FALSE; | |
329 str = g_string_append_c(str, '\2'); | |
330 } | |
331 cur = cur + 3; | |
332 } else if (!g_ascii_strncasecmp(cur, "/I>", 3)) { | |
333 if (italics) { | |
334 italics = FALSE; | |
335 str = g_string_append_c(str, '\2'); | |
336 } | |
337 cur = cur + 3; | |
338 } else if (!g_ascii_strncasecmp(cur, "/U>", 3)) { | |
339 if (underline) { | |
340 underline = FALSE; | |
341 str = g_string_append_c(str, '\37'); | |
342 } | |
343 cur = cur + 3; | |
344 } else { | |
345 str = g_string_append_c(str, '<'); | |
346 } | |
347 | |
348 } | |
349 str = g_string_append(str, cur); | |
350 return str; | |
351 } | |
352 | |
353 static GString * | |
354 decode_html(char *msg) | |
355 { | |
356 GString /* oo la la */ *str = g_string_new(""); | |
357 char *cur = msg, *end = msg; | |
358 gboolean bold = FALSE, underline = FALSE, fg = FALSE, bg = FALSE; | |
359 int fore, back; | |
360 while (*end) { | |
361 switch (*end) { | |
362 case 02: /* ^B */ | |
363 *end = 0; | |
364 str = g_string_append(str, cur); | |
365 if (bold) | |
366 str = g_string_append(str, "</B>"); | |
367 else | |
368 str = g_string_append(str, "<B>"); | |
369 bold = !bold; | |
370 cur = end + 1; | |
371 break; | |
372 case 03: /* ^C */ | |
373 *end++ = 0; | |
374 str = g_string_append(str, cur); | |
375 fore = back = -1; | |
376 if (isdigit(*end)) { | |
377 fore = *end++ - '0'; | |
378 if (isdigit(*end)) { | |
379 fore *= 10; | |
380 fore += *end++ - '0'; | |
381 } | |
382 if (*end == ',' && isdigit(end[1])) { | |
383 end++; | |
384 back = *end++ - '0'; | |
385 if (isdigit(*end)) { | |
386 back *= 10; | |
387 back += *end++ - '0'; | |
388 } | |
389 } | |
390 } | |
391 if (fore == -1) { | |
392 if (fg) | |
393 str = g_string_append(str, "</FONT>"); | |
394 if (bg) | |
395 str = g_string_append(str, "</FONT>"); | |
396 fg = bg = FALSE; | |
397 } else { | |
398 fore %= 16; | |
399 if (fg) | |
400 str = g_string_append(str, "</FONT>"); | |
401 if (back != -1) { | |
402 if (bg) | |
403 str = g_string_append(str, "</FONT>"); | |
404 back %= 16; | |
405 str = g_string_append(str, "<FONT BACK="); | |
406 str = g_string_append(str, int_to_col(back)); | |
407 str = g_string_append_c(str, '>'); | |
408 bg = TRUE; | |
409 } | |
410 str = g_string_append(str, "<FONT COLOR="); | |
411 str = g_string_append(str, int_to_col(fore)); | |
412 str = g_string_append_c(str, '>'); | |
413 fg = TRUE; | |
414 } | |
415 cur = end--; | |
416 break; | |
417 case 017: /* ^O */ | |
418 if (!bold && !underline && !fg && !bg) | |
419 break; | |
420 *end = 0; | |
421 str = g_string_append(str, cur); | |
422 if (bold) | |
423 str = g_string_append(str, "</B>"); | |
424 if (underline) | |
425 str = g_string_append(str, "</U>"); | |
426 if (fg) | |
427 str = g_string_append(str, "</FONT>"); | |
428 if (bg) | |
429 str = g_string_append(str, "</FONT>"); | |
430 bold = underline = fg = bg = FALSE; | |
431 cur = end + 1; | |
432 break; | |
433 case 037: /* ^_ */ | |
434 *end = 0; | |
435 str = g_string_append(str, cur); | |
436 if (underline) | |
437 str = g_string_append(str, "</U>"); | |
438 else | |
439 str = g_string_append(str, "<U>"); | |
440 underline = !underline; | |
441 cur = end + 1; | |
442 break; | |
443 } | |
444 end++; | |
445 } | |
446 if (*cur) | |
447 str = g_string_append(str, cur); | |
448 return str; | |
449 } | |
450 | |
451 static void | |
452 irc_got_im(GaimConnection *gc, char *who, char *what, int flags, time_t t) | |
453 { | |
454 char *utf8 = irc_recv_convert(gc, what); | |
455 GString *str = decode_html(utf8); | |
456 serv_got_im(gc, who, str->str, flags, t, -1); | |
457 g_string_free(str, TRUE); | |
458 g_free(utf8); | |
459 } | |
460 | |
461 static void | |
462 dcc_chat_cancel(struct dcc_chat *); | |
463 | |
464 void | |
465 dcc_chat_in (gpointer data, gint source, GaimInputCondition condition) | |
466 { | |
467 struct dcc_chat *chat = data; | |
468 gchar buffer[IRC_BUF_LEN]; | |
469 gchar buf[128]; | |
470 int n = 0; | |
471 GaimConversation *convo; | |
472 gaim_debug(GAIM_DEBUG_MISC, "irc", "THIS IS TOO MUCH EFFORT\n"); | |
473 n = read (chat->fd, buffer, IRC_BUF_LEN); | |
474 if (n > 0) { | |
475 | |
476 buffer[n] = 0; | |
477 g_strstrip(buffer); | |
478 | |
479 /* Convert to HTML */ | |
480 if (strlen(buffer)) { | |
481 gaim_debug(GAIM_DEBUG_INFO, "irc", | |
482 "DCC Message from: %s\n", chat->nick); | |
483 irc_got_im(chat->gc, chat->nick, buffer, 0, | |
484 time(NULL)); | |
485 } | |
486 } | |
487 else { | |
488 g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"), | |
489 chat->nick); | |
490 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, | |
491 chat->nick); | |
492 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, | |
493 time(NULL)); | |
494 dcc_chat_cancel (chat); | |
495 } | |
496 } | |
497 | |
498 void | |
499 irc_read_dcc_ack (gpointer data, gint source, GaimInputCondition condition) { | |
500 /* Read ACK Here */ | |
501 | |
502 } | |
503 | |
504 void | |
505 dcc_send_callback (gpointer data, gint source, GaimInputCondition condition) { | |
506 #if 0 | |
507 struct irc_file_transfer *ift = data; | |
508 struct sockaddr_in addr; | |
509 int len = sizeof(addr); | |
510 | |
511 addr.sin_family = AF_INET; | |
512 addr.sin_port = htons(ift->port); | |
513 addr.sin_addr.s_addr = INADDR_ANY; | |
514 | |
515 ift->fd = accept(ift->fd, (struct sockaddr *)&addr, &len); | |
516 if (!ift->fd) { | |
517 /* FIXME: Handle this gracefully XXX */ | |
518 printf("Something bad happened here, bubba!\n"); | |
519 return; | |
520 } | |
521 | |
522 /* ift->awatcher = gaim_input_add(ift->fd, GAIM_INPUT_READ, irc_read_dcc_ack, ift); */ | |
523 | |
524 if (transfer_out_do(ift->xfer, ift->fd, 0)) { | |
525 gaim_input_remove(ift->watcher); | |
526 ift->watcher = 0; | |
527 } | |
528 #endif | |
529 } | |
530 | |
531 void | |
532 dcc_chat_callback (gpointer data, gint source, GaimInputCondition condition) { | |
533 struct dcc_chat *chat = data; | |
534 GaimConversation *convo; | |
535 char buf[IRC_BUF_LEN]; | |
536 | |
537 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick); | |
538 | |
539 chat->fd = source; | |
540 g_snprintf (buf, sizeof buf, | |
541 _("DCC Chat with %s established"), | |
542 chat->nick); | |
543 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); | |
544 gaim_debug(GAIM_DEBUG_INFO, "irc", | |
545 "Chat with %s established\n", chat->nick); | |
546 dcc_chat_list = g_slist_append (dcc_chat_list, chat); | |
547 gaim_input_remove(chat->inpa); | |
548 chat->inpa = gaim_input_add(source, GAIM_INPUT_READ, dcc_chat_in, chat); | |
549 } | |
550 | |
551 static void | |
552 irc_got_chat_in(GaimConnection *gc, int id, char *who, int whisper, char *msg, time_t t) | |
553 { | |
554 char *utf8 = irc_recv_convert(gc, msg); | |
555 GString *str = decode_html(utf8); | |
556 serv_got_chat_in(gc, id, who, whisper, str->str, t); | |
557 g_string_free(str, TRUE); | |
558 g_free(utf8); | |
559 } | |
560 | |
561 static void | |
562 handle_list(GaimConnection *gc, char *list) | |
563 { | |
564 struct irc_data *id = gc->proto_data; | |
565 char *tmp; | |
566 GaimBlistNode *gnode, *bnode; | |
567 | |
568 tmp = g_utf8_strdown(list, -1); | |
569 | |
570 id->str = g_string_append_c(id->str, ' '); | |
571 id->str = g_string_append(id->str, tmp); | |
572 id->bc--; | |
573 g_free(tmp); | |
574 if (id->bc) | |
575 return; | |
576 | |
577 | |
578 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { | |
579 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
580 continue; | |
581 for(bnode = gnode->child; bnode; bnode = bnode->next) { | |
582 struct buddy *b = (struct buddy *)bnode; | |
583 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
584 continue; | |
585 if(b->account->gc == gc) { | |
586 char *tmp = g_utf8_strdown(b->name, -1); | |
587 char *x, *l; | |
588 x = strstr(id->str->str, tmp); | |
589 l = x + strlen(b->name); | |
590 if (x && (*l != ' ' && *l != 0)) | |
591 x = 0; | |
592 if (!GAIM_BUDDY_IS_ONLINE(b) && x) | |
593 serv_got_update(gc, b->name, 1, 0, 0, 0, 0); | |
594 else if (GAIM_BUDDY_IS_ONLINE(b) && !x) | |
595 serv_got_update(gc, b->name, 0, 0, 0, 0, 0); | |
596 g_free(tmp); | |
597 } | |
598 } | |
599 } | |
600 g_string_free(id->str, TRUE); | |
601 id->str = g_string_new(""); | |
602 } | |
603 | |
604 static gboolean | |
605 irc_request_buddy_update(gpointer data) | |
606 { | |
607 GaimConnection *gc = data; | |
608 struct irc_data *id = gc->proto_data; | |
609 char buf[500]; | |
610 int n = g_snprintf(buf, sizeof(buf), "ISON"); | |
611 gboolean found = FALSE; | |
612 | |
613 GaimBlistNode *gnode, *bnode; | |
614 | |
615 if (id->bc) | |
616 return TRUE; | |
617 | |
618 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { | |
619 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) | |
620 continue; | |
621 for(bnode = gnode->child; bnode; bnode = bnode->next) { | |
622 struct buddy *b = (struct buddy *)bnode; | |
623 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) | |
624 continue; | |
625 if(b->account->gc == gc) { | |
626 if (n + strlen(b->name) + 2 > sizeof(buf)) { | |
627 g_snprintf(buf + n, sizeof(buf) - n, "\r\n"); | |
628 irc_write(id->fd, buf, n); | |
629 id->bc++; | |
630 n = g_snprintf(buf, sizeof(buf), "ISON"); | |
631 } | |
632 n += g_snprintf(buf + n, sizeof(buf) - n, " %s", b->name); | |
633 | |
634 found = TRUE; | |
635 } | |
636 } | |
637 } | |
638 | |
639 if (found) { | |
640 g_snprintf(buf + n, sizeof(buf) - n, "\r\n"); | |
641 irc_write(id->fd, buf, strlen(buf)); | |
642 id->bc++; | |
643 } | |
644 | |
645 return TRUE; | |
646 } | |
647 | |
648 static void | |
649 handle_names(GaimConnection *gc, char *chan, char *names) | |
650 { | |
651 GaimConversation *c = irc_find_chat(gc, chan); | |
652 GaimChat *chat; | |
653 char **buf, **tmp; | |
654 | |
655 if (!c) return; | |
656 if (*names == ':') names++; | |
657 | |
658 chat = GAIM_CHAT(c); | |
659 | |
660 buf = g_strsplit(names, " ", -1); | |
661 | |
662 for (tmp = buf; *tmp; tmp++) | |
663 gaim_chat_add_user(chat, *tmp, NULL); | |
664 | |
665 g_strfreev(buf); | |
666 } | |
667 | |
668 static void | |
669 handle_notopic(GaimConnection *gc, char *text) | |
670 { | |
671 GaimConversation *c; | |
672 | |
673 if ((c = irc_find_chat(gc, text))) { | |
674 char buf[IRC_BUF_LEN]; | |
675 | |
676 g_snprintf(buf, sizeof(buf), _("No topic is set")); | |
677 | |
678 gaim_chat_set_topic(GAIM_CHAT(c), NULL, buf); | |
679 } | |
680 } | |
681 | |
682 static void | |
683 handle_topic(GaimConnection *gc, char *text) | |
684 { | |
685 GaimConversation *c; | |
686 char *po = strchr(text, ' '), *buf; | |
687 | |
688 if (!po) | |
689 return; | |
690 | |
691 *po = 0; | |
692 po += 2; | |
693 | |
694 if ((c = irc_find_chat(gc, text))) { | |
695 po = irc_recv_convert(gc, po); | |
696 gaim_chat_set_topic(GAIM_CHAT(c), NULL, po); | |
697 buf = g_strdup_printf(_("<B>%s has changed the topic to: %s</B>"), text, po); | |
698 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); | |
699 g_free(buf); | |
700 g_free(po); | |
701 } | |
702 } | |
703 | |
704 static gboolean | |
705 mode_has_arg(GaimConnection *gc, char sign, char mode) | |
706 { | |
707 struct irc_data *id = gc->proto_data; | |
708 char *cm = id->chanmodes; | |
709 int type = 0; | |
710 | |
711 if (strchr(id->nickmodes, mode)) | |
712 return TRUE; | |
713 | |
714 while (*cm) { | |
715 if (*cm == ',') | |
716 type++; | |
717 else if (*cm == mode) { | |
718 switch (type) { | |
719 case 0: | |
720 case 1: | |
721 return TRUE; | |
722 case 2: | |
723 if (sign == '+') | |
724 return TRUE; | |
725 case 3: | |
726 return FALSE; | |
727 } | |
728 } | |
729 cm++; | |
730 } | |
731 | |
732 return FALSE; | |
733 } | |
734 | |
735 static void | |
736 irc_chan_mode(GaimConnection *gc, char *room, char sign, char mode, char *argstr, char *who) | |
737 { | |
738 GaimConversation *c = irc_find_chat(gc, room); | |
739 char buf[IRC_BUF_LEN]; | |
740 char *nick = g_strndup(who, strchr(who, '!') - who); | |
741 | |
742 g_snprintf(buf, sizeof(buf), _("-:- mode/%s [%c%c %s] by %s"), | |
743 room, sign, mode, strlen(argstr) ? argstr : "", | |
744 nick); | |
745 g_free(nick); | |
746 | |
747 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); | |
748 } | |
749 | |
750 static void | |
751 irc_user_mode(GaimConnection *gc, char *room, char sign, char mode, char *nick) | |
752 { | |
753 GaimConversation *c = irc_find_chat(gc, room); | |
754 GList *r; | |
755 | |
756 if (mode != 'o' && mode != 'v' && mode != 'h') | |
757 return; | |
758 | |
759 if (!c) | |
760 return; | |
761 | |
762 r = gaim_chat_get_users(GAIM_CHAT(c)); | |
763 while (r) { | |
764 gboolean op = FALSE, halfop = FALSE, voice = FALSE; | |
765 char *who = r->data; | |
766 | |
767 if (*who == '@') { | |
768 op = TRUE; | |
769 who++; | |
770 } | |
771 | |
772 if (*who == '%') { | |
773 halfop = TRUE; | |
774 who++; | |
775 } | |
776 | |
777 if (*who == '+') { | |
778 voice = TRUE; | |
779 who++; | |
780 } | |
781 | |
782 if (!strcmp(who, nick)) { | |
783 char *tmp, buf[IRC_BUF_LEN]; | |
784 | |
785 if (mode == 'o') { | |
786 if (sign == '-') | |
787 op = FALSE; | |
788 else | |
789 op = TRUE; | |
790 } | |
791 | |
792 if (mode == 'h') { | |
793 if (sign == '-') | |
794 halfop = FALSE; | |
795 else | |
796 halfop = TRUE; | |
797 } | |
798 | |
799 if (mode == 'v') { | |
800 if (sign == '-') | |
801 voice = FALSE; | |
802 else | |
803 voice = TRUE; | |
804 } | |
805 | |
806 tmp = g_strdup(r->data); | |
807 g_snprintf(buf, sizeof(buf), "%s%s%s", | |
808 (op ? "@" : (halfop ? "%" : "")), | |
809 voice ? "+" : "", nick); | |
810 gaim_chat_rename_user(GAIM_CHAT(c), tmp, buf); | |
811 g_free(tmp); | |
812 return; | |
813 } | |
814 r = r->next; | |
815 } | |
816 } | |
817 | |
818 static void | |
819 handle_mode(GaimConnection *gc, char *word[], char *word_eol[], gboolean n324) | |
820 { | |
821 struct irc_data *id = gc->proto_data; | |
822 int offset = n324 ? 4 : 3; | |
823 char *chan = word[offset]; | |
824 GaimConversation *c = irc_find_chat(gc, chan); | |
825 char *modes = word[offset + 1]; | |
826 int len = strlen(word_eol[offset]) - 1; | |
827 char sign = *modes++; | |
828 int arg = 1; | |
829 char *argstr; | |
830 char *who = word[1]; | |
831 | |
832 if (!c) | |
833 return; | |
834 | |
835 if (word_eol[offset][len] == ' ') | |
836 word_eol[offset][len] = 0; | |
837 | |
838 while (TRUE) { | |
839 switch (*modes) { | |
840 case 0: | |
841 return; | |
842 case '+': | |
843 case '-': | |
844 sign = *modes; | |
845 break; | |
846 default: | |
847 if (mode_has_arg(gc, sign, *modes)) | |
848 argstr = word[++arg + offset]; | |
849 else | |
850 argstr = ""; | |
851 if (strchr(id->nickmodes, *modes)) | |
852 irc_user_mode(gc, chan, sign, *modes, argstr); | |
853 else if (strchr(who, '!')) | |
854 irc_chan_mode(gc, chan, sign, *modes, argstr, who); | |
855 } | |
856 modes++; | |
857 } | |
858 } | |
859 | |
860 static void | |
861 handle_version(GaimConnection *gc, char *word[], char *word_eol[], int num) | |
862 { | |
863 struct irc_data *id = gc->proto_data; | |
864 GString *str; | |
865 | |
866 id->liststr = g_string_new(""); | |
867 | |
868 id->liststr = g_string_append(id->liststr, "<b>Version: </b>"); | |
869 id->liststr = g_string_append(id->liststr, word_eol[4]); | |
870 | |
871 str = decode_html(id->liststr->str); | |
872 g_show_info_text(gc, NULL, 2, str->str, NULL); | |
873 g_string_free(str, TRUE); | |
874 g_string_free(id->liststr, TRUE); | |
875 id->liststr = NULL; | |
876 } | |
877 | |
878 static void | |
879 handle_who(GaimConnection *gc, char *word[], char *word_eol[], int num) | |
880 { | |
881 struct irc_data *id = gc->proto_data; | |
882 char buf[IRC_BUF_LEN]; | |
883 | |
884 if (!id->in_whois) { | |
885 id->in_whois = TRUE; | |
886 id->liststr = g_string_new(""); | |
887 } | |
888 | |
889 switch (num) { | |
890 case 352: | |
891 g_snprintf(buf, sizeof(buf), "<b>%s</b> (%s@%s): %s<br>", | |
892 word[8], word[5], word[6], word_eol[11]); | |
893 id->liststr = g_string_append(id->liststr, buf); | |
894 break; | |
895 } | |
896 } | |
897 | |
898 /* Handle our whois stuff here. You know what, I have a sore throat. You know | |
899 * what I think about that? I'm not too pleased with it. Perhaps I should take | |
900 * some medicine, or perhaps I should go to bed? Blah!! */ | |
901 | |
902 static void | |
903 handle_whois(GaimConnection *gc, char *word[], char *word_eol[], int num) | |
904 { | |
905 struct irc_data *id = gc->proto_data; | |
906 char tmp[1024]; | |
907 | |
908 if (!id->in_whois) { | |
909 id->in_whois = TRUE; | |
910 id->liststr = g_string_new(""); | |
911 } else { | |
912 /* I can't decide if we should have one break or two */ | |
913 id->liststr = g_string_append(id->liststr, "<BR>"); | |
914 id->in_whois = TRUE; | |
915 } | |
916 | |
917 switch (num) { | |
918 case 311: | |
919 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("User")); | |
920 id->liststr = g_string_append(id->liststr, tmp); | |
921 break; | |
922 case 312: | |
923 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Server")); | |
924 id->liststr = g_string_append(id->liststr, tmp); | |
925 break; | |
926 case 313: | |
927 g_snprintf(tmp, sizeof(tmp), "<b>%s:</b> %s ", _("IRC Operator"), word[4]); | |
928 id->liststr = g_string_append(id->liststr, tmp); | |
929 break; | |
930 case 314: | |
931 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b><b>%s</b> (%s@%s) %s", | |
932 _("User"), word[4], word[5], word[6], word_eol[8]); | |
933 id->liststr = g_string_append(id->liststr, tmp); | |
934 return; | |
935 case 317: | |
936 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Idle Time")); | |
937 id->liststr = g_string_append(id->liststr, tmp); | |
938 break; | |
939 case 319: | |
940 g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Channels")); | |
941 id->liststr = g_string_append(id->liststr, tmp); | |
942 break; | |
943 /* Numeric 320 is used by the freenode irc network for showing | |
944 * that a user is identified to services (Jason Straw <misato@wopn.org>)*/ | |
945 case 320: | |
946 g_snprintf(tmp, sizeof(tmp), _("%s is an Identified User"), word[4]); | |
947 id->liststr = g_string_append(id->liststr, tmp); | |
948 return; | |
949 default: | |
950 break; | |
951 } | |
952 | |
953 if (word_eol[5][0] == ':') | |
954 id->liststr = g_string_append(id->liststr, word_eol[5] + 1); | |
955 /* Nicer idle time output, by jonas@birme.se */ | |
956 else if (isdigit(word_eol[5][0])) { | |
957 time_t idle = atol(word_eol[5]); | |
958 time_t signon = atol(strchr(word_eol[5], ' ')); | |
959 | |
960 g_snprintf(tmp, sizeof(tmp), | |
961 _("%ld seconds [signon: %s]"), (idle / 1000), ctime(&signon)); | |
962 id->liststr = g_string_append(id->liststr, tmp); | |
963 } | |
964 else | |
965 id->liststr = g_string_append(id->liststr, word_eol[5]); | |
966 } | |
967 | |
968 static void | |
969 handle_roomlist(GaimConnection *gc, char *word[], char *word_eol[]) | |
970 { | |
971 struct irc_data *id = gc->proto_data; | |
972 | |
973 if (!id->in_list) { | |
974 id->in_list = TRUE; | |
975 id->liststr = g_string_new(""); | |
976 } else { | |
977 id->liststr = g_string_append(id->liststr, "<BR>"); | |
978 id->in_list = TRUE; | |
979 } | |
980 | |
981 id->liststr = g_string_append(id->liststr, word_eol[4]); | |
982 } | |
983 | |
984 static void | |
985 irc_change_nick(void *a, const char *b) { | |
986 GaimConnection *gc = a; | |
987 struct irc_data *id = gc->proto_data; | |
988 char buf[IRC_BUF_LEN]; | |
989 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", b); | |
990 irc_write(id->fd, buf, strlen(buf)); | |
991 gaim_connection_set_display_name(gc, b); | |
992 } | |
993 | |
994 static void | |
995 process_numeric(GaimConnection *gc, char *word[], char *word_eol[]) | |
996 { | |
997 const char *displayname = gaim_connection_get_display_name(gc); | |
998 struct irc_data *id = gc->proto_data; | |
999 char *text = word_eol[3]; | |
1000 int n = atoi(word[2]); | |
1001 char tmp[1024]; | |
1002 | |
1003 if (!g_ascii_strncasecmp(displayname, text, strlen(displayname))) | |
1004 text += strlen(displayname) + 1; | |
1005 if (*text == ':') | |
1006 text++; | |
1007 | |
1008 /* RPL_ and ERR_ */ | |
1009 switch (n) { | |
1010 case 4: | |
1011 if (!strncmp(word[5], "u2.10", 5)) | |
1012 id->six_modes = TRUE; | |
1013 else | |
1014 id->six_modes = FALSE; | |
1015 break; | |
1016 case 5: | |
1017 handle_005(gc, word, word_eol); | |
1018 break; | |
1019 case 301: /* RPL_AWAY */ | |
1020 if (id->in_whois) { | |
1021 g_snprintf(tmp, sizeof(tmp), "<BR><b>%s: </b>", _("Away")); | |
1022 id->liststr = g_string_append(id->liststr, tmp); | |
1023 | |
1024 if (word_eol[5][0] == ':') | |
1025 id->liststr = g_string_append(id->liststr, word_eol[5] + 1); | |
1026 else | |
1027 id->liststr = g_string_append(id->liststr, word_eol[5]); | |
1028 } else | |
1029 irc_got_im(gc, word[4], word_eol[5], IM_FLAG_AWAY, time(NULL)); | |
1030 break; | |
1031 case 303: /* RPL_ISON */ | |
1032 handle_list(gc, &word_eol[4][1]); | |
1033 break; | |
1034 case 311: /* RPL_WHOISUSER */ | |
1035 case 312: /* RPL_WHOISSERVER */ | |
1036 case 313: /* RPL_WHOISOPERATOR */ | |
1037 case 314: /* RPL_WHOWASUSER */ | |
1038 case 317: /* RPL_WHOISIDLE */ | |
1039 case 319: /* RPL_WHOISCHANNELS */ | |
1040 case 320: /* FreeNode Identified */ | |
1041 handle_whois(gc, word, word_eol, n); | |
1042 break; | |
1043 case 322: /* RPL_LIST */ | |
1044 handle_roomlist(gc, word, word_eol); | |
1045 break; | |
1046 case 315: /* RPL_ENDOFWHO */ | |
1047 case 318: /* RPL_ENDOFWHOIS */ | |
1048 case 323: /* RPL_LISTEND */ | |
1049 case 369: /* RPL_ENDOFWHOWAS */ | |
1050 if ((id->in_whois || id->in_list) && id->liststr) { | |
1051 GString *str = decode_html(id->liststr->str); | |
1052 g_show_info_text(gc, NULL, 2, str->str, NULL); | |
1053 g_string_free(str, TRUE); | |
1054 g_string_free(id->liststr, TRUE); | |
1055 id->liststr = NULL; | |
1056 id->in_whois = FALSE; | |
1057 id->in_list = FALSE; | |
1058 } | |
1059 break; | |
1060 case 324: /* RPL_CHANNELMODEIS */ | |
1061 handle_mode(gc, word, word_eol, TRUE); | |
1062 break; | |
1063 case 331: /* RPL_NOTOPIC */ | |
1064 handle_notopic(gc, text); | |
1065 break; | |
1066 case 332: /* RPL_TOPIC */ | |
1067 handle_topic(gc, text); | |
1068 break; | |
1069 case 351: /* RPL_VERSION */ | |
1070 handle_version(gc, word, word_eol, n); | |
1071 break; | |
1072 case 352: /* RPL_WHOREPLY */ | |
1073 handle_who(gc, word, word_eol, n); | |
1074 break; | |
1075 case 353: /* RPL_NAMREPLY */ | |
1076 handle_names(gc, word[5], word_eol[6]); | |
1077 break; | |
1078 case 376: /* RPL_ENDOFMOTD */ | |
1079 irc_request_buddy_update(gc); | |
1080 break; | |
1081 case 382: /* RPL_REHASHING */ | |
1082 gaim_notify_error(gc, NULL, _("Rehashing server"), _("IRC Operator")); | |
1083 break; | |
1084 case 401: /* ERR_NOSUCHNICK */ | |
1085 gaim_notify_error(gc, NULL, _("No such nick/channel"), _("IRC Error")); | |
1086 break; | |
1087 case 402: /* ERR_NOSUCHSERVER */ | |
1088 gaim_notify_error(gc, NULL, _("No such server"), _("IRC Error")); | |
1089 break; | |
1090 case 422: /* ERR_NOMOTD */ | |
1091 break; /* drop it - bringing up dialog for NOMOTD is annoying */ | |
1092 case 431: /* ERR_NONICKNAMEGIVEN */ | |
1093 gaim_notify_error(gc, NULL, _("No nickname given"), _("IRC Error")); | |
1094 break; | |
1095 case 481: /* ERR_NOPRIVILEGES */ | |
1096 gaim_notify_error(gc, NULL, _("You're not an IRC operator!"), | |
1097 _("IRC Error")); | |
1098 break; | |
1099 case 433: | |
1100 gaim_request_input(gc, NULL, _("That nick is already in use. " | |
1101 "Please enter a new nick"), | |
1102 NULL, gaim_connection_get_display_name(gc), | |
1103 FALSE, FALSE, | |
1104 _("OK"), G_CALLBACK(irc_change_nick), | |
1105 _("Cancel"), NULL, gc); | |
1106 break; | |
1107 default: | |
1108 /* Other error messages */ | |
1109 if (n > 400 && n < 502) { | |
1110 char errmsg[IRC_BUF_LEN]; | |
1111 char *errmsg1 = strrchr(text, ':'); | |
1112 | |
1113 g_snprintf(errmsg, sizeof(errmsg), "IRC Error %d", n); | |
1114 | |
1115 if (errmsg) { | |
1116 gaim_notify_error(gc, NULL, errmsg, | |
1117 (errmsg1 ? errmsg1 + 1 : NULL)); | |
1118 } | |
1119 } | |
1120 | |
1121 break; | |
1122 } | |
1123 } | |
1124 | |
1125 static gboolean | |
1126 is_channel(GaimConnection *gc, const char *name) | |
1127 { | |
1128 struct irc_data *id = gc->proto_data; | |
1129 if (strchr(id->chantypes, *name)) | |
1130 return TRUE; | |
1131 return FALSE; | |
1132 } | |
1133 | |
1134 static void | |
1135 irc_rem_chat_bud(GaimConnection *gc, char *nick, GaimConversation *b, char *reason) | |
1136 { | |
1137 | |
1138 GaimChat *chat; | |
1139 | |
1140 if (b) { | |
1141 GList *r; | |
1142 | |
1143 chat = GAIM_CHAT(b); | |
1144 | |
1145 r = gaim_chat_get_users(chat); | |
1146 | |
1147 while (r) { | |
1148 char *who = r->data; | |
1149 if (*who == '@') | |
1150 who++; | |
1151 if (*who == '%') | |
1152 who++; | |
1153 if (*who == '+') | |
1154 who++; | |
1155 if (!gaim_utf8_strcasecmp(who, nick)) { | |
1156 gaim_chat_remove_user(chat, who, reason); | |
1157 break; | |
1158 } | |
1159 r = r->next; | |
1160 } | |
1161 } else { | |
1162 GSList *bcs = gc->buddy_chats; | |
1163 while (bcs) { | |
1164 GaimConversation *bc = bcs->data; | |
1165 irc_rem_chat_bud(gc, nick, bc, reason); | |
1166 bcs = bcs->next; | |
1167 } | |
1168 } | |
1169 } | |
1170 | |
1171 static void | |
1172 irc_change_name(GaimConnection *gc, char *old, char *new) | |
1173 { | |
1174 GSList *bcs = gc->buddy_chats; | |
1175 char buf[IRC_BUF_LEN]; | |
1176 | |
1177 while (bcs) { | |
1178 GaimConversation *b = bcs->data; | |
1179 GaimChat *chat; | |
1180 GList *r; | |
1181 | |
1182 chat = GAIM_CHAT(b); | |
1183 | |
1184 r = gaim_chat_get_users(chat); | |
1185 | |
1186 while (r) { | |
1187 char *who = r->data; | |
1188 int n = 0; | |
1189 if (*who == '@') | |
1190 buf[n++] = *who++; | |
1191 if (*who == '%') | |
1192 buf[n++] = *who++; | |
1193 if (*who == '+') | |
1194 buf[n++] = *who++; | |
1195 g_snprintf(buf + n, sizeof(buf) - n, "%s", new); | |
1196 if (!strcmp(who, old)) { | |
1197 char *tmp = g_strdup(r->data); | |
1198 gaim_chat_rename_user(chat, tmp, buf); | |
1199 r = gaim_chat_get_users(chat); | |
1200 g_free(tmp); | |
1201 break; | |
1202 } else | |
1203 r = r->next; | |
1204 } | |
1205 bcs = bcs->next; | |
1206 } | |
1207 } | |
1208 | |
1209 static void | |
1210 handle_privmsg(GaimConnection *gc, char *to, char *nick, char *msg) | |
1211 { | |
1212 if (is_channel(gc, to)) { | |
1213 GaimConversation *c = irc_find_chat(gc, to); | |
1214 if (!c) | |
1215 return; | |
1216 irc_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)), | |
1217 nick, 0, msg, time(NULL)); | |
1218 } else { | |
1219 char *tmp = g_malloc(strlen(nick) + 2); | |
1220 g_snprintf(tmp, strlen(nick) + 2, "@%s", nick); | |
1221 if (gaim_find_conversation(tmp)) | |
1222 irc_got_im(gc, tmp, msg, 0, time(NULL)); | |
1223 else { | |
1224 *tmp = '+'; | |
1225 if (gaim_find_conversation(tmp)) | |
1226 irc_got_im(gc, tmp, msg, 0, time(NULL)); | |
1227 else | |
1228 irc_got_im(gc, nick, msg, 0, time(NULL)); | |
1229 } | |
1230 g_free(tmp); | |
1231 } | |
1232 } | |
1233 | |
1234 static void | |
1235 dcc_chat_init(struct dcc_chat *data) { | |
1236 if (g_list_find(gaim_connections_get_all(), data->gc)) { | |
1237 gaim_proxy_connect(data->gc->account, data->ip_address, data->port, dcc_chat_callback, data); | |
1238 } else { | |
1239 g_free(data); | |
1240 } | |
1241 } | |
1242 | |
1243 static void | |
1244 dcc_chat_cancel(struct dcc_chat *data){ | |
1245 if (g_list_find(gaim_connections_get_all(), data->gc) && find_dcc_chat(data->gc, data->nick)) { | |
1246 dcc_chat_list = g_slist_remove(dcc_chat_list, data); | |
1247 gaim_input_remove (data->inpa); | |
1248 close (data->fd); | |
1249 } | |
1250 g_free(data); | |
1251 } | |
1252 | |
1253 static void | |
1254 irc_convo_closed(GaimConnection *gc, const char *who) | |
1255 { | |
1256 struct dcc_chat *dchat = find_dcc_chat(gc, who); | |
1257 if (!dchat) | |
1258 return; | |
1259 | |
1260 dcc_chat_cancel(dchat); | |
1261 } | |
1262 | |
1263 static void | |
1264 irc_xfer_init(GaimXfer *xfer) | |
1265 { | |
1266 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; | |
1267 | |
1268 gaim_xfer_start(xfer, -1, data->ip, data->port); | |
1269 } | |
1270 | |
1271 static void | |
1272 irc_xfer_end(GaimXfer *xfer) | |
1273 { | |
1274 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; | |
1275 | |
1276 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, | |
1277 xfer); | |
1278 | |
1279 g_free(data); | |
1280 xfer->data = NULL; | |
1281 } | |
1282 | |
1283 static void | |
1284 irc_xfer_cancel_send(GaimXfer *xfer) | |
1285 { | |
1286 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; | |
1287 | |
1288 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, | |
1289 xfer); | |
1290 | |
1291 g_free(data); | |
1292 xfer->data = NULL; | |
1293 } | |
1294 | |
1295 static void | |
1296 irc_xfer_cancel_recv(GaimXfer *xfer) | |
1297 { | |
1298 struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data; | |
1299 | |
1300 data->idata->file_transfers = g_slist_remove(data->idata->file_transfers, | |
1301 xfer); | |
1302 | |
1303 g_free(data); | |
1304 xfer->data = NULL; | |
1305 } | |
1306 | |
1307 static void | |
1308 irc_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size) | |
1309 { | |
1310 guint32 pos; | |
1311 | |
1312 pos = htonl(gaim_xfer_get_bytes_sent(xfer)); | |
1313 | |
1314 write(xfer->fd, (char *)&pos, 4); | |
1315 } | |
1316 | |
1317 /* NOTE: This was taken from irssi. Thanks irssi! */ | |
1318 | |
1319 static gboolean | |
1320 is_numeric(const char *str, char end_char) | |
1321 { | |
1322 g_return_val_if_fail(str != NULL, FALSE); | |
1323 | |
1324 if (*str == '\0' || *str == end_char) | |
1325 return FALSE; | |
1326 | |
1327 while (*str != '\0' && *str != end_char) { | |
1328 if (*str < '0' || *str > '9') | |
1329 return FALSE; | |
1330 | |
1331 str++; | |
1332 } | |
1333 | |
1334 return TRUE; | |
1335 } | |
1336 | |
1337 #define get_params_match(params, pos) \ | |
1338 (is_numeric(params[pos], '\0') && \ | |
1339 is_numeric(params[(pos)+1], '\0') && atol(params[(pos)+1]) < 65536 && \ | |
1340 is_numeric(params[(pos)+2], '\0')) | |
1341 | |
1342 /* Return number of parameters in `params' that belong to file name. | |
1343 Normally it's paramcount-3, but I don't think anything forbids of | |
1344 adding some extension where there could be more parameters after | |
1345 file size. | |
1346 | |
1347 MIRC sends filenames with spaces quoted ("file name"), but I'd rather | |
1348 not trust that entirely either. At least some clients that don't really | |
1349 understand the problem with spaces in file names sends the file name | |
1350 without any quotes. */ | |
1351 static int | |
1352 get_file_params_count(char **params, int paramcount) | |
1353 { | |
1354 int pos, best; | |
1355 | |
1356 if (*params[0] == '"') { | |
1357 /* quoted file name? */ | |
1358 for (pos = 0; pos < paramcount - 3; pos++) { | |
1359 if (params[pos][strlen(params[pos]) - 1] == '"' && | |
1360 get_params_match(params, pos + 1)) { | |
1361 | |
1362 return pos + 1; | |
1363 } | |
1364 } | |
1365 } | |
1366 | |
1367 best = paramcount - 3; | |
1368 | |
1369 for (pos = paramcount - 3; pos > 0; pos--) { | |
1370 if (get_params_match(params, pos)) | |
1371 best = pos; | |
1372 } | |
1373 | |
1374 return best; | |
1375 } | |
1376 | |
1377 static void | |
1378 handle_ctcp(GaimConnection *gc, char *to, char *nick, | |
1379 char *msg, char *word[], char *word_eol[]) | |
1380 { | |
1381 struct irc_data *id = gc->proto_data; | |
1382 char buf[IRC_BUF_LEN]; | |
1383 char out[IRC_BUF_LEN]; | |
1384 | |
1385 if (!g_ascii_strncasecmp(msg, "VERSION", 7)) { | |
1386 g_snprintf(buf, sizeof(buf), "\001VERSION Gaim " VERSION ": The Penguin Pimpin' " | |
1387 "Multi-protocol Messaging Client: " WEBSITE "\001"); | |
1388 irc_send_notice (gc, nick, buf); | |
1389 g_snprintf(out, sizeof(out), ">> CTCP VERSION requested from %s", nick); | |
1390 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); | |
1391 } | |
1392 else if (!g_ascii_strncasecmp(msg, "CLIENTINFO", 10)) { | |
1393 g_snprintf(buf, sizeof(buf), "\001CLIENTINFO USERINFO CLIENTINFO VERSION\001"); | |
1394 irc_send_notice (gc, nick, buf); | |
1395 g_snprintf(out, sizeof(out), ">> CTCP CLIENTINFO requested from %s", nick); | |
1396 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); | |
1397 } | |
1398 else if (!g_ascii_strncasecmp(msg, "USERINFO", 8)) { | |
1399 g_snprintf(buf, sizeof(buf), "\001USERINFO Alias: %s\001", gc->account->alias); | |
1400 irc_send_notice (gc, nick, buf); | |
1401 g_snprintf(out, sizeof(out), ">> CTCP USERINFO requested from %s", nick); | |
1402 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); | |
1403 } | |
1404 else if (!g_ascii_strncasecmp(msg, "ACTION", 6)) { | |
1405 char *po = strchr(msg + 6, 1); | |
1406 char *tmp; | |
1407 if (po) *po = 0; | |
1408 tmp = g_strconcat("/me", msg + 6, NULL); | |
1409 handle_privmsg(gc, to, nick, tmp); | |
1410 g_free(tmp); | |
1411 } | |
1412 else if (!g_ascii_strncasecmp(msg, "PING", 4)) { | |
1413 g_snprintf(buf, sizeof(buf), "\001%s\001", msg); | |
1414 irc_send_notice (gc, nick, buf); | |
1415 g_snprintf(out, sizeof(out), ">> CTCP PING requested from %s", nick); | |
1416 gaim_notify_info(gc, NULL, out, _("IRC CTCP info")); | |
1417 } | |
1418 else if (!g_ascii_strncasecmp(msg, "DCC CHAT", 8)) { | |
1419 char **chat_args = g_strsplit(msg, " ", 5); | |
1420 char ask[1024]; | |
1421 struct dcc_chat *dccchat = g_new0(struct dcc_chat, 1); | |
1422 dccchat->gc = gc; | |
1423 g_snprintf(dccchat->ip_address, sizeof(dccchat->ip_address), chat_args[3]); | |
1424 dccchat->port=atoi(chat_args[4]); | |
1425 g_snprintf(dccchat->nick, sizeof(dccchat->nick), nick); | |
1426 g_snprintf(ask, sizeof(ask), _("%s would like to establish a DCC chat"), nick); | |
1427 | |
1428 gaim_request_action(gc, NULL, ask, | |
1429 _("This requires a direct connection to be " | |
1430 "established between the two computers. " | |
1431 "Messages sent will not pass through the " | |
1432 "IRC server"), 0, dccchat, 2, | |
1433 _("Connect"), G_CALLBACK(dcc_chat_init), | |
1434 _("Cancel"), G_CALLBACK(dcc_chat_cancel)); | |
1435 } | |
1436 else if (!g_ascii_strncasecmp(msg, "DCC SEND", 8)) { | |
1437 GaimXfer *xfer; | |
1438 char **send_args; | |
1439 char *ip, *filename; | |
1440 struct irc_xfer_data *xfer_data; | |
1441 size_t size; | |
1442 int param_count, file_params, len; | |
1443 int port; | |
1444 | |
1445 /* Okay, this is ugly, but should get us past "DCC SEND" */ | |
1446 msg = strstr(msg, "DCC SEND"); | |
1447 msg = strchr(msg, ' ') + 1; | |
1448 msg = strchr(msg, ' ') + 1; | |
1449 | |
1450 /* SEND <file name> <address> <port> <size> [...] */ | |
1451 send_args = g_strsplit(msg, " ", -1); | |
1452 | |
1453 for (param_count = 0; send_args[param_count] != NULL; param_count++) | |
1454 ; | |
1455 | |
1456 if (param_count < 4) { | |
1457 char buf[IRC_BUF_LEN]; | |
1458 | |
1459 g_snprintf(buf, sizeof(buf), | |
1460 _("Received an invalid file send request from %s."), | |
1461 nick); | |
1462 | |
1463 gaim_notify_error(gc, NULL, buf, _("IRC Error")); | |
1464 | |
1465 return; | |
1466 } | |
1467 | |
1468 file_params = get_file_params_count(send_args, param_count); | |
1469 | |
1470 /* send_args[paramcount - 1][strlen(send_args[5])-1] = 0; */ | |
1471 | |
1472 /* Give these better names. */ | |
1473 ip = send_args[file_params]; | |
1474 port = atoi(send_args[file_params + 1]); | |
1475 size = atoi(send_args[file_params + 2]); | |
1476 | |
1477 send_args[file_params] = NULL; | |
1478 | |
1479 filename = g_strjoinv(" ", send_args); | |
1480 | |
1481 g_strfreev(send_args); | |
1482 | |
1483 len = strlen(filename); | |
1484 | |
1485 if (len > 1 && *filename == '"' && filename[len - 1] == '"') { | |
1486 /* "file name" - MIRC sends filenames with spaces like this */ | |
1487 filename[len - 1] = '\0'; | |
1488 g_memmove(filename, filename + 1, len); | |
1489 } | |
1490 | |
1491 /* Setup the IRC-specific transfer data. */ | |
1492 xfer_data = g_malloc0(sizeof(struct irc_xfer_data)); | |
1493 xfer_data->ip = ip; | |
1494 xfer_data->port = port; | |
1495 xfer_data->idata = id; | |
1496 | |
1497 /* Build the file transfer handle. */ | |
1498 xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, nick); | |
1499 xfer->data = xfer_data; | |
1500 | |
1501 /* Set the info about the incoming file. */ | |
1502 gaim_xfer_set_filename(xfer, filename); | |
1503 gaim_xfer_set_size(xfer, size); | |
1504 | |
1505 g_free(filename); | |
1506 | |
1507 /* Setup our I/O op functions. */ | |
1508 gaim_xfer_set_init_fnc(xfer, irc_xfer_init); | |
1509 gaim_xfer_set_end_fnc(xfer, irc_xfer_end); | |
1510 gaim_xfer_set_cancel_send_fnc(xfer, irc_xfer_cancel_send); | |
1511 gaim_xfer_set_cancel_recv_fnc(xfer, irc_xfer_cancel_recv); | |
1512 gaim_xfer_set_ack_fnc(xfer, irc_xfer_ack); | |
1513 | |
1514 /* Keep track of this transfer for later. */ | |
1515 id->file_transfers = g_slist_append(id->file_transfers, xfer); | |
1516 | |
1517 /* Now perform the request! */ | |
1518 gaim_xfer_request(xfer); | |
1519 } | |
1520 } | |
1521 | |
1522 static gboolean | |
1523 irc_parse(GaimConnection *gc, char *buf) | |
1524 { | |
1525 struct irc_data *idata = gc->proto_data; | |
1526 gchar outbuf[IRC_BUF_LEN]; | |
1527 char *word[PDIWORDS], *word_eol[PDIWORDS]; | |
1528 char pdibuf[522]; | |
1529 char *ex, ip[128], nick[128]; | |
1530 char *cmd; | |
1531 | |
1532 /* Check for errors */ | |
1533 | |
1534 if (*buf != ':') { | |
1535 if (!strncmp(buf, "NOTICE ", 7)) | |
1536 buf += 7; | |
1537 if (!strncmp(buf, "PING ", 5)) { | |
1538 int r = FALSE; | |
1539 g_snprintf(outbuf, sizeof(outbuf), "PONG %s\r\n", buf + 5); | |
1540 if (irc_write(idata->fd, outbuf, strlen(outbuf)) < 0) { | |
1541 gaim_connection_error(gc, _("Unable to write")); | |
1542 r = TRUE; | |
1543 } | |
1544 return r; | |
1545 } | |
1546 /* XXX doesn't handle ERROR */ | |
1547 return FALSE; | |
1548 } | |
1549 | |
1550 if (!idata->online) { | |
1551 /* Now lets sign ourselves on */ | |
1552 gaim_connection_set_state(gc, GAIM_CONNECTED); | |
1553 serv_finish_login(gc); | |
1554 | |
1555 /* we don't call this now because otherwise some IRC servers might not like us */ | |
1556 idata->timer = g_timeout_add(20000, irc_request_buddy_update, gc); | |
1557 idata->online = TRUE; | |
1558 } | |
1559 | |
1560 buf++; | |
1561 | |
1562 process_data_init(pdibuf, buf, word, word_eol, FALSE); | |
1563 | |
1564 if (atoi(word[2])) { | |
1565 if (*word_eol[3]) | |
1566 process_numeric(gc, word, word_eol); | |
1567 return FALSE; | |
1568 } | |
1569 | |
1570 cmd = word[2]; | |
1571 | |
1572 ex = strchr(pdibuf, '!'); | |
1573 if (!ex) { | |
1574 strncpy(ip, pdibuf, sizeof(ip)); | |
1575 ip[sizeof(ip)-1] = 0; | |
1576 strncpy(nick, pdibuf, sizeof(nick)); | |
1577 nick[sizeof(nick)-1] = 0; | |
1578 } else { | |
1579 strncpy(ip, ex + 1, sizeof(ip)); | |
1580 ip[sizeof(ip)-1] = 0; | |
1581 strncpy(nick, pdibuf, sizeof(nick)); | |
1582 nick[sizeof(nick)-1] = 0; | |
1583 if ((ex - pdibuf) < sizeof (nick)) | |
1584 nick[ex - pdibuf] = 0; /* cut the buffer at the '!' */ | |
1585 } | |
1586 | |
1587 if (!strcmp(cmd, "INVITE")) { | |
1588 GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal, | |
1589 g_free, g_free); | |
1590 | |
1591 g_hash_table_replace(components, g_strdup("channel"), g_strdup(word[4])); | |
1592 | |
1593 serv_got_chat_invite(gc, word[4] + 1, nick, NULL, components); | |
1594 } else if (!strcmp(cmd, "JOIN")) { | |
1595 irc_parse_join(gc, nick, word, word_eol); | |
1596 } else if (!strcmp(cmd, "KICK")) { | |
1597 if (!strcmp(gaim_connection_get_display_name(gc), word[4])) { | |
1598 GaimConversation *c = irc_find_chat(gc, word[3]); | |
1599 if (!c) | |
1600 return FALSE; | |
1601 gc->buddy_chats = g_slist_remove(gc->buddy_chats, c); | |
1602 gaim_conversation_set_account(c, NULL); | |
1603 g_snprintf(outbuf, sizeof(outbuf), _("You have been kicked from %s: %s"), | |
1604 word[3], *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5]); | |
1605 gaim_notify_error(gc, NULL, outbuf, _("IRC Error")); | |
1606 } else { | |
1607 char *reason = *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5]; | |
1608 char *msg = g_strdup_printf(_("Kicked by %s: %s"), nick, reason); | |
1609 GaimConversation *c = irc_find_chat(gc, word[3]); | |
1610 irc_rem_chat_bud(gc, word[4], c, msg); | |
1611 g_free(msg); | |
1612 } | |
1613 } else if (!strcmp(cmd, "KILL")) { /* */ | |
1614 } else if (!strcmp(cmd, "MODE")) { | |
1615 handle_mode(gc, word, word_eol, FALSE); | |
1616 } else if (!strcmp(cmd, "NICK")) { | |
1617 char *new = *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3]; | |
1618 if (!strcmp(gaim_connection_get_display_name(gc), nick)) | |
1619 gaim_connection_set_display_name(gc, new); | |
1620 irc_change_name(gc, nick, new); | |
1621 } else if (!strcmp(cmd, "NOTICE")) { | |
1622 irc_parse_notice(gc, nick, ex, word, word_eol); | |
1623 } else if (!strcmp(cmd, "PART")) { | |
1624 if (!irc_parse_part(gc, nick, cmd, word, word_eol)) | |
1625 return FALSE; | |
1626 } else if (!strcmp(cmd, "PRIVMSG")) { | |
1627 char *to, *msg; | |
1628 if (!*word[3]) | |
1629 return FALSE; | |
1630 to = word[3]; | |
1631 msg = *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4]; | |
1632 if (msg[0] == 1 && msg[strlen (msg) - 1] == 1) { /* ctcp */ | |
1633 if (!g_ascii_strncasecmp(msg + 1, "DCC ", 4)) | |
1634 process_data_init(pdibuf, buf, word, word_eol, TRUE); | |
1635 handle_ctcp(gc, to, nick, msg + 1, word, word_eol); | |
1636 } else { | |
1637 handle_privmsg(gc, to, nick, msg); | |
1638 } | |
1639 } else if (!strcmp(cmd, "PONG")) { /* */ | |
1640 } else if (!strcmp(cmd, "QUIT")) { | |
1641 irc_rem_chat_bud(gc, nick, irc_find_chat(gc, word[3]), *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3]); | |
1642 } else if (!strcmp(cmd, "TOPIC")) { | |
1643 irc_parse_topic(gc, nick, word, word_eol); | |
1644 } else if (!strcmp(cmd, "WALLOPS")) { /* Don't know if a dialog box is the right way? */ | |
1645 char *msg = strrchr(word_eol[0], ':'); | |
1646 if (msg) | |
1647 gaim_notify_error(gc, NULL, msg+1, _("IRC Operator")); | |
1648 } | |
1649 | |
1650 return FALSE; | |
1651 } | |
1652 | |
1653 /* CTCP by jonas@birme.se */ | |
1654 static void | |
1655 irc_parse_notice(GaimConnection *gc, char *nick, char *ex, | |
1656 char *word[], char *word_eol[]) | |
1657 { | |
1658 char buf[IRC_BUF_LEN]; | |
1659 | |
1660 if (!g_ascii_strcasecmp(word[4], ":\001CLIENTINFO")) { | |
1661 char *p = g_strrstr(word_eol[5], "\001"); | |
1662 *p = 0; | |
1663 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); | |
1664 gaim_notify_info(gc, NULL, buf, _("CTCP ClientInfo")); | |
1665 | |
1666 } else if (!g_ascii_strcasecmp(word[4], ":\001USERINFO")) { | |
1667 char *p = g_strrstr(word_eol[5], "\001"); | |
1668 *p = 0; | |
1669 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); | |
1670 gaim_notify_info(gc, NULL, buf, _("CTCP UserInfo")); | |
1671 | |
1672 } else if (!g_ascii_strcasecmp(word[4], ":\001VERSION")) { | |
1673 char *p = g_strrstr(word_eol[5], "\001"); | |
1674 *p = 0; | |
1675 g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]); | |
1676 gaim_notify_info(gc, NULL, buf, _("CTCP Version")); | |
1677 | |
1678 } else if (!g_ascii_strcasecmp(word[4], ":\001PING")) { | |
1679 char *p = g_strrstr(word_eol[5], "\001"); | |
1680 struct timeval ping_time; | |
1681 struct timeval now; | |
1682 gchar **vector; | |
1683 | |
1684 if (p) | |
1685 *p = 0; | |
1686 | |
1687 vector = g_strsplit(word_eol[5], ".", 2); | |
1688 | |
1689 if (gettimeofday(&now, NULL) == 0 && vector != NULL) { | |
1690 if (vector[1] && now.tv_usec - atol(vector[1]) < 0) { | |
1691 ping_time.tv_sec = now.tv_sec - atol(vector[0]) - 1; | |
1692 ping_time.tv_usec = now.tv_usec - atol(vector[1]) + 1000000; | |
1693 | |
1694 } else { | |
1695 ping_time.tv_sec = now.tv_sec - atol(vector[0]); | |
1696 if(vector[1]) | |
1697 ping_time.tv_usec = now.tv_usec - atol(vector[1]); | |
1698 } | |
1699 | |
1700 g_snprintf(buf, sizeof(buf), | |
1701 "CTCP Ping reply from %s: %lu.%.03lu seconds", | |
1702 nick, ping_time.tv_sec, (ping_time.tv_usec/1000)); | |
1703 | |
1704 gaim_notify_info(gc, NULL, buf, _("CTCP Ping")); | |
1705 g_strfreev(vector); | |
1706 } | |
1707 } else { | |
1708 if (*word_eol[4] == ':') word_eol[4]++; | |
1709 if (ex) | |
1710 irc_got_im(gc, nick, word_eol[4], 0, time(NULL)); | |
1711 } | |
1712 } | |
1713 | |
1714 static void | |
1715 irc_parse_join(GaimConnection *gc, char *nick, | |
1716 char *word[], char *word_eol[]) | |
1717 { | |
1718 char *chan = *word[3] == ':' ? word[3] + 1 : word[3]; | |
1719 static int id = 1; | |
1720 GaimConversation *c; | |
1721 char *hostmask, *p; | |
1722 | |
1723 if (!gaim_utf8_strcasecmp(gaim_connection_get_display_name(gc), nick)) { | |
1724 serv_got_joined_chat(gc, id++, chan); | |
1725 } else { | |
1726 c = irc_find_chat(gc, chan); | |
1727 if (c) { | |
1728 hostmask = g_strdup(word[1]); | |
1729 p = strchr(hostmask, '!'); | |
1730 if (p) { | |
1731 char *pend = strchr(p, ' '); | |
1732 if (pend) { | |
1733 *pend = 0; | |
1734 } | |
1735 | |
1736 gaim_chat_add_user(GAIM_CHAT(c), nick, p + 1); | |
1737 | |
1738 g_free(hostmask); | |
1739 } | |
1740 } | |
1741 } | |
1742 } | |
1743 | |
1744 static void | |
1745 irc_parse_topic(GaimConnection *gc, char *nick, | |
1746 char *word[], char *word_eol[]) | |
1747 { | |
1748 GaimConversation *c = irc_find_chat(gc, word[3]); | |
1749 char *topic = irc_recv_convert(gc, *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4]); | |
1750 char buf[IRC_BUF_LEN]; | |
1751 | |
1752 if (c) { | |
1753 gaim_chat_set_topic(GAIM_CHAT(c), nick, topic); | |
1754 g_snprintf(buf, sizeof(buf), | |
1755 _("<B>%s has changed the topic to: %s</B>"), nick, topic); | |
1756 | |
1757 gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); | |
1758 } | |
1759 g_free(topic); | |
1760 } | |
1761 | |
1762 static gboolean | |
1763 irc_parse_part(GaimConnection *gc, char *nick, char *cmd, | |
1764 char *word[], char *word_eol[]) | |
1765 { | |
1766 char *chan = cmd + 5; | |
1767 GaimConversation *c; | |
1768 GaimChat *chat; | |
1769 char *reason = word_eol[4]; | |
1770 GList *r; | |
1771 | |
1772 if (*chan == ':') | |
1773 chan++; | |
1774 if (*reason == ':') | |
1775 reason++; | |
1776 if (!(c = irc_find_chat(gc, chan))) | |
1777 return FALSE; | |
1778 if (!strcmp(nick, gaim_connection_get_display_name(gc))) { | |
1779 serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(c))); | |
1780 return FALSE; | |
1781 } | |
1782 | |
1783 chat = GAIM_CHAT(c); | |
1784 | |
1785 r = gaim_chat_get_users(GAIM_CHAT(c)); | |
1786 | |
1787 while (r) { | |
1788 char *who = r->data; | |
1789 if (*who == '@') | |
1790 who++; | |
1791 if (*who == '%') | |
1792 who++; | |
1793 if (*who == '+') | |
1794 who++; | |
1795 if (!gaim_utf8_strcasecmp(who, nick)) { | |
1796 gaim_chat_remove_user(chat, who, reason); | |
1797 break; | |
1798 } | |
1799 r = r->next; | |
1800 } | |
1801 return TRUE; | |
1802 } | |
1803 | |
1804 static void | |
1805 irc_callback(gpointer data, gint source, GaimInputCondition condition) | |
1806 { | |
1807 GaimConnection *gc = data; | |
1808 struct irc_data *idata = gc->proto_data; | |
1809 int i = 0; | |
1810 gchar buf[1024]; | |
1811 gboolean off; | |
1812 | |
1813 i = read(idata->fd, buf, 1024); | |
1814 if (i <= 0) { | |
1815 gaim_connection_error(gc, _("Read error")); | |
1816 return; | |
1817 } | |
1818 | |
1819 idata->rxqueue = g_realloc(idata->rxqueue, i + idata->rxlen + 1); | |
1820 memcpy(idata->rxqueue + idata->rxlen, buf, i); | |
1821 idata->rxlen += i; | |
1822 idata->rxqueue[idata->rxlen] = 0; | |
1823 | |
1824 while (1) { | |
1825 char *d, *e; | |
1826 int len; | |
1827 | |
1828 if (!idata->rxqueue || ((e = strchr(idata->rxqueue, '\n')) == NULL)) | |
1829 return; | |
1830 | |
1831 len = e - idata->rxqueue + 1; | |
1832 d = g_strndup(idata->rxqueue, len); | |
1833 g_strchomp(d); | |
1834 gaim_debug(GAIM_DEBUG_MISC, "irc", "S: %s\n", d); | |
1835 | |
1836 /* REMOVE ME BEFORE SUBMIT! */ | |
1837 /*fprintf(stderr, "IRC S: %s\n", d);*/ | |
1838 | |
1839 idata->rxlen -= len; | |
1840 if (idata->rxlen) { | |
1841 char *tmp = g_strdup(e + 1); | |
1842 g_free(idata->rxqueue); | |
1843 idata->rxqueue = tmp; | |
1844 } else { | |
1845 g_free(idata->rxqueue); | |
1846 idata->rxqueue = NULL; | |
1847 } | |
1848 | |
1849 off = irc_parse(gc, d); | |
1850 | |
1851 g_free(d); | |
1852 | |
1853 if (off) | |
1854 return; | |
1855 } | |
1856 } | |
1857 | |
1858 static void | |
1859 irc_login_callback(gpointer data, gint source, GaimInputCondition condition) | |
1860 { | |
1861 GaimConnection *gc = data; | |
1862 GaimAccount *account = gaim_connection_get_account(gc); | |
1863 struct irc_data *idata; | |
1864 char hostname[256]; | |
1865 char buf[IRC_BUF_LEN]; | |
1866 char *test; | |
1867 const char *alias; | |
1868 const char *charset = gaim_account_get_string(account, "charset", "UTF-8"); | |
1869 GError *err = NULL; | |
1870 | |
1871 idata = gc->proto_data; | |
1872 | |
1873 if (source < 0) { | |
1874 gaim_connection_error(gc, _("Write error")); | |
1875 return; | |
1876 } | |
1877 idata->fd = source; | |
1878 | |
1879 /* Try a quick conversion to see if the specified encoding is OK */ | |
1880 test = g_convert("test", strlen("test"), charset, | |
1881 "UTF-8", NULL, NULL, &err); | |
1882 if (err) { | |
1883 gaim_debug(GAIM_DEBUG_ERROR, "irc", | |
1884 "Couldn't initialize %s for IRC charset conversion, using ISO-8859-1\n", | |
1885 charset); | |
1886 gaim_account_set_string(account, "charset", "UTF-8"); | |
1887 } | |
1888 | |
1889 g_free(test); | |
1890 | |
1891 gethostname(hostname, sizeof(hostname) - 1); | |
1892 hostname[sizeof(hostname) - 1] = 0; | |
1893 | |
1894 if (!*hostname) | |
1895 g_snprintf(hostname, sizeof(hostname), "localhost"); | |
1896 | |
1897 if (gaim_account_get_password(account) != NULL) { | |
1898 g_snprintf(buf, sizeof(buf), "PASS %s\r\n", | |
1899 gaim_account_get_password(account)); | |
1900 | |
1901 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { | |
1902 gaim_connection_error(gc, _("Write error")); | |
1903 return; | |
1904 } | |
1905 } | |
1906 | |
1907 alias = gaim_account_get_alias(account); | |
1908 | |
1909 g_snprintf(buf, sizeof(buf), "USER %s %s %s :%s\r\n", | |
1910 g_get_user_name(), hostname, | |
1911 idata->server, | |
1912 (alias == NULL ? "gaim" : alias)); | |
1913 | |
1914 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { | |
1915 gaim_connection_error(gc, _("Write error")); | |
1916 return; | |
1917 } | |
1918 | |
1919 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", | |
1920 gaim_connection_get_display_name(gc)); | |
1921 | |
1922 if (irc_write(idata->fd, buf, strlen(buf)) < 0) { | |
1923 gaim_connection_error(gc, _("Write error")); | |
1924 return; | |
1925 } | |
1926 | |
1927 gc->inpa = gaim_input_add(idata->fd, GAIM_INPUT_READ, irc_callback, gc); | |
1928 } | |
1929 | |
1930 static void | |
1931 irc_login(GaimAccount *account) | |
1932 { | |
1933 const char *username = gaim_account_get_username(account); | |
1934 char buf[IRC_BUF_LEN]; | |
1935 int rc; | |
1936 | |
1937 GaimConnection *gc; | |
1938 struct irc_data *idata; | |
1939 char **parts; | |
1940 | |
1941 gc = gaim_account_get_connection(account); | |
1942 idata = gc->proto_data = g_new0(struct irc_data, 1); | |
1943 | |
1944 parts = g_strsplit(username, "@", 2); | |
1945 gaim_connection_set_display_name(gc, parts[0]); | |
1946 idata->server = g_strdup(parts[1]); | |
1947 g_strfreev(parts); | |
1948 | |
1949 g_snprintf(buf, sizeof(buf), _("Signon: %s"), username); | |
1950 gaim_connection_update_progress(gc, buf, 1, 2); | |
1951 | |
1952 idata->chantypes = g_strdup("#&!+"); | |
1953 idata->chanmodes = g_strdup("beI,k,lnt"); | |
1954 idata->nickmodes = g_strdup("ohv"); | |
1955 idata->str = g_string_new(""); | |
1956 idata->fd = -1; | |
1957 | |
1958 rc = gaim_proxy_connect(account, idata->server, | |
1959 gaim_account_get_int(account, "port", 6667), | |
1960 irc_login_callback, gc); | |
1961 | |
1962 if (rc != 0) { | |
1963 gaim_connection_error(gc, _("Unable to create socket")); | |
1964 return; | |
1965 } | |
1966 } | |
1967 | |
1968 static void | |
1969 irc_close(GaimConnection *gc) | |
1970 { | |
1971 struct irc_data *idata = (struct irc_data *)gc->proto_data; | |
1972 | |
1973 gchar buf[IRC_BUF_LEN]; | |
1974 | |
1975 if (idata->str->len > 0) { | |
1976 g_snprintf(buf, sizeof(buf), "QUIT :%s\r\n", idata->str->str); | |
1977 } else { | |
1978 g_snprintf(buf, sizeof(buf), | |
1979 "QUIT :Download Gaim [%s]\r\n", WEBSITE); | |
1980 } | |
1981 irc_write(idata->fd, buf, strlen(buf)); | |
1982 | |
1983 if (idata->rxqueue) | |
1984 g_free(idata->rxqueue); | |
1985 | |
1986 idata->rxqueue = NULL; | |
1987 idata->rxlen = 0; | |
1988 | |
1989 /* Kill any existing transfers */ | |
1990 while (idata->file_transfers) { | |
1991 GaimXfer *xfer; | |
1992 | |
1993 xfer = (GaimXfer *)idata->file_transfers->data; | |
1994 | |
1995 gaim_xfer_end(xfer); | |
1996 gaim_xfer_destroy(xfer); | |
1997 | |
1998 idata->file_transfers = idata->file_transfers->next; | |
1999 } | |
2000 idata->file_transfers = NULL; | |
2001 | |
2002 | |
2003 g_free(idata->chantypes); | |
2004 g_free(idata->chanmodes); | |
2005 g_free(idata->nickmodes); | |
2006 | |
2007 g_string_free(idata->str, TRUE); | |
2008 if (idata->liststr) | |
2009 g_string_free(idata->liststr, TRUE); | |
2010 | |
2011 if (idata->timer) | |
2012 g_source_remove(idata->timer); | |
2013 | |
2014 if (gc->inpa) | |
2015 gaim_input_remove(gc->inpa); | |
2016 | |
2017 close(idata->fd); | |
2018 g_free(gc->proto_data); | |
2019 } | |
2020 | |
2021 static void | |
2022 set_mode_3(GaimConnection *gc, const char *who, int sign, int mode, | |
2023 int start, int end, char *word[]) | |
2024 { | |
2025 struct irc_data *id = gc->proto_data; | |
2026 char buf[IRC_BUF_LEN]; | |
2027 int left; | |
2028 int i = start; | |
2029 | |
2030 while (1) { | |
2031 left = end - i; | |
2032 switch (left) { | |
2033 case 0: | |
2034 return; | |
2035 case 1: | |
2036 g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n", | |
2037 who, sign, mode, word[i]); | |
2038 i += 1; | |
2039 break; | |
2040 case 2: | |
2041 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n", | |
2042 who, sign, mode, mode, word[i], word[i + 1]); | |
2043 i += 2; | |
2044 break; | |
2045 default: | |
2046 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n", | |
2047 who, sign, mode, mode, mode, | |
2048 word[i], word[i + 1], word[i + 2]); | |
2049 i += 2; | |
2050 break; | |
2051 } | |
2052 irc_write(id->fd, buf, strlen(buf)); | |
2053 if (left < 3) | |
2054 return; | |
2055 } | |
2056 } | |
2057 | |
2058 static void | |
2059 set_mode_6(GaimConnection *gc, const char *who, int sign, int mode, | |
2060 int start, int end, char *word[]) | |
2061 { | |
2062 struct irc_data *id = gc->proto_data; | |
2063 char buf[IRC_BUF_LEN]; | |
2064 int left; | |
2065 int i = start; | |
2066 | |
2067 while (1) { | |
2068 left = end - i; | |
2069 switch (left) { | |
2070 case 0: | |
2071 return; | |
2072 case 1: | |
2073 g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n", | |
2074 who, sign, mode, word[i]); | |
2075 i += 1; | |
2076 break; | |
2077 case 2: | |
2078 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n", | |
2079 who, sign, mode, mode, word[i], word[i + 1]); | |
2080 i += 2; | |
2081 break; | |
2082 case 3: | |
2083 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n", | |
2084 who, sign, mode, mode, mode, | |
2085 word[i], word[i + 1], word[i + 2]); | |
2086 i += 3; | |
2087 break; | |
2088 case 4: | |
2089 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c %s %s %s %s\r\n", | |
2090 who, sign, mode, mode, mode, mode, | |
2091 word[i], word[i + 1], word[i + 2], word[i + 3]); | |
2092 i += 4; | |
2093 break; | |
2094 case 5: | |
2095 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c %s %s %s %s %s\r\n", | |
2096 who, sign, mode, mode, mode, mode, mode, | |
2097 word[i], word[i + 1], word[i + 2], | |
2098 word[i + 3], word[i + 4]); | |
2099 i += 5; | |
2100 break; | |
2101 default: | |
2102 g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c%c %s %s %s %s %s %s\r\n", | |
2103 who, sign, mode, mode, mode, mode, mode, mode, | |
2104 word[i], word[i + 1], word[i + 2], | |
2105 word[i + 3], word[i + 4], word[i + 5]); | |
2106 i += 6; | |
2107 break; | |
2108 } | |
2109 irc_write(id->fd, buf, strlen(buf)); | |
2110 if (left < 6) | |
2111 return; | |
2112 } | |
2113 } | |
2114 | |
2115 static void | |
2116 set_mode(GaimConnection *gc, const char *who, int sign, int mode, char *word[]) | |
2117 { | |
2118 struct irc_data *id = gc->proto_data; | |
2119 int i = 2; | |
2120 | |
2121 while (1) { | |
2122 if (!*word[i]) { | |
2123 if (i == 2) | |
2124 return; | |
2125 if (id->six_modes) | |
2126 set_mode_6(gc, who, sign, mode, 2, i, word); | |
2127 else | |
2128 set_mode_3(gc, who, sign, mode, 2, i, word); | |
2129 return; | |
2130 } | |
2131 i++; | |
2132 } | |
2133 } | |
2134 | |
2135 static void | |
2136 set_chan_mode(GaimConnection *gc, const char *chan, const char *mode_str) | |
2137 { | |
2138 struct irc_data *id = gc->proto_data; | |
2139 char buf[IRC_BUF_LEN]; | |
2140 | |
2141 if ((mode_str[0] == '-') || (mode_str[0] == '+')) { | |
2142 g_snprintf(buf, sizeof(buf), "MODE %s %s\r\n", chan, mode_str); | |
2143 irc_write(id->fd, buf, strlen(buf)); | |
2144 } | |
2145 } | |
2146 | |
2147 static int | |
2148 handle_command(GaimConnection *gc, const char *who, const char *in_what) | |
2149 { | |
2150 char buf[IRC_BUF_LEN]; | |
2151 char pdibuf[IRC_BUF_LEN]; | |
2152 char *word[PDIWORDS], *word_eol[PDIWORDS]; | |
2153 char *tmp = g_strdup(in_what); | |
2154 GString *str = encode_html(tmp); | |
2155 char *intl; | |
2156 int len; | |
2157 struct dcc_chat *dccchat = find_dcc_chat(gc, who); | |
2158 struct irc_data *id = gc->proto_data; | |
2159 char *what = str->str; | |
2160 | |
2161 g_free(tmp); | |
2162 | |
2163 if (*what != '/') { | |
2164 if (dccchat) { | |
2165 intl = irc_send_convert(gc, what, sizeof(buf), &len); | |
2166 g_snprintf(buf, sizeof(buf), "%s\r\n", intl); | |
2167 g_free(intl); | |
2168 irc_write(dccchat->fd, buf, strlen(buf)); | |
2169 g_string_free(str, TRUE); | |
2170 return 1; | |
2171 } | |
2172 irc_send_privmsg (gc, who, what, TRUE); | |
2173 g_string_free(str, TRUE); | |
2174 return 1; | |
2175 } | |
2176 | |
2177 process_data_init(pdibuf, what + 1, word, word_eol, TRUE); | |
2178 g_string_free(str, FALSE); | |
2179 if (!g_ascii_strcasecmp(pdibuf, "ME")) { | |
2180 if (dccchat) { | |
2181 intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len); | |
2182 g_snprintf(buf, sizeof(buf), "\001ACTION %s\001\r\n", intl); | |
2183 g_free(intl); | |
2184 irc_write(dccchat->fd, buf, strlen(buf)); | |
2185 g_free(what); | |
2186 return 1; | |
2187 } | |
2188 g_snprintf(buf, sizeof(buf), "\001ACTION %s\001", word_eol[2]); | |
2189 irc_send_privmsg (gc, who, buf, FALSE); | |
2190 g_free(what); | |
2191 return 1; | |
2192 } else if (!g_ascii_strcasecmp(pdibuf, "INVITE")) { | |
2193 char buf[IRC_BUF_LEN]; | |
2194 g_snprintf(buf, sizeof(buf), "INVITE %s\r\n", word_eol[2]); | |
2195 irc_write(id->fd, buf, strlen(buf)); | |
2196 } else if (!g_ascii_strcasecmp(pdibuf, "TOPIC")) { | |
2197 if (!*word_eol[2]) { | |
2198 GaimConversation *c; | |
2199 GaimChat *chat; | |
2200 | |
2201 c = irc_find_chat(gc, who); | |
2202 chat = GAIM_CHAT(c); | |
2203 | |
2204 g_snprintf(buf, sizeof(buf), _("Topic for %s is %s"), | |
2205 who, (gaim_chat_get_topic(chat) | |
2206 ? gaim_chat_get_topic(chat) | |
2207 : "(no topic set)")); | |
2208 | |
2209 gaim_conversation_write(c, NULL, buf, -1, | |
2210 WFLAG_SYSTEM | WFLAG_NOLOG, time(NULL)); | |
2211 } else { | |
2212 /* This could be too long */ | |
2213 intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len); | |
2214 g_snprintf(buf, sizeof(buf), "TOPIC %s :%s\r\n", who, intl); | |
2215 g_free(intl); | |
2216 irc_write(id->fd, buf, strlen(buf)); | |
2217 } | |
2218 } else if (!g_ascii_strcasecmp(pdibuf, "NICK")) { | |
2219 if (!*word_eol[2]) { | |
2220 g_free(what); | |
2221 return -EINVAL; | |
2222 } | |
2223 g_snprintf(buf, sizeof(buf), "NICK %s\r\n", word_eol[2]); | |
2224 irc_write(id->fd, buf, strlen(buf)); | |
2225 } else if (!g_ascii_strcasecmp(pdibuf, "OP")) { | |
2226 set_mode(gc, who, '+', 'o', word); | |
2227 } else if (!g_ascii_strcasecmp(pdibuf, "DEOP")) { | |
2228 set_mode(gc, who, '-', 'o', word); | |
2229 } else if (!g_ascii_strcasecmp(pdibuf, "VOICE")) { | |
2230 set_mode(gc, who, '+', 'v', word); | |
2231 } else if (!g_ascii_strcasecmp(pdibuf, "DEVOICE")) { | |
2232 set_mode(gc, who, '-', 'v', word); | |
2233 } else if (!g_ascii_strcasecmp(pdibuf, "MODE")) { | |
2234 set_chan_mode(gc, who, word_eol[2]); | |
2235 } else if (!g_ascii_strcasecmp(pdibuf, "QUOTE")) { | |
2236 if (!*word_eol[2]) { | |
2237 g_free(what); | |
2238 return -EINVAL; | |
2239 } | |
2240 g_snprintf(buf, sizeof(buf), "%s\r\n", word_eol[2]); | |
2241 irc_write(id->fd, buf, strlen(buf)); | |
2242 } else if (!g_ascii_strcasecmp(pdibuf, "SAY")) { | |
2243 if (!*word_eol[2]) { | |
2244 g_free(what); | |
2245 return -EINVAL; | |
2246 } | |
2247 irc_send_privmsg (gc, who, word_eol[2], TRUE); | |
2248 return 1; | |
2249 } else if (!g_ascii_strcasecmp(pdibuf, "MSG")) { | |
2250 if (!*word[2]) { | |
2251 g_free(what); | |
2252 return -EINVAL; | |
2253 } | |
2254 if (!*word_eol[3]) { | |
2255 g_free(what); | |
2256 return -EINVAL; | |
2257 } | |
2258 irc_send_privmsg (gc, word[2], word_eol[3], TRUE); | |
2259 } else if (!g_ascii_strcasecmp(pdibuf, "KICK")) { | |
2260 if (!*word[2]) { | |
2261 g_free(what); | |
2262 return -EINVAL; | |
2263 } | |
2264 if (*word_eol[3]) { | |
2265 intl = irc_send_convert(gc, word_eol[3], sizeof(buf), &len); | |
2266 g_snprintf(buf, sizeof(buf), "KICK %s %s :%s\r\n", who, word[2], intl); | |
2267 g_free(intl); | |
2268 } else | |
2269 g_snprintf(buf, sizeof(buf), "KICK %s %s\r\n", who, word[2]); | |
2270 irc_write(id->fd, buf, strlen(buf)); | |
2271 } else if (!g_ascii_strcasecmp(pdibuf, "JOIN") || !g_ascii_strcasecmp(pdibuf, "J")) { | |
2272 if (!*word[2]) { | |
2273 g_free(what); | |
2274 return -EINVAL; | |
2275 } | |
2276 if (*word[3]) | |
2277 g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", word[2], word[3]); | |
2278 else | |
2279 g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", word[2]); | |
2280 irc_write(id->fd, buf, strlen(buf)); | |
2281 } else if (!g_ascii_strcasecmp(pdibuf, "PART")) { | |
2282 const char *chan = *word[2] ? word[2] : who; | |
2283 char *reason = word_eol[3]; | |
2284 GaimConversation *c; | |
2285 if (!is_channel(gc, chan)) { | |
2286 g_free(what); | |
2287 return -EINVAL; | |
2288 } | |
2289 c = irc_find_chat(gc, chan); | |
2290 if (*reason) { | |
2291 intl = irc_send_convert(gc, reason, sizeof(buf), &len); | |
2292 g_snprintf(buf, sizeof(buf), "PART %s :%s\r\n", chan, intl); | |
2293 g_free(intl); | |
2294 } else | |
2295 g_snprintf(buf, sizeof(buf), "PART %s\r\n", chan); | |
2296 irc_write(id->fd, buf, strlen(buf)); | |
2297 if (c) { | |
2298 gc->buddy_chats = g_slist_remove(gc->buddy_chats, c); | |
2299 gaim_conversation_set_account(c, NULL); | |
2300 g_snprintf(buf, sizeof(buf), _("You have left %s"), chan); | |
2301 gaim_notify_info(gc, NULL, buf, _("IRC Part")); | |
2302 } | |
2303 } else if (!g_ascii_strcasecmp(pdibuf, "WHOIS")) { | |
2304 g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", word_eol[2]); | |
2305 irc_write(id->fd, buf, strlen(buf)); | |
2306 } else if (!g_ascii_strcasecmp(pdibuf, "WHOWAS")) { | |
2307 g_snprintf(buf, sizeof(buf), "WHOWAS %s\r\n", word_eol[2]); | |
2308 irc_write(id->fd, buf, strlen(buf)); | |
2309 } else if (!g_ascii_strcasecmp(pdibuf, "LIST")) { | |
2310 g_snprintf(buf, sizeof(buf), "LIST\r\n"); | |
2311 irc_write(id->fd, buf, strlen(buf)); | |
2312 } else if (!g_ascii_strcasecmp(pdibuf, "QUIT")) { | |
2313 char *reason = word_eol[2]; | |
2314 id->str = g_string_insert(id->str, 0, reason); | |
2315 gaim_core_quit(); | |
2316 } else if (!g_ascii_strcasecmp(pdibuf, "VERSION")) { | |
2317 g_snprintf(buf, sizeof(buf), "VERSION\r\n"); | |
2318 irc_write(id->fd, buf, strlen(buf)); | |
2319 } else if (!g_ascii_strcasecmp(pdibuf, "W")) { | |
2320 g_snprintf(buf, sizeof(buf), "WHO *\r\n"); | |
2321 irc_write(id->fd, buf, strlen(buf)); | |
2322 } else if (!g_ascii_strcasecmp(pdibuf, "REHASH")) { | |
2323 g_snprintf(buf, sizeof(buf), "REHASH\r\n"); | |
2324 irc_write(id->fd, buf, strlen(buf)); | |
2325 } else if (!g_ascii_strcasecmp(pdibuf, "RESTART")) { | |
2326 g_snprintf(buf, sizeof(buf), "RESTART\r\n"); | |
2327 irc_write(id->fd, buf, strlen(buf)); | |
2328 } else if (!g_ascii_strcasecmp(pdibuf, "CTCP")) { | |
2329 if (!g_ascii_strcasecmp(word[2], "CLIENTINFO")) { | |
2330 if (word[3]) | |
2331 irc_ctcp_clientinfo(gc, word[3]); | |
2332 } else if (!g_ascii_strcasecmp(word[2], "USERINFO")) { | |
2333 if (word[3]) | |
2334 irc_ctcp_userinfo(gc, word[3]); | |
2335 } else if (!g_ascii_strcasecmp(word[2], "VERSION")) { | |
2336 if (word[3]) | |
2337 irc_ctcp_version(gc, word[3]); | |
2338 | |
2339 } else if (!g_ascii_strcasecmp(word[2], "PING")) { | |
2340 if (word[3]) | |
2341 irc_ctcp_ping(gc, word[3]); | |
2342 } | |
2343 } else if (!g_ascii_strcasecmp(pdibuf, "DCC")) { | |
2344 GaimConversation *c = NULL; | |
2345 if (!g_ascii_strcasecmp(word[2], "CHAT")) { | |
2346 if (word[3]) | |
2347 irc_start_chat(gc, word[3]); | |
2348 | |
2349 if (is_channel(gc, who)) { | |
2350 c = irc_find_chat(gc, who); | |
2351 } else { | |
2352 c = gaim_find_conversation(who); | |
2353 } | |
2354 if (c) { | |
2355 gaim_conversation_write(c, NULL, | |
2356 _("<I>Requesting DCC CHAT</I>"), | |
2357 -1, WFLAG_SYSTEM, time(NULL)); | |
2358 } | |
2359 } | |
2360 } else if (!g_ascii_strcasecmp(pdibuf, "HELP")) { | |
2361 GaimConversation *c = NULL; | |
2362 if (is_channel(gc, who)) { | |
2363 c = irc_find_chat(gc, who); | |
2364 } else { | |
2365 c = gaim_find_conversation(who); | |
2366 } | |
2367 if (!c) { | |
2368 g_free(what); | |
2369 return -EINVAL; | |
2370 } | |
2371 if (!g_ascii_strcasecmp(word[2], "OPER")) { | |
2372 gaim_conversation_write(c, NULL, | |
2373 _("<B>Operator commands:<BR>" | |
2374 "REHASH RESTART</B>"), | |
2375 -1, WFLAG_NOLOG, time(NULL)); | |
2376 } else if (!g_ascii_strcasecmp(word[2], "CTCP")) { | |
2377 gaim_conversation_write(c, NULL, | |
2378 _("<B>CTCP commands:<BR>" | |
2379 "CLIENTINFO <nick><BR>" | |
2380 "USERINFO <nick><BR>" | |
2381 "VERSION <nick><BR>" | |
2382 "PING <nick></B><BR>"), | |
2383 -1, WFLAG_NOLOG, time(NULL)); | |
2384 } else if (!g_ascii_strcasecmp(word[2], "DCC")) { | |
2385 gaim_conversation_write(c, NULL, | |
2386 _("<B>DCC commands:<BR>" | |
2387 "CHAT <nick></B>"), | |
2388 -1, WFLAG_NOLOG, time(NULL)); | |
2389 } else { | |
2390 gaim_conversation_write(c, NULL, | |
2391 _("<B>Currently supported commands:<BR>" | |
2392 "WHOIS INVITE NICK LIST<BR>" | |
2393 "JOIN PART TOPIC KICK<BR>" | |
2394 "OP DEOP VOICE DEVOICE<BR>" | |
2395 "ME MSG QUOTE SAY QUIT<BR>" | |
2396 "MODE VERSION W WHOWAS<BR>" | |
2397 "Type /HELP OPER for operator commands<BR>" | |
2398 "Type /HELP CTCP for CTCP commands<BR>" | |
2399 "Type /HELP DCC for DCC commands"), | |
2400 -1, WFLAG_NOLOG, time(NULL)); | |
2401 } | |
2402 } else { | |
2403 GaimConversation *c = NULL; | |
2404 if (is_channel(gc, who)) { | |
2405 c = irc_find_chat(gc, who); | |
2406 } else { | |
2407 c = gaim_find_conversation(who); | |
2408 } | |
2409 if (!c) { | |
2410 g_free(what); | |
2411 return -EINVAL; | |
2412 } | |
2413 | |
2414 gaim_conversation_write(c, NULL, _("<B>Unknown command</B>"), | |
2415 -1, WFLAG_NOLOG, time(NULL)); | |
2416 } | |
2417 g_free(what); | |
2418 return 0; | |
2419 } | |
2420 | |
2421 static int | |
2422 send_msg(GaimConnection *gc, const char *who, const char *what) | |
2423 { | |
2424 char *cr = strchr(what, '\n'); | |
2425 if (cr) { | |
2426 int ret = 0; | |
2427 while (TRUE) { | |
2428 if (cr) | |
2429 *cr = 0; | |
2430 ret = handle_command(gc, who, what); | |
2431 if (!cr) | |
2432 break; | |
2433 what = cr + 1; | |
2434 if (!*what) | |
2435 break; | |
2436 *cr = '\n'; | |
2437 cr = strchr(what, '\n'); | |
2438 } | |
2439 return ret; | |
2440 } else | |
2441 return handle_command(gc, who, what); | |
2442 } | |
2443 | |
2444 static void | |
2445 irc_chat_invite(GaimConnection *gc, int idn, const char *message, const char *name) { | |
2446 char buf[IRC_BUF_LEN]; | |
2447 struct irc_data *id = gc->proto_data; | |
2448 GaimConversation *c = gaim_find_chat(gc, idn); | |
2449 g_snprintf(buf, sizeof(buf), "INVITE %s %s\r\n", name, c->name); | |
2450 irc_write(id->fd, buf, strlen(buf)); | |
2451 } | |
2452 | |
2453 static int | |
2454 irc_send_im(GaimConnection *gc, const char *who, const char *what, int len, int flags) | |
2455 { | |
2456 if (*who == '@' || *who == '%' || *who == '+') | |
2457 return send_msg(gc, who + 1, what); | |
2458 return send_msg(gc, who, what); | |
2459 } | |
2460 | |
2461 /* IRC doesn't have a buddy list, but we can still figure out who's online with ISON */ | |
2462 static void | |
2463 irc_add_buddy(GaimConnection *gc, const char *who) {} | |
2464 static void | |
2465 irc_remove_buddy(GaimConnection *gc, const char *who, const char *group) {} | |
2466 | |
2467 static GList * | |
2468 irc_chat_info(GaimConnection *gc) | |
2469 { | 118 { |
2470 GList *m = NULL; | 119 GList *m = NULL; |
2471 struct proto_chat_entry *pce; | 120 struct proto_chat_entry *pce; |
2472 | 121 |
2473 pce = g_new0(struct proto_chat_entry, 1); | 122 pce = g_new0(struct proto_chat_entry, 1); |
2476 m = g_list_append(m, pce); | 125 m = g_list_append(m, pce); |
2477 | 126 |
2478 pce = g_new0(struct proto_chat_entry, 1); | 127 pce = g_new0(struct proto_chat_entry, 1); |
2479 pce->label = _("Password:"); | 128 pce->label = _("Password:"); |
2480 pce->identifier = "password"; | 129 pce->identifier = "password"; |
2481 pce->secret = TRUE; | |
2482 m = g_list_append(m, pce); | 130 m = g_list_append(m, pce); |
2483 | 131 |
2484 return m; | 132 return m; |
2485 } | 133 } |
2486 | 134 |
2487 static void | 135 static void irc_login(GaimAccount *account) |
2488 irc_join_chat(GaimConnection *gc, GHashTable *data) | 136 { |
2489 { | 137 GaimConnection *gc; |
2490 struct irc_data *id = gc->proto_data; | 138 struct irc_conn *irc; |
2491 char buf[IRC_BUF_LEN]; | 139 char *buf, **userparts; |
2492 char *name, *pass; | 140 const char *username = gaim_account_get_username(account); |
2493 | 141 int err; |
2494 if (!data) | 142 |
2495 return; | 143 gc = gaim_account_get_connection(account); |
2496 | 144 |
2497 name = g_hash_table_lookup(data, "channel"); | 145 gc->flags |= OPT_CONN_AUTO_RESP; |
2498 pass = g_hash_table_lookup(data, "password"); | 146 |
2499 if (pass) { | 147 gc->proto_data = irc = g_new0(struct irc_conn, 1); |
2500 g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", name, pass); | 148 irc->account = account; |
2501 } else | 149 |
2502 g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", name); | 150 userparts = g_strsplit(username, "@", 2); |
2503 irc_write(id->fd, buf, strlen(buf)); | 151 gaim_connection_set_display_name(gc, userparts[0]); |
2504 } | 152 irc->server = g_strdup(userparts[1]); |
2505 | 153 g_strfreev(userparts); |
2506 static void | 154 |
2507 irc_chat_leave(GaimConnection *gc, int id) | 155 irc->buddies = g_hash_table_new_full((GHashFunc)irc_nick_hash, (GEqualFunc)irc_nick_equal, |
2508 { | 156 NULL, (GDestroyNotify)irc_buddy_free); |
2509 struct irc_data *idata = gc->proto_data; | 157 irc->cmds = g_hash_table_new(g_str_hash, g_str_equal); |
2510 GaimConversation *c = gaim_find_chat(gc, id); | 158 irc_cmd_table_build(irc); |
2511 char buf[IRC_BUF_LEN]; | 159 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal); |
2512 | 160 irc_msg_table_build(irc); |
2513 if (!c) return; | 161 |
2514 | 162 buf = g_strdup_printf(_("Signon: %s"), username); |
2515 g_snprintf(buf, sizeof(buf), "PART %s\r\n", c->name); | 163 gaim_connection_update_progress(gc, buf, 1, 2); |
2516 irc_write(idata->fd, buf, strlen(buf)); | 164 g_free(buf); |
2517 } | 165 |
2518 | 166 err = gaim_proxy_connect(account, irc->server, |
2519 static int | 167 gaim_account_get_int(account, "port", IRC_DEFAULT_PORT), |
2520 irc_chat_send(GaimConnection *gc, int id, const char *what) | 168 irc_login_cb, gc); |
2521 { | 169 |
2522 GaimConversation *c = gaim_find_chat(gc, id); | 170 if (err || !account->gc) { |
2523 if (!c) | 171 gaim_connection_error(gc, _("Couldn't create socket")); |
2524 return -EINVAL; | 172 return; |
2525 if (send_msg(gc, c->name, what) > 0) | 173 } |
2526 serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)), | 174 } |
2527 gaim_connection_get_display_name(gc), 0, what, time(NULL)); | 175 |
2528 return 0; | 176 static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond) |
2529 } | 177 { |
2530 | 178 GaimConnection *gc = data; |
2531 static GList * | 179 struct irc_conn *irc = gc->proto_data; |
2532 irc_away_states(GaimConnection *gc) | 180 char hostname[256]; |
2533 { | 181 char *buf; |
2534 return g_list_append(NULL, GAIM_AWAY_CUSTOM); | 182 GList *connections = gaim_connections_get_all(); |
2535 } | 183 |
2536 | 184 if (source < 0) |
2537 static void | 185 return; |
2538 irc_set_away(GaimConnection *gc, const char *state, const char *msg) | 186 |
2539 { | 187 if (!g_list_find(connections, gc)) { |
2540 struct irc_data *idata = gc->proto_data; | 188 close(source); |
2541 char buf[IRC_BUF_LEN]; | 189 return; |
190 } | |
191 | |
192 irc->fd = source; | |
193 | |
194 if (gc->account->password && *gc->account->password) { | |
195 buf = irc_format(irc, "vv", "PASS", gc->account->password); | |
196 if (irc_send(irc, buf) < 0) { | |
197 gaim_connection_error(gc, "Error sending password"); | |
198 return; | |
199 } | |
200 g_free(buf); | |
201 } | |
202 | |
203 gethostname(hostname, sizeof(hostname)); | |
204 hostname[sizeof(hostname) - 1] = '\0'; | |
205 buf = irc_format(irc, "vvvv:", "USER", g_get_user_name(), hostname, irc->server, | |
206 gc->account->alias && *gc->account->alias ? gc->account->alias : IRC_DEFAULT_ALIAS); | |
207 if (irc_send(irc, buf) < 0) { | |
208 gaim_connection_error(gc, "Error registering with server"); | |
209 return; | |
210 } | |
211 g_free(buf); | |
212 buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc)); | |
213 if (irc_send(irc, buf) < 0) { | |
214 gaim_connection_error(gc, "Error sending nickname"); | |
215 return; | |
216 } | |
217 g_free(buf); | |
218 | |
219 gc->inpa = gaim_input_add(irc->fd, GAIM_INPUT_READ, irc_input_cb, gc); | |
220 } | |
221 | |
222 static void irc_close(GaimConnection *gc) | |
223 { | |
224 struct irc_conn *irc = gc->proto_data; | |
225 | |
226 irc_cmd_quit(irc, "quit", NULL, NULL); | |
227 | |
228 if (gc->inpa) | |
229 g_source_remove(gc->inpa); | |
230 | |
231 g_free(irc->inbuf); | |
232 close(irc->fd); | |
233 if (irc->timer) | |
234 g_source_remove(irc->timer); | |
235 g_hash_table_destroy(irc->cmds); | |
236 g_hash_table_destroy(irc->msgs); | |
237 if (irc->motd) | |
238 g_string_free(irc->motd, TRUE); | |
239 g_free(irc->server); | |
240 g_free(irc); | |
241 } | |
242 | |
243 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags) | |
244 { | |
245 struct irc_conn *irc = gc->proto_data; | |
246 const char *args[2]; | |
247 | |
248 if (*who == '@' || *who == '%' || *who == '+') | |
249 args[0] = who + 1; | |
250 else | |
251 args[0] = who; | |
252 args[1] = what; | |
253 | |
254 if (*what == '/') { | |
255 return irc_parse_cmd(irc, who, what + 1); | |
256 } | |
257 | |
258 irc_cmd_privmsg(irc, "msg", NULL, args); | |
259 return 1; | |
260 } | |
261 | |
262 static void irc_get_info(GaimConnection *gc, const char *who) | |
263 { | |
264 struct irc_conn *irc = gc->proto_data; | |
265 const char *args[1]; | |
266 args[0] = who; | |
267 irc_cmd_whois(irc, "whois", NULL, args); | |
268 } | |
269 | |
270 static void irc_set_away(GaimConnection *gc, const char *state, const char *msg) | |
271 { | |
272 struct irc_conn *irc = gc->proto_data; | |
273 const char *args[1]; | |
2542 | 274 |
2543 if (gc->away) { | 275 if (gc->away) { |
2544 g_free(gc->away); | 276 g_free(gc->away); |
2545 gc->away = NULL; | 277 gc->away = NULL; |
2546 } | 278 } |
2547 | 279 |
2548 if (msg) { | 280 if (msg) |
2549 g_snprintf(buf, sizeof(buf), "AWAY :%s\r\n", msg); | |
2550 gc->away = g_strdup(msg); | 281 gc->away = g_strdup(msg); |
2551 } else | 282 |
2552 g_snprintf(buf, sizeof(buf), "AWAY\r\n"); | 283 args[0] = msg; |
2553 | 284 irc_cmd_away(irc, "away", NULL, args); |
2554 irc_write(idata->fd, buf, strlen(buf)); | 285 } |
2555 } | 286 |
2556 | 287 static void irc_add_buddy(GaimConnection *gc, const char *who) |
2557 static const char * | 288 { |
2558 irc_list_icon(GaimAccount *a, struct buddy *b) | 289 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; |
2559 { | 290 struct irc_buddy *ib = g_new0(struct irc_buddy, 1); |
2560 return "irc"; | 291 ib->name = g_strdup(who); |
2561 } | 292 g_hash_table_insert(irc->buddies, ib->name, ib); |
2562 | 293 } |
2563 static void irc_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) | 294 |
2564 { | 295 static void irc_remove_buddy(GaimConnection *gc, const char *who, const char *group) |
2565 if (b->present == GAIM_BUDDY_OFFLINE) | 296 { |
2566 *se = "offline"; | 297 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; |
2567 } | 298 g_hash_table_remove(irc->buddies, who); |
2568 | 299 } |
2569 static void | 300 |
2570 dcc_chat_connected(gpointer data, gint source, GdkInputCondition condition) | 301 |
2571 { | 302 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond) |
2572 struct dcc_chat *chat = data; | 303 { |
2573 GaimConversation *convo; | 304 GaimConnection *gc = data; |
2574 char buf[128]; | 305 struct irc_conn *irc = gc->proto_data; |
2575 struct sockaddr_in addr; | 306 char *cur, *end; |
2576 int addrlen = sizeof (addr); | |
2577 addr.sin_family = AF_INET; | |
2578 addr.sin_port = htons (chat->port); | |
2579 addr.sin_addr.s_addr = INADDR_ANY; | |
2580 chat->fd = accept (chat->fd, (struct sockaddr *) (&addr), &addrlen); | |
2581 if (!chat->fd) { | |
2582 dcc_chat_cancel (chat); | |
2583 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, | |
2584 chat->nick); | |
2585 g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"), | |
2586 chat->nick); | |
2587 gaim_conversation_write(convo, NULL, buf, -1, | |
2588 WFLAG_SYSTEM, time(NULL)); | |
2589 return; | |
2590 } | |
2591 chat->inpa = | |
2592 gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_in, chat); | |
2593 convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick); | |
2594 g_snprintf (buf, sizeof buf, _("DCC Chat with %s established"), | |
2595 chat->nick); | |
2596 gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL)); | |
2597 gaim_debug(GAIM_DEBUG_INFO, "irc", | |
2598 "Chat with %s established\n", chat->nick); | |
2599 dcc_chat_list = g_slist_append (dcc_chat_list, chat); | |
2600 } | |
2601 #if 0 | |
2602 static void | |
2603 irc_ask_send_file(GaimConnection *gc, char *destsn) { | |
2604 struct irc_data *id = (struct irc_data *)gc->proto_data; | |
2605 struct irc_file_transfer *ift = g_new0(struct irc_file_transfer, 1); | |
2606 char *localip = (char *)malloc(12); | |
2607 | |
2608 if (getlocalip(localip) == -1) { | |
2609 free(localip); | |
2610 return; | |
2611 } | |
2612 | |
2613 ift->type = IFT_SENDFILE_OUT; | |
2614 ift->sn = g_strdup(destsn); | |
2615 ift->gc = gc; | |
2616 snprintf(ift->ip, sizeof(ift->ip), "%s", localip); | |
2617 id->file_transfers = g_slist_append(id->file_transfers, ift); | |
2618 | |
2619 ift->xfer = transfer_out_add(gc, ift->sn); | |
2620 } | |
2621 | |
2622 static struct | |
2623 irc_file_transfer *find_ift_by_xfer(GaimConnection *gc, | |
2624 struct file_transfer *xfer) { | |
2625 | |
2626 GSList *g = ((struct irc_data *)gc->proto_data)->file_transfers; | |
2627 struct irc_file_transfer *f = NULL; | |
2628 | |
2629 while (g) { | |
2630 f = (struct irc_file_transfer *)g->data; | |
2631 if (f->xfer == xfer) | |
2632 break; | |
2633 g = g->next; | |
2634 f = NULL; | |
2635 } | |
2636 | |
2637 return f; | |
2638 } | |
2639 | |
2640 static void | |
2641 irc_file_transfer_data_chunk(GaimConnection *gc, struct file_transfer *xfer, const char *data, int len) { | |
2642 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); | |
2643 guint32 pos; | |
2644 | |
2645 ift->cur += len; | |
2646 pos = htonl(ift->cur); | |
2647 write(ift->fd, (char *)&pos, 4); | |
2648 | |
2649 // FIXME: You should check to verify that they received the data when | |
2650 // you are sending a file ... | |
2651 } | |
2652 | |
2653 static void | |
2654 irc_file_transfer_cancel (GaimConnection *gc, struct file_transfer *xfer) { | |
2655 struct irc_data *id = (struct irc_data *)gc->proto_data; | |
2656 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); | |
2657 | |
2658 printf("Our shit got canceled, yo!\n"); | |
2659 | |
2660 /* Remove the FT from our list of transfers */ | |
2661 id->file_transfers = g_slist_remove(id->file_transfers, ift); | |
2662 | |
2663 gaim_input_remove(ift->watcher); | |
2664 | |
2665 /* Close our FT because we're done */ | |
2666 close(ift->fd); | |
2667 | |
2668 g_free(ift->sn); | |
2669 g_free(ift->name); | |
2670 | |
2671 g_free(ift); | |
2672 } | |
2673 | |
2674 static void | |
2675 irc_file_transfer_done(GaimConnection *gc, struct file_transfer *xfer) { | |
2676 struct irc_data *id = (struct irc_data *)gc->proto_data; | |
2677 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); | |
2678 | |
2679 | |
2680 printf("Our shit be done, yo.\n"); | |
2681 | |
2682 /* Remove the FT from our list of transfers */ | |
2683 id->file_transfers = g_slist_remove(id->file_transfers, ift); | |
2684 | |
2685 gaim_input_remove(ift->watcher); | |
2686 | |
2687 /* Close our FT because we're done */ | |
2688 close(ift->fd); | |
2689 | |
2690 g_free(ift->sn); | |
2691 g_free(ift->name); | |
2692 | |
2693 g_free(ift); | |
2694 } | |
2695 | |
2696 static void | |
2697 irc_file_transfer_out (GaimConnection *gc, struct file_transfer *xfer, const char *name, int totfiles, int totsize) { | |
2698 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); | |
2699 struct sockaddr_in addr; | |
2700 char buf[IRC_BUF_LEN]; | |
2701 int len; | 307 int len; |
2702 | 308 |
2703 | 309 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { |
2704 ift->fd = socket (AF_INET, SOCK_STREAM, 0); | 310 irc->inbuflen += IRC_INITIAL_BUFSIZE; |
2705 addr.sin_family = AF_INET; | 311 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); |
2706 addr.sin_port = 0; | 312 } |
2707 addr.sin_addr.s_addr = INADDR_ANY; | 313 |
2708 bind (ift->fd, (struct sockaddr *) &addr, sizeof(addr)); | 314 if ((len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0) { |
2709 listen(ift->fd, 1); | 315 gaim_connection_error(gc, "Read error"); |
2710 | 316 return; |
2711 len = sizeof(addr); | 317 } |
2712 getsockname (ift->fd, (struct sockaddr *) &addr, &len); | 318 irc->inbufused += len; |
2713 | 319 irc->inbuf[irc->inbufused] = '\0'; |
2714 ift->port = ntohs(addr.sin_port); | 320 |
2715 | 321 for (cur = irc->inbuf; cur < irc->inbuf + irc->inbufused && (end = strstr(cur, "\r\n")); cur = end + 2) { |
2716 ift->watcher = gaim_input_add (ift->fd, GAIM_INPUT_READ, dcc_send_callback, ift); | 322 *end = '\0'; |
2717 printf("watcher is %d\n", ift->watcher); | 323 irc_parse_msg(irc, cur); |
2718 | 324 } |
2719 snprintf(buf, sizeof(buf), "\001DCC SEND %s %s %d %d\001\n", name, ift->ip, ift->port, totsize); | 325 if (cur != irc->inbuf + irc->inbufused) { /* leftover */ |
2720 printf("Trying: %s\n", buf); | 326 irc->inbufused -= (cur - irc->inbuf); |
2721 irc_send_im (gc, ift->sn, buf, -1, 0); | 327 memmove(irc->inbuf, cur, irc->inbufused); |
2722 } | 328 } else { |
2723 | 329 irc->inbufused = 0; |
2724 static void | 330 } |
2725 irc_file_transfer_in(GaimConnection *gc, | 331 } |
2726 struct file_transfer *xfer, int offset) { | 332 |
2727 | 333 static void irc_chat_join (GaimConnection *gc, GHashTable *data) |
2728 struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer); | 334 { |
2729 | 335 struct irc_conn *irc = gc->proto_data; |
2730 ift->xfer = xfer; | 336 const char *args[2]; |
2731 gaim_proxy_connect(gc->account, ift->ip, ift->port, dcc_recv_callback, ift); | 337 |
2732 } | 338 args[0] = g_hash_table_lookup(data, "channel"); |
2733 #endif | 339 args[1] = g_hash_table_lookup(data, "password"); |
2734 | 340 irc_cmd_join(irc, "join", NULL, args); |
2735 static void | 341 } |
2736 irc_ctcp_clientinfo(GaimConnection *gc, const char *who) | 342 |
2737 { | 343 static void irc_chat_invite(GaimConnection *gc, int id, const char *message, const char *name) |
2738 char buf[IRC_BUF_LEN]; | 344 { |
2739 | 345 struct irc_conn *irc = gc->proto_data; |
2740 snprintf (buf, sizeof buf, "\001CLIENTINFO\001"); | 346 GaimConversation *convo = gaim_find_chat(gc, id); |
2741 irc_send_privmsg(gc, who, buf, FALSE); | 347 const char *args[2]; |
2742 } | 348 |
2743 | 349 if (!convo) { |
2744 static void | 350 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n"); |
2745 irc_ctcp_userinfo(GaimConnection *gc, const char *who) | 351 return; |
2746 { | 352 } |
2747 char buf[IRC_BUF_LEN]; | 353 args[0] = name; |
2748 | 354 args[1] = gaim_conversation_get_name(convo); |
2749 snprintf (buf, sizeof buf, "\001USERINFO\001"); | 355 irc_cmd_invite(irc, "invite", gaim_conversation_get_name(convo), args); |
2750 irc_send_privmsg(gc, who, buf, FALSE); | 356 } |
2751 } | 357 |
2752 | 358 |
2753 static void | 359 static void irc_chat_leave (GaimConnection *gc, int id) |
2754 irc_ctcp_version(GaimConnection *gc, const char *who) | 360 { |
2755 { | 361 struct irc_conn *irc = gc->proto_data; |
2756 char buf[IRC_BUF_LEN]; | 362 GaimConversation *convo = gaim_find_chat(gc, id); |
2757 | 363 const char *args[2]; |
2758 snprintf (buf, sizeof buf, "\001VERSION\001"); | 364 |
2759 irc_send_privmsg(gc, who, buf, FALSE); | 365 if (!convo) |
2760 } | 366 return; |
2761 | 367 |
2762 static void | 368 args[0] = gaim_conversation_get_name(convo); |
2763 irc_ctcp_ping(GaimConnection *gc, const char *who) | 369 args[1] = NULL; |
2764 { | 370 irc_cmd_part(irc, "part", gaim_conversation_get_name(convo), args); |
2765 char buf[IRC_BUF_LEN]; | 371 serv_got_chat_left(gc, id); |
2766 struct timeval now; | 372 } |
2767 | 373 |
2768 gettimeofday(&now, NULL); | 374 static int irc_chat_send(GaimConnection *gc, int id, const char *what) |
2769 g_snprintf (buf, sizeof(buf), "\001PING %lu.%.03lu\001", now.tv_sec, | 375 { |
2770 now.tv_usec/1000); | 376 struct irc_conn *irc = gc->proto_data; |
2771 irc_send_privmsg(gc, who, buf, FALSE); | 377 GaimConversation *convo = gaim_find_chat(gc, id); |
2772 } | 378 const char *args[2]; |
2773 | 379 |
2774 static void | 380 if (!convo) { |
2775 irc_send_notice(GaimConnection *gc, char *who, char *what) | 381 gaim_debug(GAIM_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n"); |
2776 { | 382 return -EINVAL; |
2777 char buf[IRC_BUF_LEN], *intl; | 383 } |
2778 struct irc_data *id = gc->proto_data; | 384 |
2779 int len; | 385 if (*what == '/') { |
2780 | 386 return irc_parse_cmd(irc, convo->name, what + 1); |
2781 intl = irc_send_convert(gc, what, 501, &len); | 387 } |
2782 g_snprintf(buf, sizeof(buf), "NOTICE %s :%s\r\n", who, intl); | 388 |
2783 g_free(intl); | 389 args[0] = convo->name; |
2784 irc_write(id->fd, buf, strlen(buf)); | 390 args[1] = what; |
2785 } | 391 |
2786 | 392 irc_cmd_privmsg(irc, "msg", NULL, args); |
2787 /* Don't call this guy with fragment = 1 for anything but straight | 393 serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, what, time(NULL)); |
2788 * up privmsgs. (no CTCP/whatever) It's still dangerous for CTCPs | 394 return 0; |
2789 * (it might not include the trailing \001), but I think this behavior | 395 } |
2790 * is generally better than not fragmenting at all on lots of our | 396 |
2791 * packets. */ | 397 static guint irc_nick_hash(const char *nick) |
2792 /* From RFC2812: | 398 { |
2793 * IRC messages are always lines of characters terminated with a CR-LF | 399 char *lc; |
2794 * (Carriage Return - Line Feed) pair, and these messages SHALL NOT | 400 guint bucket; |
2795 * exceed 512 characters in length, counting all characters including | 401 |
2796 * the trailing CR-LF. Thus, there are 510 characters maximum allowed | 402 lc = g_utf8_strdown(nick, -1); |
2797 * for the command and its parameters. */ | 403 bucket = g_str_hash(lc); |
2798 /* So apparently that includes all the inter-server crap, which is up | 404 g_free(lc); |
2799 * to NINETY-THREE chars on dancer, which seems to be a pretty liberal | 405 |
2800 * ircd. My rough calculation for now is ":<nick>!~<user>@<host> ", | 406 return bucket; |
2801 * where <host> is a max of an (uncalculated) 63 chars. Thanks to | 407 } |
2802 * trelane and #freenode for giving a hand here. */ | 408 |
2803 static void | 409 static gboolean irc_nick_equal(const char *nick1, const char *nick2) |
2804 irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment) | 410 { |
2805 { | 411 return (gaim_utf8_strcasecmp(nick1, nick2) == 0); |
2806 char buf[IRC_BUF_LEN], *intl; | 412 } |
2807 struct irc_data *id = gc->proto_data; | 413 |
2808 /* 512 - 12 (for PRIVMSG" "" :""\r\n") - namelen - nicklen - 68 */ | 414 static void irc_buddy_free(struct irc_buddy *ib) |
2809 int nicklen = (gc->account->alias && strlen(gc->account->alias)) ? strlen(gc->account->alias) : 4; | 415 { |
2810 int max = 444 - strlen(who) - strlen(g_get_user_name()) - nicklen; | 416 g_free(ib->name); |
2811 | 417 g_free(ib); |
2812 int len; | |
2813 | |
2814 do { | |
2815 /* the \001 on CTCPs may cause a problem here for some | |
2816 * charsets, but probably not ones people use for IRC. */ | |
2817 intl = irc_send_convert(gc, what, max, &len); | |
2818 g_snprintf(buf, sizeof(buf), "PRIVMSG %s :%s\r\n", who, intl); | |
2819 g_free(intl); | |
2820 irc_write(id->fd, buf, strlen(buf)); | |
2821 what += len; | |
2822 } while (fragment && strlen(what)); | |
2823 } | |
2824 | |
2825 static void | |
2826 irc_start_chat(GaimConnection *gc, const char *who) { | |
2827 struct dcc_chat *chat; | |
2828 int len; | |
2829 struct sockaddr_in addr; | |
2830 char buf[IRC_BUF_LEN]; | |
2831 const char *ip; | |
2832 | |
2833 /* Create a socket */ | |
2834 chat = g_new0 (struct dcc_chat, 1); | |
2835 chat->fd = socket (AF_INET, SOCK_STREAM, 0); | |
2836 chat->gc = gc; | |
2837 g_snprintf (chat->nick, sizeof (chat->nick), "%s", who); | |
2838 if (chat->fd < 0) { | |
2839 dcc_chat_cancel (chat); | |
2840 return; | |
2841 } | |
2842 addr.sin_family = AF_INET; | |
2843 addr.sin_port = 0; | |
2844 addr.sin_addr.s_addr = INADDR_ANY; | |
2845 bind (chat->fd, (struct sockaddr *) &addr, sizeof (addr)); | |
2846 listen (chat->fd, 1); | |
2847 len = sizeof (addr); | |
2848 getsockname (chat->fd, (struct sockaddr *) &addr, &len); | |
2849 chat->port = ntohs (addr.sin_port); | |
2850 | |
2851 ip = gaim_xfers_get_ip_for_account(gaim_connection_get_account(gc)); | |
2852 strncpy(chat->ip_address, ip, INET6_ADDRSTRLEN); | |
2853 | |
2854 chat->inpa = | |
2855 gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_connected, | |
2856 chat); | |
2857 g_snprintf (buf, sizeof buf, "\001DCC CHAT chat %s %d\001\n", | |
2858 chat->ip_address, chat->port); | |
2859 irc_send_im (gc, who, buf, -1, 0); | |
2860 } | |
2861 | |
2862 static void | |
2863 irc_get_info(GaimConnection *gc, const char *who) | |
2864 { | |
2865 struct irc_data *idata = gc->proto_data; | |
2866 char buf[IRC_BUF_LEN]; | |
2867 | |
2868 if (*who == '@') | |
2869 who++; | |
2870 if (*who == '%') | |
2871 who++; | |
2872 if (*who == '+') | |
2873 who++; | |
2874 | |
2875 g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", who); | |
2876 irc_write(idata->fd, buf, strlen(buf)); | |
2877 } | |
2878 | |
2879 static GList * | |
2880 irc_buddy_menu(GaimConnection *gc, const char *who) | |
2881 { | |
2882 GList *m = NULL; | |
2883 struct proto_buddy_menu *pbm; | |
2884 | |
2885 pbm = g_new0(struct proto_buddy_menu, 1); | |
2886 pbm->label = _("DCC Chat"); | |
2887 pbm->callback = irc_start_chat; | |
2888 pbm->gc = gc; | |
2889 m = g_list_append(m, pbm); | |
2890 /* | |
2891 pbm = g_new0(struct proto_buddy_menu, 1); | |
2892 pbm->label = _("DCC Send"); | |
2893 pbm->callback = irc_ask_send_file; | |
2894 pbm->gc = gc; | |
2895 m = g_list_append(m, pbm); | |
2896 */ | |
2897 | |
2898 pbm = g_new0(struct proto_buddy_menu, 1); | |
2899 pbm->label = _("CTCP ClientInfo"); | |
2900 pbm->callback = irc_ctcp_clientinfo; | |
2901 pbm->gc = gc; | |
2902 m = g_list_append(m, pbm); | |
2903 | |
2904 pbm = g_new0(struct proto_buddy_menu, 1); | |
2905 pbm->label = _("CTCP UserInfo"); | |
2906 pbm->callback = irc_ctcp_userinfo; | |
2907 pbm->gc = gc; | |
2908 m = g_list_append(m, pbm); | |
2909 | |
2910 pbm = g_new0(struct proto_buddy_menu, 1); | |
2911 pbm->label = _("CTCP Version"); | |
2912 pbm->callback = irc_ctcp_version; | |
2913 pbm->gc = gc; | |
2914 m = g_list_append(m, pbm); | |
2915 | |
2916 pbm = g_new0(struct proto_buddy_menu, 1); | |
2917 pbm->label = _("CTCP Ping"); | |
2918 pbm->callback = irc_ctcp_ping; | |
2919 pbm->gc = gc; | |
2920 m = g_list_append(m, pbm); | |
2921 | |
2922 return m; | |
2923 } | 418 } |
2924 | 419 |
2925 static GaimPluginProtocolInfo prpl_info = | 420 static GaimPluginProtocolInfo prpl_info = |
2926 { | 421 { |
2927 GAIM_PROTO_IRC, | 422 GAIM_PROTO_IRC, |
2928 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL, | 423 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL, |
2929 NULL, | 424 NULL, |
2930 NULL, | 425 NULL, |
2931 irc_list_icon, | 426 irc_blist_icon, |
2932 irc_list_emblems, | 427 irc_blist_emblems, |
2933 NULL, | 428 NULL, |
2934 NULL, | 429 NULL, |
2935 irc_away_states, | 430 irc_away_states, |
2936 NULL, | 431 NULL, |
2937 irc_buddy_menu, | 432 NULL, /*irc_buddy_menu,*/ |
2938 irc_chat_info, | 433 irc_chat_join_info, |
2939 irc_login, | 434 irc_login, |
2940 irc_close, | 435 irc_close, |
2941 irc_send_im, | 436 irc_im_send, |
2942 NULL, | 437 NULL, |
2943 NULL, | 438 NULL, |
2944 irc_get_info, | 439 irc_get_info, |
2945 irc_set_away, | 440 irc_set_away, |
2946 NULL, | 441 NULL, |
2957 NULL, | 452 NULL, |
2958 NULL, | 453 NULL, |
2959 NULL, | 454 NULL, |
2960 NULL, | 455 NULL, |
2961 NULL, | 456 NULL, |
2962 irc_join_chat, | 457 irc_chat_join, |
2963 irc_chat_invite, | 458 irc_chat_invite, |
2964 irc_chat_leave, | 459 irc_chat_leave, |
2965 NULL, | 460 NULL, |
2966 irc_chat_send, | 461 irc_chat_send, |
2967 NULL, | 462 NULL, |
2970 NULL, | 465 NULL, |
2971 NULL, | 466 NULL, |
2972 NULL, | 467 NULL, |
2973 NULL, | 468 NULL, |
2974 NULL, | 469 NULL, |
2975 irc_convo_closed, | 470 NULL, /*irc_convo_closed,*/ |
2976 NULL | 471 NULL |
2977 }; | 472 }; |
2978 | 473 |
2979 static GaimPluginInfo info = | 474 static GaimPluginInfo info = |
2980 { | 475 { |
2986 GAIM_PRIORITY_DEFAULT, /**< priority */ | 481 GAIM_PRIORITY_DEFAULT, /**< priority */ |
2987 | 482 |
2988 "prpl-irc", /**< id */ | 483 "prpl-irc", /**< id */ |
2989 "IRC", /**< name */ | 484 "IRC", /**< name */ |
2990 VERSION, /**< version */ | 485 VERSION, /**< version */ |
2991 /** summary */ | 486 N_("IRC Protocol Plugin"), /** summary */ |
2992 N_("IRC Protocol Plugin"), | 487 N_("The IRC Protocol Plugin that Sucks Less"), /** description */ |
2993 /** description */ | |
2994 N_("IRC Protocol Plugin"), | |
2995 NULL, /**< author */ | 488 NULL, /**< author */ |
2996 WEBSITE, /**< homepage */ | 489 WEBSITE, /**< homepage */ |
2997 | 490 |
2998 NULL, /**< load */ | 491 NULL, /**< load */ |
2999 NULL, /**< unload */ | 492 NULL, /**< unload */ |
3001 | 494 |
3002 NULL, /**< ui_info */ | 495 NULL, /**< ui_info */ |
3003 &prpl_info /**< extra_info */ | 496 &prpl_info /**< extra_info */ |
3004 }; | 497 }; |
3005 | 498 |
3006 static void | 499 static void _init_plugin(GaimPlugin *plugin) |
3007 init_plugin(GaimPlugin *plugin) | |
3008 { | 500 { |
3009 GaimAccountUserSplit *split; | 501 GaimAccountUserSplit *split; |
3010 GaimAccountOption *option; | 502 GaimAccountOption *option; |
3011 | 503 |
3012 split = gaim_account_user_split_new(_("Server"), DEFAULT_SERVER, '@'); | 504 split = gaim_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@'); |
3013 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); | 505 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); |
3014 | 506 |
3015 | 507 option = gaim_account_option_int_new(_("Port:"), "port", IRC_DEFAULT_PORT); |
3016 option = gaim_account_option_int_new(_("Port"), "port", 6667); | 508 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); |
3017 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, | 509 |
3018 option); | 510 option = gaim_account_option_string_new(_("Encoding"), "encoding", IRC_DEFAULT_CHARSET); |
3019 | 511 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); |
3020 option = gaim_account_option_string_new(_("Encoding"), "charset", | 512 |
3021 "ISO-8859-1"); | 513 _irc_plugin = plugin; |
3022 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, | 514 } |
3023 option); | 515 |
3024 | 516 GAIM_INIT_PLUGIN(irc, _init_plugin, info); |
3025 my_protocol = plugin; | |
3026 } | |
3027 | |
3028 GAIM_INIT_PLUGIN(irc, init_plugin, info); |