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