Mercurial > pidgin.yaz
annotate libgaim/connection.c @ 14752:4124030c3f3a
[gaim-migrate @ 17509]
Fix the situation (I think it will only happen while shutting down) where GaimBuddy->proto_data could be accessed after it is freed.
Specifically, it happened when gtkblist was requesting the emblems from the msn prpl.
Big thanks to henningn for noticing the funkiness in valgrind.
I discovered that there is no function to retrieve all the buddies for an account (shock, horror) - so I modified gaim_find_buddies() to return all the account buddies if "name" is NULL.
committer: Tailor Script <tailor@pidgin.im>
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Wed, 18 Oct 2006 03:32:14 +0000 |
parents | f23506e8f812 |
children | 71404dbedabc |
rev | line source |
---|---|
14192 | 1 /** |
2 * @file connection.c Connection API | |
3 * @ingroup core | |
4 * | |
5 * gaim | |
6 * | |
7 * Gaim is the legal property of its developers, whose names are too numerous | |
8 * to list here. Please refer to the COPYRIGHT file distributed with this | |
9 * source distribution. | |
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 #include "internal.h" | |
26 #include "account.h" | |
27 #include "blist.h" | |
28 #include "connection.h" | |
29 #include "dbus-maybe.h" | |
30 #include "debug.h" | |
31 #include "log.h" | |
32 #include "notify.h" | |
33 #include "prefs.h" | |
34 #include "request.h" | |
35 #include "server.h" | |
36 #include "signals.h" | |
37 #include "util.h" | |
38 | |
39 static GList *connections = NULL; | |
40 static GList *connections_connecting = NULL; | |
41 static GaimConnectionUiOps *connection_ui_ops = NULL; | |
42 | |
43 static int connections_handle; | |
44 | |
45 static gboolean | |
46 send_keepalive(gpointer data) | |
47 { | |
48 GaimConnection *gc = data; | |
49 GaimPluginProtocolInfo *prpl_info = NULL; | |
50 | |
51 if (gc != NULL && gc->prpl != NULL) | |
52 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
53 | |
54 if (prpl_info && prpl_info->keepalive) | |
55 prpl_info->keepalive(gc); | |
56 | |
57 return TRUE; | |
58 } | |
59 | |
60 static void | |
61 update_keepalive(GaimConnection *gc, gboolean on) | |
62 { | |
14483
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
63 GaimPluginProtocolInfo *prpl_info = NULL; |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
64 |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
65 if (gc != NULL && gc->prpl != NULL) |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
66 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
67 |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
68 if (!prpl_info || !prpl_info->keepalive) |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
69 return; |
af856551902b
[gaim-migrate @ 17202]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14192
diff
changeset
|
70 |
14192 | 71 if (on && !gc->keepalive) |
72 { | |
73 gaim_debug_info("connection", "Activating keepalive.\n"); | |
74 gc->keepalive = gaim_timeout_add(30000, send_keepalive, gc); | |
75 } | |
76 else if (!on && gc->keepalive > 0) | |
77 { | |
78 gaim_debug_info("connection", "Deactivating keepalive.\n"); | |
79 gaim_timeout_remove(gc->keepalive); | |
80 gc->keepalive = 0; | |
81 } | |
82 } | |
83 | |
84 void | |
85 gaim_connection_new(GaimAccount *account, gboolean regist, const char *password) | |
86 { | |
87 GaimConnection *gc; | |
88 GaimPlugin *prpl; | |
89 GaimPluginProtocolInfo *prpl_info; | |
90 | |
91 g_return_if_fail(account != NULL); | |
92 | |
93 if (!gaim_account_is_disconnected(account)) | |
94 return; | |
95 | |
96 prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); | |
97 | |
98 if (prpl != NULL) | |
99 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); | |
100 else { | |
101 gchar *message; | |
102 | |
103 message = g_strdup_printf(_("Missing protocol plugin for %s"), | |
104 gaim_account_get_username(account)); | |
105 gaim_notify_error(NULL, regist ? _("Registration Error") : | |
106 _("Connection Error"), message, NULL); | |
107 g_free(message); | |
108 return; | |
109 } | |
110 | |
111 if (regist) | |
112 { | |
113 if (prpl_info->register_user == NULL) | |
114 return; | |
115 } | |
116 else | |
117 { | |
118 if (((password == NULL) || (*password == '\0')) && | |
119 !(prpl_info->options & OPT_PROTO_NO_PASSWORD) && | |
120 !(prpl_info->options & OPT_PROTO_PASSWORD_OPTIONAL)) | |
121 { | |
122 gaim_debug_error("connection", "Can not connect to account %s without " | |
123 "a password.\n", gaim_account_get_username(account)); | |
124 return; | |
125 } | |
126 } | |
127 | |
128 gc = g_new0(GaimConnection, 1); | |
129 GAIM_DBUS_REGISTER_POINTER(gc, GaimConnection); | |
130 | |
131 gc->prpl = prpl; | |
132 if ((password != NULL) && (*password != '\0')) | |
133 gc->password = g_strdup(password); | |
134 gaim_connection_set_account(gc, account); | |
135 gaim_connection_set_state(gc, GAIM_CONNECTING); | |
136 connections = g_list_append(connections, gc); | |
137 gaim_account_set_connection(account, gc); | |
138 | |
139 gaim_signal_emit(gaim_connections_get_handle(), "signing-on", gc); | |
140 | |
141 if (regist) | |
142 { | |
143 gaim_debug_info("connection", "Registering. gc = %p\n", gc); | |
144 | |
145 /* set this so we don't auto-reconnect after registering */ | |
146 gc->wants_to_die = TRUE; | |
147 | |
148 prpl_info->register_user(account); | |
149 } | |
150 else | |
151 { | |
152 gaim_debug_info("connection", "Connecting. gc = %p\n", gc); | |
153 | |
154 gaim_signal_emit(gaim_accounts_get_handle(), "account-connecting", account); | |
155 prpl_info->login(account); | |
156 } | |
157 } | |
158 | |
159 void | |
160 gaim_connection_destroy(GaimConnection *gc) | |
161 { | |
162 GaimAccount *account; | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
163 GSList *buddies, *tmp; |
14192 | 164 #if 0 |
165 GList *wins; | |
166 #endif | |
167 GaimPluginProtocolInfo *prpl_info = NULL; | |
168 gboolean remove = FALSE; | |
169 | |
170 g_return_if_fail(gc != NULL); | |
171 | |
172 account = gaim_connection_get_account(gc); | |
173 | |
174 gaim_debug_info("connection", "Disconnecting connection %p\n", gc); | |
175 | |
176 if (gaim_connection_get_state(gc) != GAIM_CONNECTING) | |
177 remove = TRUE; | |
178 | |
179 gaim_signal_emit(gaim_connections_get_handle(), "signing-off", gc); | |
180 | |
181 while (gc->buddy_chats) | |
182 { | |
183 GaimConversation *b = gc->buddy_chats->data; | |
184 | |
185 gc->buddy_chats = g_slist_remove(gc->buddy_chats, b); | |
186 gaim_conv_chat_left(GAIM_CONV_CHAT(b)); | |
187 } | |
188 | |
189 update_keepalive(gc, FALSE); | |
190 | |
191 if (gc->prpl != NULL) | |
192 { | |
193 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); | |
194 | |
195 if (prpl_info->close) | |
196 (prpl_info->close)(gc); | |
197 } | |
198 | |
14752
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
199 /* Clear out the proto data that was freed in the prpl close method*/ |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
200 buddies = gaim_find_buddies(account, NULL); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
201 for (tmp = buddies; tmp; tmp = tmp->next) { |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
202 GaimBuddy *buddy = tmp->data; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
203 buddy->proto_data = NULL; |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
204 } |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
205 g_slist_free(buddies); |
4124030c3f3a
[gaim-migrate @ 17509]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14607
diff
changeset
|
206 |
14192 | 207 connections = g_list_remove(connections, gc); |
208 | |
209 gaim_connection_set_state(gc, GAIM_DISCONNECTED); | |
210 | |
211 if (remove) | |
212 gaim_blist_remove_account(account); | |
213 | |
214 gaim_signal_emit(gaim_connections_get_handle(), "signed-off", gc); | |
215 | |
216 #if 0 | |
217 /* see comment later in file on if 0'd same code */ | |
218 /* | |
219 * XXX This is a hack! Remove this and replace it with a better event | |
220 * notification system. | |
221 */ | |
222 for (wins = gaim_get_windows(); wins != NULL; wins = wins->next) { | |
223 GaimConvWindow *win = (GaimConvWindow *)wins->data; | |
224 gaim_conversation_update(gaim_conv_window_get_conversation_at(win, 0), | |
225 GAIM_CONV_ACCOUNT_OFFLINE); | |
226 } | |
227 #endif | |
228 | |
229 gaim_request_close_with_handle(gc); | |
230 gaim_notify_close_with_handle(gc); | |
231 | |
232 gaim_debug_info("connection", "Destroying connection %p\n", gc); | |
233 | |
234 gaim_account_set_connection(account, NULL); | |
235 | |
236 g_free(gc->password); | |
237 g_free(gc->display_name); | |
238 | |
239 if (gc->disconnect_timeout) | |
240 gaim_timeout_remove(gc->disconnect_timeout); | |
241 | |
242 GAIM_DBUS_UNREGISTER_POINTER(gc); | |
243 g_free(gc); | |
244 } | |
245 | |
246 /* | |
247 * d:)->-< | |
248 * | |
249 * d:O-\-< | |
250 * | |
251 * d:D-/-< | |
252 * | |
253 * d8D->-< DANCE! | |
254 */ | |
255 | |
256 void | |
257 gaim_connection_set_state(GaimConnection *gc, GaimConnectionState state) | |
258 { | |
259 GaimConnectionUiOps *ops; | |
260 | |
261 g_return_if_fail(gc != NULL); | |
262 | |
263 if (gc->state == state) | |
264 return; | |
265 | |
266 gc->state = state; | |
267 | |
268 ops = gaim_connections_get_ui_ops(); | |
269 | |
270 if (gc->state == GAIM_CONNECTING) { | |
271 connections_connecting = g_list_append(connections_connecting, gc); | |
272 } | |
273 else { | |
274 connections_connecting = g_list_remove(connections_connecting, gc); | |
275 } | |
276 | |
277 if (gc->state == GAIM_CONNECTED) { | |
278 GaimAccount *account; | |
279 GaimPresence *presence; | |
280 | |
281 account = gaim_connection_get_account(gc); | |
282 presence = gaim_account_get_presence(account); | |
283 | |
284 /* Set the time the account came online */ | |
285 gaim_presence_set_login_time(presence, time(NULL)); | |
286 | |
287 if (gaim_prefs_get_bool("/core/logging/log_system")) | |
288 { | |
289 GaimLog *log = gaim_account_get_log(account, TRUE); | |
290 | |
291 if (log != NULL) | |
292 { | |
293 char *msg = g_strdup_printf(_("+++ %s signed on"), | |
294 gaim_account_get_username(account)); | |
295 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, | |
296 gaim_account_get_username(account), | |
297 gaim_presence_get_login_time(presence), | |
298 msg); | |
299 g_free(msg); | |
300 } | |
301 } | |
302 | |
303 if (ops != NULL && ops->connected != NULL) | |
304 ops->connected(gc); | |
305 | |
306 gaim_blist_add_account(account); | |
307 | |
308 gaim_signal_emit(gaim_connections_get_handle(), "signed-on", gc); | |
309 | |
310 serv_set_permit_deny(gc); | |
311 | |
312 update_keepalive(gc, TRUE); | |
313 | |
314 if (gaim_account_get_user_info(account) != NULL) | |
315 serv_set_info(gc, gaim_account_get_user_info(account)); | |
316 } | |
317 else if (gc->state == GAIM_DISCONNECTED) { | |
318 GaimAccount *account = gaim_connection_get_account(gc); | |
319 | |
320 if (gaim_prefs_get_bool("/core/logging/log_system")) | |
321 { | |
322 GaimLog *log = gaim_account_get_log(account, FALSE); | |
323 | |
324 if (log != NULL) | |
325 { | |
326 char *msg = g_strdup_printf(_("+++ %s signed off"), | |
327 gaim_account_get_username(account)); | |
328 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, | |
329 gaim_account_get_username(account), time(NULL), | |
330 msg); | |
331 g_free(msg); | |
332 } | |
333 } | |
334 | |
335 gaim_account_destroy_log(account); | |
336 | |
337 if (ops != NULL && ops->disconnected != NULL) | |
338 ops->disconnected(gc); | |
339 } | |
340 } | |
341 | |
342 void | |
343 gaim_connection_set_account(GaimConnection *gc, GaimAccount *account) | |
344 { | |
345 g_return_if_fail(gc != NULL); | |
346 g_return_if_fail(account != NULL); | |
347 | |
348 gc->account = account; | |
349 } | |
350 | |
351 void | |
352 gaim_connection_set_display_name(GaimConnection *gc, const char *name) | |
353 { | |
354 g_return_if_fail(gc != NULL); | |
355 | |
356 g_free(gc->display_name); | |
357 gc->display_name = g_strdup(name); | |
358 } | |
359 | |
360 GaimConnectionState | |
361 gaim_connection_get_state(const GaimConnection *gc) | |
362 { | |
363 g_return_val_if_fail(gc != NULL, GAIM_DISCONNECTED); | |
364 | |
365 return gc->state; | |
366 } | |
367 | |
368 GaimAccount * | |
369 gaim_connection_get_account(const GaimConnection *gc) | |
370 { | |
371 g_return_val_if_fail(gc != NULL, NULL); | |
372 | |
373 return gc->account; | |
374 } | |
375 | |
376 const char * | |
377 gaim_connection_get_password(const GaimConnection *gc) | |
378 { | |
379 g_return_val_if_fail(gc != NULL, NULL); | |
380 | |
381 return gc->password; | |
382 } | |
383 | |
384 const char * | |
385 gaim_connection_get_display_name(const GaimConnection *gc) | |
386 { | |
387 g_return_val_if_fail(gc != NULL, NULL); | |
388 | |
389 return gc->display_name; | |
390 } | |
391 | |
392 void | |
393 gaim_connection_update_progress(GaimConnection *gc, const char *text, | |
394 size_t step, size_t count) | |
395 { | |
396 GaimConnectionUiOps *ops; | |
397 | |
398 g_return_if_fail(gc != NULL); | |
399 g_return_if_fail(text != NULL); | |
400 g_return_if_fail(step < count); | |
401 g_return_if_fail(count > 1); | |
402 | |
403 ops = gaim_connections_get_ui_ops(); | |
404 | |
405 if (ops != NULL && ops->connect_progress != NULL) | |
406 ops->connect_progress(gc, text, step, count); | |
407 } | |
408 | |
409 void | |
410 gaim_connection_notice(GaimConnection *gc, const char *text) | |
411 { | |
412 GaimConnectionUiOps *ops; | |
413 | |
414 g_return_if_fail(gc != NULL); | |
415 g_return_if_fail(text != NULL); | |
416 | |
417 ops = gaim_connections_get_ui_ops(); | |
418 | |
419 if (ops != NULL && ops->notice != NULL) | |
420 ops->notice(gc, text); | |
421 } | |
422 | |
423 static gboolean | |
424 gaim_connection_disconnect_cb(gpointer data) | |
425 { | |
426 GaimAccount *account = data; | |
427 char *password = g_strdup(gaim_account_get_password(account)); | |
428 gaim_account_disconnect(account); | |
429 gaim_account_set_password(account, password); | |
430 g_free(password); | |
431 return FALSE; | |
432 } | |
433 | |
434 void | |
435 gaim_connection_error(GaimConnection *gc, const char *text) | |
436 { | |
437 GaimConnectionUiOps *ops; | |
438 | |
439 g_return_if_fail(gc != NULL); | |
440 g_return_if_fail(text != NULL); | |
441 | |
442 /* If we've already got one error, we don't need any more */ | |
443 if (gc->disconnect_timeout) | |
444 return; | |
445 | |
446 ops = gaim_connections_get_ui_ops(); | |
447 | |
448 if (ops != NULL) { | |
449 if (ops->report_disconnect != NULL) | |
450 ops->report_disconnect(gc, text); | |
451 } | |
452 | |
453 gc->disconnect_timeout = gaim_timeout_add(0, gaim_connection_disconnect_cb, | |
454 gaim_connection_get_account(gc)); | |
455 } | |
456 | |
457 void | |
458 gaim_connections_disconnect_all(void) | |
459 { | |
460 GList *l; | |
461 GaimConnection *gc; | |
462 | |
463 while ((l = gaim_connections_get_all()) != NULL) { | |
464 gc = l->data; | |
465 gc->wants_to_die = TRUE; | |
466 gaim_account_disconnect(gc->account); | |
467 } | |
468 } | |
469 | |
470 GList * | |
471 gaim_connections_get_all(void) | |
472 { | |
473 return connections; | |
474 } | |
475 | |
476 GList * | |
477 gaim_connections_get_connecting(void) | |
478 { | |
479 return connections_connecting; | |
480 } | |
481 | |
482 void | |
483 gaim_connections_set_ui_ops(GaimConnectionUiOps *ops) | |
484 { | |
485 connection_ui_ops = ops; | |
486 } | |
487 | |
488 GaimConnectionUiOps * | |
489 gaim_connections_get_ui_ops(void) | |
490 { | |
491 return connection_ui_ops; | |
492 } | |
493 | |
494 void | |
495 gaim_connections_init(void) | |
496 { | |
497 void *handle = gaim_connections_get_handle(); | |
498 | |
499 gaim_signal_register(handle, "signing-on", | |
500 gaim_marshal_VOID__POINTER, NULL, 1, | |
501 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
502 GAIM_SUBTYPE_CONNECTION)); | |
503 | |
504 gaim_signal_register(handle, "signed-on", | |
505 gaim_marshal_VOID__POINTER, NULL, 1, | |
506 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
507 GAIM_SUBTYPE_CONNECTION)); | |
508 | |
509 gaim_signal_register(handle, "signing-off", | |
510 gaim_marshal_VOID__POINTER, NULL, 1, | |
511 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
512 GAIM_SUBTYPE_CONNECTION)); | |
513 | |
514 gaim_signal_register(handle, "signed-off", | |
515 gaim_marshal_VOID__POINTER, NULL, 1, | |
516 gaim_value_new(GAIM_TYPE_SUBTYPE, | |
517 GAIM_SUBTYPE_CONNECTION)); | |
518 } | |
519 | |
520 void | |
521 gaim_connections_uninit(void) | |
522 { | |
523 gaim_signals_unregister_by_instance(gaim_connections_get_handle()); | |
524 } | |
525 | |
526 void * | |
527 gaim_connections_get_handle(void) | |
528 { | |
529 return &connections_handle; | |
530 } |