core/protocols/irc/irc.c

changeset 14253
b63ebf84c42b
parent 14222
71d8761db708
equal deleted inserted replaced
14252:d10dda2777a9 14253:b63ebf84c42b
1 /**
2 * @file irc.c
3 *
4 * gaim
5 *
6 * Copyright (C) 2003, Robbert Haarman <gaim@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 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string);
41
42 static const char *irc_blist_icon(GaimAccount *a, GaimBuddy *b);
43 static void irc_blist_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne);
44 static GList *irc_status_types(GaimAccount *account);
45 static GList *irc_actions(GaimPlugin *plugin, gpointer context);
46 /* static GList *irc_chat_info(GaimConnection *gc); */
47 static void irc_login(GaimAccount *account);
48 static void irc_login_cb_ssl(gpointer data, GaimSslConnection *gsc, GaimInputCondition cond);
49 static void irc_login_cb(gpointer data, gint source, const gchar *error_message);
50 static void irc_ssl_connect_failure(GaimSslConnection *gsc, GaimSslErrorType error, gpointer data);
51 static void irc_close(GaimConnection *gc);
52 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags);
53 static int irc_chat_send(GaimConnection *gc, int id, const char *what, GaimMessageFlags flags);
54 static void irc_chat_join (GaimConnection *gc, GHashTable *data);
55 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond);
56 static void irc_input_cb_ssl(gpointer data, GaimSslConnection *gsc, GaimInputCondition cond);
57
58 static guint irc_nick_hash(const char *nick);
59 static gboolean irc_nick_equal(const char *nick1, const char *nick2);
60 static void irc_buddy_free(struct irc_buddy *ib);
61
62 static GaimPlugin *_irc_plugin = NULL;
63
64 static const char *status_chars = "@+%&";
65
66 static void irc_view_motd(GaimPluginAction *action)
67 {
68 GaimConnection *gc = (GaimConnection *) action->context;
69 struct irc_conn *irc;
70 char *title;
71
72 if (gc == NULL || gc->proto_data == NULL) {
73 gaim_debug(GAIM_DEBUG_ERROR, "irc", "got MOTD request for NULL gc\n");
74 return;
75 }
76 irc = gc->proto_data;
77 if (irc->motd == NULL) {
78 gaim_notify_error(gc, _("Error displaying MOTD"), _("No MOTD available"),
79 _("There is no MOTD associated with this connection."));
80 return;
81 }
82 title = g_strdup_printf(_("MOTD for %s"), irc->server);
83 gaim_notify_formatted(gc, title, title, NULL, irc->motd->str, NULL, NULL);
84 }
85
86 static int do_send(struct irc_conn *irc, const char *buf, gsize len)
87 {
88 int ret;
89
90 if (irc->gsc) {
91 ret = gaim_ssl_write(irc->gsc, buf, len);
92 } else {
93 ret = write(irc->fd, buf, len);
94 }
95
96 return ret;
97 }
98
99 static void
100 irc_send_cb(gpointer data, gint source, GaimInputCondition cond)
101 {
102 struct irc_conn *irc = data;
103 int ret, writelen;
104
105 writelen = gaim_circ_buffer_get_max_read(irc->outbuf);
106
107 if (writelen == 0) {
108 gaim_input_remove(irc->writeh);
109 irc->writeh = 0;
110 return;
111 }
112
113 ret = do_send(irc, irc->outbuf->outptr, writelen);
114
115 if (ret < 0 && errno == EAGAIN)
116 return;
117 else if (ret <= 0) {
118 gaim_connection_error(gaim_account_get_connection(irc->account),
119 _("Server has disconnected"));
120 return;
121 }
122
123 gaim_circ_buffer_mark_read(irc->outbuf, ret);
124
125 #if 0
126 /* We *could* try to write more if we wrote it all */
127 if (ret == write_len) {
128 irc_send_cb(data, source, cond);
129 }
130 #endif
131 }
132
133 int irc_send(struct irc_conn *irc, const char *buf)
134 {
135 int ret, buflen = strlen(buf);
136
137 /* If we're not buffering writes, try to send immediately */
138 if (!irc->writeh)
139 ret = do_send(irc, buf, buflen);
140 else {
141 ret = -1;
142 errno = EAGAIN;
143 }
144
145 /* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent%s: %s",
146 irc->gsc ? " (ssl)" : "", buf); */
147 if (ret <= 0 && errno != EAGAIN) {
148 gaim_connection_error(gaim_account_get_connection(irc->account),
149 _("Server has disconnected"));
150 } else if (ret < buflen) {
151 if (ret < 0)
152 ret = 0;
153 if (!irc->writeh)
154 irc->writeh = gaim_input_add(
155 irc->gsc ? irc->gsc->fd : irc->fd,
156 GAIM_INPUT_WRITE, irc_send_cb, irc);
157 gaim_circ_buffer_append(irc->outbuf, buf + ret,
158 buflen - ret);
159 }
160
161 return ret;
162 }
163
164 /* XXX I don't like messing directly with these buddies */
165 gboolean irc_blist_timeout(struct irc_conn *irc)
166 {
167 GString *string = g_string_sized_new(512);
168 char *list, *buf;
169
170 g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string);
171
172 list = g_string_free(string, FALSE);
173 if (!list || !strlen(list)) {
174 g_free(list);
175 return TRUE;
176 }
177
178 buf = irc_format(irc, "vn", "ISON", list);
179 g_free(list);
180 irc_send(irc, buf);
181 g_free(buf);
182
183 return TRUE;
184 }
185
186 static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string)
187 {
188 ib->flag = FALSE;
189 g_string_append_printf(string, "%s ", name);
190 }
191
192 static void irc_ison_one(struct irc_conn *irc, struct irc_buddy *ib)
193 {
194 char *buf;
195
196 ib->flag = FALSE;
197 buf = irc_format(irc, "vn", "ISON", ib->name);
198 irc_send(irc, buf);
199 g_free(buf);
200 }
201
202
203 static const char *irc_blist_icon(GaimAccount *a, GaimBuddy *b)
204 {
205 return "irc";
206 }
207
208 static void irc_blist_emblems(GaimBuddy *b, const char **se, const char **sw, const char **nw, const char **ne)
209 {
210 GaimPresence *presence = gaim_buddy_get_presence(b);
211
212 if (gaim_presence_is_online(presence) == FALSE) {
213 *se = "offline";
214 }
215 }
216
217 static GList *irc_status_types(GaimAccount *account)
218 {
219 GaimStatusType *type;
220 GList *types = NULL;
221
222 type = gaim_status_type_new(GAIM_STATUS_AVAILABLE, NULL, NULL, TRUE);
223 types = g_list_append(types, type);
224
225 type = gaim_status_type_new_with_attrs(
226 GAIM_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
227 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING),
228 NULL);
229 types = g_list_append(types, type);
230
231 type = gaim_status_type_new(GAIM_STATUS_OFFLINE, NULL, NULL, TRUE);
232 types = g_list_append(types, type);
233
234 return types;
235 }
236
237 static GList *irc_actions(GaimPlugin *plugin, gpointer context)
238 {
239 GList *list = NULL;
240 GaimPluginAction *act = NULL;
241
242 act = gaim_plugin_action_new(_("View MOTD"), irc_view_motd);
243 list = g_list_append(list, act);
244
245 return list;
246 }
247
248 static GList *irc_chat_join_info(GaimConnection *gc)
249 {
250 GList *m = NULL;
251 struct proto_chat_entry *pce;
252
253 pce = g_new0(struct proto_chat_entry, 1);
254 pce->label = _("_Channel:");
255 pce->identifier = "channel";
256 pce->required = TRUE;
257 m = g_list_append(m, pce);
258
259 pce = g_new0(struct proto_chat_entry, 1);
260 pce->label = _("_Password:");
261 pce->identifier = "password";
262 pce->secret = TRUE;
263 m = g_list_append(m, pce);
264
265 return m;
266 }
267
268 static GHashTable *irc_chat_info_defaults(GaimConnection *gc, const char *chat_name)
269 {
270 GHashTable *defaults;
271
272 defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
273
274 if (chat_name != NULL)
275 g_hash_table_insert(defaults, "channel", g_strdup(chat_name));
276
277 return defaults;
278 }
279
280 static void irc_login(GaimAccount *account)
281 {
282 GaimConnection *gc;
283 struct irc_conn *irc;
284 char **userparts;
285 const char *username = gaim_account_get_username(account);
286
287 gc = gaim_account_get_connection(account);
288 gc->flags |= GAIM_CONNECTION_NO_NEWLINES;
289
290 if (strpbrk(username, " \t\v\r\n") != NULL) {
291 gaim_connection_error(gc, _("IRC nicks may not contain whitespace"));
292 return;
293 }
294
295 gc->proto_data = irc = g_new0(struct irc_conn, 1);
296 irc->fd = -1;
297 irc->account = account;
298 irc->outbuf = gaim_circ_buffer_new(512);
299
300 userparts = g_strsplit(username, "@", 2);
301 gaim_connection_set_display_name(gc, userparts[0]);
302 irc->server = g_strdup(userparts[1]);
303 g_strfreev(userparts);
304
305 irc->buddies = g_hash_table_new_full((GHashFunc)irc_nick_hash, (GEqualFunc)irc_nick_equal,
306 NULL, (GDestroyNotify)irc_buddy_free);
307 irc->cmds = g_hash_table_new(g_str_hash, g_str_equal);
308 irc_cmd_table_build(irc);
309 irc->msgs = g_hash_table_new(g_str_hash, g_str_equal);
310 irc_msg_table_build(irc);
311
312 gaim_connection_update_progress(gc, _("Connecting"), 1, 2);
313
314 if (gaim_account_get_bool(account, "ssl", FALSE)) {
315 if (gaim_ssl_is_supported()) {
316 irc->gsc = gaim_ssl_connect(account, irc->server,
317 gaim_account_get_int(account, "port", IRC_DEFAULT_SSL_PORT),
318 irc_login_cb_ssl, irc_ssl_connect_failure, gc);
319 } else {
320 gaim_connection_error(gc, _("SSL support unavailable"));
321 return;
322 }
323 }
324
325 if (!irc->gsc) {
326
327 irc->connect_info = gaim_proxy_connect(account, irc->server,
328 gaim_account_get_int(account, "port", IRC_DEFAULT_PORT),
329 irc_login_cb, gc);
330
331 if (!irc->connect_info || !gaim_account_get_connection(account)) {
332 gaim_connection_error(gc, _("Couldn't create socket"));
333 return;
334 }
335 }
336 }
337
338 static gboolean do_login(GaimConnection *gc) {
339 char *buf;
340 char hostname[256];
341 const char *username, *realname;
342 struct irc_conn *irc = gc->proto_data;
343 const char *pass = gaim_connection_get_password(gc);
344
345 if (pass && *pass) {
346 buf = irc_format(irc, "vv", "PASS", pass);
347 if (irc_send(irc, buf) < 0) {
348 /* gaim_connection_error(gc, "Error sending password"); */
349 g_free(buf);
350 return FALSE;
351 }
352 g_free(buf);
353 }
354
355 gethostname(hostname, sizeof(hostname));
356 hostname[sizeof(hostname) - 1] = '\0';
357 username = gaim_account_get_string(irc->account, "username", "");
358 realname = gaim_account_get_string(irc->account, "realname", "");
359 buf = irc_format(irc, "vvvv:", "USER", strlen(username) ? username : g_get_user_name(), hostname, irc->server,
360 strlen(realname) ? realname : IRC_DEFAULT_ALIAS);
361 if (irc_send(irc, buf) < 0) {
362 /* gaim_connection_error(gc, "Error registering with server");*/
363 g_free(buf);
364 return FALSE;
365 }
366 g_free(buf);
367 buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc));
368 if (irc_send(irc, buf) < 0) {
369 /* gaim_connection_error(gc, "Error sending nickname");*/
370 g_free(buf);
371 return FALSE;
372 }
373 g_free(buf);
374
375 return TRUE;
376 }
377
378 static void irc_login_cb_ssl(gpointer data, GaimSslConnection *gsc,
379 GaimInputCondition cond)
380 {
381 GaimConnection *gc = data;
382 struct irc_conn *irc = gc->proto_data;
383
384 if(!g_list_find(gaim_connections_get_all(), gc)) {
385 gaim_ssl_close(gsc);
386 return;
387 }
388
389 irc->gsc = gsc;
390
391 if (do_login(gc)) {
392 gaim_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 GaimConnection *gc = data;
399 struct irc_conn *irc = gc->proto_data;
400
401 irc->connect_info = NULL;
402
403 if (source < 0) {
404 gaim_connection_error(gc, _("Couldn't connect to host"));
405 return;
406 }
407
408 irc->fd = source;
409
410 if (do_login(gc)) {
411 gc->inpa = gaim_input_add(irc->fd, GAIM_INPUT_READ, irc_input_cb, gc);
412 }
413 }
414
415 static void
416 irc_ssl_connect_failure(GaimSslConnection *gsc, GaimSslErrorType error,
417 gpointer data)
418 {
419 GaimConnection *gc = data;
420 struct irc_conn *irc = gc->proto_data;
421
422 switch(error) {
423 case GAIM_SSL_CONNECT_FAILED:
424 gaim_connection_error(gc, _("Connection Failed"));
425 break;
426 case GAIM_SSL_HANDSHAKE_FAILED:
427 gaim_connection_error(gc, _("SSL Handshake Failed"));
428 break;
429 }
430
431 irc->gsc = NULL;
432 }
433
434 static void irc_close(GaimConnection *gc)
435 {
436 struct irc_conn *irc = gc->proto_data;
437
438 if (irc == NULL)
439 return;
440
441 if (irc->gsc || (irc->fd >= 0))
442 irc_cmd_quit(irc, "quit", NULL, NULL);
443
444 if (irc->connect_info)
445 gaim_proxy_connect_cancel(irc->connect_info);
446
447 if (gc->inpa)
448 gaim_input_remove(gc->inpa);
449
450 g_free(irc->inbuf);
451 if (irc->gsc) {
452 gaim_ssl_close(irc->gsc);
453 } else if (irc->fd >= 0) {
454 close(irc->fd);
455 }
456 if (irc->timer)
457 gaim_timeout_remove(irc->timer);
458 g_hash_table_destroy(irc->cmds);
459 g_hash_table_destroy(irc->msgs);
460 g_hash_table_destroy(irc->buddies);
461 if (irc->motd)
462 g_string_free(irc->motd, TRUE);
463 g_free(irc->server);
464
465 if (irc->writeh)
466 gaim_input_remove(irc->writeh);
467
468 gaim_circ_buffer_destroy(irc->outbuf);
469
470 g_free(irc);
471 }
472
473 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags)
474 {
475 struct irc_conn *irc = gc->proto_data;
476 char *plain;
477 const char *args[2];
478
479 if (strchr(status_chars, *who) != NULL)
480 args[0] = who + 1;
481 else
482 args[0] = who;
483
484 plain = gaim_unescape_html(what);
485 args[1] = plain;
486
487 irc_cmd_privmsg(irc, "msg", NULL, args);
488 g_free(plain);
489 return 1;
490 }
491
492 static void irc_get_info(GaimConnection *gc, const char *who)
493 {
494 struct irc_conn *irc = gc->proto_data;
495 const char *args[2];
496 args[0] = who;
497 args[1] = NULL;
498 irc_cmd_whois(irc, "whois", NULL, args);
499 }
500
501 static void irc_set_status(GaimAccount *account, GaimStatus *status)
502 {
503 GaimConnection *gc = gaim_account_get_connection(account);
504 struct irc_conn *irc;
505 const char *args[1];
506 const char *status_id = gaim_status_get_id(status);
507
508 g_return_if_fail(gc != NULL);
509 irc = gc->proto_data;
510
511 if (!gaim_status_is_active(status))
512 return;
513
514 args[0] = NULL;
515
516 if (!strcmp(status_id, "away")) {
517 args[0] = gaim_status_get_attr_string(status, "message");
518 if ((args[0] == NULL) || (*args[0] == '\0'))
519 args[0] = _("Away");
520 irc_cmd_away(irc, "away", NULL, args);
521 } else if (!strcmp(status_id, "available")) {
522 irc_cmd_away(irc, "back", NULL, args);
523 }
524 }
525
526 static void irc_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
527 {
528 struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
529 struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
530 ib->name = g_strdup(buddy->name);
531 g_hash_table_insert(irc->buddies, ib->name, ib);
532
533 /* if the timer isn't set, this is during signon, so we don't want to flood
534 * ourself off with ISON's, so we don't, but after that we want to know when
535 * someone's online asap */
536 if (irc->timer)
537 irc_ison_one(irc, ib);
538 }
539
540 static void irc_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group)
541 {
542 struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
543 g_hash_table_remove(irc->buddies, buddy->name);
544 }
545
546 static void read_input(struct irc_conn *irc, int len)
547 {
548 char *cur, *end;
549
550 irc->inbufused += len;
551 irc->inbuf[irc->inbufused] = '\0';
552
553 cur = irc->inbuf;
554
555 /* This is a hack to work around the fact that marv gets messages
556 * with null bytes in them while using some weird irc server at work
557 */
558 while ((cur < (irc->inbuf + irc->inbufused)) && !*cur)
559 cur++;
560
561 while (cur < irc->inbuf + irc->inbufused &&
562 ((end = strstr(cur, "\r\n")) || (end = strstr(cur, "\n")))) {
563 int step = (*end == '\r' ? 2 : 1);
564 *end = '\0';
565 irc_parse_msg(irc, cur);
566 cur = end + step;
567 }
568 if (cur != irc->inbuf + irc->inbufused) { /* leftover */
569 irc->inbufused -= (cur - irc->inbuf);
570 memmove(irc->inbuf, cur, irc->inbufused);
571 } else {
572 irc->inbufused = 0;
573 }
574 }
575
576 static void irc_input_cb_ssl(gpointer data, GaimSslConnection *gsc,
577 GaimInputCondition cond)
578 {
579
580 GaimConnection *gc = data;
581 struct irc_conn *irc = gc->proto_data;
582 int len;
583
584 if(!g_list_find(gaim_connections_get_all(), gc)) {
585 gaim_ssl_close(gsc);
586 return;
587 }
588
589 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
590 irc->inbuflen += IRC_INITIAL_BUFSIZE;
591 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
592 }
593
594 len = gaim_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1);
595
596 if (len < 0 && errno == EAGAIN) {
597 /* Try again later */
598 return;
599 } else if (len < 0) {
600 gaim_connection_error(gc, _("Read error"));
601 return;
602 } else if (len == 0) {
603 gaim_connection_error(gc, _("Server has disconnected"));
604 return;
605 }
606
607 read_input(irc, len);
608 }
609
610 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond)
611 {
612 GaimConnection *gc = data;
613 struct irc_conn *irc = gc->proto_data;
614 int len;
615
616 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
617 irc->inbuflen += IRC_INITIAL_BUFSIZE;
618 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
619 }
620
621 len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1);
622 if (len < 0 && errno == EAGAIN) {
623 return;
624 } else if (len < 0) {
625 gaim_connection_error(gc, _("Read error"));
626 return;
627 } else if (len == 0) {
628 gaim_connection_error(gc, _("Server has disconnected"));
629 return;
630 }
631
632 read_input(irc, len);
633 }
634
635 static void irc_chat_join (GaimConnection *gc, GHashTable *data)
636 {
637 struct irc_conn *irc = gc->proto_data;
638 const char *args[2];
639
640 args[0] = g_hash_table_lookup(data, "channel");
641 args[1] = g_hash_table_lookup(data, "password");
642 irc_cmd_join(irc, "join", NULL, args);
643 }
644
645 static char *irc_get_chat_name(GHashTable *data) {
646 return g_strdup(g_hash_table_lookup(data, "channel"));
647 }
648
649 static void irc_chat_invite(GaimConnection *gc, int id, const char *message, const char *name)
650 {
651 struct irc_conn *irc = gc->proto_data;
652 GaimConversation *convo = gaim_find_chat(gc, id);
653 const char *args[2];
654
655 if (!convo) {
656 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n");
657 return;
658 }
659 args[0] = name;
660 args[1] = gaim_conversation_get_name(convo);
661 irc_cmd_invite(irc, "invite", gaim_conversation_get_name(convo), args);
662 }
663
664
665 static void irc_chat_leave (GaimConnection *gc, int id)
666 {
667 struct irc_conn *irc = gc->proto_data;
668 GaimConversation *convo = gaim_find_chat(gc, id);
669 const char *args[2];
670
671 if (!convo)
672 return;
673
674 args[0] = gaim_conversation_get_name(convo);
675 args[1] = NULL;
676 irc_cmd_part(irc, "part", gaim_conversation_get_name(convo), args);
677 serv_got_chat_left(gc, id);
678 }
679
680 static int irc_chat_send(GaimConnection *gc, int id, const char *what, GaimMessageFlags flags)
681 {
682 struct irc_conn *irc = gc->proto_data;
683 GaimConversation *convo = gaim_find_chat(gc, id);
684 const char *args[2];
685 char *tmp;
686
687 if (!convo) {
688 gaim_debug(GAIM_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n");
689 return -EINVAL;
690 }
691 #if 0
692 if (*what == '/') {
693 return irc_parse_cmd(irc, convo->name, what + 1);
694 }
695 #endif
696 tmp = gaim_unescape_html(what);
697 args[0] = convo->name;
698 args[1] = tmp;
699
700 irc_cmd_privmsg(irc, "msg", NULL, args);
701
702 serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, what, time(NULL));
703 g_free(tmp);
704 return 0;
705 }
706
707 static guint irc_nick_hash(const char *nick)
708 {
709 char *lc;
710 guint bucket;
711
712 lc = g_utf8_strdown(nick, -1);
713 bucket = g_str_hash(lc);
714 g_free(lc);
715
716 return bucket;
717 }
718
719 static gboolean irc_nick_equal(const char *nick1, const char *nick2)
720 {
721 return (gaim_utf8_strcasecmp(nick1, nick2) == 0);
722 }
723
724 static void irc_buddy_free(struct irc_buddy *ib)
725 {
726 g_free(ib->name);
727 g_free(ib);
728 }
729
730 static void irc_chat_set_topic(GaimConnection *gc, int id, const char *topic)
731 {
732 char *buf;
733 const char *name = NULL;
734 struct irc_conn *irc;
735
736 irc = gc->proto_data;
737 name = gaim_conversation_get_name(gaim_find_chat(gc, id));
738
739 if (name == NULL)
740 return;
741
742 buf = irc_format(irc, "vt:", "TOPIC", name, topic);
743 irc_send(irc, buf);
744 g_free(buf);
745 }
746
747 static GaimRoomlist *irc_roomlist_get_list(GaimConnection *gc)
748 {
749 struct irc_conn *irc;
750 GList *fields = NULL;
751 GaimRoomlistField *f;
752 char *buf;
753
754 irc = gc->proto_data;
755
756 if (irc->roomlist)
757 gaim_roomlist_unref(irc->roomlist);
758
759 irc->roomlist = gaim_roomlist_new(gaim_connection_get_account(gc));
760
761 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "channel", TRUE);
762 fields = g_list_append(fields, f);
763
764 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE);
765 fields = g_list_append(fields, f);
766
767 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE);
768 fields = g_list_append(fields, f);
769
770 gaim_roomlist_set_fields(irc->roomlist, fields);
771
772 buf = irc_format(irc, "v", "LIST");
773 irc_send(irc, buf);
774 g_free(buf);
775
776 return irc->roomlist;
777 }
778
779 static void irc_roomlist_cancel(GaimRoomlist *list)
780 {
781 GaimConnection *gc = gaim_account_get_connection(list->account);
782 struct irc_conn *irc;
783
784 if (gc == NULL)
785 return;
786
787 irc = gc->proto_data;
788
789 gaim_roomlist_set_in_progress(list, FALSE);
790
791 if (irc->roomlist == list) {
792 irc->roomlist = NULL;
793 gaim_roomlist_unref(list);
794 }
795 }
796
797 static GaimPluginProtocolInfo prpl_info =
798 {
799 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL,
800 NULL, /* user_splits */
801 NULL, /* protocol_options */
802 NO_BUDDY_ICONS, /* icon_spec */
803 irc_blist_icon, /* list_icon */
804 irc_blist_emblems, /* list_emblems */
805 NULL, /* status_text */
806 NULL, /* tooltip_text */
807 irc_status_types, /* away_states */
808 NULL, /* blist_node_menu */
809 irc_chat_join_info, /* chat_info */
810 irc_chat_info_defaults, /* chat_info_defaults */
811 irc_login, /* login */
812 irc_close, /* close */
813 irc_im_send, /* send_im */
814 NULL, /* set_info */
815 NULL, /* send_typing */
816 irc_get_info, /* get_info */
817 irc_set_status, /* set_status */
818 NULL, /* set_idle */
819 NULL, /* change_passwd */
820 irc_add_buddy, /* add_buddy */
821 NULL, /* add_buddies */
822 irc_remove_buddy, /* remove_buddy */
823 NULL, /* remove_buddies */
824 NULL, /* add_permit */
825 NULL, /* add_deny */
826 NULL, /* rem_permit */
827 NULL, /* rem_deny */
828 NULL, /* set_permit_deny */
829 irc_chat_join, /* join_chat */
830 NULL, /* reject_chat */
831 irc_get_chat_name, /* get_chat_name */
832 irc_chat_invite, /* chat_invite */
833 irc_chat_leave, /* chat_leave */
834 NULL, /* chat_whisper */
835 irc_chat_send, /* chat_send */
836 NULL, /* keepalive */
837 NULL, /* register_user */
838 NULL, /* get_cb_info */
839 NULL, /* get_cb_away */
840 NULL, /* alias_buddy */
841 NULL, /* group_buddy */
842 NULL, /* rename_group */
843 NULL, /* buddy_free */
844 NULL, /* convo_closed */
845 gaim_normalize_nocase, /* normalize */
846 NULL, /* set_buddy_icon */
847 NULL, /* remove_group */
848 NULL, /* get_cb_real_name */
849 irc_chat_set_topic, /* set_chat_topic */
850 NULL, /* find_blist_chat */
851 irc_roomlist_get_list, /* roomlist_get_list */
852 irc_roomlist_cancel, /* roomlist_cancel */
853 NULL, /* roomlist_expand_category */
854 NULL, /* can_receive_file */
855 irc_dccsend_send_file, /* send_file */
856 irc_dccsend_new_xfer, /* new_xfer */
857 NULL, /* offline_message */
858 NULL, /* whiteboard_prpl_ops */
859 };
860
861
862 static GaimPluginInfo info =
863 {
864 GAIM_PLUGIN_MAGIC,
865 GAIM_MAJOR_VERSION,
866 GAIM_MINOR_VERSION,
867 GAIM_PLUGIN_PROTOCOL, /**< type */
868 NULL, /**< ui_requirement */
869 0, /**< flags */
870 NULL, /**< dependencies */
871 GAIM_PRIORITY_DEFAULT, /**< priority */
872
873 "prpl-irc", /**< id */
874 "IRC", /**< name */
875 VERSION, /**< version */
876 N_("IRC Protocol Plugin"), /** summary */
877 N_("The IRC Protocol Plugin that Sucks Less"), /** description */
878 NULL, /**< author */
879 GAIM_WEBSITE, /**< homepage */
880
881 NULL, /**< load */
882 NULL, /**< unload */
883 NULL, /**< destroy */
884
885 NULL, /**< ui_info */
886 &prpl_info, /**< extra_info */
887 NULL, /**< prefs_info */
888 irc_actions
889 };
890
891 static void _init_plugin(GaimPlugin *plugin)
892 {
893 GaimAccountUserSplit *split;
894 GaimAccountOption *option;
895
896 split = gaim_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@');
897 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
898
899 option = gaim_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT);
900 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
901
902 option = gaim_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET);
903 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
904
905 option = gaim_account_option_string_new(_("Username"), "username", "");
906 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
907
908 option = gaim_account_option_string_new(_("Real name"), "realname", "");
909 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
910
911 /*
912 option = gaim_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT);
913 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
914 */
915
916 option = gaim_account_option_bool_new(_("Use SSL"), "ssl", FALSE);
917 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
918
919 _irc_plugin = plugin;
920
921 gaim_prefs_remove("/plugins/prpl/irc/quitmsg");
922 gaim_prefs_remove("/plugins/prpl/irc");
923
924 irc_register_commands();
925 }
926
927 GAIM_INIT_PLUGIN(irc, _init_plugin, info);

mercurial