comparison src/protocols/sametime/meanwhile/srvc_aware.c @ 10969:3ef77720e577

[gaim-migrate @ 12790] importing meanwhile library for use in the sametime plugin committer: Tailor Script <tailor@pidgin.im>
author Christopher O'Brien <siege@pidgin.im>
date Sun, 05 Jun 2005 02:50:13 +0000
parents
children 57fccea36e36
comparison
equal deleted inserted replaced
10968:e0d5038fbb7e 10969:3ef77720e577
1
2 /*
3 Meanwhile - Unofficial Lotus Sametime Community Client Library
4 Copyright (C) 2004 Christopher (siege) O'Brien
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with this library; if not, write to the Free
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <glib.h>
22 #include <glib/ghash.h>
23 #include <glib/glist.h>
24 #include <string.h>
25
26 #include "mw_channel.h"
27 #include "mw_debug.h"
28 #include "mw_error.h"
29 #include "mw_message.h"
30 #include "mw_service.h"
31 #include "mw_session.h"
32 #include "mw_srvc_aware.h"
33 #include "mw_util.h"
34
35
36 struct mwServiceAware {
37 struct mwService service;
38
39 struct mwAwareHandler *handler;
40
41 /** map of ENTRY_KEY(aware_entry):aware_entry */
42 GHashTable *entries;
43
44 /** set of guint32:attrib_watch_entry attribute keys */
45 GHashTable *attribs;
46
47 /** collection of lists of awareness for this service. Each item is
48 a mwAwareList */
49 GList *lists;
50
51 /** the buddy list channel */
52 struct mwChannel *channel;
53 };
54
55
56 struct mwAwareList {
57
58 /** the owning service */
59 struct mwServiceAware *service;
60
61 /** map of ENTRY_KEY(aware_entry):aware_entry */
62 GHashTable *entries;
63
64 /** set of guint32:attrib_watch_entry attribute keys */
65 GHashTable *attribs;
66
67 struct mwAwareListHandler *handler;
68 struct mw_datum client_data;
69 };
70
71
72 struct mwAwareAttribute {
73 guint32 key;
74 struct mwOpaque data;
75 };
76
77
78 struct attrib_entry {
79 guint32 key;
80 GList *membership;
81 };
82
83
84 /** an actual awareness entry, belonging to any number of aware lists */
85 struct aware_entry {
86 struct mwAwareSnapshot aware;
87
88 /** list of mwAwareList containing this entry */
89 GList *membership;
90
91 /** collection of attribute values for this entry.
92 map of ATTRIB_KEY(mwAwareAttribute):mwAwareAttribute */
93 GHashTable *attribs;
94 };
95
96
97 #define ENTRY_KEY(entry) &entry->aware.id
98
99
100 /** the channel send types used by this service */
101 enum msg_types {
102 msg_AWARE_ADD = 0x0068, /**< remove an aware */
103 msg_AWARE_REMOVE = 0x0069, /**< add an aware */
104
105 msg_OPT_DO_SET = 0x00c9, /**< set an attribute */
106 msg_OPT_DO_UNSET = 0x00ca, /**< unset an attribute */
107 msg_OPT_WATCH = 0x00cb, /**< set the attribute watch list */
108
109 msg_AWARE_SNAPSHOT = 0x01f4, /**< recv aware snapshot */
110 msg_AWARE_UPDATE = 0x01f5, /**< recv aware update */
111 msg_AWARE_GROUP = 0x01f6, /**< recv group aware */
112
113 msg_OPT_GOT_SET = 0x0259, /**< recv attribute set update */
114 msg_OPT_GOT_UNSET = 0x025a, /**< recv attribute unset update */
115
116 msg_OPT_DID_SET = 0x025d, /**< attribute set response */
117 msg_OPT_DID_UNSET = 0x025e, /**< attribute unset response */
118 msg_OPT_DID_ERROR = 0x025f, /**< attribute set/unset error */
119 };
120
121
122 static void aware_entry_free(struct aware_entry *ae) {
123 mwAwareSnapshot_clear(&ae->aware);
124 g_list_free(ae->membership);
125 g_hash_table_destroy(ae->attribs);
126 g_free(ae);
127 }
128
129
130 static void attrib_entry_free(struct attrib_entry *ae) {
131 g_list_free(ae->membership);
132 g_free(ae);
133 }
134
135
136 static void attrib_free(struct mwAwareAttribute *attrib) {
137 mwOpaque_clear(&attrib->data);
138 g_free(attrib);
139 }
140
141
142 static struct aware_entry *aware_find(struct mwServiceAware *srvc,
143 struct mwAwareIdBlock *srch) {
144 g_assert(srvc != NULL);
145 g_assert(srvc->entries != NULL);
146 g_return_val_if_fail(srch != NULL, NULL);
147
148 return g_hash_table_lookup(srvc->entries, srch);
149 }
150
151
152 static struct aware_entry *list_aware_find(struct mwAwareList *list,
153 struct mwAwareIdBlock *srch) {
154 g_return_val_if_fail(list != NULL, NULL);
155 g_return_val_if_fail(list->entries != NULL, NULL);
156 g_return_val_if_fail(srch != NULL, NULL);
157
158 return g_hash_table_lookup(list->entries, srch);
159 }
160
161
162 static void compose_list(struct mwPutBuffer *b, GList *id_list) {
163 guint32_put(b, g_list_length(id_list));
164 for(; id_list; id_list = id_list->next)
165 mwAwareIdBlock_put(b, id_list->data);
166 }
167
168
169 static int send_add(struct mwChannel *chan, GList *id_list) {
170 struct mwPutBuffer *b = mwPutBuffer_new();
171 struct mwOpaque o;
172 int ret;
173
174 compose_list(b, id_list);
175
176 mwPutBuffer_finalize(&o, b);
177
178 ret = mwChannel_send(chan, msg_AWARE_ADD, &o);
179 mwOpaque_clear(&o);
180
181 return ret;
182 }
183
184
185 static int send_rem(struct mwChannel *chan, GList *id_list) {
186 struct mwPutBuffer *b = mwPutBuffer_new();
187 struct mwOpaque o;
188 int ret;
189
190 compose_list(b, id_list);
191 mwPutBuffer_finalize(&o, b);
192
193 ret = mwChannel_send(chan, msg_AWARE_REMOVE, &o);
194 mwOpaque_clear(&o);
195
196 return ret;
197 }
198
199
200 static gboolean collect_dead(gpointer key, gpointer val, gpointer data) {
201 struct aware_entry *aware = val;
202 GList **dead = data;
203
204 if(aware->membership == NULL) {
205 g_info(" removing %s, %s",
206 NSTR(aware->aware.id.user), NSTR(aware->aware.id.community));
207 *dead = g_list_append(*dead, aware);
208 return TRUE;
209
210 } else {
211 return FALSE;
212 }
213 }
214
215
216 static int remove_unused(struct mwServiceAware *srvc) {
217 /* - create a GList of all the unused aware entries
218 - remove each unused aware from the service
219 - if the service is alive, send a removal message for the collected
220 unused.
221 */
222
223 int ret = 0;
224 GList *dead = NULL, *l;
225
226 if(srvc->entries) {
227 g_info("bring out your dead *clang*");
228 g_hash_table_foreach_steal(srvc->entries, collect_dead, &dead);
229 }
230
231 if(dead) {
232 if(MW_SERVICE_IS_LIVE(srvc))
233 ret = send_rem(srvc->channel, dead) || ret;
234
235 for(l = dead; l; l = l->next)
236 aware_entry_free(l->data);
237
238 g_list_free(dead);
239 }
240
241 return ret;
242 }
243
244
245 static int send_attrib_list(struct mwServiceAware *srvc) {
246 struct mwPutBuffer *b;
247 struct mwOpaque o;
248
249 int tmp;
250 GList *l;
251
252 l = map_collect_keys(srvc->attribs);
253 tmp = g_list_length(l);
254
255 b = mwPutBuffer_new();
256 guint32_put(b, 0x00);
257 guint32_put(b, tmp);
258
259 for(; l; l = g_list_delete_link(l, l)) {
260 guint32_put(b, GPOINTER_TO_UINT(l->data));
261 }
262
263 mwPutBuffer_finalize(&o, b);
264 tmp = mwChannel_send(srvc->channel, msg_OPT_WATCH, &o);
265 mwOpaque_clear(&o);
266
267 return tmp;
268 }
269
270
271 static gboolean collect_attrib_dead(gpointer key, gpointer val,
272 gpointer data) {
273
274 struct attrib_entry *attrib = val;
275 GList **dead = data;
276
277 if(attrib->membership == NULL) {
278 g_info(" removing 0x%08x", GPOINTER_TO_UINT(key));
279 *dead = g_list_append(*dead, attrib);
280 return TRUE;
281
282 } else {
283 return FALSE;
284 }
285 }
286
287
288 static int remove_unused_attrib(struct mwServiceAware *srvc) {
289 GList *dead = NULL;
290
291 if(srvc->entries) {
292 g_info("collecting dead attributes");
293 g_hash_table_foreach_steal(srvc->attribs, collect_attrib_dead, &dead);
294 }
295
296 /* since we stole them, we'll have to clean 'em up manually */
297 for(; dead; dead = g_list_delete_link(dead, dead)) {
298 attrib_entry_free(dead->data);
299 }
300
301 return send_attrib_list(srvc);
302 }
303
304
305 static void recv_accept(struct mwServiceAware *srvc,
306 struct mwChannel *chan,
307 struct mwMsgChannelAccept *msg) {
308
309 g_return_if_fail(srvc->channel != NULL);
310 g_return_if_fail(srvc->channel == chan);
311
312 if(MW_SERVICE_IS_STARTING(MW_SERVICE(srvc))) {
313 GList *list = NULL;
314
315 list = map_collect_values(srvc->entries);
316 send_add(chan, list);
317 g_list_free(list);
318
319 send_attrib_list(srvc);
320
321 mwService_started(MW_SERVICE(srvc));
322
323 } else {
324 mwChannel_destroy(chan, ERR_FAILURE, NULL);
325 }
326 }
327
328
329 static void recv_destroy(struct mwServiceAware *srvc,
330 struct mwChannel *chan,
331 struct mwMsgChannelDestroy *msg) {
332
333 srvc->channel = NULL;
334 mwService_stop(MW_SERVICE(srvc));
335
336 /** @todo session sense service and mwService_start */
337 }
338
339
340 /** called from SNAPSHOT_recv, UPDATE_recv, and
341 mwServiceAware_setStatus */
342 static void status_recv(struct mwServiceAware *srvc,
343 struct mwAwareSnapshot *idb) {
344
345 struct aware_entry *aware;
346 GList *l;
347
348 aware = aware_find(srvc, &idb->id);
349
350 if(! aware) {
351 /* we don't deal with receiving status for something we're not
352 monitoring, but it will happen sometimes, eg from manually set
353 status */
354 return;
355 }
356
357 /* clear the existing status, then clone in the new status */
358 mwAwareSnapshot_clear(&aware->aware);
359 mwAwareSnapshot_clone(&aware->aware, idb);
360
361 /* trigger each of the entry's lists */
362 for(l = aware->membership; l; l = l->next) {
363 struct mwAwareList *alist = l->data;
364 struct mwAwareListHandler *handler = alist->handler;
365
366 if(handler && handler->on_aware)
367 handler->on_aware(alist, idb);
368 }
369 }
370
371
372 static void attrib_recv(struct mwServiceAware *srvc,
373 struct mwAwareIdBlock *idb,
374 struct mwAwareAttribute *attrib) {
375
376 struct aware_entry *aware;
377 struct mwAwareAttribute *old_attrib;
378 GList *l;
379 guint32 key;
380 gpointer k;
381
382 aware = aware_find(srvc, idb);
383 g_return_if_fail(aware != NULL);
384
385 key = attrib->key;
386 k = GUINT_TO_POINTER(key);
387
388 old_attrib = g_hash_table_lookup(aware->attribs, k);
389 if(! old_attrib) {
390 old_attrib = g_new0(struct mwAwareAttribute, 1);
391 old_attrib->key = key;
392 g_hash_table_insert(aware->attribs, k, old_attrib);
393 }
394
395 mwOpaque_clear(&old_attrib->data);
396 mwOpaque_clone(&old_attrib->data, &attrib->data);
397
398 for(l = aware->membership; l; l = l->next) {
399 struct mwAwareList *list = l->data;
400 struct mwAwareListHandler *h = list->handler;
401
402 if(h && h->on_attrib && g_hash_table_lookup(list->attribs, k))
403 h->on_attrib(list, idb, old_attrib);
404 }
405 }
406
407
408 gboolean list_add(struct mwAwareList *list, struct mwAwareIdBlock *id) {
409
410 struct mwServiceAware *srvc = list->service;
411 struct aware_entry *aware;
412
413 g_return_val_if_fail(id->user != NULL, FALSE);
414 g_return_val_if_fail(strlen(id->user) > 0, FALSE);
415
416 aware = list_aware_find(list, id);
417 if(aware) return FALSE;
418
419 aware = aware_find(srvc, id);
420 if(! aware) {
421 aware = g_new0(struct aware_entry, 1);
422 aware->attribs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
423 (GDestroyNotify) attrib_free);
424 mwAwareIdBlock_clone(ENTRY_KEY(aware), id);
425
426 g_hash_table_insert(srvc->entries, ENTRY_KEY(aware), aware);
427 }
428
429 aware->membership = g_list_append(aware->membership, list);
430 g_hash_table_insert(list->entries, ENTRY_KEY(aware), aware);
431 return TRUE;
432 }
433
434
435 static void group_member_recv(struct mwServiceAware *srvc,
436 struct mwAwareSnapshot *idb) {
437 /* @todo
438 - look up group by id
439 - find each list group belongs to
440 - add user to lists
441 */
442
443 struct mwAwareIdBlock gsrch = { mwAware_GROUP, idb->group, NULL };
444 struct aware_entry *grp;
445 GList *l, *m;
446
447 grp = aware_find(srvc, &gsrch);
448 g_return_if_fail(grp != NULL); /* this could happen, with timing. */
449
450 l = g_list_prepend(NULL, &idb->id);
451
452 for(m = grp->membership; m; m = m->next) {
453
454 /* if we just list_add, we won't receive updates for attributes,
455 so annoyingly we have to turn around and send out an add aware
456 message for each incoming group member */
457
458 /* list_add(m->data, &idb->id); */
459 mwAwareList_addAware(m->data, l);
460 }
461
462 g_list_free(l);
463 }
464
465
466 static void recv_SNAPSHOT(struct mwServiceAware *srvc,
467 struct mwGetBuffer *b) {
468
469 guint32 count;
470
471 struct mwAwareSnapshot *snap;
472 snap = g_new0(struct mwAwareSnapshot, 1);
473
474 guint32_get(b, &count);
475
476 while(count--) {
477 mwAwareSnapshot_get(b, snap);
478
479 if(mwGetBuffer_error(b)) {
480 mwAwareSnapshot_clear(snap);
481 break;
482 }
483
484 if(snap->group)
485 group_member_recv(srvc, snap);
486
487 status_recv(srvc, snap);
488 mwAwareSnapshot_clear(snap);
489 }
490
491 g_free(snap);
492 }
493
494
495 static void recv_UPDATE(struct mwServiceAware *srvc,
496 struct mwGetBuffer *b) {
497
498 struct mwAwareSnapshot *snap;
499
500 snap = g_new0(struct mwAwareSnapshot, 1);
501 mwAwareSnapshot_get(b, snap);
502
503 if(snap->group)
504 group_member_recv(srvc, snap);
505
506 if(! mwGetBuffer_error(b))
507 status_recv(srvc, snap);
508
509 mwAwareSnapshot_clear(snap);
510 g_free(snap);
511 }
512
513
514 static void recv_GROUP(struct mwServiceAware *srvc,
515 struct mwGetBuffer *b) {
516
517 struct mwAwareIdBlock idb = { 0, 0, 0 };
518
519 /* really nothing to be done with this. The group should have
520 already been added to the list and service, and is now simply
521 awaiting a snapshot/update with users listed as belonging in said
522 group. */
523
524 mwAwareIdBlock_get(b, &idb);
525 mwAwareIdBlock_clear(&idb);
526 }
527
528
529 static void recv_OPT_GOT_SET(struct mwServiceAware *srvc,
530 struct mwGetBuffer *b) {
531
532 struct mwAwareAttribute attrib;
533 struct mwAwareIdBlock idb;
534 guint32 junk, check;
535
536 guint32_get(b, &junk);
537 mwAwareIdBlock_get(b, &idb);
538 guint32_get(b, &junk);
539 guint32_get(b, &check);
540 guint32_get(b, &junk);
541 guint32_get(b, &attrib.key);
542
543 if(check) {
544 mwOpaque_get(b, &attrib.data);
545 } else {
546 attrib.data.len = 0;
547 attrib.data.data = NULL;
548 }
549
550 attrib_recv(srvc, &idb, &attrib);
551
552 mwAwareIdBlock_clear(&idb);
553 mwOpaque_clear(&attrib.data);
554 }
555
556
557 static void recv_OPT_GOT_UNSET(struct mwServiceAware *srvc,
558 struct mwGetBuffer *b) {
559
560 struct mwAwareAttribute attrib;
561 struct mwAwareIdBlock idb;
562 guint32 junk;
563
564 attrib.key = 0;
565 attrib.data.len = 0;
566 attrib.data.data = NULL;
567
568 guint32_get(b, &junk);
569 mwAwareIdBlock_get(b, &idb);
570 guint32_get(b, &attrib.key);
571
572 attrib_recv(srvc, &idb, &attrib);
573
574 mwAwareIdBlock_clear(&idb);
575 }
576
577
578 static void recv(struct mwService *srvc, struct mwChannel *chan,
579 guint16 type, struct mwOpaque *data) {
580
581 struct mwServiceAware *srvc_aware = (struct mwServiceAware *) srvc;
582 struct mwGetBuffer *b;
583
584 g_return_if_fail(srvc_aware->channel == chan);
585 g_return_if_fail(srvc->session == mwChannel_getSession(chan));
586 g_return_if_fail(data != NULL);
587
588 b = mwGetBuffer_wrap(data);
589
590 switch(type) {
591 case msg_AWARE_SNAPSHOT:
592 recv_SNAPSHOT(srvc_aware, b);
593 break;
594
595 case msg_AWARE_UPDATE:
596 recv_UPDATE(srvc_aware, b);
597 break;
598
599 case msg_AWARE_GROUP:
600 recv_GROUP(srvc_aware, b);
601 break;
602
603 case msg_OPT_GOT_SET:
604 recv_OPT_GOT_SET(srvc_aware, b);
605 break;
606
607 case msg_OPT_GOT_UNSET:
608 recv_OPT_GOT_UNSET(srvc_aware, b);
609 break;
610
611 case msg_OPT_DID_SET:
612 case msg_OPT_DID_UNSET:
613 case msg_OPT_DID_ERROR:
614 break;
615
616 default:
617 mw_debug_mailme(data, "unknown message in aware service: 0x%04x", type);
618 }
619
620 mwGetBuffer_free(b);
621 }
622
623
624 static void clear(struct mwService *srvc) {
625 struct mwServiceAware *srvc_aware = (struct mwServiceAware *) srvc;
626
627 g_return_if_fail(srvc != NULL);
628
629 while(srvc_aware->lists)
630 mwAwareList_free( (struct mwAwareList *) srvc_aware->lists->data );
631
632 g_hash_table_destroy(srvc_aware->entries);
633 srvc_aware->entries = NULL;
634 }
635
636
637 static const char *name(struct mwService *srvc) {
638 return "Presence Awareness";
639 }
640
641
642 static const char *desc(struct mwService *srvc) {
643 return "Buddy list service with support for server-side groups";
644 }
645
646
647 static struct mwChannel *make_blist(struct mwServiceAware *srvc,
648 struct mwChannelSet *cs) {
649
650 struct mwChannel *chan = mwChannel_newOutgoing(cs);
651
652 mwChannel_setService(chan, MW_SERVICE(srvc));
653 mwChannel_setProtoType(chan, 0x00000011);
654 mwChannel_setProtoVer(chan, 0x00030005);
655
656 return mwChannel_create(chan)? NULL: chan;
657 }
658
659
660 static void start(struct mwService *srvc) {
661 struct mwServiceAware *srvc_aware;
662 struct mwChannel *chan;
663
664 srvc_aware = (struct mwServiceAware *) srvc;
665 chan = make_blist(srvc_aware, mwSession_getChannels(srvc->session));
666
667 if(chan != NULL) {
668 srvc_aware->channel = chan;
669 } else {
670 mwService_stopped(srvc);
671 }
672 }
673
674
675 static void stop(struct mwService *srvc) {
676 struct mwServiceAware *srvc_aware;
677
678 srvc_aware = (struct mwServiceAware *) srvc;
679 mwChannel_destroy(srvc_aware->channel, ERR_SUCCESS, NULL);
680 mwService_stopped(srvc);
681 }
682
683
684 struct mwServiceAware *
685 mwServiceAware_new(struct mwSession *session,
686 struct mwAwareHandler *handler) {
687
688 struct mwService *service;
689 struct mwServiceAware *srvc;
690
691 g_return_val_if_fail(session != NULL, NULL);
692 g_return_val_if_fail(handler != NULL, NULL);
693
694 srvc = g_new0(struct mwServiceAware, 1);
695 srvc->handler = handler;
696 srvc->entries = g_hash_table_new_full((GHashFunc) mwAwareIdBlock_hash,
697 (GEqualFunc) mwAwareIdBlock_equal,
698 NULL,
699 (GDestroyNotify) aware_entry_free);
700
701 srvc->attribs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
702 (GDestroyNotify) attrib_entry_free);
703
704 service = MW_SERVICE(srvc);
705 mwService_init(service, session, SERVICE_AWARE);
706
707 service->recv_accept = (mwService_funcRecvAccept) recv_accept;
708 service->recv_destroy = (mwService_funcRecvDestroy) recv_destroy;
709 service->recv = recv;
710 service->start = start;
711 service->stop = stop;
712 service->clear = clear;
713 service->get_name = name;
714 service->get_desc = desc;
715
716 return srvc;
717 }
718
719
720 int mwServiceAware_setAttribute(struct mwServiceAware *srvc,
721 guint32 key, struct mwOpaque *data) {
722 struct mwPutBuffer *b;
723 struct mwOpaque o;
724 int ret;
725
726 b = mwPutBuffer_new();
727
728 guint32_put(b, 0x00);
729 guint32_put(b, data->len);
730 guint32_put(b, 0x00);
731 guint32_put(b, key);
732 mwOpaque_put(b, data);
733
734 mwPutBuffer_finalize(&o, b);
735 ret = mwChannel_send(srvc->channel, msg_OPT_DO_SET, &o);
736 mwOpaque_clear(&o);
737
738 return ret;
739 }
740
741
742 int mwServiceAware_setAttributeBoolean(struct mwServiceAware *srvc,
743 guint32 key, gboolean val) {
744 int ret;
745 struct mwPutBuffer *b;
746 struct mwOpaque o;
747
748 b = mwPutBuffer_new();
749
750 gboolean_put(b, FALSE);
751 gboolean_put(b, val);
752
753 mwPutBuffer_finalize(&o, b);
754
755 ret = mwServiceAware_setAttribute(srvc, key, &o);
756 mwOpaque_clear(&o);
757
758 return ret;
759 }
760
761
762 int mwServiceAware_setAttributeInteger(struct mwServiceAware *srvc,
763 guint32 key, guint32 val) {
764 int ret;
765 struct mwPutBuffer *b;
766 struct mwOpaque o;
767
768 b = mwPutBuffer_new();
769 guint32_put(b, val);
770
771 mwPutBuffer_finalize(&o, b);
772
773 ret = mwServiceAware_setAttribute(srvc, key, &o);
774 mwOpaque_clear(&o);
775
776 return ret;
777 }
778
779
780 int mwServiceAware_setAttributeString(struct mwServiceAware *srvc,
781 guint32 key, const char *str) {
782 int ret;
783 struct mwPutBuffer *b;
784 struct mwOpaque o;
785
786 b = mwPutBuffer_new();
787 mwString_put(b, str);
788
789 mwPutBuffer_finalize(&o, b);
790
791 ret = mwServiceAware_setAttribute(srvc, key, &o);
792 mwOpaque_clear(&o);
793
794 return ret;
795 }
796
797
798 int mwServiceAware_unsetAttribute(struct mwServiceAware *srvc,
799 guint32 key) {
800 struct mwPutBuffer *b;
801 struct mwOpaque o;
802 int ret;
803
804 b = mwPutBuffer_new();
805
806 guint32_put(b, 0x00);
807 guint32_put(b, key);
808
809 mwPutBuffer_finalize(&o, b);
810 ret = mwChannel_send(srvc->channel, msg_OPT_DO_UNSET, &o);
811 mwOpaque_clear(&o);
812
813 return ret;
814 }
815
816
817 guint32 mwAwareAttribute_getKey(const struct mwAwareAttribute *attrib) {
818 g_return_val_if_fail(attrib != NULL, 0x00);
819 return attrib->key;
820 }
821
822
823 gboolean mwAwareAttribute_asBoolean(const struct mwAwareAttribute *attrib) {
824 struct mwGetBuffer *b;
825 gboolean ret;
826
827 if(! attrib) return FALSE;
828
829 b = mwGetBuffer_wrap(&attrib->data);
830 if(attrib->data.len >= 4) {
831 guint32 r32 = 0x00;
832 guint32_get(b, &r32);
833 ret = !! r32;
834
835 } else if(attrib->data.len >= 2) {
836 guint16 r16 = 0x00;
837 guint16_get(b, &r16);
838 ret = !! r16;
839
840 } else if(attrib->data.len) {
841 gboolean_get(b, &ret);
842 }
843
844 mwGetBuffer_free(b);
845
846 return ret;
847 }
848
849
850 guint32 mwAwareAttribute_asInteger(const struct mwAwareAttribute *attrib) {
851 struct mwGetBuffer *b;
852 guint32 r32 = 0x00;
853
854 if(! attrib) return 0x00;
855
856 b = mwGetBuffer_wrap(&attrib->data);
857 if(attrib->data.len >= 4) {
858 guint32_get(b, &r32);
859
860 } else if(attrib->data.len == 3) {
861 gboolean rb = FALSE;
862 guint16 r16 = 0x00;
863 gboolean_get(b, &rb);
864 guint16_get(b, &r16);
865 r32 = (guint32) r16;
866
867 } else if(attrib->data.len == 2) {
868 guint16 r16 = 0x00;
869 guint16_get(b, &r16);
870 r32 = (guint32) r16;
871
872 } else if(attrib->data.len) {
873 gboolean rb = FALSE;
874 gboolean_get(b, &rb);
875 r32 = (guint32) rb;
876 }
877
878 mwGetBuffer_free(b);
879
880 return r32;
881 }
882
883
884 char *mwAwareAttribute_asString(const struct mwAwareAttribute *attrib) {
885 struct mwGetBuffer *b;
886 char *ret = NULL;
887
888 if(! attrib) return NULL;
889
890 b = mwGetBuffer_wrap(&attrib->data);
891 mwString_get(b, &ret);
892 mwGetBuffer_free(b);
893
894 return ret;
895 }
896
897
898 const struct mwOpaque *
899 mwAwareAttribute_asOpaque(const struct mwAwareAttribute *attrib) {
900 g_return_val_if_fail(attrib != NULL, NULL);
901 return &attrib->data;
902 }
903
904
905 struct mwAwareList *
906 mwAwareList_new(struct mwServiceAware *srvc,
907 struct mwAwareListHandler *handler) {
908
909 struct mwAwareList *al;
910
911 g_return_val_if_fail(srvc != NULL, NULL);
912 g_return_val_if_fail(handler != NULL, NULL);
913
914 al = g_new0(struct mwAwareList, 1);
915 al->service = srvc;
916 al->entries = g_hash_table_new((GHashFunc) mwAwareIdBlock_hash,
917 (GEqualFunc) mwAwareIdBlock_equal);
918 al->attribs = g_hash_table_new(g_direct_hash, g_direct_equal);
919 al->handler = handler;
920
921 return al;
922 }
923
924
925 void mwAwareList_free(struct mwAwareList *list) {
926 struct mwServiceAware *srvc;
927 struct mwAwareListHandler *handler;
928
929 g_return_if_fail(list != NULL);
930 g_return_if_fail(list->entries != NULL);
931 g_return_if_fail(list->service != NULL);
932
933 handler = list->handler;
934 if(handler && handler->clear) {
935 handler->clear(list);
936 list->handler = NULL;
937 }
938
939 mw_datum_clear(&list->client_data);
940
941 srvc = list->service;
942 srvc->lists = g_list_remove(srvc->lists, list);
943
944 mwAwareList_unwatchAllAttributes(list);
945 mwAwareList_removeAllAware(list);
946 }
947
948
949 struct mwAwareListHandler *mwAwareList_getHandler(struct mwAwareList *list) {
950 g_return_val_if_fail(list != NULL, NULL);
951 return list->handler;
952 }
953
954
955 static void watch_add(struct mwAwareList *list, guint32 key) {
956 struct mwServiceAware *srvc;
957 struct attrib_entry *watch;
958 gpointer k = GUINT_TO_POINTER(key);
959
960 if(g_hash_table_lookup(list->attribs, k))
961 return;
962
963 srvc = list->service;
964
965 watch = g_hash_table_lookup(srvc->attribs, k);
966 if(! watch) {
967 watch = g_new0(struct attrib_entry, 1);
968 watch->key = key;
969 g_hash_table_insert(srvc->attribs, k, watch);
970 }
971
972 g_hash_table_insert(list->attribs, k, watch);
973 watch->membership = g_list_prepend(watch->membership, list);
974 }
975
976
977 static void watch_remove(struct mwAwareList *list, guint32 key) {
978 struct attrib_entry *watch;
979 gpointer k = GUINT_TO_POINTER(key);
980
981 watch = g_hash_table_lookup(list->attribs, k);
982 g_return_if_fail(watch != NULL);
983
984 g_hash_table_remove(list->attribs, k);
985 watch->membership = g_list_remove(watch->membership, list);
986 }
987
988
989 int mwAwareList_watchAttributeArray(struct mwAwareList *list,
990 guint32 *keys) {
991 guint32 k;
992
993 g_return_val_if_fail(list != NULL, -1);
994 g_return_val_if_fail(list->service != NULL, -1);
995
996 if(! keys) return 0;
997
998 for(k = *keys; k; keys++)
999 watch_add(list, k);
1000
1001 return send_attrib_list(list->service);
1002 }
1003
1004
1005 int mwAwareList_watchAttributes(struct mwAwareList *list,
1006 guint32 key, ...) {
1007 guint32 k;
1008 va_list args;
1009
1010 g_return_val_if_fail(list != NULL, -1);
1011 g_return_val_if_fail(list->service != NULL, -1);
1012
1013 va_start(args, key);
1014 for(k = key; k; k = va_arg(args, guint32))
1015 watch_add(list, k);
1016 va_end(args);
1017
1018 return send_attrib_list(list->service);
1019 }
1020
1021
1022 int mwAwareList_unwatchAttributeArray(struct mwAwareList *list,
1023 guint32 *keys) {
1024 guint32 k;
1025
1026 g_return_val_if_fail(list != NULL, -1);
1027 g_return_val_if_fail(list->service != NULL, -1);
1028
1029 if(! keys) return 0;
1030
1031 for(k = *keys; k; keys++)
1032 watch_add(list, k);
1033
1034 remove_unused_attrib(list->service);
1035 return send_attrib_list(list->service);
1036 }
1037
1038
1039 int mwAwareList_unwatchAttributes(struct mwAwareList *list,
1040 guint32 key, ...) {
1041 guint32 k;
1042 va_list args;
1043
1044 g_return_val_if_fail(list != NULL, -1);
1045 g_return_val_if_fail(list->service != NULL, -1);
1046
1047 va_start(args, key);
1048 for(k = key; k; k = va_arg(args, guint32))
1049 watch_remove(list, k);
1050 va_end(args);
1051
1052 remove_unused_attrib(list->service);
1053 return send_attrib_list(list->service);
1054 }
1055
1056
1057 static void dismember_attrib(gpointer k, struct attrib_entry *watch,
1058 struct mwAwareList *list) {
1059
1060 watch->membership = g_list_remove(watch->membership, list);
1061 }
1062
1063
1064 int mwAwareList_unwatchAllAttributes(struct mwAwareList *list) {
1065
1066 struct mwServiceAware *srvc;
1067
1068 g_return_val_if_fail(list != NULL, -1);
1069 srvc = list->service;
1070
1071 g_hash_table_foreach(list->attribs, (GHFunc) dismember_attrib, list);
1072 g_hash_table_destroy(list->attribs);
1073 list->attribs = g_hash_table_new(g_direct_hash, g_direct_equal);
1074
1075 remove_unused_attrib(srvc);
1076 return send_attrib_list(list->service);
1077 }
1078
1079
1080 static void collect_attrib_keys(gpointer key, struct attrib_entry *attrib,
1081 guint32 **ck) {
1082 guint32 *keys = (*ck)++;
1083 *keys = GPOINTER_TO_UINT(key);
1084 }
1085
1086
1087 guint32 *mwAwareList_getWatchedAttributes(struct mwAwareList *list) {
1088 guint32 *keys, **ck;
1089 guint count;
1090
1091 g_return_val_if_fail(list != NULL, NULL);
1092 g_return_val_if_fail(list->attribs != NULL, NULL);
1093
1094 count = g_hash_table_size(list->attribs);
1095 keys = g_new0(guint32, count + 1);
1096
1097 ck = &keys;
1098 g_hash_table_foreach(list->attribs, (GHFunc) collect_attrib_keys, ck);
1099
1100 return keys;
1101 }
1102
1103
1104 int mwAwareList_addAware(struct mwAwareList *list, GList *id_list) {
1105
1106 /* for each awareness id:
1107 - if it's already in the list, continue
1108 - if it's not in the service list:
1109 - create an awareness
1110 - add it to the service list
1111 - add this list to the membership
1112 - add to the list
1113 */
1114
1115 struct mwServiceAware *srvc;
1116 GList *additions = NULL;
1117 int ret = 0;
1118
1119 g_return_val_if_fail(list != NULL, -1);
1120
1121 srvc = list->service;
1122 g_return_val_if_fail(srvc != NULL, -1);
1123
1124 for(; id_list; id_list = id_list->next) {
1125 if(list_add(list, id_list->data))
1126 additions = g_list_prepend(additions, id_list->data);
1127 }
1128
1129 /* if the service is alive-- or getting there-- we'll need to send
1130 these additions upstream */
1131 if(MW_SERVICE_IS_LIVE(srvc) && additions)
1132 ret = send_add(srvc->channel, additions);
1133
1134 g_list_free(additions);
1135 return ret;
1136 }
1137
1138
1139 int mwAwareList_removeAware(struct mwAwareList *list, GList *id_list) {
1140
1141 /* for each awareness id:
1142 - if it's not in the list, forget it
1143 - remove from the list
1144 - remove list from the membership
1145
1146 - call remove round
1147 */
1148
1149 struct mwServiceAware *srvc;
1150 struct mwAwareIdBlock *id;
1151 struct aware_entry *aware;
1152
1153 g_return_val_if_fail(list != NULL, -1);
1154
1155 srvc = list->service;
1156 g_return_val_if_fail(srvc != NULL, -1);
1157
1158 for(; id_list; id_list = id_list->next) {
1159 id = id_list->data;
1160 aware = list_aware_find(list, id);
1161
1162 if(! aware) {
1163 g_warning("buddy %s, %s not in list",
1164 NSTR(id->user),
1165 NSTR(id->community));
1166 continue;
1167 }
1168
1169 aware->membership = g_list_remove(aware->membership, list);
1170 g_hash_table_remove(list->entries, id);
1171 }
1172
1173 return remove_unused(srvc);
1174 }
1175
1176
1177 static void dismember_aware(gpointer k, struct aware_entry *aware,
1178 struct mwAwareList *list) {
1179
1180 aware->membership = g_list_remove(aware->membership, list);
1181 }
1182
1183
1184 int mwAwareList_removeAllAware(struct mwAwareList *list) {
1185 struct mwServiceAware *srvc;
1186
1187 g_return_val_if_fail(list != NULL, -1);
1188 srvc = list->service;
1189
1190 g_return_val_if_fail(srvc != NULL, -1);
1191
1192 /* for each entry, remove the aware list from the service entry's
1193 membership collection */
1194 g_hash_table_foreach(list->entries, (GHFunc) dismember_aware, list);
1195 g_hash_table_destroy(list->entries);
1196 g_free(list);
1197
1198 return remove_unused(srvc);
1199 }
1200
1201
1202 void mwAwareList_setClientData(struct mwAwareList *list,
1203 gpointer data, GDestroyNotify clear) {
1204
1205 g_return_if_fail(list != NULL);
1206 mw_datum_set(&list->client_data, data, clear);
1207 }
1208
1209
1210 gpointer mwAwareList_getClientData(struct mwAwareList *list) {
1211 g_return_val_if_fail(list != NULL, NULL);
1212 return mw_datum_get(&list->client_data);
1213 }
1214
1215
1216 void mwAwareList_removeClientData(struct mwAwareList *list) {
1217 g_return_if_fail(list != NULL);
1218 mw_datum_clear(&list->client_data);
1219 }
1220
1221
1222 void mwServiceAware_setStatus(struct mwServiceAware *srvc,
1223 struct mwAwareIdBlock *user,
1224 struct mwUserStatus *stat) {
1225
1226 struct mwAwareSnapshot idb;
1227
1228 g_return_if_fail(srvc != NULL);
1229 g_return_if_fail(user != NULL);
1230 g_return_if_fail(stat != NULL);
1231
1232 /* just reference the strings. then we don't need to free them */
1233 idb.id.type = user->type;
1234 idb.id.user = user->user;
1235 idb.id.community = user->community;
1236
1237 idb.group = NULL;
1238 idb.online = TRUE;
1239 idb.alt_id = NULL;
1240
1241 idb.status.status = stat->status;
1242 idb.status.time = stat->time;
1243 idb.status.desc = stat->desc;
1244
1245 idb.name = NULL;
1246
1247 status_recv(srvc, &idb);
1248 }
1249
1250
1251 const struct mwAwareAttribute *
1252 mwServiceAware_getAttribute(struct mwServiceAware *srvc,
1253 struct mwAwareIdBlock *user,
1254 guint32 key) {
1255
1256 struct aware_entry *aware;
1257
1258 g_return_val_if_fail(srvc != NULL, NULL);
1259 g_return_val_if_fail(user != NULL, NULL);
1260 g_return_val_if_fail(key != 0x00, NULL);
1261
1262 aware = aware_find(srvc, user);
1263 g_return_val_if_fail(aware != NULL, NULL);
1264
1265 return g_hash_table_lookup(aware->attribs, GUINT_TO_POINTER(key));
1266 }
1267
1268
1269 const char *mwServiceAware_getText(struct mwServiceAware *srvc,
1270 struct mwAwareIdBlock *user) {
1271
1272 struct aware_entry *aware;
1273
1274 g_return_val_if_fail(srvc != NULL, NULL);
1275 g_return_val_if_fail(user != NULL, NULL);
1276
1277 aware = aware_find(srvc, user);
1278 g_return_val_if_fail(aware != NULL, NULL);
1279
1280 return aware->aware.status.desc;
1281 }
1282
1283