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