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