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