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);