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