libpurple/protocols/irc/irc.c

changeset 16238
33bf2fd32108
parent 12752
612eb998c9b5
parent 16168
5a602ddb698f
equal deleted inserted replaced
13071:b98e72d4089a 16238:33bf2fd32108
1 /**
2 * @file irc.c
3 *
4 * purple
5 *
6 * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
7 * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
8 * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com>
9 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26 #include "internal.h"
27
28 #include "accountopt.h"
29 #include "blist.h"
30 #include "conversation.h"
31 #include "debug.h"
32 #include "notify.h"
33 #include "prpl.h"
34 #include "plugin.h"
35 #include "util.h"
36 #include "version.h"
37
38 #include "irc.h"
39
40 #define PING_TIMEOUT 60
41
42 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string);
43
44 static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b);
45 static GList *irc_status_types(PurpleAccount *account);
46 static GList *irc_actions(PurplePlugin *plugin, gpointer context);
47 /* static GList *irc_chat_info(PurpleConnection *gc); */
48 static void irc_login(PurpleAccount *account);
49 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
50 static void irc_login_cb(gpointer data, gint source, const gchar *error_message);
51 static void irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error, gpointer data);
52 static void irc_close(PurpleConnection *gc);
53 static int irc_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags);
54 static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags);
55 static void irc_chat_join (PurpleConnection *gc, GHashTable *data);
56 static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond);
57 static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond);
58
59 static guint irc_nick_hash(const char *nick);
60 static gboolean irc_nick_equal(const char *nick1, const char *nick2);
61 static void irc_buddy_free(struct irc_buddy *ib);
62
63 PurplePlugin *_irc_plugin = NULL;
64
65 static const char *status_chars = "@+%&";
66
67 static void irc_view_motd(PurplePluginAction *action)
68 {
69 PurpleConnection *gc = (PurpleConnection *) action->context;
70 struct irc_conn *irc;
71 char *title;
72
73 if (gc == NULL || gc->proto_data == NULL) {
74 purple_debug(PURPLE_DEBUG_ERROR, "irc", "got MOTD request for NULL gc\n");
75 return;
76 }
77 irc = gc->proto_data;
78 if (irc->motd == NULL) {
79 purple_notify_error(gc, _("Error displaying MOTD"), _("No MOTD available"),
80 _("There is no MOTD associated with this connection."));
81 return;
82 }
83 title = g_strdup_printf(_("MOTD for %s"), irc->server);
84 purple_notify_formatted(gc, title, title, NULL, irc->motd->str, NULL, NULL);
85 g_free(title);
86 }
87
88 static int do_send(struct irc_conn *irc, const char *buf, gsize len)
89 {
90 int ret;
91
92 if (irc->gsc) {
93 ret = purple_ssl_write(irc->gsc, buf, len);
94 } else {
95 ret = write(irc->fd, buf, len);
96 }
97
98 return ret;
99 }
100
101 static int irc_send_raw(PurpleConnection *gc, const char *buf, int len)
102 {
103 struct irc_conn *irc = (struct irc_conn*)gc->proto_data;
104 return do_send(irc, buf, len);
105 }
106
107 static void
108 irc_send_cb(gpointer data, gint source, PurpleInputCondition cond)
109 {
110 struct irc_conn *irc = data;
111 int ret, writelen;
112
113 writelen = purple_circ_buffer_get_max_read(irc->outbuf);
114
115 if (writelen == 0) {
116 purple_input_remove(irc->writeh);
117 irc->writeh = 0;
118 return;
119 }
120
121 ret = do_send(irc, irc->outbuf->outptr, writelen);
122
123 if (ret < 0 && errno == EAGAIN)
124 return;
125 else if (ret <= 0) {
126 purple_connection_error(purple_account_get_connection(irc->account),
127 _("Server has disconnected"));
128 return;
129 }
130
131 purple_circ_buffer_mark_read(irc->outbuf, ret);
132
133 #if 0
134 /* We *could* try to write more if we wrote it all */
135 if (ret == write_len) {
136 irc_send_cb(data, source, cond);
137 }
138 #endif
139 }
140
141 int irc_send(struct irc_conn *irc, const char *buf)
142 {
143 int ret, buflen;
144 char *tosend= g_strdup(buf);
145
146 purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend);
147 if (tosend == NULL)
148 return 0;
149
150 buflen = strlen(tosend);
151
152
153 /* If we're not buffering writes, try to send immediately */
154 if (!irc->writeh)
155 ret = do_send(irc, tosend, buflen);
156 else {
157 ret = -1;
158 errno = EAGAIN;
159 }
160
161 /* purple_debug(PURPLE_DEBUG_MISC, "irc", "sent%s: %s",
162 irc->gsc ? " (ssl)" : "", tosend); */
163 if (ret <= 0 && errno != EAGAIN) {
164 purple_connection_error(purple_account_get_connection(irc->account),
165 _("Server has disconnected"));
166 } else if (ret < buflen) {
167 if (ret < 0)
168 ret = 0;
169 if (!irc->writeh)
170 irc->writeh = purple_input_add(
171 irc->gsc ? irc->gsc->fd : irc->fd,
172 PURPLE_INPUT_WRITE, irc_send_cb, irc);
173 purple_circ_buffer_append(irc->outbuf, tosend + ret,
174 buflen - ret);
175 }
176 g_free(tosend);
177 return ret;
178 }
179
180 /* XXX I don't like messing directly with these buddies */
181 gboolean irc_blist_timeout(struct irc_conn *irc)
182 {
183 GString *string = g_string_sized_new(512);
184 char *list, *buf;
185
186 g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string);
187
188 list = g_string_free(string, FALSE);
189 if (!list || !strlen(list)) {
190 g_free(list);
191 return TRUE;
192 }
193
194 buf = irc_format(irc, "vn", "ISON", list);
195 g_free(list);
196 irc_send(irc, buf);
197 g_free(buf);
198
199 return TRUE;
200 }
201
202 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string)
203 {
204 ib->flag = FALSE;
205 g_string_append_printf(string, "%s ", name);
206 }
207
208 static void irc_ison_one(struct irc_conn *irc, struct irc_buddy *ib)
209 {
210 char *buf;
211
212 ib->flag = FALSE;
213 buf = irc_format(irc, "vn", "ISON", ib->name);
214 irc_send(irc, buf);
215 g_free(buf);
216 }
217
218
219 static const char *irc_blist_icon(PurpleAccount *a, PurpleBuddy *b)
220 {
221 return "irc";
222 }
223
224 static GList *irc_status_types(PurpleAccount *account)
225 {
226 PurpleStatusType *type;
227 GList *types = NULL;
228
229 type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE);
230 types = g_list_append(types, type);
231
232 type = purple_status_type_new_with_attrs(
233 PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
234 "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING),
235 NULL);
236 types = g_list_append(types, type);
237
238 type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE);
239 types = g_list_append(types, type);
240
241 return types;
242 }
243
244 static GList *irc_actions(PurplePlugin *plugin, gpointer context)
245 {
246 GList *list = NULL;
247 PurplePluginAction *act = NULL;
248
249 act = purple_plugin_action_new(_("View MOTD"), irc_view_motd);
250 list = g_list_append(list, act);
251
252 return list;
253 }
254
255 static GList *irc_chat_join_info(PurpleConnection *gc)
256 {
257 GList *m = NULL;
258 struct proto_chat_entry *pce;
259
260 pce = g_new0(struct proto_chat_entry, 1);
261 pce->label = _("_Channel:");
262 pce->identifier = "channel";
263 pce->required = TRUE;
264 m = g_list_append(m, pce);
265
266 pce = g_new0(struct proto_chat_entry, 1);
267 pce->label = _("_Password:");
268 pce->identifier = "password";
269 pce->secret = TRUE;
270 m = g_list_append(m, pce);
271
272 return m;
273 }
274
275 static GHashTable *irc_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
276 {
277 GHashTable *defaults;
278
279 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
280
281 if (chat_name != NULL)
282 g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
283
284 return defaults;
285 }
286
287 static void irc_login(PurpleAccount *account)
288 {
289 PurpleConnection *gc;
290 struct irc_conn *irc;
291 char **userparts;
292 const char *username = purple_account_get_username(account);
293
294 gc = purple_account_get_connection(account);
295 gc->flags |= PURPLE_CONNECTION_NO_NEWLINES;
296
297 if (strpbrk(username, " \t\v\r\n") != NULL) {
298 purple_connection_error(gc, _("IRC nicks may not contain whitespace"));
299 return;
300 }
301
302 gc->proto_data = irc = g_new0(struct irc_conn, 1);
303 irc->fd = -1;
304 irc->account = account;
305 irc->outbuf = purple_circ_buffer_new(512);
306
307 userparts = g_strsplit(username, "@", 2);
308 purple_connection_set_display_name(gc, userparts[0]);
309 irc->server = g_strdup(userparts[1]);
310 g_strfreev(userparts);
311
312 irc->buddies = g_hash_table_new_full((GHashFunc)irc_nick_hash, (GEqualFunc)irc_nick_equal,
313 NULL, (GDestroyNotify)irc_buddy_free);
314 irc->cmds = g_hash_table_new(g_str_hash, g_str_equal);
315 irc_cmd_table_build(irc);
316 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal);
317 irc_msg_table_build(irc);
318
319 purple_connection_update_progress(gc, _("Connecting"), 1, 2);
320
321 if (purple_account_get_bool(account, "ssl", FALSE)) {
322 if (purple_ssl_is_supported()) {
323 irc->gsc = purple_ssl_connect(account, irc->server,
324 purple_account_get_int(account, "port", IRC_DEFAULT_SSL_PORT),
325 irc_login_cb_ssl, irc_ssl_connect_failure, gc);
326 } else {
327 purple_connection_error(gc, _("SSL support unavailable"));
328 return;
329 }
330 }
331
332 if (!irc->gsc) {
333
334 if (purple_proxy_connect(gc, account, irc->server,
335 purple_account_get_int(account, "port", IRC_DEFAULT_PORT),
336 irc_login_cb, gc) == NULL)
337 {
338 purple_connection_error(gc, _("Couldn't create socket"));
339 return;
340 }
341 }
342 }
343
344 static gboolean do_login(PurpleConnection *gc) {
345 char *buf;
346 char hostname[256];
347 const char *username, *realname;
348 struct irc_conn *irc = gc->proto_data;
349 const char *pass = purple_connection_get_password(gc);
350
351 if (pass && *pass) {
352 buf = irc_format(irc, "vv", "PASS", pass);
353 if (irc_send(irc, buf) < 0) {
354 /* purple_connection_error(gc, "Error sending password"); */
355 g_free(buf);
356 return FALSE;
357 }
358 g_free(buf);
359 }
360
361 gethostname(hostname, sizeof(hostname));
362 hostname[sizeof(hostname) - 1] = '\0';
363 username = purple_account_get_string(irc->account, "username", "");
364 realname = purple_account_get_string(irc->account, "realname", "");
365 buf = irc_format(irc, "vvvv:", "USER", strlen(username) ? username : g_get_user_name(), hostname, irc->server,
366 strlen(realname) ? realname : IRC_DEFAULT_ALIAS);
367 if (irc_send(irc, buf) < 0) {
368 /* purple_connection_error(gc, "Error registering with server");*/
369 g_free(buf);
370 return FALSE;
371 }
372 g_free(buf);
373 buf = irc_format(irc, "vn", "NICK", purple_connection_get_display_name(gc));
374 if (irc_send(irc, buf) < 0) {
375 /* purple_connection_error(gc, "Error sending nickname");*/
376 g_free(buf);
377 return FALSE;
378 }
379 g_free(buf);
380
381 irc->recv_time = time(NULL);
382
383 return TRUE;
384 }
385
386 static void irc_login_cb_ssl(gpointer data, PurpleSslConnection *gsc,
387 PurpleInputCondition cond)
388 {
389 PurpleConnection *gc = data;
390
391 if (do_login(gc)) {
392 purple_ssl_input_add(gsc, irc_input_cb_ssl, gc);
393 }
394 }
395
396 static void irc_login_cb(gpointer data, gint source, const gchar *error_message)
397 {
398 PurpleConnection *gc = data;
399 struct irc_conn *irc = gc->proto_data;
400
401 if (source < 0) {
402 purple_connection_error(gc, _("Couldn't connect to host"));
403 return;
404 }
405
406 irc->fd = source;
407
408 if (do_login(gc)) {
409 gc->inpa = purple_input_add(irc->fd, PURPLE_INPUT_READ, irc_input_cb, gc);
410 }
411 }
412
413 static void
414 irc_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
415 gpointer data)
416 {
417 PurpleConnection *gc = data;
418 struct irc_conn *irc = gc->proto_data;
419
420 irc->gsc = NULL;
421
422 switch(error) {
423 case PURPLE_SSL_CONNECT_FAILED:
424 purple_connection_error(gc, _("Connection Failed"));
425 break;
426 case PURPLE_SSL_HANDSHAKE_FAILED:
427 purple_connection_error(gc, _("SSL Handshake Failed"));
428 break;
429 }
430 }
431
432 static void irc_close(PurpleConnection *gc)
433 {
434 struct irc_conn *irc = gc->proto_data;
435
436 if (irc == NULL)
437 return;
438
439 if (irc->gsc || (irc->fd >= 0))
440 irc_cmd_quit(irc, "quit", NULL, NULL);
441
442 if (gc->inpa)
443 purple_input_remove(gc->inpa);
444
445 g_free(irc->inbuf);
446 if (irc->gsc) {
447 purple_ssl_close(irc->gsc);
448 } else if (irc->fd >= 0) {
449 close(irc->fd);
450 }
451 if (irc->timer)
452 purple_timeout_remove(irc->timer);
453 g_hash_table_destroy(irc->cmds);
454 g_hash_table_destroy(irc->msgs);
455 g_hash_table_destroy(irc->buddies);
456 if (irc->motd)
457 g_string_free(irc->motd, TRUE);
458 g_free(irc->server);
459
460 if (irc->writeh)
461 purple_input_remove(irc->writeh);
462
463 purple_circ_buffer_destroy(irc->outbuf);
464
465 g_free(irc->mode_chars);
466
467 g_free(irc);
468 }
469
470 static int irc_im_send(PurpleConnection *gc, const char *who, const char *what, PurpleMessageFlags flags)
471 {
472 struct irc_conn *irc = gc->proto_data;
473 char *plain;
474 const char *args[2];
475
476 if (strchr(status_chars, *who) != NULL)
477 args[0] = who + 1;
478 else
479 args[0] = who;
480
481 purple_markup_html_to_xhtml(what, NULL, &plain);
482 args[1] = plain;
483
484 irc_cmd_privmsg(irc, "msg", NULL, args);
485 g_free(plain);
486 return 1;
487 }
488
489 static void irc_get_info(PurpleConnection *gc, const char *who)
490 {
491 struct irc_conn *irc = gc->proto_data;
492 const char *args[2];
493 args[0] = who;
494 args[1] = NULL;
495 irc_cmd_whois(irc, "whois", NULL, args);
496 }
497
498 static void irc_set_status(PurpleAccount *account, PurpleStatus *status)
499 {
500 PurpleConnection *gc = purple_account_get_connection(account);
501 struct irc_conn *irc;
502 const char *args[1];
503 const char *status_id = purple_status_get_id(status);
504
505 g_return_if_fail(gc != NULL);
506 irc = gc->proto_data;
507
508 if (!purple_status_is_active(status))
509 return;
510
511 args[0] = NULL;
512
513 if (!strcmp(status_id, "away")) {
514 args[0] = purple_status_get_attr_string(status, "message");
515 if ((args[0] == NULL) || (*args[0] == '\0'))
516 args[0] = _("Away");
517 irc_cmd_away(irc, "away", NULL, args);
518 } else if (!strcmp(status_id, "available")) {
519 irc_cmd_away(irc, "back", NULL, args);
520 }
521 }
522
523 static void irc_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
524 {
525 struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
526 struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
527 ib->name = g_strdup(buddy->name);
528 g_hash_table_insert(irc->buddies, ib->name, ib);
529
530 /* if the timer isn't set, this is during signon, so we don't want to flood
531 * ourself off with ISON's, so we don't, but after that we want to know when
532 * someone's online asap */
533 if (irc->timer)
534 irc_ison_one(irc, ib);
535 }
536
537 static void irc_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group)
538 {
539 struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
540 g_hash_table_remove(irc->buddies, buddy->name);
541 }
542
543 static void read_input(struct irc_conn *irc, int len)
544 {
545 char *cur, *end;
546
547 irc->inbufused += len;
548 irc->inbuf[irc->inbufused] = '\0';
549
550 cur = irc->inbuf;
551
552 /* This is a hack to work around the fact that marv gets messages
553 * with null bytes in them while using some weird irc server at work
554 */
555 while ((cur < (irc->inbuf + irc->inbufused)) && !*cur)
556 cur++;
557
558 while (cur < irc->inbuf + irc->inbufused &&
559 ((end = strstr(cur, "\r\n")) || (end = strstr(cur, "\n")))) {
560 int step = (*end == '\r' ? 2 : 1);
561 *end = '\0';
562 irc_parse_msg(irc, cur);
563 cur = end + step;
564 }
565 if (cur != irc->inbuf + irc->inbufused) { /* leftover */
566 irc->inbufused -= (cur - irc->inbuf);
567 memmove(irc->inbuf, cur, irc->inbufused);
568 } else {
569 irc->inbufused = 0;
570 }
571 }
572
573 static void irc_input_cb_ssl(gpointer data, PurpleSslConnection *gsc,
574 PurpleInputCondition cond)
575 {
576
577 PurpleConnection *gc = data;
578 struct irc_conn *irc = gc->proto_data;
579 int len;
580
581 if(!g_list_find(purple_connections_get_all(), gc)) {
582 purple_ssl_close(gsc);
583 return;
584 }
585
586 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
587 irc->inbuflen += IRC_INITIAL_BUFSIZE;
588 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
589 }
590
591 len = purple_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1);
592
593 if (len < 0 && errno == EAGAIN) {
594 /* Try again later */
595 return;
596 } else if (len < 0) {
597 purple_connection_error(gc, _("Read error"));
598 return;
599 } else if (len == 0) {
600 purple_connection_error(gc, _("Server has disconnected"));
601 return;
602 }
603
604 read_input(irc, len);
605 }
606
607 static void irc_input_cb(gpointer data, gint source, PurpleInputCondition cond)
608 {
609 PurpleConnection *gc = data;
610 struct irc_conn *irc = gc->proto_data;
611 int len;
612
613 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
614 irc->inbuflen += IRC_INITIAL_BUFSIZE;
615 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
616 }
617
618 len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1);
619 if (len < 0 && errno == EAGAIN) {
620 return;
621 } else if (len < 0) {
622 purple_connection_error(gc, _("Read error"));
623 return;
624 } else if (len == 0) {
625 purple_connection_error(gc, _("Server has disconnected"));
626 return;
627 }
628
629 read_input(irc, len);
630 }
631
632 static void irc_chat_join (PurpleConnection *gc, GHashTable *data)
633 {
634 struct irc_conn *irc = gc->proto_data;
635 const char *args[2];
636
637 args[0] = g_hash_table_lookup(data, "channel");
638 args[1] = g_hash_table_lookup(data, "password");
639 irc_cmd_join(irc, "join", NULL, args);
640 }
641
642 static char *irc_get_chat_name(GHashTable *data) {
643 return g_strdup(g_hash_table_lookup(data, "channel"));
644 }
645
646 static void irc_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
647 {
648 struct irc_conn *irc = gc->proto_data;
649 PurpleConversation *convo = purple_find_chat(gc, id);
650 const char *args[2];
651
652 if (!convo) {
653 purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n");
654 return;
655 }
656 args[0] = name;
657 args[1] = purple_conversation_get_name(convo);
658 irc_cmd_invite(irc, "invite", purple_conversation_get_name(convo), args);
659 }
660
661
662 static void irc_chat_leave (PurpleConnection *gc, int id)
663 {
664 struct irc_conn *irc = gc->proto_data;
665 PurpleConversation *convo = purple_find_chat(gc, id);
666 const char *args[2];
667
668 if (!convo)
669 return;
670
671 args[0] = purple_conversation_get_name(convo);
672 args[1] = NULL;
673 irc_cmd_part(irc, "part", purple_conversation_get_name(convo), args);
674 serv_got_chat_left(gc, id);
675 }
676
677 static int irc_chat_send(PurpleConnection *gc, int id, const char *what, PurpleMessageFlags flags)
678 {
679 struct irc_conn *irc = gc->proto_data;
680 PurpleConversation *convo = purple_find_chat(gc, id);
681 const char *args[2];
682 char *tmp;
683
684 if (!convo) {
685 purple_debug(PURPLE_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n");
686 return -EINVAL;
687 }
688 #if 0
689 if (*what == '/') {
690 return irc_parse_cmd(irc, convo->name, what + 1);
691 }
692 #endif
693 purple_markup_html_to_xhtml(what, NULL, &tmp);
694 args[0] = convo->name;
695 args[1] = tmp;
696
697 irc_cmd_privmsg(irc, "msg", NULL, args);
698
699 serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), 0, what, time(NULL));
700 g_free(tmp);
701 return 0;
702 }
703
704 static guint irc_nick_hash(const char *nick)
705 {
706 char *lc;
707 guint bucket;
708
709 lc = g_utf8_strdown(nick, -1);
710 bucket = g_str_hash(lc);
711 g_free(lc);
712
713 return bucket;
714 }
715
716 static gboolean irc_nick_equal(const char *nick1, const char *nick2)
717 {
718 return (purple_utf8_strcasecmp(nick1, nick2) == 0);
719 }
720
721 static void irc_buddy_free(struct irc_buddy *ib)
722 {
723 g_free(ib->name);
724 g_free(ib);
725 }
726
727 static void irc_chat_set_topic(PurpleConnection *gc, int id, const char *topic)
728 {
729 char *buf;
730 const char *name = NULL;
731 struct irc_conn *irc;
732
733 irc = gc->proto_data;
734 name = purple_conversation_get_name(purple_find_chat(gc, id));
735
736 if (name == NULL)
737 return;
738
739 buf = irc_format(irc, "vt:", "TOPIC", name, topic);
740 irc_send(irc, buf);
741 g_free(buf);
742 }
743
744 static PurpleRoomlist *irc_roomlist_get_list(PurpleConnection *gc)
745 {
746 struct irc_conn *irc;
747 GList *fields = NULL;
748 PurpleRoomlistField *f;
749 char *buf;
750
751 irc = gc->proto_data;
752
753 if (irc->roomlist)
754 purple_roomlist_unref(irc->roomlist);
755
756 irc->roomlist = purple_roomlist_new(purple_connection_get_account(gc));
757
758 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
759 fields = g_list_append(fields, f);
760
761 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE);
762 fields = g_list_append(fields, f);
763
764 f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE);
765 fields = g_list_append(fields, f);
766
767 purple_roomlist_set_fields(irc->roomlist, fields);
768
769 buf = irc_format(irc, "v", "LIST");
770 irc_send(irc, buf);
771 g_free(buf);
772
773 return irc->roomlist;
774 }
775
776 static void irc_roomlist_cancel(PurpleRoomlist *list)
777 {
778 PurpleConnection *gc = purple_account_get_connection(list->account);
779 struct irc_conn *irc;
780
781 if (gc == NULL)
782 return;
783
784 irc = gc->proto_data;
785
786 purple_roomlist_set_in_progress(list, FALSE);
787
788 if (irc->roomlist == list) {
789 irc->roomlist = NULL;
790 purple_roomlist_unref(list);
791 }
792 }
793
794 static void irc_keepalive(PurpleConnection *gc)
795 {
796 struct irc_conn *irc = gc->proto_data;
797 if ((time(NULL) - irc->recv_time) > PING_TIMEOUT)
798 irc_cmd_ping(irc, NULL, NULL, NULL);
799 }
800
801 static PurplePluginProtocolInfo prpl_info =
802 {
803 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL,
804 NULL, /* user_splits */
805 NULL, /* protocol_options */
806 NO_BUDDY_ICONS, /* icon_spec */
807 irc_blist_icon, /* list_icon */
808 NULL, /* list_emblems */
809 NULL, /* status_text */
810 NULL, /* tooltip_text */
811 irc_status_types, /* away_states */
812 NULL, /* blist_node_menu */
813 irc_chat_join_info, /* chat_info */
814 irc_chat_info_defaults, /* chat_info_defaults */
815 irc_login, /* login */
816 irc_close, /* close */
817 irc_im_send, /* send_im */
818 NULL, /* set_info */
819 NULL, /* send_typing */
820 irc_get_info, /* get_info */
821 irc_set_status, /* set_status */
822 NULL, /* set_idle */
823 NULL, /* change_passwd */
824 irc_add_buddy, /* add_buddy */
825 NULL, /* add_buddies */
826 irc_remove_buddy, /* remove_buddy */
827 NULL, /* remove_buddies */
828 NULL, /* add_permit */
829 NULL, /* add_deny */
830 NULL, /* rem_permit */
831 NULL, /* rem_deny */
832 NULL, /* set_permit_deny */
833 irc_chat_join, /* join_chat */
834 NULL, /* reject_chat */
835 irc_get_chat_name, /* get_chat_name */
836 irc_chat_invite, /* chat_invite */
837 irc_chat_leave, /* chat_leave */
838 NULL, /* chat_whisper */
839 irc_chat_send, /* chat_send */
840 irc_keepalive, /* keepalive */
841 NULL, /* register_user */
842 NULL, /* get_cb_info */
843 NULL, /* get_cb_away */
844 NULL, /* alias_buddy */
845 NULL, /* group_buddy */
846 NULL, /* rename_group */
847 NULL, /* buddy_free */
848 NULL, /* convo_closed */
849 purple_normalize_nocase, /* normalize */
850 NULL, /* set_buddy_icon */
851 NULL, /* remove_group */
852 NULL, /* get_cb_real_name */
853 irc_chat_set_topic, /* set_chat_topic */
854 NULL, /* find_blist_chat */
855 irc_roomlist_get_list, /* roomlist_get_list */
856 irc_roomlist_cancel, /* roomlist_cancel */
857 NULL, /* roomlist_expand_category */
858 NULL, /* can_receive_file */
859 irc_dccsend_send_file, /* send_file */
860 irc_dccsend_new_xfer, /* new_xfer */
861 NULL, /* offline_message */
862 NULL, /* whiteboard_prpl_ops */
863 irc_send_raw, /* send_raw */
864 NULL, /* roomlist_room_serialize */
865 };
866
867 static gboolean load_plugin (PurplePlugin *plugin) {
868
869 purple_signal_register(plugin, "irc-sending-text",
870 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
871 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
872 purple_value_new_outgoing(PURPLE_TYPE_STRING));
873 purple_signal_register(plugin, "irc-receiving-text",
874 purple_marshal_VOID__POINTER_POINTER, NULL, 2,
875 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION),
876 purple_value_new_outgoing(PURPLE_TYPE_STRING));
877 return TRUE;
878 }
879
880
881 static PurplePluginInfo info =
882 {
883 PURPLE_PLUGIN_MAGIC,
884 PURPLE_MAJOR_VERSION,
885 PURPLE_MINOR_VERSION,
886 PURPLE_PLUGIN_PROTOCOL, /**< type */
887 NULL, /**< ui_requirement */
888 0, /**< flags */
889 NULL, /**< dependencies */
890 PURPLE_PRIORITY_DEFAULT, /**< priority */
891
892 "prpl-irc", /**< id */
893 "IRC", /**< name */
894 VERSION, /**< version */
895 N_("IRC Protocol Plugin"), /** summary */
896 N_("The IRC Protocol Plugin that Sucks Less"), /** description */
897 NULL, /**< author */
898 PURPLE_WEBSITE, /**< homepage */
899
900 load_plugin, /**< load */
901 NULL, /**< unload */
902 NULL, /**< destroy */
903
904 NULL, /**< ui_info */
905 &prpl_info, /**< extra_info */
906 NULL, /**< prefs_info */
907 irc_actions
908 };
909
910 static void _init_plugin(PurplePlugin *plugin)
911 {
912 PurpleAccountUserSplit *split;
913 PurpleAccountOption *option;
914
915 split = purple_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@');
916 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
917
918 option = purple_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT);
919 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
920
921 option = purple_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET);
922 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
923
924 option = purple_account_option_string_new(_("Username"), "username", "");
925 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
926
927 option = purple_account_option_string_new(_("Real name"), "realname", "");
928 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
929
930 /*
931 option = purple_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
932 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
933 */
934
935 option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
936 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
937
938 _irc_plugin = plugin;
939
940 purple_prefs_remove("/plugins/prpl/irc/quitmsg");
941 purple_prefs_remove("/plugins/prpl/irc");
942
943 irc_register_commands();
944 }
945
946 PURPLE_INIT_PLUGIN(irc, _init_plugin, info);

mercurial