| |
1 /** |
| |
2 * @file status.c Status 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 |
| |
27 #include "blist.h" |
| |
28 #include "core.h" |
| |
29 #include "dbus-maybe.h" |
| |
30 #include "debug.h" |
| |
31 #include "notify.h" |
| |
32 #include "prefs.h" |
| |
33 #include "status.h" |
| |
34 |
| |
35 /** |
| |
36 * A type of status. |
| |
37 */ |
| |
38 struct _GaimStatusType |
| |
39 { |
| |
40 GaimStatusPrimitive primitive; |
| |
41 |
| |
42 char *id; |
| |
43 char *name; |
| |
44 char *primary_attr_id; |
| |
45 |
| |
46 gboolean saveable; |
| |
47 gboolean user_settable; |
| |
48 gboolean independent; |
| |
49 |
| |
50 GList *attrs; |
| |
51 }; |
| |
52 |
| |
53 /** |
| |
54 * A status attribute. |
| |
55 */ |
| |
56 struct _GaimStatusAttr |
| |
57 { |
| |
58 char *id; |
| |
59 char *name; |
| |
60 GaimValue *value_type; |
| |
61 }; |
| |
62 |
| |
63 /** |
| |
64 * A list of statuses. |
| |
65 */ |
| |
66 struct _GaimPresence |
| |
67 { |
| |
68 GaimPresenceContext context; |
| |
69 |
| |
70 gboolean idle; |
| |
71 time_t idle_time; |
| |
72 time_t login_time; |
| |
73 |
| |
74 GList *statuses; |
| |
75 GHashTable *status_table; |
| |
76 |
| |
77 GaimStatus *active_status; |
| |
78 |
| |
79 union |
| |
80 { |
| |
81 GaimAccount *account; |
| |
82 |
| |
83 struct |
| |
84 { |
| |
85 GaimConversation *conv; |
| |
86 char *user; |
| |
87 |
| |
88 } chat; |
| |
89 |
| |
90 struct |
| |
91 { |
| |
92 GaimAccount *account; |
| |
93 char *name; |
| |
94 size_t ref_count; |
| |
95 GList *buddies; |
| |
96 |
| |
97 } buddy; |
| |
98 |
| |
99 } u; |
| |
100 }; |
| |
101 |
| |
102 /** |
| |
103 * An active status. |
| |
104 */ |
| |
105 struct _GaimStatus |
| |
106 { |
| |
107 GaimStatusType *type; |
| |
108 GaimPresence *presence; |
| |
109 |
| |
110 const char *title; |
| |
111 |
| |
112 gboolean active; |
| |
113 |
| |
114 GHashTable *attr_values; |
| |
115 }; |
| |
116 |
| |
117 typedef struct |
| |
118 { |
| |
119 GaimAccount *account; |
| |
120 char *name; |
| |
121 } GaimStatusBuddyKey; |
| |
122 |
| |
123 static int primitive_scores[] = |
| |
124 { |
| |
125 0, /* unset */ |
| |
126 -500, /* offline */ |
| |
127 100, /* available */ |
| |
128 -75, /* unavailable */ |
| |
129 -50, /* invisible */ |
| |
130 -100, /* away */ |
| |
131 -200, /* extended away */ |
| |
132 -400, /* mobile */ |
| |
133 -10, /* idle, special case. */ |
| |
134 -5 /* idle time, special case. */ |
| |
135 }; |
| |
136 |
| |
137 static GHashTable *buddy_presences = NULL; |
| |
138 |
| |
139 #define SCORE_IDLE 8 |
| |
140 #define SCORE_IDLE_TIME 9 |
| |
141 |
| |
142 /************************************************************************** |
| |
143 * GaimStatusPrimitive API |
| |
144 **************************************************************************/ |
| |
145 static struct GaimStatusPrimitiveMap |
| |
146 { |
| |
147 GaimStatusPrimitive type; |
| |
148 const char *id; |
| |
149 const char *name; |
| |
150 |
| |
151 } const status_primitive_map[] = |
| |
152 { |
| |
153 { GAIM_STATUS_UNSET, "unset", N_("Unset") }, |
| |
154 { GAIM_STATUS_OFFLINE, "offline", N_("Offline") }, |
| |
155 { GAIM_STATUS_AVAILABLE, "available", N_("Available") }, |
| |
156 { GAIM_STATUS_UNAVAILABLE, "unavailable", N_("Unavailable") }, |
| |
157 { GAIM_STATUS_INVISIBLE, "invisible", N_("Invisible") }, |
| |
158 { GAIM_STATUS_AWAY, "away", N_("Away") }, |
| |
159 { GAIM_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended Away") }, |
| |
160 { GAIM_STATUS_MOBILE, "mobile", N_("Mobile") } |
| |
161 }; |
| |
162 |
| |
163 const char * |
| |
164 gaim_primitive_get_id_from_type(GaimStatusPrimitive type) |
| |
165 { |
| |
166 int i; |
| |
167 |
| |
168 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) |
| |
169 { |
| |
170 if (type == status_primitive_map[i].type) |
| |
171 return status_primitive_map[i].id; |
| |
172 } |
| |
173 |
| |
174 return status_primitive_map[0].id; |
| |
175 } |
| |
176 |
| |
177 const char * |
| |
178 gaim_primitive_get_name_from_type(GaimStatusPrimitive type) |
| |
179 { |
| |
180 int i; |
| |
181 |
| |
182 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) |
| |
183 { |
| |
184 if (type == status_primitive_map[i].type) |
| |
185 return _(status_primitive_map[i].name); |
| |
186 } |
| |
187 |
| |
188 return _(status_primitive_map[0].name); |
| |
189 } |
| |
190 |
| |
191 GaimStatusPrimitive |
| |
192 gaim_primitive_get_type_from_id(const char *id) |
| |
193 { |
| |
194 int i; |
| |
195 |
| |
196 g_return_val_if_fail(id != NULL, GAIM_STATUS_UNSET); |
| |
197 |
| |
198 for (i = 0; i < GAIM_STATUS_NUM_PRIMITIVES; i++) |
| |
199 { |
| |
200 if (!strcmp(id, status_primitive_map[i].id)) |
| |
201 return status_primitive_map[i].type; |
| |
202 } |
| |
203 |
| |
204 return status_primitive_map[0].type; |
| |
205 } |
| |
206 |
| |
207 |
| |
208 /************************************************************************** |
| |
209 * GaimStatusType API |
| |
210 **************************************************************************/ |
| |
211 GaimStatusType * |
| |
212 gaim_status_type_new_full(GaimStatusPrimitive primitive, const char *id, |
| |
213 const char *name, gboolean saveable, |
| |
214 gboolean user_settable, gboolean independent) |
| |
215 { |
| |
216 GaimStatusType *status_type; |
| |
217 |
| |
218 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL); |
| |
219 |
| |
220 status_type = g_new0(GaimStatusType, 1); |
| |
221 GAIM_DBUS_REGISTER_POINTER(status_type, GaimStatusType); |
| |
222 |
| |
223 status_type->primitive = primitive; |
| |
224 status_type->saveable = saveable; |
| |
225 status_type->user_settable = user_settable; |
| |
226 status_type->independent = independent; |
| |
227 |
| |
228 if (id != NULL) |
| |
229 status_type->id = g_strdup(id); |
| |
230 else |
| |
231 status_type->id = g_strdup(gaim_primitive_get_id_from_type(primitive)); |
| |
232 |
| |
233 if (name != NULL) |
| |
234 status_type->name = g_strdup(name); |
| |
235 else |
| |
236 status_type->name = g_strdup(gaim_primitive_get_name_from_type(primitive)); |
| |
237 |
| |
238 return status_type; |
| |
239 } |
| |
240 |
| |
241 GaimStatusType * |
| |
242 gaim_status_type_new(GaimStatusPrimitive primitive, const char *id, |
| |
243 const char *name, gboolean user_settable) |
| |
244 { |
| |
245 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL); |
| |
246 |
| |
247 return gaim_status_type_new_full(primitive, id, name, FALSE, |
| |
248 user_settable, FALSE); |
| |
249 } |
| |
250 |
| |
251 GaimStatusType * |
| |
252 gaim_status_type_new_with_attrs(GaimStatusPrimitive primitive, |
| |
253 const char *id, const char *name, |
| |
254 gboolean saveable, gboolean user_settable, |
| |
255 gboolean independent, const char *attr_id, |
| |
256 const char *attr_name, GaimValue *attr_value, |
| |
257 ...) |
| |
258 { |
| |
259 GaimStatusType *status_type; |
| |
260 va_list args; |
| |
261 |
| |
262 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, NULL); |
| |
263 g_return_val_if_fail(attr_id != NULL, NULL); |
| |
264 g_return_val_if_fail(attr_name != NULL, NULL); |
| |
265 g_return_val_if_fail(attr_value != NULL, NULL); |
| |
266 |
| |
267 status_type = gaim_status_type_new_full(primitive, id, name, saveable, |
| |
268 user_settable, independent); |
| |
269 |
| |
270 /* Add the first attribute */ |
| |
271 gaim_status_type_add_attr(status_type, attr_id, attr_name, attr_value); |
| |
272 |
| |
273 va_start(args, attr_value); |
| |
274 gaim_status_type_add_attrs_vargs(status_type, args); |
| |
275 va_end(args); |
| |
276 |
| |
277 return status_type; |
| |
278 } |
| |
279 |
| |
280 void |
| |
281 gaim_status_type_destroy(GaimStatusType *status_type) |
| |
282 { |
| |
283 g_return_if_fail(status_type != NULL); |
| |
284 |
| |
285 g_free(status_type->id); |
| |
286 g_free(status_type->name); |
| |
287 g_free(status_type->primary_attr_id); |
| |
288 |
| |
289 g_list_foreach(status_type->attrs, (GFunc)gaim_status_attr_destroy, NULL); |
| |
290 g_list_free(status_type->attrs); |
| |
291 |
| |
292 GAIM_DBUS_UNREGISTER_POINTER(status_type); |
| |
293 g_free(status_type); |
| |
294 } |
| |
295 |
| |
296 void |
| |
297 gaim_status_type_set_primary_attr(GaimStatusType *status_type, const char *id) |
| |
298 { |
| |
299 g_return_if_fail(status_type != NULL); |
| |
300 |
| |
301 g_free(status_type->primary_attr_id); |
| |
302 status_type->primary_attr_id = g_strdup(id); |
| |
303 } |
| |
304 |
| |
305 void |
| |
306 gaim_status_type_add_attr(GaimStatusType *status_type, const char *id, |
| |
307 const char *name, GaimValue *value) |
| |
308 { |
| |
309 GaimStatusAttr *attr; |
| |
310 |
| |
311 g_return_if_fail(status_type != NULL); |
| |
312 g_return_if_fail(id != NULL); |
| |
313 g_return_if_fail(name != NULL); |
| |
314 g_return_if_fail(value != NULL); |
| |
315 |
| |
316 attr = gaim_status_attr_new(id, name, value); |
| |
317 |
| |
318 status_type->attrs = g_list_append(status_type->attrs, attr); |
| |
319 } |
| |
320 |
| |
321 void |
| |
322 gaim_status_type_add_attrs_vargs(GaimStatusType *status_type, va_list args) |
| |
323 { |
| |
324 const char *id, *name; |
| |
325 GaimValue *value; |
| |
326 |
| |
327 g_return_if_fail(status_type != NULL); |
| |
328 |
| |
329 while ((id = va_arg(args, const char *)) != NULL) |
| |
330 { |
| |
331 name = va_arg(args, const char *); |
| |
332 g_return_if_fail(name != NULL); |
| |
333 |
| |
334 value = va_arg(args, GaimValue *); |
| |
335 g_return_if_fail(value != NULL); |
| |
336 |
| |
337 gaim_status_type_add_attr(status_type, id, name, value); |
| |
338 } |
| |
339 } |
| |
340 |
| |
341 void |
| |
342 gaim_status_type_add_attrs(GaimStatusType *status_type, const char *id, |
| |
343 const char *name, GaimValue *value, ...) |
| |
344 { |
| |
345 va_list args; |
| |
346 |
| |
347 g_return_if_fail(status_type != NULL); |
| |
348 g_return_if_fail(id != NULL); |
| |
349 g_return_if_fail(name != NULL); |
| |
350 g_return_if_fail(value != NULL); |
| |
351 |
| |
352 /* Add the first attribute */ |
| |
353 gaim_status_type_add_attr(status_type, id, name, value); |
| |
354 |
| |
355 va_start(args, value); |
| |
356 gaim_status_type_add_attrs_vargs(status_type, args); |
| |
357 va_end(args); |
| |
358 } |
| |
359 |
| |
360 GaimStatusPrimitive |
| |
361 gaim_status_type_get_primitive(const GaimStatusType *status_type) |
| |
362 { |
| |
363 g_return_val_if_fail(status_type != NULL, GAIM_STATUS_UNSET); |
| |
364 |
| |
365 return status_type->primitive; |
| |
366 } |
| |
367 |
| |
368 const char * |
| |
369 gaim_status_type_get_id(const GaimStatusType *status_type) |
| |
370 { |
| |
371 g_return_val_if_fail(status_type != NULL, NULL); |
| |
372 |
| |
373 return status_type->id; |
| |
374 } |
| |
375 |
| |
376 const char * |
| |
377 gaim_status_type_get_name(const GaimStatusType *status_type) |
| |
378 { |
| |
379 g_return_val_if_fail(status_type != NULL, NULL); |
| |
380 |
| |
381 return status_type->name; |
| |
382 } |
| |
383 |
| |
384 gboolean |
| |
385 gaim_status_type_is_saveable(const GaimStatusType *status_type) |
| |
386 { |
| |
387 g_return_val_if_fail(status_type != NULL, FALSE); |
| |
388 |
| |
389 return status_type->saveable; |
| |
390 } |
| |
391 |
| |
392 gboolean |
| |
393 gaim_status_type_is_user_settable(const GaimStatusType *status_type) |
| |
394 { |
| |
395 g_return_val_if_fail(status_type != NULL, FALSE); |
| |
396 |
| |
397 return status_type->user_settable; |
| |
398 } |
| |
399 |
| |
400 gboolean |
| |
401 gaim_status_type_is_independent(const GaimStatusType *status_type) |
| |
402 { |
| |
403 g_return_val_if_fail(status_type != NULL, FALSE); |
| |
404 |
| |
405 return status_type->independent; |
| |
406 } |
| |
407 |
| |
408 gboolean |
| |
409 gaim_status_type_is_exclusive(const GaimStatusType *status_type) |
| |
410 { |
| |
411 g_return_val_if_fail(status_type != NULL, FALSE); |
| |
412 |
| |
413 return !status_type->independent; |
| |
414 } |
| |
415 |
| |
416 gboolean |
| |
417 gaim_status_type_is_available(const GaimStatusType *status_type) |
| |
418 { |
| |
419 GaimStatusPrimitive primitive; |
| |
420 |
| |
421 g_return_val_if_fail(status_type != NULL, FALSE); |
| |
422 |
| |
423 primitive = gaim_status_type_get_primitive(status_type); |
| |
424 |
| |
425 return (primitive == GAIM_STATUS_AVAILABLE); |
| |
426 } |
| |
427 |
| |
428 const char * |
| |
429 gaim_status_type_get_primary_attr(const GaimStatusType *status_type) |
| |
430 { |
| |
431 g_return_val_if_fail(status_type != NULL, NULL); |
| |
432 |
| |
433 return status_type->primary_attr_id; |
| |
434 } |
| |
435 |
| |
436 GaimStatusAttr * |
| |
437 gaim_status_type_get_attr(const GaimStatusType *status_type, const char *id) |
| |
438 { |
| |
439 GList *l; |
| |
440 |
| |
441 g_return_val_if_fail(status_type != NULL, NULL); |
| |
442 g_return_val_if_fail(id != NULL, NULL); |
| |
443 |
| |
444 for (l = status_type->attrs; l != NULL; l = l->next) |
| |
445 { |
| |
446 GaimStatusAttr *attr = (GaimStatusAttr *)l->data; |
| |
447 |
| |
448 if (!strcmp(gaim_status_attr_get_id(attr), id)) |
| |
449 return attr; |
| |
450 } |
| |
451 |
| |
452 return NULL; |
| |
453 } |
| |
454 |
| |
455 const GList * |
| |
456 gaim_status_type_get_attrs(const GaimStatusType *status_type) |
| |
457 { |
| |
458 g_return_val_if_fail(status_type != NULL, NULL); |
| |
459 |
| |
460 return status_type->attrs; |
| |
461 } |
| |
462 |
| |
463 const GaimStatusType * |
| |
464 gaim_status_type_find_with_id(GList *status_types, const char *id) |
| |
465 { |
| |
466 GaimStatusType *status_type; |
| |
467 |
| |
468 g_return_val_if_fail(id != NULL, NULL); |
| |
469 |
| |
470 while (status_types != NULL) |
| |
471 { |
| |
472 status_type = status_types->data; |
| |
473 |
| |
474 if (!strcmp(id, status_type->id)) |
| |
475 return status_type; |
| |
476 |
| |
477 status_types = status_types->next; |
| |
478 } |
| |
479 |
| |
480 return NULL; |
| |
481 } |
| |
482 |
| |
483 |
| |
484 /************************************************************************** |
| |
485 * GaimStatusAttr API |
| |
486 **************************************************************************/ |
| |
487 GaimStatusAttr * |
| |
488 gaim_status_attr_new(const char *id, const char *name, GaimValue *value_type) |
| |
489 { |
| |
490 GaimStatusAttr *attr; |
| |
491 |
| |
492 g_return_val_if_fail(id != NULL, NULL); |
| |
493 g_return_val_if_fail(name != NULL, NULL); |
| |
494 g_return_val_if_fail(value_type != NULL, NULL); |
| |
495 |
| |
496 attr = g_new0(GaimStatusAttr, 1); |
| |
497 GAIM_DBUS_REGISTER_POINTER(attr, GaimStatusAttr); |
| |
498 |
| |
499 attr->id = g_strdup(id); |
| |
500 attr->name = g_strdup(name); |
| |
501 attr->value_type = value_type; |
| |
502 |
| |
503 return attr; |
| |
504 } |
| |
505 |
| |
506 void |
| |
507 gaim_status_attr_destroy(GaimStatusAttr *attr) |
| |
508 { |
| |
509 g_return_if_fail(attr != NULL); |
| |
510 |
| |
511 g_free(attr->id); |
| |
512 g_free(attr->name); |
| |
513 |
| |
514 gaim_value_destroy(attr->value_type); |
| |
515 |
| |
516 GAIM_DBUS_UNREGISTER_POINTER(attr); |
| |
517 g_free(attr); |
| |
518 } |
| |
519 |
| |
520 const char * |
| |
521 gaim_status_attr_get_id(const GaimStatusAttr *attr) |
| |
522 { |
| |
523 g_return_val_if_fail(attr != NULL, NULL); |
| |
524 |
| |
525 return attr->id; |
| |
526 } |
| |
527 |
| |
528 const char * |
| |
529 gaim_status_attr_get_name(const GaimStatusAttr *attr) |
| |
530 { |
| |
531 g_return_val_if_fail(attr != NULL, NULL); |
| |
532 |
| |
533 return attr->name; |
| |
534 } |
| |
535 |
| |
536 GaimValue * |
| |
537 gaim_status_attr_get_value(const GaimStatusAttr *attr) |
| |
538 { |
| |
539 g_return_val_if_fail(attr != NULL, NULL); |
| |
540 |
| |
541 return attr->value_type; |
| |
542 } |
| |
543 |
| |
544 |
| |
545 /************************************************************************** |
| |
546 * GaimStatus API |
| |
547 **************************************************************************/ |
| |
548 GaimStatus * |
| |
549 gaim_status_new(GaimStatusType *status_type, GaimPresence *presence) |
| |
550 { |
| |
551 GaimStatus *status; |
| |
552 const GList *l; |
| |
553 |
| |
554 g_return_val_if_fail(status_type != NULL, NULL); |
| |
555 g_return_val_if_fail(presence != NULL, NULL); |
| |
556 |
| |
557 status = g_new0(GaimStatus, 1); |
| |
558 GAIM_DBUS_REGISTER_POINTER(status, GaimStatus); |
| |
559 |
| |
560 status->type = status_type; |
| |
561 status->presence = presence; |
| |
562 |
| |
563 status->attr_values = |
| |
564 g_hash_table_new_full(g_str_hash, g_str_equal, g_free, |
| |
565 (GDestroyNotify)gaim_value_destroy); |
| |
566 |
| |
567 for (l = gaim_status_type_get_attrs(status_type); l != NULL; l = l->next) |
| |
568 { |
| |
569 GaimStatusAttr *attr = (GaimStatusAttr *)l->data; |
| |
570 GaimValue *value = gaim_status_attr_get_value(attr); |
| |
571 GaimValue *new_value = gaim_value_dup(value); |
| |
572 |
| |
573 g_hash_table_insert(status->attr_values, |
| |
574 g_strdup(gaim_status_attr_get_id(attr)), |
| |
575 new_value); |
| |
576 } |
| |
577 |
| |
578 return status; |
| |
579 } |
| |
580 |
| |
581 /* |
| |
582 * TODO: If the GaimStatus is in a GaimPresence, then |
| |
583 * remove it from the GaimPresence? |
| |
584 */ |
| |
585 void |
| |
586 gaim_status_destroy(GaimStatus *status) |
| |
587 { |
| |
588 g_return_if_fail(status != NULL); |
| |
589 |
| |
590 g_hash_table_destroy(status->attr_values); |
| |
591 |
| |
592 GAIM_DBUS_UNREGISTER_POINTER(status); |
| |
593 g_free(status); |
| |
594 } |
| |
595 |
| |
596 static void |
| |
597 notify_buddy_status_update(GaimBuddy *buddy, GaimPresence *presence, |
| |
598 GaimStatus *old_status, GaimStatus *new_status) |
| |
599 { |
| |
600 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); |
| |
601 |
| |
602 if (gaim_prefs_get_bool("/core/logging/log_system")) |
| |
603 { |
| |
604 time_t current_time = time(NULL); |
| |
605 const char *buddy_alias = gaim_buddy_get_alias(buddy); |
| |
606 char *tmp; |
| |
607 GaimLog *log; |
| |
608 |
| |
609 if (old_status != NULL) |
| |
610 { |
| |
611 tmp = g_strdup_printf(_("%s changed status from %s to %s"), buddy_alias, |
| |
612 gaim_status_get_name(old_status), |
| |
613 gaim_status_get_name(new_status)); |
| |
614 } |
| |
615 else |
| |
616 { |
| |
617 /* old_status == NULL when an independent status is toggled. */ |
| |
618 |
| |
619 if (gaim_status_is_active(new_status)) |
| |
620 { |
| |
621 tmp = g_strdup_printf(_("%s is now %s"), buddy_alias, |
| |
622 gaim_status_get_name(new_status)); |
| |
623 } |
| |
624 else |
| |
625 { |
| |
626 tmp = g_strdup_printf(_("%s is no longer %s"), buddy_alias, |
| |
627 gaim_status_get_name(new_status)); |
| |
628 } |
| |
629 } |
| |
630 |
| |
631 log = gaim_account_get_log(buddy->account, FALSE); |
| |
632 if (log != NULL) |
| |
633 { |
| |
634 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, buddy_alias, |
| |
635 current_time, tmp); |
| |
636 } |
| |
637 |
| |
638 g_free(tmp); |
| |
639 } |
| |
640 |
| |
641 if (ops != NULL && ops->update != NULL) |
| |
642 ops->update(gaim_get_blist(), (GaimBlistNode*)buddy); |
| |
643 } |
| |
644 |
| |
645 static void |
| |
646 notify_status_update(GaimPresence *presence, GaimStatus *old_status, |
| |
647 GaimStatus *new_status) |
| |
648 { |
| |
649 GaimPresenceContext context = gaim_presence_get_context(presence); |
| |
650 |
| |
651 if (context == GAIM_PRESENCE_CONTEXT_ACCOUNT) |
| |
652 { |
| |
653 GaimAccount *account = gaim_presence_get_account(presence); |
| |
654 GaimAccountUiOps *ops = gaim_accounts_get_ui_ops(); |
| |
655 |
| |
656 if (gaim_account_get_enabled(account, gaim_core_get_ui())) |
| |
657 gaim_prpl_change_account_status(account, old_status, new_status); |
| |
658 |
| |
659 if (ops != NULL && ops->status_changed != NULL) |
| |
660 { |
| |
661 ops->status_changed(account, new_status); |
| |
662 } |
| |
663 } |
| |
664 else if (context == GAIM_PRESENCE_CONTEXT_BUDDY) |
| |
665 { |
| |
666 const GList *l; |
| |
667 |
| |
668 for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next) |
| |
669 { |
| |
670 notify_buddy_status_update((GaimBuddy *)l->data, presence, |
| |
671 old_status, new_status); |
| |
672 } |
| |
673 } |
| |
674 } |
| |
675 |
| |
676 static void |
| |
677 status_has_changed(GaimStatus *status) |
| |
678 { |
| |
679 GaimPresence *presence; |
| |
680 GaimStatus *old_status; |
| |
681 |
| |
682 presence = gaim_status_get_presence(status); |
| |
683 |
| |
684 /* |
| |
685 * If this status is exclusive, then we must be setting it to "active." |
| |
686 * Since we are setting it to active, we want to set the currently |
| |
687 * active status to "inactive." |
| |
688 */ |
| |
689 if (gaim_status_is_exclusive(status)) |
| |
690 { |
| |
691 old_status = gaim_presence_get_active_status(presence); |
| |
692 if (old_status != NULL && (old_status != status)) |
| |
693 old_status->active = FALSE; |
| |
694 presence->active_status = status; |
| |
695 } |
| |
696 else |
| |
697 old_status = NULL; |
| |
698 |
| |
699 notify_status_update(presence, old_status, status); |
| |
700 } |
| |
701 |
| |
702 void |
| |
703 gaim_status_set_active(GaimStatus *status, gboolean active) |
| |
704 { |
| |
705 gaim_status_set_active_with_attrs_list(status, active, NULL); |
| |
706 } |
| |
707 |
| |
708 /* |
| |
709 * This used to parse the va_list directly, but now it creates a GList |
| |
710 * and passes it to gaim_status_set_active_with_attrs_list(). That |
| |
711 * function was created because accounts.c needs to pass a GList of |
| |
712 * attributes to the status API. |
| |
713 */ |
| |
714 void |
| |
715 gaim_status_set_active_with_attrs(GaimStatus *status, gboolean active, va_list args) |
| |
716 { |
| |
717 GList *attrs = NULL; |
| |
718 const gchar *id; |
| |
719 gpointer data; |
| |
720 |
| |
721 while ((id = va_arg(args, const char *)) != NULL) |
| |
722 { |
| |
723 attrs = g_list_append(attrs, (char *)id); |
| |
724 data = va_arg(args, void *); |
| |
725 attrs = g_list_append(attrs, data); |
| |
726 } |
| |
727 gaim_status_set_active_with_attrs_list(status, active, attrs); |
| |
728 g_list_free(attrs); |
| |
729 } |
| |
730 |
| |
731 void |
| |
732 gaim_status_set_active_with_attrs_list(GaimStatus *status, gboolean active, |
| |
733 const GList *attrs) |
| |
734 { |
| |
735 gboolean changed = FALSE; |
| |
736 const GList *l; |
| |
737 GList *specified_attr_ids = NULL; |
| |
738 GaimStatusType *status_type; |
| |
739 |
| |
740 g_return_if_fail(status != NULL); |
| |
741 |
| |
742 if (!active && gaim_status_is_exclusive(status)) |
| |
743 { |
| |
744 gaim_debug_error("status", |
| |
745 "Cannot deactivate an exclusive status (%s).\n", |
| |
746 gaim_status_get_id(status)); |
| |
747 return; |
| |
748 } |
| |
749 |
| |
750 if (status->active != active) |
| |
751 { |
| |
752 changed = TRUE; |
| |
753 } |
| |
754 |
| |
755 status->active = active; |
| |
756 |
| |
757 /* Set any attributes */ |
| |
758 l = attrs; |
| |
759 while (l != NULL) |
| |
760 { |
| |
761 const gchar *id; |
| |
762 GaimValue *value; |
| |
763 |
| |
764 id = l->data; |
| |
765 l = l->next; |
| |
766 value = gaim_status_get_attr_value(status, id); |
| |
767 if (value == NULL) |
| |
768 { |
| |
769 gaim_debug_warning("status", "The attribute \"%s\" on the status \"%s\" is " |
| |
770 "not supported.\n", id, status->type->name); |
| |
771 /* Skip over the data and move on to the next attribute */ |
| |
772 l = l->next; |
| |
773 continue; |
| |
774 } |
| |
775 |
| |
776 specified_attr_ids = g_list_prepend(specified_attr_ids, (gpointer)id); |
| |
777 |
| |
778 if (value->type == GAIM_TYPE_STRING) |
| |
779 { |
| |
780 const gchar *string_data = l->data; |
| |
781 l = l->next; |
| |
782 if (((string_data == NULL) && (value->data.string_data == NULL)) || |
| |
783 ((string_data != NULL) && (value->data.string_data != NULL) && |
| |
784 !strcmp(string_data, value->data.string_data))) |
| |
785 { |
| |
786 continue; |
| |
787 } |
| |
788 gaim_status_set_attr_string(status, id, string_data); |
| |
789 changed = TRUE; |
| |
790 } |
| |
791 else if (value->type == GAIM_TYPE_INT) |
| |
792 { |
| |
793 int int_data = GPOINTER_TO_INT(l->data); |
| |
794 l = l->next; |
| |
795 if (int_data == value->data.int_data) |
| |
796 continue; |
| |
797 gaim_status_set_attr_int(status, id, int_data); |
| |
798 changed = TRUE; |
| |
799 } |
| |
800 else if (value->type == GAIM_TYPE_BOOLEAN) |
| |
801 { |
| |
802 gboolean boolean_data = GPOINTER_TO_INT(l->data); |
| |
803 l = l->next; |
| |
804 if (boolean_data == value->data.boolean_data) |
| |
805 continue; |
| |
806 gaim_status_set_attr_boolean(status, id, boolean_data); |
| |
807 changed = TRUE; |
| |
808 } |
| |
809 else |
| |
810 { |
| |
811 /* We don't know what the data is--skip over it */ |
| |
812 l = l->next; |
| |
813 } |
| |
814 } |
| |
815 |
| |
816 /* Reset any unspecified attributes to their default value */ |
| |
817 status_type = gaim_status_get_type(status); |
| |
818 l = gaim_status_type_get_attrs(status_type); |
| |
819 while (l != NULL) |
| |
820 { |
| |
821 GaimStatusAttr *attr; |
| |
822 |
| |
823 attr = l->data; |
| |
824 if (!g_list_find_custom(specified_attr_ids, attr->id, (GCompareFunc)strcmp)) |
| |
825 { |
| |
826 GaimValue *default_value; |
| |
827 default_value = gaim_status_attr_get_value(attr); |
| |
828 if (default_value->type == GAIM_TYPE_STRING) |
| |
829 gaim_status_set_attr_string(status, attr->id, |
| |
830 gaim_value_get_string(default_value)); |
| |
831 else if (default_value->type == GAIM_TYPE_INT) |
| |
832 gaim_status_set_attr_int(status, attr->id, |
| |
833 gaim_value_get_int(default_value)); |
| |
834 else if (default_value->type == GAIM_TYPE_BOOLEAN) |
| |
835 gaim_status_set_attr_boolean(status, attr->id, |
| |
836 gaim_value_get_boolean(default_value)); |
| |
837 changed = TRUE; |
| |
838 } |
| |
839 |
| |
840 l = l->next; |
| |
841 } |
| |
842 g_list_free(specified_attr_ids); |
| |
843 |
| |
844 if (!changed) |
| |
845 return; |
| |
846 status_has_changed(status); |
| |
847 } |
| |
848 |
| |
849 void |
| |
850 gaim_status_set_attr_boolean(GaimStatus *status, const char *id, |
| |
851 gboolean value) |
| |
852 { |
| |
853 GaimValue *attr_value; |
| |
854 |
| |
855 g_return_if_fail(status != NULL); |
| |
856 g_return_if_fail(id != NULL); |
| |
857 |
| |
858 /* Make sure this attribute exists and is the correct type. */ |
| |
859 attr_value = gaim_status_get_attr_value(status, id); |
| |
860 g_return_if_fail(attr_value != NULL); |
| |
861 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_BOOLEAN); |
| |
862 |
| |
863 gaim_value_set_boolean(attr_value, value); |
| |
864 } |
| |
865 |
| |
866 void |
| |
867 gaim_status_set_attr_int(GaimStatus *status, const char *id, int value) |
| |
868 { |
| |
869 GaimValue *attr_value; |
| |
870 |
| |
871 g_return_if_fail(status != NULL); |
| |
872 g_return_if_fail(id != NULL); |
| |
873 |
| |
874 /* Make sure this attribute exists and is the correct type. */ |
| |
875 attr_value = gaim_status_get_attr_value(status, id); |
| |
876 g_return_if_fail(attr_value != NULL); |
| |
877 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_INT); |
| |
878 |
| |
879 gaim_value_set_int(attr_value, value); |
| |
880 } |
| |
881 |
| |
882 void |
| |
883 gaim_status_set_attr_string(GaimStatus *status, const char *id, |
| |
884 const char *value) |
| |
885 { |
| |
886 GaimValue *attr_value; |
| |
887 |
| |
888 g_return_if_fail(status != NULL); |
| |
889 g_return_if_fail(id != NULL); |
| |
890 |
| |
891 /* Make sure this attribute exists and is the correct type. */ |
| |
892 attr_value = gaim_status_get_attr_value(status, id); |
| |
893 /* This used to be g_return_if_fail, but it's failing a LOT, so |
| |
894 * let's generate a log error for now. */ |
| |
895 /* g_return_if_fail(attr_value != NULL); */ |
| |
896 if (attr_value == NULL) { |
| |
897 gaim_debug_error("status", |
| |
898 "Attempted to set status attribute '%s' for " |
| |
899 "status '%s', which is not legal. Fix " |
| |
900 "this!\n", id, |
| |
901 gaim_status_type_get_name(gaim_status_get_type(status))); |
| |
902 return; |
| |
903 } |
| |
904 g_return_if_fail(gaim_value_get_type(attr_value) == GAIM_TYPE_STRING); |
| |
905 |
| |
906 gaim_value_set_string(attr_value, value); |
| |
907 } |
| |
908 |
| |
909 GaimStatusType * |
| |
910 gaim_status_get_type(const GaimStatus *status) |
| |
911 { |
| |
912 g_return_val_if_fail(status != NULL, NULL); |
| |
913 |
| |
914 return status->type; |
| |
915 } |
| |
916 |
| |
917 GaimPresence * |
| |
918 gaim_status_get_presence(const GaimStatus *status) |
| |
919 { |
| |
920 g_return_val_if_fail(status != NULL, NULL); |
| |
921 |
| |
922 return status->presence; |
| |
923 } |
| |
924 |
| |
925 const char * |
| |
926 gaim_status_get_id(const GaimStatus *status) |
| |
927 { |
| |
928 g_return_val_if_fail(status != NULL, NULL); |
| |
929 |
| |
930 return gaim_status_type_get_id(gaim_status_get_type(status)); |
| |
931 } |
| |
932 |
| |
933 const char * |
| |
934 gaim_status_get_name(const GaimStatus *status) |
| |
935 { |
| |
936 g_return_val_if_fail(status != NULL, NULL); |
| |
937 |
| |
938 return gaim_status_type_get_name(gaim_status_get_type(status)); |
| |
939 } |
| |
940 |
| |
941 gboolean |
| |
942 gaim_status_is_independent(const GaimStatus *status) |
| |
943 { |
| |
944 g_return_val_if_fail(status != NULL, FALSE); |
| |
945 |
| |
946 return gaim_status_type_is_independent(gaim_status_get_type(status)); |
| |
947 } |
| |
948 |
| |
949 gboolean |
| |
950 gaim_status_is_exclusive(const GaimStatus *status) |
| |
951 { |
| |
952 g_return_val_if_fail(status != NULL, FALSE); |
| |
953 |
| |
954 return gaim_status_type_is_exclusive(gaim_status_get_type(status)); |
| |
955 } |
| |
956 |
| |
957 gboolean |
| |
958 gaim_status_is_available(const GaimStatus *status) |
| |
959 { |
| |
960 g_return_val_if_fail(status != NULL, FALSE); |
| |
961 |
| |
962 return gaim_status_type_is_available(gaim_status_get_type(status)); |
| |
963 } |
| |
964 |
| |
965 gboolean |
| |
966 gaim_status_is_active(const GaimStatus *status) |
| |
967 { |
| |
968 g_return_val_if_fail(status != NULL, FALSE); |
| |
969 |
| |
970 return status->active; |
| |
971 } |
| |
972 |
| |
973 gboolean |
| |
974 gaim_status_is_online(const GaimStatus *status) |
| |
975 { |
| |
976 GaimStatusPrimitive primitive; |
| |
977 |
| |
978 g_return_val_if_fail( status != NULL, FALSE); |
| |
979 |
| |
980 primitive = gaim_status_type_get_primitive(gaim_status_get_type(status)); |
| |
981 |
| |
982 return (primitive != GAIM_STATUS_UNSET && |
| |
983 primitive != GAIM_STATUS_OFFLINE); |
| |
984 } |
| |
985 |
| |
986 GaimValue * |
| |
987 gaim_status_get_attr_value(const GaimStatus *status, const char *id) |
| |
988 { |
| |
989 g_return_val_if_fail(status != NULL, NULL); |
| |
990 g_return_val_if_fail(id != NULL, NULL); |
| |
991 |
| |
992 return (GaimValue *)g_hash_table_lookup(status->attr_values, id); |
| |
993 } |
| |
994 |
| |
995 gboolean |
| |
996 gaim_status_get_attr_boolean(const GaimStatus *status, const char *id) |
| |
997 { |
| |
998 const GaimValue *value; |
| |
999 |
| |
1000 g_return_val_if_fail(status != NULL, FALSE); |
| |
1001 g_return_val_if_fail(id != NULL, FALSE); |
| |
1002 |
| |
1003 if ((value = gaim_status_get_attr_value(status, id)) == NULL) |
| |
1004 return FALSE; |
| |
1005 |
| |
1006 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_BOOLEAN, FALSE); |
| |
1007 |
| |
1008 return gaim_value_get_boolean(value); |
| |
1009 } |
| |
1010 |
| |
1011 int |
| |
1012 gaim_status_get_attr_int(const GaimStatus *status, const char *id) |
| |
1013 { |
| |
1014 const GaimValue *value; |
| |
1015 |
| |
1016 g_return_val_if_fail(status != NULL, 0); |
| |
1017 g_return_val_if_fail(id != NULL, 0); |
| |
1018 |
| |
1019 if ((value = gaim_status_get_attr_value(status, id)) == NULL) |
| |
1020 return 0; |
| |
1021 |
| |
1022 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_INT, 0); |
| |
1023 |
| |
1024 return gaim_value_get_int(value); |
| |
1025 } |
| |
1026 |
| |
1027 const char * |
| |
1028 gaim_status_get_attr_string(const GaimStatus *status, const char *id) |
| |
1029 { |
| |
1030 const GaimValue *value; |
| |
1031 |
| |
1032 g_return_val_if_fail(status != NULL, NULL); |
| |
1033 g_return_val_if_fail(id != NULL, NULL); |
| |
1034 |
| |
1035 if ((value = gaim_status_get_attr_value(status, id)) == NULL) |
| |
1036 return NULL; |
| |
1037 |
| |
1038 g_return_val_if_fail(gaim_value_get_type(value) == GAIM_TYPE_STRING, NULL); |
| |
1039 |
| |
1040 return gaim_value_get_string(value); |
| |
1041 } |
| |
1042 |
| |
1043 gint |
| |
1044 gaim_status_compare(const GaimStatus *status1, const GaimStatus *status2) |
| |
1045 { |
| |
1046 GaimStatusType *type1, *type2; |
| |
1047 int score1 = 0, score2 = 0; |
| |
1048 |
| |
1049 if ((status1 == NULL && status2 == NULL) || |
| |
1050 (status1 == status2)) |
| |
1051 { |
| |
1052 return 0; |
| |
1053 } |
| |
1054 else if (status1 == NULL) |
| |
1055 return 1; |
| |
1056 else if (status2 == NULL) |
| |
1057 return -1; |
| |
1058 |
| |
1059 type1 = gaim_status_get_type(status1); |
| |
1060 type2 = gaim_status_get_type(status2); |
| |
1061 |
| |
1062 if (gaim_status_is_active(status1)) |
| |
1063 score1 = primitive_scores[gaim_status_type_get_primitive(type1)]; |
| |
1064 |
| |
1065 if (gaim_status_is_active(status2)) |
| |
1066 score2 = primitive_scores[gaim_status_type_get_primitive(type2)]; |
| |
1067 |
| |
1068 if (score1 > score2) |
| |
1069 return -1; |
| |
1070 else if (score1 < score2) |
| |
1071 return 1; |
| |
1072 |
| |
1073 return 0; |
| |
1074 } |
| |
1075 |
| |
1076 |
| |
1077 /************************************************************************** |
| |
1078 * GaimPresence API |
| |
1079 **************************************************************************/ |
| |
1080 GaimPresence * |
| |
1081 gaim_presence_new(GaimPresenceContext context) |
| |
1082 { |
| |
1083 GaimPresence *presence; |
| |
1084 |
| |
1085 g_return_val_if_fail(context != GAIM_PRESENCE_CONTEXT_UNSET, NULL); |
| |
1086 |
| |
1087 presence = g_new0(GaimPresence, 1); |
| |
1088 GAIM_DBUS_REGISTER_POINTER(presence, GaimPresence); |
| |
1089 |
| |
1090 presence->context = context; |
| |
1091 |
| |
1092 presence->status_table = |
| |
1093 g_hash_table_new_full(g_str_hash, g_str_equal, |
| |
1094 g_free, NULL); |
| |
1095 |
| |
1096 return presence; |
| |
1097 } |
| |
1098 |
| |
1099 GaimPresence * |
| |
1100 gaim_presence_new_for_account(GaimAccount *account) |
| |
1101 { |
| |
1102 GaimPresence *presence = NULL; |
| |
1103 g_return_val_if_fail(account != NULL, NULL); |
| |
1104 |
| |
1105 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_ACCOUNT); |
| |
1106 presence->u.account = account; |
| |
1107 presence->statuses = gaim_prpl_get_statuses(account, presence); |
| |
1108 |
| |
1109 return presence; |
| |
1110 } |
| |
1111 |
| |
1112 GaimPresence * |
| |
1113 gaim_presence_new_for_conv(GaimConversation *conv) |
| |
1114 { |
| |
1115 GaimPresence *presence; |
| |
1116 |
| |
1117 g_return_val_if_fail(conv != NULL, NULL); |
| |
1118 |
| |
1119 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_CONV); |
| |
1120 presence->u.chat.conv = conv; |
| |
1121 /* presence->statuses = gaim_prpl_get_statuses(conv->account, presence); ? */ |
| |
1122 |
| |
1123 return presence; |
| |
1124 } |
| |
1125 |
| |
1126 GaimPresence * |
| |
1127 gaim_presence_new_for_buddy(GaimBuddy *buddy) |
| |
1128 { |
| |
1129 GaimPresence *presence; |
| |
1130 GaimStatusBuddyKey *key; |
| |
1131 GaimAccount *account; |
| |
1132 |
| |
1133 g_return_val_if_fail(buddy != NULL, NULL); |
| |
1134 account = buddy->account; |
| |
1135 |
| |
1136 key = g_new0(GaimStatusBuddyKey, 1); |
| |
1137 key->account = buddy->account; |
| |
1138 key->name = g_strdup(buddy->name); |
| |
1139 |
| |
1140 presence = g_hash_table_lookup(buddy_presences, key); |
| |
1141 if (presence == NULL) |
| |
1142 { |
| |
1143 presence = gaim_presence_new(GAIM_PRESENCE_CONTEXT_BUDDY); |
| |
1144 |
| |
1145 presence->u.buddy.name = g_strdup(buddy->name); |
| |
1146 presence->u.buddy.account = buddy->account; |
| |
1147 presence->statuses = gaim_prpl_get_statuses(buddy->account, presence); |
| |
1148 |
| |
1149 g_hash_table_insert(buddy_presences, key, presence); |
| |
1150 } |
| |
1151 else |
| |
1152 { |
| |
1153 g_free(key->name); |
| |
1154 g_free(key); |
| |
1155 } |
| |
1156 |
| |
1157 presence->u.buddy.ref_count++; |
| |
1158 presence->u.buddy.buddies = g_list_append(presence->u.buddy.buddies, |
| |
1159 buddy); |
| |
1160 |
| |
1161 return presence; |
| |
1162 } |
| |
1163 |
| |
1164 void |
| |
1165 gaim_presence_destroy(GaimPresence *presence) |
| |
1166 { |
| |
1167 g_return_if_fail(presence != NULL); |
| |
1168 |
| |
1169 if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY) |
| |
1170 { |
| |
1171 GaimStatusBuddyKey key; |
| |
1172 |
| |
1173 if(presence->u.buddy.ref_count != 0) |
| |
1174 return; |
| |
1175 |
| |
1176 key.account = presence->u.buddy.account; |
| |
1177 key.name = presence->u.buddy.name; |
| |
1178 |
| |
1179 g_hash_table_remove(buddy_presences, &key); |
| |
1180 |
| |
1181 g_free(presence->u.buddy.name); |
| |
1182 } |
| |
1183 else if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_CONV) |
| |
1184 { |
| |
1185 g_free(presence->u.chat.user); |
| |
1186 } |
| |
1187 |
| |
1188 g_list_foreach(presence->statuses, (GFunc)gaim_status_destroy, NULL); |
| |
1189 g_list_free(presence->statuses); |
| |
1190 |
| |
1191 g_hash_table_destroy(presence->status_table); |
| |
1192 |
| |
1193 GAIM_DBUS_UNREGISTER_POINTER(presence); |
| |
1194 g_free(presence); |
| |
1195 } |
| |
1196 |
| |
1197 /* |
| |
1198 * TODO: Maybe we should cal gaim_presence_destroy() after we |
| |
1199 * decrement the ref count? I don't see why we should |
| |
1200 * make other places do it manually when we can do it here. |
| |
1201 */ |
| |
1202 void |
| |
1203 gaim_presence_remove_buddy(GaimPresence *presence, GaimBuddy *buddy) |
| |
1204 { |
| |
1205 g_return_if_fail(presence != NULL); |
| |
1206 g_return_if_fail(buddy != NULL); |
| |
1207 g_return_if_fail(gaim_presence_get_context(presence) == |
| |
1208 GAIM_PRESENCE_CONTEXT_BUDDY); |
| |
1209 |
| |
1210 if (g_list_find(presence->u.buddy.buddies, buddy) != NULL) |
| |
1211 { |
| |
1212 presence->u.buddy.buddies = g_list_remove(presence->u.buddy.buddies, |
| |
1213 buddy); |
| |
1214 presence->u.buddy.ref_count--; |
| |
1215 } |
| |
1216 } |
| |
1217 |
| |
1218 void |
| |
1219 gaim_presence_add_status(GaimPresence *presence, GaimStatus *status) |
| |
1220 { |
| |
1221 g_return_if_fail(presence != NULL); |
| |
1222 g_return_if_fail(status != NULL); |
| |
1223 |
| |
1224 presence->statuses = g_list_append(presence->statuses, status); |
| |
1225 |
| |
1226 g_hash_table_insert(presence->status_table, |
| |
1227 g_strdup(gaim_status_get_id(status)), status); |
| |
1228 } |
| |
1229 |
| |
1230 void |
| |
1231 gaim_presence_add_list(GaimPresence *presence, const GList *source_list) |
| |
1232 { |
| |
1233 const GList *l; |
| |
1234 |
| |
1235 g_return_if_fail(presence != NULL); |
| |
1236 g_return_if_fail(source_list != NULL); |
| |
1237 |
| |
1238 for (l = source_list; l != NULL; l = l->next) |
| |
1239 gaim_presence_add_status(presence, (GaimStatus *)l->data); |
| |
1240 } |
| |
1241 |
| |
1242 void |
| |
1243 gaim_presence_set_status_active(GaimPresence *presence, const char *status_id, |
| |
1244 gboolean active) |
| |
1245 { |
| |
1246 GaimStatus *status; |
| |
1247 |
| |
1248 g_return_if_fail(presence != NULL); |
| |
1249 g_return_if_fail(status_id != NULL); |
| |
1250 |
| |
1251 status = gaim_presence_get_status(presence, status_id); |
| |
1252 |
| |
1253 g_return_if_fail(status != NULL); |
| |
1254 /* TODO: Should we do the following? */ |
| |
1255 /* g_return_if_fail(active == status->active); */ |
| |
1256 |
| |
1257 if (gaim_status_is_exclusive(status)) |
| |
1258 { |
| |
1259 if (!active) |
| |
1260 { |
| |
1261 gaim_debug_warning("status", |
| |
1262 "Attempted to set a non-independent status " |
| |
1263 "(%s) inactive. Only independent statuses " |
| |
1264 "can be specifically marked inactive.", |
| |
1265 status_id); |
| |
1266 return; |
| |
1267 } |
| |
1268 } |
| |
1269 |
| |
1270 gaim_status_set_active(status, active); |
| |
1271 } |
| |
1272 |
| |
1273 void |
| |
1274 gaim_presence_switch_status(GaimPresence *presence, const char *status_id) |
| |
1275 { |
| |
1276 gaim_presence_set_status_active(presence, status_id, TRUE); |
| |
1277 } |
| |
1278 |
| |
1279 static void |
| |
1280 update_buddy_idle(GaimBuddy *buddy, GaimPresence *presence, |
| |
1281 time_t current_time, gboolean old_idle, gboolean idle) |
| |
1282 { |
| |
1283 GaimBlistUiOps *ops = gaim_blist_get_ui_ops(); |
| |
1284 |
| |
1285 if (!old_idle && idle) |
| |
1286 { |
| |
1287 if (gaim_prefs_get_bool("/core/logging/log_system")) |
| |
1288 { |
| |
1289 GaimLog *log = gaim_account_get_log(buddy->account, FALSE); |
| |
1290 |
| |
1291 if (log != NULL) |
| |
1292 { |
| |
1293 char *tmp = g_strdup_printf(_("%s became idle"), |
| |
1294 gaim_buddy_get_alias(buddy)); |
| |
1295 |
| |
1296 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, |
| |
1297 gaim_buddy_get_alias(buddy), current_time, tmp); |
| |
1298 g_free(tmp); |
| |
1299 } |
| |
1300 } |
| |
1301 } |
| |
1302 else if (old_idle && !idle) |
| |
1303 { |
| |
1304 if (gaim_prefs_get_bool("/core/logging/log_system")) |
| |
1305 { |
| |
1306 GaimLog *log = gaim_account_get_log(buddy->account, FALSE); |
| |
1307 |
| |
1308 if (log != NULL) |
| |
1309 { |
| |
1310 char *tmp = g_strdup_printf(_("%s became unidle"), |
| |
1311 gaim_buddy_get_alias(buddy)); |
| |
1312 |
| |
1313 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, |
| |
1314 gaim_buddy_get_alias(buddy), current_time, tmp); |
| |
1315 g_free(tmp); |
| |
1316 } |
| |
1317 } |
| |
1318 } |
| |
1319 |
| |
1320 if (old_idle != idle) |
| |
1321 gaim_signal_emit(gaim_blist_get_handle(), "buddy-idle-changed", buddy, |
| |
1322 old_idle, idle); |
| |
1323 |
| |
1324 gaim_contact_invalidate_priority_buddy(gaim_buddy_get_contact(buddy)); |
| |
1325 |
| |
1326 /* Should this be done here? It'd perhaps make more sense to |
| |
1327 * connect to buddy-[un]idle signals and update from there |
| |
1328 */ |
| |
1329 |
| |
1330 if (ops != NULL && ops->update != NULL) |
| |
1331 ops->update(gaim_get_blist(), (GaimBlistNode *)buddy); |
| |
1332 } |
| |
1333 |
| |
1334 void |
| |
1335 gaim_presence_set_idle(GaimPresence *presence, gboolean idle, time_t idle_time) |
| |
1336 { |
| |
1337 gboolean old_idle; |
| |
1338 |
| |
1339 g_return_if_fail(presence != NULL); |
| |
1340 |
| |
1341 if (presence->idle == idle && presence->idle_time == idle_time) |
| |
1342 return; |
| |
1343 |
| |
1344 old_idle = presence->idle; |
| |
1345 presence->idle = idle; |
| |
1346 presence->idle_time = (idle ? idle_time : 0); |
| |
1347 |
| |
1348 if (gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_BUDDY) |
| |
1349 { |
| |
1350 const GList *l; |
| |
1351 time_t current_time = time(NULL); |
| |
1352 |
| |
1353 for (l = gaim_presence_get_buddies(presence); l != NULL; l = l->next) |
| |
1354 { |
| |
1355 update_buddy_idle((GaimBuddy *)l->data, presence, current_time, |
| |
1356 old_idle, idle); |
| |
1357 } |
| |
1358 } |
| |
1359 else if(gaim_presence_get_context(presence) == GAIM_PRESENCE_CONTEXT_ACCOUNT) |
| |
1360 { |
| |
1361 GaimAccount *account; |
| |
1362 GaimConnection *gc; |
| |
1363 GaimPluginProtocolInfo *prpl_info = NULL; |
| |
1364 |
| |
1365 account = gaim_presence_get_account(presence); |
| |
1366 |
| |
1367 if (gaim_prefs_get_bool("/core/logging/log_system")) |
| |
1368 { |
| |
1369 GaimLog *log = gaim_account_get_log(account, FALSE); |
| |
1370 |
| |
1371 if (log != NULL) |
| |
1372 { |
| |
1373 char *msg; |
| |
1374 |
| |
1375 if (idle) |
| |
1376 msg = g_strdup_printf(_("+++ %s became idle"), gaim_account_get_username(account)); |
| |
1377 else |
| |
1378 msg = g_strdup_printf(_("+++ %s became unidle"), gaim_account_get_username(account)); |
| |
1379 gaim_log_write(log, GAIM_MESSAGE_SYSTEM, |
| |
1380 gaim_account_get_username(account), |
| |
1381 idle_time, msg); |
| |
1382 g_free(msg); |
| |
1383 } |
| |
1384 } |
| |
1385 |
| |
1386 gc = gaim_account_get_connection(account); |
| |
1387 |
| |
1388 if (gc != NULL && GAIM_CONNECTION_IS_CONNECTED(gc) && |
| |
1389 gc->prpl != NULL) |
| |
1390 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
| |
1391 |
| |
1392 if (prpl_info && prpl_info->set_idle) |
| |
1393 prpl_info->set_idle(gc, (idle ? (time(NULL) - idle_time) : 0)); |
| |
1394 } |
| |
1395 } |
| |
1396 |
| |
1397 void |
| |
1398 gaim_presence_set_login_time(GaimPresence *presence, time_t login_time) |
| |
1399 { |
| |
1400 g_return_if_fail(presence != NULL); |
| |
1401 |
| |
1402 if (presence->login_time == login_time) |
| |
1403 return; |
| |
1404 |
| |
1405 presence->login_time = login_time; |
| |
1406 } |
| |
1407 |
| |
1408 GaimPresenceContext |
| |
1409 gaim_presence_get_context(const GaimPresence *presence) |
| |
1410 { |
| |
1411 g_return_val_if_fail(presence != NULL, GAIM_PRESENCE_CONTEXT_UNSET); |
| |
1412 |
| |
1413 return presence->context; |
| |
1414 } |
| |
1415 |
| |
1416 GaimAccount * |
| |
1417 gaim_presence_get_account(const GaimPresence *presence) |
| |
1418 { |
| |
1419 GaimPresenceContext context; |
| |
1420 |
| |
1421 g_return_val_if_fail(presence != NULL, NULL); |
| |
1422 |
| |
1423 context = gaim_presence_get_context(presence); |
| |
1424 |
| |
1425 g_return_val_if_fail(context == GAIM_PRESENCE_CONTEXT_ACCOUNT || |
| |
1426 context == GAIM_PRESENCE_CONTEXT_BUDDY, NULL); |
| |
1427 |
| |
1428 return presence->u.account; |
| |
1429 } |
| |
1430 |
| |
1431 GaimConversation * |
| |
1432 gaim_presence_get_conversation(const GaimPresence *presence) |
| |
1433 { |
| |
1434 g_return_val_if_fail(presence != NULL, NULL); |
| |
1435 g_return_val_if_fail(gaim_presence_get_context(presence) == |
| |
1436 GAIM_PRESENCE_CONTEXT_CONV, NULL); |
| |
1437 |
| |
1438 return presence->u.chat.conv; |
| |
1439 } |
| |
1440 |
| |
1441 const char * |
| |
1442 gaim_presence_get_chat_user(const GaimPresence *presence) |
| |
1443 { |
| |
1444 g_return_val_if_fail(presence != NULL, NULL); |
| |
1445 g_return_val_if_fail(gaim_presence_get_context(presence) == |
| |
1446 GAIM_PRESENCE_CONTEXT_CONV, NULL); |
| |
1447 |
| |
1448 return presence->u.chat.user; |
| |
1449 } |
| |
1450 |
| |
1451 const GList * |
| |
1452 gaim_presence_get_buddies(const GaimPresence *presence) |
| |
1453 { |
| |
1454 g_return_val_if_fail(presence != NULL, NULL); |
| |
1455 g_return_val_if_fail(gaim_presence_get_context(presence) == |
| |
1456 GAIM_PRESENCE_CONTEXT_BUDDY, NULL); |
| |
1457 |
| |
1458 return presence->u.buddy.buddies; |
| |
1459 } |
| |
1460 |
| |
1461 const GList * |
| |
1462 gaim_presence_get_statuses(const GaimPresence *presence) |
| |
1463 { |
| |
1464 g_return_val_if_fail(presence != NULL, NULL); |
| |
1465 |
| |
1466 return presence->statuses; |
| |
1467 } |
| |
1468 |
| |
1469 GaimStatus * |
| |
1470 gaim_presence_get_status(const GaimPresence *presence, const char *status_id) |
| |
1471 { |
| |
1472 GaimStatus *status; |
| |
1473 const GList *l = NULL; |
| |
1474 |
| |
1475 g_return_val_if_fail(presence != NULL, NULL); |
| |
1476 g_return_val_if_fail(status_id != NULL, NULL); |
| |
1477 |
| |
1478 /* What's the purpose of this hash table? */ |
| |
1479 status = (GaimStatus *)g_hash_table_lookup(presence->status_table, |
| |
1480 status_id); |
| |
1481 |
| |
1482 if (status == NULL) { |
| |
1483 for (l = gaim_presence_get_statuses(presence); |
| |
1484 l != NULL && status == NULL; l = l->next) |
| |
1485 { |
| |
1486 GaimStatus *temp_status = l->data; |
| |
1487 |
| |
1488 if (!strcmp(status_id, gaim_status_get_id(temp_status))) |
| |
1489 status = temp_status; |
| |
1490 } |
| |
1491 |
| |
1492 if (status != NULL) |
| |
1493 g_hash_table_insert(presence->status_table, |
| |
1494 g_strdup(gaim_status_get_id(status)), status); |
| |
1495 } |
| |
1496 |
| |
1497 return status; |
| |
1498 } |
| |
1499 |
| |
1500 GaimStatus * |
| |
1501 gaim_presence_get_active_status(const GaimPresence *presence) |
| |
1502 { |
| |
1503 g_return_val_if_fail(presence != NULL, NULL); |
| |
1504 |
| |
1505 return presence->active_status; |
| |
1506 } |
| |
1507 |
| |
1508 gboolean |
| |
1509 gaim_presence_is_available(const GaimPresence *presence) |
| |
1510 { |
| |
1511 GaimStatus *status; |
| |
1512 |
| |
1513 g_return_val_if_fail(presence != NULL, FALSE); |
| |
1514 |
| |
1515 status = gaim_presence_get_active_status(presence); |
| |
1516 |
| |
1517 return ((status != NULL && gaim_status_is_available(status)) && |
| |
1518 !gaim_presence_is_idle(presence)); |
| |
1519 } |
| |
1520 |
| |
1521 gboolean |
| |
1522 gaim_presence_is_online(const GaimPresence *presence) |
| |
1523 { |
| |
1524 GaimStatus *status; |
| |
1525 |
| |
1526 g_return_val_if_fail(presence != NULL, FALSE); |
| |
1527 |
| |
1528 if ((status = gaim_presence_get_active_status(presence)) == NULL) |
| |
1529 return FALSE; |
| |
1530 |
| |
1531 return gaim_status_is_online(status); |
| |
1532 } |
| |
1533 |
| |
1534 gboolean |
| |
1535 gaim_presence_is_status_active(const GaimPresence *presence, |
| |
1536 const char *status_id) |
| |
1537 { |
| |
1538 GaimStatus *status; |
| |
1539 |
| |
1540 g_return_val_if_fail(presence != NULL, FALSE); |
| |
1541 g_return_val_if_fail(status_id != NULL, FALSE); |
| |
1542 |
| |
1543 status = gaim_presence_get_status(presence, status_id); |
| |
1544 |
| |
1545 return (status != NULL && gaim_status_is_active(status)); |
| |
1546 } |
| |
1547 |
| |
1548 gboolean |
| |
1549 gaim_presence_is_status_primitive_active(const GaimPresence *presence, |
| |
1550 GaimStatusPrimitive primitive) |
| |
1551 { |
| |
1552 GaimStatus *status; |
| |
1553 GaimStatusType *status_type; |
| |
1554 |
| |
1555 g_return_val_if_fail(presence != NULL, FALSE); |
| |
1556 g_return_val_if_fail(primitive != GAIM_STATUS_UNSET, FALSE); |
| |
1557 |
| |
1558 status = gaim_presence_get_active_status(presence); |
| |
1559 status_type = gaim_status_get_type(status); |
| |
1560 |
| |
1561 if (gaim_status_type_get_primitive(status_type) == primitive) |
| |
1562 return TRUE; |
| |
1563 |
| |
1564 return FALSE; |
| |
1565 } |
| |
1566 |
| |
1567 gboolean |
| |
1568 gaim_presence_is_idle(const GaimPresence *presence) |
| |
1569 { |
| |
1570 g_return_val_if_fail(presence != NULL, FALSE); |
| |
1571 |
| |
1572 return gaim_presence_is_online(presence) && presence->idle; |
| |
1573 } |
| |
1574 |
| |
1575 time_t |
| |
1576 gaim_presence_get_idle_time(const GaimPresence *presence) |
| |
1577 { |
| |
1578 g_return_val_if_fail(presence != NULL, 0); |
| |
1579 |
| |
1580 return presence->idle_time; |
| |
1581 } |
| |
1582 |
| |
1583 time_t |
| |
1584 gaim_presence_get_login_time(const GaimPresence *presence) |
| |
1585 { |
| |
1586 g_return_val_if_fail(presence != NULL, 0); |
| |
1587 |
| |
1588 return gaim_presence_is_online(presence) ? presence->login_time : 0; |
| |
1589 } |
| |
1590 |
| |
1591 gint |
| |
1592 gaim_presence_compare(const GaimPresence *presence1, |
| |
1593 const GaimPresence *presence2) |
| |
1594 { |
| |
1595 gboolean idle1, idle2; |
| |
1596 time_t idle_time_1, idle_time_2; |
| |
1597 int score1 = 0, score2 = 0; |
| |
1598 const GList *l; |
| |
1599 |
| |
1600 if (presence1 == presence2) |
| |
1601 return 0; |
| |
1602 else if (presence1 == NULL) |
| |
1603 return 1; |
| |
1604 else if (presence2 == NULL) |
| |
1605 return -1; |
| |
1606 |
| |
1607 /* Compute the score of the first set of statuses. */ |
| |
1608 for (l = gaim_presence_get_statuses(presence1); l != NULL; l = l->next) |
| |
1609 { |
| |
1610 GaimStatus *status = (GaimStatus *)l->data; |
| |
1611 GaimStatusType *type = gaim_status_get_type(status); |
| |
1612 |
| |
1613 if (gaim_status_is_active(status)) |
| |
1614 score1 += primitive_scores[gaim_status_type_get_primitive(type)]; |
| |
1615 } |
| |
1616 score1 += gaim_account_get_int(gaim_presence_get_account(presence1), "score", 0); |
| |
1617 |
| |
1618 /* Compute the score of the second set of statuses. */ |
| |
1619 for (l = gaim_presence_get_statuses(presence2); l != NULL; l = l->next) |
| |
1620 { |
| |
1621 GaimStatus *status = (GaimStatus *)l->data; |
| |
1622 GaimStatusType *type = gaim_status_get_type(status); |
| |
1623 |
| |
1624 if (gaim_status_is_active(status)) |
| |
1625 score2 += primitive_scores[gaim_status_type_get_primitive(type)]; |
| |
1626 } |
| |
1627 score2 += gaim_account_get_int(gaim_presence_get_account(presence2), "score", 0); |
| |
1628 |
| |
1629 idle1 = gaim_presence_is_idle(presence1); |
| |
1630 idle2 = gaim_presence_is_idle(presence2); |
| |
1631 |
| |
1632 if (idle1) |
| |
1633 score1 += primitive_scores[SCORE_IDLE]; |
| |
1634 |
| |
1635 if (idle2) |
| |
1636 score2 += primitive_scores[SCORE_IDLE]; |
| |
1637 |
| |
1638 idle_time_1 = time(NULL) - gaim_presence_get_idle_time(presence1); |
| |
1639 idle_time_2 = time(NULL) - gaim_presence_get_idle_time(presence2); |
| |
1640 |
| |
1641 if (idle_time_1 > idle_time_2) |
| |
1642 score1 += primitive_scores[SCORE_IDLE_TIME]; |
| |
1643 else if (idle_time_1 < idle_time_2) |
| |
1644 score2 += primitive_scores[SCORE_IDLE_TIME]; |
| |
1645 |
| |
1646 if (score1 < score2) |
| |
1647 return 1; |
| |
1648 else if (score1 > score2) |
| |
1649 return -1; |
| |
1650 |
| |
1651 return 0; |
| |
1652 } |
| |
1653 |
| |
1654 |
| |
1655 /************************************************************************** |
| |
1656 * Status subsystem |
| |
1657 **************************************************************************/ |
| |
1658 static void |
| |
1659 score_pref_changed_cb(const char *name, GaimPrefType type, |
| |
1660 gconstpointer value, gpointer data) |
| |
1661 { |
| |
1662 int index = GPOINTER_TO_INT(data); |
| |
1663 |
| |
1664 primitive_scores[index] = GPOINTER_TO_INT(value); |
| |
1665 } |
| |
1666 |
| |
1667 static guint |
| |
1668 gaim_buddy_presences_hash(gconstpointer key) |
| |
1669 { |
| |
1670 const GaimStatusBuddyKey *me = key; |
| |
1671 guint ret; |
| |
1672 char *str; |
| |
1673 |
| |
1674 str = g_strdup_printf("%p%s", me->account, me->name); |
| |
1675 ret = g_str_hash(str); |
| |
1676 g_free(str); |
| |
1677 |
| |
1678 return ret; |
| |
1679 } |
| |
1680 |
| |
1681 static gboolean |
| |
1682 gaim_buddy_presences_equal(gconstpointer a, gconstpointer b) |
| |
1683 { |
| |
1684 GaimStatusBuddyKey *key_a = (GaimStatusBuddyKey *)a; |
| |
1685 GaimStatusBuddyKey *key_b = (GaimStatusBuddyKey *)b; |
| |
1686 |
| |
1687 if(key_a->account == key_b->account && |
| |
1688 !strcmp(key_a->name, key_b->name)) |
| |
1689 return TRUE; |
| |
1690 else |
| |
1691 return FALSE; |
| |
1692 } |
| |
1693 |
| |
1694 static void |
| |
1695 gaim_buddy_presences_key_free(gpointer a) |
| |
1696 { |
| |
1697 GaimStatusBuddyKey *key = (GaimStatusBuddyKey *)a; |
| |
1698 g_free(key->name); |
| |
1699 g_free(key); |
| |
1700 } |
| |
1701 |
| |
1702 void * |
| |
1703 gaim_status_get_handle(void) { |
| |
1704 static int handle; |
| |
1705 |
| |
1706 return &handle; |
| |
1707 } |
| |
1708 |
| |
1709 void |
| |
1710 gaim_status_init(void) |
| |
1711 { |
| |
1712 void *handle = gaim_status_get_handle; |
| |
1713 |
| |
1714 gaim_prefs_add_none("/core/status"); |
| |
1715 gaim_prefs_add_none("/core/status/scores"); |
| |
1716 |
| |
1717 gaim_prefs_add_int("/core/status/scores/offline", |
| |
1718 primitive_scores[GAIM_STATUS_OFFLINE]); |
| |
1719 gaim_prefs_add_int("/core/status/scores/available", |
| |
1720 primitive_scores[GAIM_STATUS_AVAILABLE]); |
| |
1721 gaim_prefs_add_int("/core/status/scores/invisible", |
| |
1722 primitive_scores[GAIM_STATUS_INVISIBLE]); |
| |
1723 gaim_prefs_add_int("/core/status/scores/away", |
| |
1724 primitive_scores[GAIM_STATUS_AWAY]); |
| |
1725 gaim_prefs_add_int("/core/status/scores/extended_away", |
| |
1726 primitive_scores[GAIM_STATUS_EXTENDED_AWAY]); |
| |
1727 gaim_prefs_add_int("/core/status/scores/idle", |
| |
1728 primitive_scores[SCORE_IDLE]); |
| |
1729 |
| |
1730 gaim_prefs_connect_callback(handle, "/core/status/scores/offline", |
| |
1731 score_pref_changed_cb, |
| |
1732 GINT_TO_POINTER(GAIM_STATUS_OFFLINE)); |
| |
1733 gaim_prefs_connect_callback(handle, "/core/status/scores/available", |
| |
1734 score_pref_changed_cb, |
| |
1735 GINT_TO_POINTER(GAIM_STATUS_AVAILABLE)); |
| |
1736 gaim_prefs_connect_callback(handle, "/core/status/scores/invisible", |
| |
1737 score_pref_changed_cb, |
| |
1738 GINT_TO_POINTER(GAIM_STATUS_INVISIBLE)); |
| |
1739 gaim_prefs_connect_callback(handle, "/core/status/scores/away", |
| |
1740 score_pref_changed_cb, |
| |
1741 GINT_TO_POINTER(GAIM_STATUS_AWAY)); |
| |
1742 gaim_prefs_connect_callback(handle, "/core/status/scores/extended_away", |
| |
1743 score_pref_changed_cb, |
| |
1744 GINT_TO_POINTER(GAIM_STATUS_EXTENDED_AWAY)); |
| |
1745 gaim_prefs_connect_callback(handle, "/core/status/scores/idle", |
| |
1746 score_pref_changed_cb, |
| |
1747 GINT_TO_POINTER(SCORE_IDLE)); |
| |
1748 |
| |
1749 buddy_presences = g_hash_table_new_full(gaim_buddy_presences_hash, |
| |
1750 gaim_buddy_presences_equal, |
| |
1751 gaim_buddy_presences_key_free, NULL); |
| |
1752 } |
| |
1753 |
| |
1754 void |
| |
1755 gaim_status_uninit(void) |
| |
1756 { |
| |
1757 if (buddy_presences != NULL) |
| |
1758 { |
| |
1759 g_hash_table_destroy(buddy_presences); |
| |
1760 |
| |
1761 buddy_presences = NULL; |
| |
1762 } |
| |
1763 } |