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