Mercurial > pidgin
annotate libgaim/protocols/irc/irc.c @ 14656:d1a76ccb93c4
[gaim-migrate @ 17402]
leak-be-gone
committer: Tailor Script <tailor@pidgin.im>
author | Mark Huetsch <markhuetsch> |
---|---|
date | Sat, 30 Sep 2006 00:14:00 +0000 |
parents | f8beee12ade9 |
children | 118fd0dc5b6e |
rev | line source |
---|---|
14192 | 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 | |
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
40 #define PING_TIMEOUT 60 |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
41 |
14192 | 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 | |
14621 | 64 GaimPlugin *_irc_plugin = NULL; |
14192 | 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); | |
14472
34de373e45c1
[gaim-migrate @ 17188]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14262
diff
changeset
|
86 g_free(title); |
14192 | 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 | |
14542 | 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 | |
14192 | 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 { | |
14621 | 144 int ret, buflen; |
145 char *tosend= g_strdup(buf); | |
14192 | 146 |
14622 | 147 gaim_signal_emit(_irc_plugin, "irc-sending-text", gaim_account_get_connection(irc->account), &tosend); |
14621 | 148 if (tosend == NULL) |
149 return 0; | |
150 | |
151 buflen = strlen(tosend); | |
152 | |
153 | |
14192 | 154 /* If we're not buffering writes, try to send immediately */ |
155 if (!irc->writeh) | |
14621 | 156 ret = do_send(irc, tosend, buflen); |
14192 | 157 else { |
158 ret = -1; | |
159 errno = EAGAIN; | |
160 } | |
161 | |
162 /* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent%s: %s", | |
14621 | 163 irc->gsc ? " (ssl)" : "", tosend); */ |
14192 | 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); | |
14621 | 174 gaim_circ_buffer_append(irc->outbuf, tosend + ret, |
14192 | 175 buflen - ret); |
176 } | |
14621 | 177 g_free(tosend); |
14192 | 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 | |
14262 | 344 irc->connect_data = gaim_proxy_connect(account, irc->server, |
14192 | 345 gaim_account_get_int(account, "port", IRC_DEFAULT_PORT), |
346 irc_login_cb, gc); | |
347 | |
14262 | 348 if (!irc->connect_data || !gaim_account_get_connection(account)) { |
14192 | 349 gaim_connection_error(gc, _("Couldn't create socket")); |
350 return; | |
351 } | |
352 } | |
353 } | |
354 | |
355 static gboolean do_login(GaimConnection *gc) { | |
356 char *buf; | |
357 char hostname[256]; | |
358 const char *username, *realname; | |
359 struct irc_conn *irc = gc->proto_data; | |
360 const char *pass = gaim_connection_get_password(gc); | |
361 | |
362 if (pass && *pass) { | |
363 buf = irc_format(irc, "vv", "PASS", pass); | |
364 if (irc_send(irc, buf) < 0) { | |
365 /* gaim_connection_error(gc, "Error sending password"); */ | |
366 g_free(buf); | |
367 return FALSE; | |
368 } | |
369 g_free(buf); | |
370 } | |
371 | |
372 gethostname(hostname, sizeof(hostname)); | |
373 hostname[sizeof(hostname) - 1] = '\0'; | |
374 username = gaim_account_get_string(irc->account, "username", ""); | |
375 realname = gaim_account_get_string(irc->account, "realname", ""); | |
376 buf = irc_format(irc, "vvvv:", "USER", strlen(username) ? username : g_get_user_name(), hostname, irc->server, | |
377 strlen(realname) ? realname : IRC_DEFAULT_ALIAS); | |
378 if (irc_send(irc, buf) < 0) { | |
379 /* gaim_connection_error(gc, "Error registering with server");*/ | |
380 g_free(buf); | |
381 return FALSE; | |
382 } | |
383 g_free(buf); | |
384 buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc)); | |
385 if (irc_send(irc, buf) < 0) { | |
386 /* gaim_connection_error(gc, "Error sending nickname");*/ | |
387 g_free(buf); | |
388 return FALSE; | |
389 } | |
390 g_free(buf); | |
391 | |
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
392 irc->recv_time = time(NULL); |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
393 |
14192 | 394 return TRUE; |
395 } | |
396 | |
397 static void irc_login_cb_ssl(gpointer data, GaimSslConnection *gsc, | |
398 GaimInputCondition cond) | |
399 { | |
400 GaimConnection *gc = data; | |
401 | |
402 if (do_login(gc)) { | |
403 gaim_ssl_input_add(gsc, irc_input_cb_ssl, gc); | |
404 } | |
405 } | |
406 | |
407 static void irc_login_cb(gpointer data, gint source, const gchar *error_message) | |
408 { | |
409 GaimConnection *gc = data; | |
410 struct irc_conn *irc = gc->proto_data; | |
411 | |
14262 | 412 irc->connect_data = NULL; |
14192 | 413 |
414 if (source < 0) { | |
415 gaim_connection_error(gc, _("Couldn't connect to host")); | |
416 return; | |
417 } | |
418 | |
419 irc->fd = source; | |
420 | |
421 if (do_login(gc)) { | |
422 gc->inpa = gaim_input_add(irc->fd, GAIM_INPUT_READ, irc_input_cb, gc); | |
423 } | |
424 } | |
425 | |
426 static void | |
427 irc_ssl_connect_failure(GaimSslConnection *gsc, GaimSslErrorType error, | |
428 gpointer data) | |
429 { | |
430 GaimConnection *gc = data; | |
431 struct irc_conn *irc = gc->proto_data; | |
432 | |
14248 | 433 irc->gsc = NULL; |
434 | |
14192 | 435 switch(error) { |
436 case GAIM_SSL_CONNECT_FAILED: | |
437 gaim_connection_error(gc, _("Connection Failed")); | |
438 break; | |
439 case GAIM_SSL_HANDSHAKE_FAILED: | |
440 gaim_connection_error(gc, _("SSL Handshake Failed")); | |
441 break; | |
442 } | |
443 } | |
444 | |
445 static void irc_close(GaimConnection *gc) | |
446 { | |
447 struct irc_conn *irc = gc->proto_data; | |
448 | |
449 if (irc == NULL) | |
450 return; | |
451 | |
452 if (irc->gsc || (irc->fd >= 0)) | |
453 irc_cmd_quit(irc, "quit", NULL, NULL); | |
454 | |
14262 | 455 if (irc->connect_data) |
456 gaim_proxy_connect_cancel(irc->connect_data); | |
14192 | 457 |
458 if (gc->inpa) | |
459 gaim_input_remove(gc->inpa); | |
460 | |
461 g_free(irc->inbuf); | |
462 if (irc->gsc) { | |
463 gaim_ssl_close(irc->gsc); | |
464 } else if (irc->fd >= 0) { | |
465 close(irc->fd); | |
466 } | |
467 if (irc->timer) | |
468 gaim_timeout_remove(irc->timer); | |
469 g_hash_table_destroy(irc->cmds); | |
470 g_hash_table_destroy(irc->msgs); | |
471 g_hash_table_destroy(irc->buddies); | |
472 if (irc->motd) | |
473 g_string_free(irc->motd, TRUE); | |
474 g_free(irc->server); | |
475 | |
476 if (irc->writeh) | |
477 gaim_input_remove(irc->writeh); | |
478 | |
479 gaim_circ_buffer_destroy(irc->outbuf); | |
480 | |
481 g_free(irc); | |
482 } | |
483 | |
484 static int irc_im_send(GaimConnection *gc, const char *who, const char *what, GaimMessageFlags flags) | |
485 { | |
486 struct irc_conn *irc = gc->proto_data; | |
487 char *plain; | |
488 const char *args[2]; | |
489 | |
490 if (strchr(status_chars, *who) != NULL) | |
491 args[0] = who + 1; | |
492 else | |
493 args[0] = who; | |
494 | |
495 plain = gaim_unescape_html(what); | |
496 args[1] = plain; | |
497 | |
498 irc_cmd_privmsg(irc, "msg", NULL, args); | |
499 g_free(plain); | |
500 return 1; | |
501 } | |
502 | |
503 static void irc_get_info(GaimConnection *gc, const char *who) | |
504 { | |
505 struct irc_conn *irc = gc->proto_data; | |
506 const char *args[2]; | |
507 args[0] = who; | |
508 args[1] = NULL; | |
509 irc_cmd_whois(irc, "whois", NULL, args); | |
510 } | |
511 | |
512 static void irc_set_status(GaimAccount *account, GaimStatus *status) | |
513 { | |
514 GaimConnection *gc = gaim_account_get_connection(account); | |
515 struct irc_conn *irc; | |
516 const char *args[1]; | |
517 const char *status_id = gaim_status_get_id(status); | |
518 | |
519 g_return_if_fail(gc != NULL); | |
520 irc = gc->proto_data; | |
521 | |
522 if (!gaim_status_is_active(status)) | |
523 return; | |
524 | |
525 args[0] = NULL; | |
526 | |
527 if (!strcmp(status_id, "away")) { | |
528 args[0] = gaim_status_get_attr_string(status, "message"); | |
529 if ((args[0] == NULL) || (*args[0] == '\0')) | |
530 args[0] = _("Away"); | |
531 irc_cmd_away(irc, "away", NULL, args); | |
532 } else if (!strcmp(status_id, "available")) { | |
533 irc_cmd_away(irc, "back", NULL, args); | |
534 } | |
535 } | |
536 | |
537 static void irc_add_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) | |
538 { | |
539 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; | |
540 struct irc_buddy *ib = g_new0(struct irc_buddy, 1); | |
541 ib->name = g_strdup(buddy->name); | |
542 g_hash_table_insert(irc->buddies, ib->name, ib); | |
543 | |
544 /* if the timer isn't set, this is during signon, so we don't want to flood | |
545 * ourself off with ISON's, so we don't, but after that we want to know when | |
546 * someone's online asap */ | |
547 if (irc->timer) | |
548 irc_ison_one(irc, ib); | |
549 } | |
550 | |
551 static void irc_remove_buddy(GaimConnection *gc, GaimBuddy *buddy, GaimGroup *group) | |
552 { | |
553 struct irc_conn *irc = (struct irc_conn *)gc->proto_data; | |
554 g_hash_table_remove(irc->buddies, buddy->name); | |
555 } | |
556 | |
557 static void read_input(struct irc_conn *irc, int len) | |
558 { | |
559 char *cur, *end; | |
560 | |
561 irc->inbufused += len; | |
562 irc->inbuf[irc->inbufused] = '\0'; | |
563 | |
564 cur = irc->inbuf; | |
565 | |
566 /* This is a hack to work around the fact that marv gets messages | |
567 * with null bytes in them while using some weird irc server at work | |
568 */ | |
569 while ((cur < (irc->inbuf + irc->inbufused)) && !*cur) | |
570 cur++; | |
571 | |
572 while (cur < irc->inbuf + irc->inbufused && | |
573 ((end = strstr(cur, "\r\n")) || (end = strstr(cur, "\n")))) { | |
574 int step = (*end == '\r' ? 2 : 1); | |
575 *end = '\0'; | |
576 irc_parse_msg(irc, cur); | |
577 cur = end + step; | |
578 } | |
579 if (cur != irc->inbuf + irc->inbufused) { /* leftover */ | |
580 irc->inbufused -= (cur - irc->inbuf); | |
581 memmove(irc->inbuf, cur, irc->inbufused); | |
582 } else { | |
583 irc->inbufused = 0; | |
584 } | |
585 } | |
586 | |
587 static void irc_input_cb_ssl(gpointer data, GaimSslConnection *gsc, | |
588 GaimInputCondition cond) | |
589 { | |
590 | |
591 GaimConnection *gc = data; | |
592 struct irc_conn *irc = gc->proto_data; | |
593 int len; | |
594 | |
595 if(!g_list_find(gaim_connections_get_all(), gc)) { | |
596 gaim_ssl_close(gsc); | |
597 return; | |
598 } | |
599 | |
600 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { | |
601 irc->inbuflen += IRC_INITIAL_BUFSIZE; | |
602 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); | |
603 } | |
604 | |
605 len = gaim_ssl_read(gsc, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); | |
606 | |
607 if (len < 0 && errno == EAGAIN) { | |
608 /* Try again later */ | |
609 return; | |
610 } else if (len < 0) { | |
611 gaim_connection_error(gc, _("Read error")); | |
612 return; | |
613 } else if (len == 0) { | |
614 gaim_connection_error(gc, _("Server has disconnected")); | |
615 return; | |
616 } | |
617 | |
618 read_input(irc, len); | |
619 } | |
620 | |
621 static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond) | |
622 { | |
623 GaimConnection *gc = data; | |
624 struct irc_conn *irc = gc->proto_data; | |
625 int len; | |
626 | |
627 if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) { | |
628 irc->inbuflen += IRC_INITIAL_BUFSIZE; | |
629 irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen); | |
630 } | |
631 | |
632 len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1); | |
633 if (len < 0 && errno == EAGAIN) { | |
634 return; | |
635 } else if (len < 0) { | |
636 gaim_connection_error(gc, _("Read error")); | |
637 return; | |
638 } else if (len == 0) { | |
639 gaim_connection_error(gc, _("Server has disconnected")); | |
640 return; | |
641 } | |
642 | |
643 read_input(irc, len); | |
644 } | |
645 | |
646 static void irc_chat_join (GaimConnection *gc, GHashTable *data) | |
647 { | |
648 struct irc_conn *irc = gc->proto_data; | |
649 const char *args[2]; | |
650 | |
651 args[0] = g_hash_table_lookup(data, "channel"); | |
652 args[1] = g_hash_table_lookup(data, "password"); | |
653 irc_cmd_join(irc, "join", NULL, args); | |
654 } | |
655 | |
656 static char *irc_get_chat_name(GHashTable *data) { | |
657 return g_strdup(g_hash_table_lookup(data, "channel")); | |
658 } | |
659 | |
660 static void irc_chat_invite(GaimConnection *gc, int id, const char *message, const char *name) | |
661 { | |
662 struct irc_conn *irc = gc->proto_data; | |
663 GaimConversation *convo = gaim_find_chat(gc, id); | |
664 const char *args[2]; | |
665 | |
666 if (!convo) { | |
667 gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n"); | |
668 return; | |
669 } | |
670 args[0] = name; | |
671 args[1] = gaim_conversation_get_name(convo); | |
672 irc_cmd_invite(irc, "invite", gaim_conversation_get_name(convo), args); | |
673 } | |
674 | |
675 | |
676 static void irc_chat_leave (GaimConnection *gc, int id) | |
677 { | |
678 struct irc_conn *irc = gc->proto_data; | |
679 GaimConversation *convo = gaim_find_chat(gc, id); | |
680 const char *args[2]; | |
681 | |
682 if (!convo) | |
683 return; | |
684 | |
685 args[0] = gaim_conversation_get_name(convo); | |
686 args[1] = NULL; | |
687 irc_cmd_part(irc, "part", gaim_conversation_get_name(convo), args); | |
688 serv_got_chat_left(gc, id); | |
689 } | |
690 | |
691 static int irc_chat_send(GaimConnection *gc, int id, const char *what, GaimMessageFlags flags) | |
692 { | |
693 struct irc_conn *irc = gc->proto_data; | |
694 GaimConversation *convo = gaim_find_chat(gc, id); | |
695 const char *args[2]; | |
696 char *tmp; | |
697 | |
698 if (!convo) { | |
699 gaim_debug(GAIM_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n"); | |
700 return -EINVAL; | |
701 } | |
702 #if 0 | |
703 if (*what == '/') { | |
704 return irc_parse_cmd(irc, convo->name, what + 1); | |
705 } | |
706 #endif | |
707 tmp = gaim_unescape_html(what); | |
708 args[0] = convo->name; | |
709 args[1] = tmp; | |
710 | |
711 irc_cmd_privmsg(irc, "msg", NULL, args); | |
712 | |
713 serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, what, time(NULL)); | |
714 g_free(tmp); | |
715 return 0; | |
716 } | |
717 | |
718 static guint irc_nick_hash(const char *nick) | |
719 { | |
720 char *lc; | |
721 guint bucket; | |
722 | |
723 lc = g_utf8_strdown(nick, -1); | |
724 bucket = g_str_hash(lc); | |
725 g_free(lc); | |
726 | |
727 return bucket; | |
728 } | |
729 | |
730 static gboolean irc_nick_equal(const char *nick1, const char *nick2) | |
731 { | |
732 return (gaim_utf8_strcasecmp(nick1, nick2) == 0); | |
733 } | |
734 | |
735 static void irc_buddy_free(struct irc_buddy *ib) | |
736 { | |
737 g_free(ib->name); | |
738 g_free(ib); | |
739 } | |
740 | |
741 static void irc_chat_set_topic(GaimConnection *gc, int id, const char *topic) | |
742 { | |
743 char *buf; | |
744 const char *name = NULL; | |
745 struct irc_conn *irc; | |
746 | |
747 irc = gc->proto_data; | |
748 name = gaim_conversation_get_name(gaim_find_chat(gc, id)); | |
749 | |
750 if (name == NULL) | |
751 return; | |
752 | |
753 buf = irc_format(irc, "vt:", "TOPIC", name, topic); | |
754 irc_send(irc, buf); | |
755 g_free(buf); | |
756 } | |
757 | |
758 static GaimRoomlist *irc_roomlist_get_list(GaimConnection *gc) | |
759 { | |
760 struct irc_conn *irc; | |
761 GList *fields = NULL; | |
762 GaimRoomlistField *f; | |
763 char *buf; | |
764 | |
765 irc = gc->proto_data; | |
766 | |
767 if (irc->roomlist) | |
768 gaim_roomlist_unref(irc->roomlist); | |
769 | |
770 irc->roomlist = gaim_roomlist_new(gaim_connection_get_account(gc)); | |
771 | |
772 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, "", "channel", TRUE); | |
773 fields = g_list_append(fields, f); | |
774 | |
775 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_INT, _("Users"), "users", FALSE); | |
776 fields = g_list_append(fields, f); | |
777 | |
778 f = gaim_roomlist_field_new(GAIM_ROOMLIST_FIELD_STRING, _("Topic"), "topic", FALSE); | |
779 fields = g_list_append(fields, f); | |
780 | |
781 gaim_roomlist_set_fields(irc->roomlist, fields); | |
782 | |
783 buf = irc_format(irc, "v", "LIST"); | |
784 irc_send(irc, buf); | |
785 g_free(buf); | |
786 | |
787 return irc->roomlist; | |
788 } | |
789 | |
790 static void irc_roomlist_cancel(GaimRoomlist *list) | |
791 { | |
792 GaimConnection *gc = gaim_account_get_connection(list->account); | |
793 struct irc_conn *irc; | |
794 | |
795 if (gc == NULL) | |
796 return; | |
797 | |
798 irc = gc->proto_data; | |
799 | |
800 gaim_roomlist_set_in_progress(list, FALSE); | |
801 | |
802 if (irc->roomlist == list) { | |
803 irc->roomlist = NULL; | |
804 gaim_roomlist_unref(list); | |
805 } | |
806 } | |
807 | |
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
808 static void irc_keepalive(GaimConnection *gc) |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
809 { |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
810 struct irc_conn *irc = gc->proto_data; |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
811 if ((time(NULL) - irc->recv_time) > PING_TIMEOUT) |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
812 irc_cmd_ping(irc, NULL, NULL, NULL); |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
813 } |
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
814 |
14192 | 815 static GaimPluginProtocolInfo prpl_info = |
816 { | |
817 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL, | |
818 NULL, /* user_splits */ | |
819 NULL, /* protocol_options */ | |
820 NO_BUDDY_ICONS, /* icon_spec */ | |
821 irc_blist_icon, /* list_icon */ | |
822 irc_blist_emblems, /* list_emblems */ | |
823 NULL, /* status_text */ | |
824 NULL, /* tooltip_text */ | |
825 irc_status_types, /* away_states */ | |
826 NULL, /* blist_node_menu */ | |
827 irc_chat_join_info, /* chat_info */ | |
828 irc_chat_info_defaults, /* chat_info_defaults */ | |
829 irc_login, /* login */ | |
830 irc_close, /* close */ | |
831 irc_im_send, /* send_im */ | |
832 NULL, /* set_info */ | |
833 NULL, /* send_typing */ | |
834 irc_get_info, /* get_info */ | |
835 irc_set_status, /* set_status */ | |
836 NULL, /* set_idle */ | |
837 NULL, /* change_passwd */ | |
838 irc_add_buddy, /* add_buddy */ | |
839 NULL, /* add_buddies */ | |
840 irc_remove_buddy, /* remove_buddy */ | |
841 NULL, /* remove_buddies */ | |
842 NULL, /* add_permit */ | |
843 NULL, /* add_deny */ | |
844 NULL, /* rem_permit */ | |
845 NULL, /* rem_deny */ | |
846 NULL, /* set_permit_deny */ | |
847 irc_chat_join, /* join_chat */ | |
848 NULL, /* reject_chat */ | |
849 irc_get_chat_name, /* get_chat_name */ | |
850 irc_chat_invite, /* chat_invite */ | |
851 irc_chat_leave, /* chat_leave */ | |
852 NULL, /* chat_whisper */ | |
853 irc_chat_send, /* chat_send */ | |
14482
a5c7db7be826
[gaim-migrate @ 17201]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14472
diff
changeset
|
854 irc_keepalive, /* keepalive */ |
14192 | 855 NULL, /* register_user */ |
856 NULL, /* get_cb_info */ | |
857 NULL, /* get_cb_away */ | |
858 NULL, /* alias_buddy */ | |
859 NULL, /* group_buddy */ | |
860 NULL, /* rename_group */ | |
861 NULL, /* buddy_free */ | |
862 NULL, /* convo_closed */ | |
863 gaim_normalize_nocase, /* normalize */ | |
864 NULL, /* set_buddy_icon */ | |
865 NULL, /* remove_group */ | |
866 NULL, /* get_cb_real_name */ | |
867 irc_chat_set_topic, /* set_chat_topic */ | |
868 NULL, /* find_blist_chat */ | |
869 irc_roomlist_get_list, /* roomlist_get_list */ | |
870 irc_roomlist_cancel, /* roomlist_cancel */ | |
871 NULL, /* roomlist_expand_category */ | |
872 NULL, /* can_receive_file */ | |
873 irc_dccsend_send_file, /* send_file */ | |
874 irc_dccsend_new_xfer, /* new_xfer */ | |
875 NULL, /* offline_message */ | |
876 NULL, /* whiteboard_prpl_ops */ | |
14542 | 877 irc_send_raw, /* send_raw */ |
14192 | 878 }; |
879 | |
14621 | 880 static gboolean load_plugin (GaimPlugin *plugin) { |
881 | |
882 gaim_signal_register(plugin, "irc-sending-text", | |
883 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
884 gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_CONNECTION), | |
885 gaim_value_new_outgoing(GAIM_TYPE_STRING)); | |
886 gaim_signal_register(plugin, "irc-receiving-text", | |
887 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, | |
888 gaim_value_new(GAIM_TYPE_SUBTYPE, GAIM_SUBTYPE_CONNECTION), | |
889 gaim_value_new_outgoing(GAIM_TYPE_STRING)); | |
890 return TRUE; | |
891 } | |
892 | |
14192 | 893 |
894 static GaimPluginInfo info = | |
895 { | |
896 GAIM_PLUGIN_MAGIC, | |
897 GAIM_MAJOR_VERSION, | |
898 GAIM_MINOR_VERSION, | |
899 GAIM_PLUGIN_PROTOCOL, /**< type */ | |
900 NULL, /**< ui_requirement */ | |
901 0, /**< flags */ | |
902 NULL, /**< dependencies */ | |
903 GAIM_PRIORITY_DEFAULT, /**< priority */ | |
904 | |
905 "prpl-irc", /**< id */ | |
906 "IRC", /**< name */ | |
907 VERSION, /**< version */ | |
908 N_("IRC Protocol Plugin"), /** summary */ | |
909 N_("The IRC Protocol Plugin that Sucks Less"), /** description */ | |
910 NULL, /**< author */ | |
911 GAIM_WEBSITE, /**< homepage */ | |
912 | |
14621 | 913 load_plugin, /**< load */ |
14192 | 914 NULL, /**< unload */ |
915 NULL, /**< destroy */ | |
916 | |
917 NULL, /**< ui_info */ | |
918 &prpl_info, /**< extra_info */ | |
919 NULL, /**< prefs_info */ | |
920 irc_actions | |
921 }; | |
922 | |
923 static void _init_plugin(GaimPlugin *plugin) | |
924 { | |
925 GaimAccountUserSplit *split; | |
926 GaimAccountOption *option; | |
927 | |
928 split = gaim_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@'); | |
929 prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); | |
930 | |
931 option = gaim_account_option_int_new(_("Port"), "port", IRC_DEFAULT_PORT); | |
932 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
933 | |
934 option = gaim_account_option_string_new(_("Encodings"), "encoding", IRC_DEFAULT_CHARSET); | |
935 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
936 | |
937 option = gaim_account_option_string_new(_("Username"), "username", ""); | |
938 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
939 | |
940 option = gaim_account_option_string_new(_("Real name"), "realname", ""); | |
941 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
942 | |
943 /* | |
944 option = gaim_account_option_string_new(_("Quit message"), "quitmsg", IRC_DEFAULT_QUIT); | |
945 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
946 */ | |
947 | |
948 option = gaim_account_option_bool_new(_("Use SSL"), "ssl", FALSE); | |
949 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); | |
950 | |
951 _irc_plugin = plugin; | |
952 | |
953 gaim_prefs_remove("/plugins/prpl/irc/quitmsg"); | |
954 gaim_prefs_remove("/plugins/prpl/irc"); | |
955 | |
956 irc_register_commands(); | |
957 } | |
958 | |
959 GAIM_INIT_PLUGIN(irc, _init_plugin, info); |