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
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29
30 #include "mw_channel.h"
31 #include "mw_debug.h"
32 #include "mw_error.h"
33 #include "mw_message.h"
34 #include "mw_service.h"
35 #include "mw_session.h"
36 #include "mw_srvc_conf.h"
37 #include "mw_util.h"
38
39
40 /* This thing needs a re-write. More than anything else, I need to
41 re-examine the conferencing service protocol from more modern
42 clients */
43
44
45 #define PROTOCOL_TYPE 0x00000010
46 #define PROTOCOL_VER 0x00000002
47
48
49 /** @see mwMsgChannelSend::type
50 @see recv */
51 enum msg_type {
52 msg_WELCOME = 0x0000, /**< welcome message */
53 msg_INVITE = 0x0001, /**< outgoing invitation */
54 msg_JOIN = 0x0002, /**< someone joined */
55 msg_PART = 0x0003, /**< someone left */
56 msg_MESSAGE = 0x0004, /**< conference message */
57 };
58
59
60 /** the conferencing service */
61 struct mwServiceConference {
62 struct mwService service;
63
64 /** call-back handler for this service */
65 struct mwConferenceHandler *handler;
66
67 /** collection of conferences in this service */
68 GList *confs;
69 };
70
71
72 /** a conference and its members */
73 struct mwConference {
74 enum mwConferenceState state; /**< state of the conference */
75 struct mwServiceConference *service; /**< owning service */
76 struct mwChannel *channel; /**< conference's channel */
77
78 char *name; /**< server identifier for the conference */
79 char *title; /**< topic for the conference */
80
81 struct mwLoginInfo owner; /**< person who created this conference */
82 GHashTable *members; /**< mapping guint16:mwLoginInfo */
83 struct mw_datum client_data;
84 };
85
86
87 #define MEMBER_FIND(conf, id) \
88 g_hash_table_lookup(conf->members, GUINT_TO_POINTER((guint) id))
89
90
91 #define MEMBER_ADD(conf, id, member) \
92 g_hash_table_insert(conf->members, GUINT_TO_POINTER((guint) id), member)
93
94
95 #define MEMBER_REM(conf, id) \
96 g_hash_table_remove(conf->members, GUINT_TO_POINTER((guint) id));
97
98
99 /** clear and free a login info block */
login_free(struct mwLoginInfo * li)100 static void login_free(struct mwLoginInfo *li) {
101 mwLoginInfo_clear(li);
102 g_free(li);
103 }
104
105
106 /** generates a random conference name built around a user name */
conf_generate_name(const char * user)107 static char *conf_generate_name(const char *user) {
108 guint a, b;
109 char *ret;
110
111 user = user? user: "";
112
113 srand(clock() + rand());
114 a = ((rand() & 0xff) << 8) | (rand() & 0xff);
115 b = time(NULL);
116
117 ret = g_strdup_printf("%s(%08x,%04x)", user, b, a);
118 g_debug("generated random conference name: '%s'", ret);
119 return ret;
120 }
121
122
123
124
125
conf_new(struct mwServiceConference * srvc)126 static struct mwConference *conf_new(struct mwServiceConference *srvc) {
127
128 struct mwConference *conf;
129 struct mwSession *session;
130 const char *user;
131
132 conf = g_new0(struct mwConference, 1);
133 conf->state = mwConference_NEW;
134 conf->service = srvc;
135 conf->members = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
136 (GDestroyNotify) login_free);
137
138 session = mwService_getSession(MW_SERVICE(srvc));
139 user = mwSession_getProperty(session, mwSession_AUTH_USER_ID);
140
141 srvc->confs = g_list_prepend(srvc->confs, conf);
142
143 return conf;
144 }
145
146
147 /** clean and free a conference structure */
conf_free(struct mwConference * conf)148 static void conf_free(struct mwConference *conf) {
149 struct mwServiceConference *srvc;
150
151 /* this shouldn't ever happen, but just to be sure */
152 g_return_if_fail(conf != NULL);
153
154 srvc = conf->service;
155
156 if(conf->members)
157 g_hash_table_destroy(conf->members);
158
159 g_list_remove_all(srvc->confs, conf);
160
161 mw_datum_clear(&conf->client_data);
162
163 g_free(conf->name);
164 g_free(conf->title);
165 g_free(conf);
166 }
167
168
conf_find(struct mwServiceConference * srvc,struct mwChannel * chan)169 static struct mwConference *conf_find(struct mwServiceConference *srvc,
170 struct mwChannel *chan) {
171 GList *l;
172
173 g_return_val_if_fail(srvc != NULL, NULL);
174 g_return_val_if_fail(chan != NULL, NULL);
175
176 for(l = srvc->confs; l; l = l->next) {
177 struct mwConference *conf = l->data;
178 if(conf->channel == chan) return conf;
179 }
180
181 return NULL;
182 }
183
184
conf_state_str(enum mwConferenceState state)185 static const char *conf_state_str(enum mwConferenceState state) {
186 switch(state) {
187 case mwConference_NEW: return "new";
188 case mwConference_PENDING: return "pending";
189 case mwConference_INVITED: return "invited";
190 case mwConference_OPEN: return "open";
191 case mwConference_CLOSING: return "closing";
192 case mwConference_ERROR: return "error";
193
194 case mwConference_UNKNOWN: /* fall through */
195 default: return "UNKNOWN";
196 }
197 }
198
199
conf_state(struct mwConference * conf,enum mwConferenceState state)200 static void conf_state(struct mwConference *conf,
201 enum mwConferenceState state) {
202 g_return_if_fail(conf != NULL);
203
204 if(conf->state == state) return;
205
206 conf->state = state;
207 g_message("conference %s state: %s",
208 NSTR(conf->name), conf_state_str(state));
209 }
210
211
recv_channelCreate(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelCreate * msg)212 static void recv_channelCreate(struct mwService *srvc,
213 struct mwChannel *chan,
214 struct mwMsgChannelCreate *msg) {
215
216 /* - this is how we really receive invitations
217 - create a conference and associate it with the channel
218 - obtain the invite data from the msg addtl info
219 - mark the conference as INVITED
220 - trigger the got_invite event
221 */
222
223 struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc;
224 struct mwConference *conf;
225
226 struct mwGetBuffer *b;
227
228 char *invite = NULL;
229 guint tmp;
230
231 conf = conf_new(srvc_conf);
232 conf->channel = chan;
233
234 b = mwGetBuffer_wrap(&msg->addtl);
235
236 guint32_get(b, &tmp);
237 mwString_get(b, &conf->name);
238 mwString_get(b, &conf->title);
239 guint32_get(b, &tmp);
240 mwLoginInfo_get(b, &conf->owner);
241 guint32_get(b, &tmp);
242 mwString_get(b, &invite);
243
244 if(mwGetBuffer_error(b)) {
245 g_warning("failure parsing addtl for conference invite");
246 mwConference_destroy(conf, ERR_FAILURE, NULL);
247
248 } else {
249 struct mwConferenceHandler *h = srvc_conf->handler;
250 conf_state(conf, mwConference_INVITED);
251 if(h->on_invited)
252 h->on_invited(conf, &conf->owner, invite);
253 }
254
255 mwGetBuffer_free(b);
256 g_free(invite);
257 }
258
259
recv_channelAccept(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelAccept * msg)260 static void recv_channelAccept(struct mwService *srvc,
261 struct mwChannel *chan,
262 struct mwMsgChannelAccept *msg) {
263
264 ;
265 }
266
267
recv_channelDestroy(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelDestroy * msg)268 static void recv_channelDestroy(struct mwService *srvc,
269 struct mwChannel *chan,
270 struct mwMsgChannelDestroy *msg) {
271
272 /* - find conference from channel
273 - trigger got_closed
274 - remove conference, dealloc
275 */
276
277 struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc;
278 struct mwConference *conf = conf_find(srvc_conf, chan);
279 struct mwConferenceHandler *h = srvc_conf->handler;
280
281 /* if there's no such conference, then I guess there's nothing to worry
282 about. Except of course for the fact that we should never receive a
283 channel destroy for a conference that doesn't exist. */
284 if(! conf) return;
285
286 conf->channel = NULL;
287
288 conf_state(conf, msg->reason? mwConference_ERROR: mwConference_CLOSING);
289
290 if(h->conf_closed)
291 h->conf_closed(conf, msg->reason);
292
293 mwConference_destroy(conf, ERR_SUCCESS, NULL);
294 }
295
296
WELCOME_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwGetBuffer * b)297 static void WELCOME_recv(struct mwServiceConference *srvc,
298 struct mwConference *conf,
299 struct mwGetBuffer *b) {
300
301 struct mwConferenceHandler *h;
302 guint16 tmp16;
303 guint32 tmp32;
304 guint32 count;
305 GList *l = NULL;
306
307 /* re-read name and title */
308 g_free(conf->name);
309 g_free(conf->title);
310 conf->name = NULL;
311 conf->title = NULL;
312 mwString_get(b, &conf->name);
313 mwString_get(b, &conf->title);
314
315 /* some numbers we don't care about, then a count of members */
316 guint16_get(b, &tmp16);
317 guint32_get(b, &tmp32);
318 guint32_get(b, &count);
319
320 if(mwGetBuffer_error(b)) {
321 g_warning("error parsing welcome message for conference");
322 mwConference_destroy(conf, ERR_FAILURE, NULL);
323 return;
324 }
325
326 while(count--) {
327 guint16 member_id;
328 struct mwLoginInfo *member = g_new0(struct mwLoginInfo, 1);
329
330 guint16_get(b, &member_id);
331 mwLoginInfo_get(b, member);
332
333 if(mwGetBuffer_error(b)) {
334 login_free(member);
335 break;
336 }
337
338 MEMBER_ADD(conf, member_id, member);
339 l = g_list_append(l, member);
340 }
341
342 conf_state(conf, mwConference_OPEN);
343
344 h = srvc->handler;
345 if(h->conf_opened)
346 h->conf_opened(conf, l);
347
348 /* get rid of the GList, but not its contents */
349 g_list_free(l);
350 }
351
352
JOIN_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwGetBuffer * b)353 static void JOIN_recv(struct mwServiceConference *srvc,
354 struct mwConference *conf,
355 struct mwGetBuffer *b) {
356
357 struct mwConferenceHandler *h;
358 guint16 m_id;
359 struct mwLoginInfo *m;
360
361 /* for some inane reason, conferences we create will send a join
362 message for ourselves before the welcome message. Since the
363 welcome message will list our ID among those in the channel,
364 we're going to just pretend that these join messages don't
365 exist */
366 if(conf->state == mwConference_PENDING)
367 return;
368
369 m = g_new0(struct mwLoginInfo, 1);
370
371 guint16_get(b, &m_id);
372 mwLoginInfo_get(b, m);
373
374 if(mwGetBuffer_error(b)) {
375 g_warning("failed parsing JOIN message in conference");
376 login_free(m);
377 return;
378 }
379
380 MEMBER_ADD(conf, m_id, m);
381
382 h = srvc->handler;
383 if(h->on_peer_joined)
384 h->on_peer_joined(conf, m);
385 }
386
387
PART_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwGetBuffer * b)388 static void PART_recv(struct mwServiceConference *srvc,
389 struct mwConference *conf,
390 struct mwGetBuffer *b) {
391
392 /* - parse who left
393 - look up their membership
394 - remove them from the members list
395 - trigger the event
396 */
397
398 struct mwConferenceHandler *h;
399 guint16 id = 0;
400 struct mwLoginInfo *m;
401
402 guint16_get(b, &id);
403
404 if(mwGetBuffer_error(b)) return;
405
406 m = MEMBER_FIND(conf, id);
407 if(! m) return;
408
409 h = srvc->handler;
410 if(h->on_peer_parted)
411 h->on_peer_parted(conf, m);
412
413 MEMBER_REM(conf, id);
414 }
415
416
text_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwLoginInfo * m,struct mwGetBuffer * b)417 static void text_recv(struct mwServiceConference *srvc,
418 struct mwConference *conf,
419 struct mwLoginInfo *m,
420 struct mwGetBuffer *b) {
421
422 /* this function acts a lot like receiving an IM Text message. The text
423 message contains only a string */
424
425 char *text = NULL;
426 struct mwConferenceHandler *h;
427
428 mwString_get(b, &text);
429
430 if(mwGetBuffer_error(b)) {
431 g_warning("failed to parse text message in conference");
432 g_free(text);
433 return;
434 }
435
436 h = srvc->handler;
437 if(text && h->on_text) {
438 h->on_text(conf, m, text);
439 }
440
441 g_free(text);
442 }
443
444
data_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwLoginInfo * m,struct mwGetBuffer * b)445 static void data_recv(struct mwServiceConference *srvc,
446 struct mwConference *conf,
447 struct mwLoginInfo *m,
448 struct mwGetBuffer *b) {
449
450 /* this function acts a lot like receiving an IM Data message. The
451 data message has a type, a subtype, and an opaque. We only
452 support typing notification though. */
453
454 /** @todo it's possible that some clients send text in a data
455 message, as we've seen rarely in the IM service. Have to add
456 support for that here */
457
458 guint32 type, subtype;
459 struct mwConferenceHandler *h;
460
461 guint32_get(b, &type);
462 guint32_get(b, &subtype);
463
464 if(mwGetBuffer_error(b)) return;
465
466 /* don't know how to deal with any others yet */
467 if(type != 0x01) {
468 g_message("unknown data message type (0x%08x, 0x%08x)", type, subtype);
469 return;
470 }
471
472 h = srvc->handler;
473 if(h->on_typing) {
474 h->on_typing(conf, m, !subtype);
475 }
476 }
477
478
MESSAGE_recv(struct mwServiceConference * srvc,struct mwConference * conf,struct mwGetBuffer * b)479 static void MESSAGE_recv(struct mwServiceConference *srvc,
480 struct mwConference *conf,
481 struct mwGetBuffer *b) {
482
483 /* - look up who send the message by their id
484 - trigger the event
485 */
486
487 guint16 id;
488 guint32 type;
489 struct mwLoginInfo *m;
490
491 /* an empty buffer isn't an error, just ignored */
492 if(! mwGetBuffer_remaining(b)) return;
493
494 guint16_get(b, &id);
495 guint32_get(b, &type); /* reuse type variable */
496 guint32_get(b, &type);
497
498 if(mwGetBuffer_error(b)) return;
499
500 m = MEMBER_FIND(conf, id);
501 if(! m) {
502 g_warning("received message type 0x%04x from"
503 " unknown conference member %u", type, id);
504 return;
505 }
506
507 switch(type) {
508 case 0x01: /* type is text */
509 text_recv(srvc, conf, m, b);
510 break;
511
512 case 0x02: /* type is data */
513 data_recv(srvc, conf, m, b);
514 break;
515
516 default:
517 g_warning("unknown message type 0x%4x received in conference", type);
518 }
519 }
520
521
recv(struct mwService * srvc,struct mwChannel * chan,guint16 type,struct mwOpaque * data)522 static void recv(struct mwService *srvc, struct mwChannel *chan,
523 guint16 type, struct mwOpaque *data) {
524
525 struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc;
526 struct mwConference *conf = conf_find(srvc_conf, chan);
527 struct mwGetBuffer *b;
528
529 g_return_if_fail(conf != NULL);
530
531 b = mwGetBuffer_wrap(data);
532
533 switch(type) {
534 case msg_WELCOME:
535 WELCOME_recv(srvc_conf, conf, b);
536 break;
537
538 case msg_JOIN:
539 JOIN_recv(srvc_conf, conf, b);
540 break;
541
542 case msg_PART:
543 PART_recv(srvc_conf, conf, b);
544 break;
545
546 case msg_MESSAGE:
547 MESSAGE_recv(srvc_conf, conf, b);
548 break;
549
550 default:
551 ; /* hrm. should log this. TODO */
552 }
553 }
554
555
clear(struct mwServiceConference * srvc)556 static void clear(struct mwServiceConference *srvc) {
557 struct mwConferenceHandler *h;
558
559 while(srvc->confs)
560 conf_free(srvc->confs->data);
561
562 h = srvc->handler;
563 if(h && h->clear)
564 h->clear(srvc);
565 srvc->handler = NULL;
566 }
567
568
name(struct mwService * srvc)569 static const char *name(struct mwService *srvc) {
570 return "Basic Conferencing";
571 }
572
573
desc(struct mwService * srvc)574 static const char *desc(struct mwService *srvc) {
575 return "Multi-user plain-text conferencing";
576 }
577
578
start(struct mwService * srvc)579 static void start(struct mwService *srvc) {
580 mwService_started(srvc);
581 }
582
583
stop(struct mwServiceConference * srvc)584 static void stop(struct mwServiceConference *srvc) {
585 while(srvc->confs)
586 mwConference_destroy(srvc->confs->data, ERR_SUCCESS, NULL);
587
588 mwService_stopped(MW_SERVICE(srvc));
589 }
590
591
592 struct mwServiceConference *
mwServiceConference_new(struct mwSession * session,struct mwConferenceHandler * handler)593 mwServiceConference_new(struct mwSession *session,
594 struct mwConferenceHandler *handler) {
595
596 struct mwServiceConference *srvc_conf;
597 struct mwService *srvc;
598
599 g_return_val_if_fail(session != NULL, NULL);
600 g_return_val_if_fail(handler != NULL, NULL);
601
602 srvc_conf = g_new0(struct mwServiceConference, 1);
603 srvc = &srvc_conf->service;
604
605 mwService_init(srvc, session, mwService_CONFERENCE);
606 srvc->start = start;
607 srvc->stop = (mwService_funcStop) stop;
608 srvc->recv_create = recv_channelCreate;
609 srvc->recv_accept = recv_channelAccept;
610 srvc->recv_destroy = recv_channelDestroy;
611 srvc->recv = recv;
612 srvc->clear = (mwService_funcClear) clear;
613 srvc->get_name = name;
614 srvc->get_desc = desc;
615
616 srvc_conf->handler = handler;
617
618 return srvc_conf;
619 }
620
621
mwConference_new(struct mwServiceConference * srvc,const char * title)622 struct mwConference *mwConference_new(struct mwServiceConference *srvc,
623 const char *title) {
624 struct mwConference *conf;
625
626 g_return_val_if_fail(srvc != NULL, NULL);
627
628 conf = conf_new(srvc);
629 conf->title = g_strdup(title);
630
631 return conf;
632 }
633
634
635 struct mwServiceConference *
mwConference_getService(struct mwConference * conf)636 mwConference_getService(struct mwConference *conf) {
637 g_return_val_if_fail(conf != NULL, NULL);
638 return conf->service;
639 }
640
641
mwConference_getName(struct mwConference * conf)642 const char *mwConference_getName(struct mwConference *conf) {
643 g_return_val_if_fail(conf != NULL, NULL);
644 return conf->name;
645 }
646
647
mwConference_getTitle(struct mwConference * conf)648 const char *mwConference_getTitle(struct mwConference *conf) {
649 g_return_val_if_fail(conf != NULL, NULL);
650 return conf->title;
651 }
652
653
mwConference_getMembers(struct mwConference * conf)654 GList *mwConference_getMembers(struct mwConference *conf) {
655 g_return_val_if_fail(conf != NULL, NULL);
656 g_return_val_if_fail(conf->members != NULL, NULL);
657
658 return map_collect_values(conf->members);
659 }
660
661
mwConference_open(struct mwConference * conf)662 int mwConference_open(struct mwConference *conf) {
663 struct mwSession *session;
664 struct mwChannel *chan;
665 struct mwPutBuffer *b;
666 int ret;
667
668 g_return_val_if_fail(conf != NULL, -1);
669 g_return_val_if_fail(conf->service != NULL, -1);
670 g_return_val_if_fail(conf->state == mwConference_NEW, -1);
671 g_return_val_if_fail(conf->channel == NULL, -1);
672
673 session = mwService_getSession(MW_SERVICE(conf->service));
674 g_return_val_if_fail(session != NULL, -1);
675
676 if(! conf->name) {
677 char *user = mwSession_getProperty(session, mwSession_AUTH_USER_ID);
678 conf->name = conf_generate_name(user? user: "meanwhile");
679 }
680
681 chan = mwChannel_newOutgoing(mwSession_getChannels(session));
682 mwChannel_setService(chan, MW_SERVICE(conf->service));
683 mwChannel_setProtoType(chan, PROTOCOL_TYPE);
684 mwChannel_setProtoVer(chan, PROTOCOL_VER);
685
686 #if 0
687 /* offer all known ciphers */
688 mwChannel_populateSupportedCipherInstances(chan);
689 #endif
690
691 b = mwPutBuffer_new();
692 mwString_put(b, conf->name);
693 mwString_put(b, conf->title);
694 guint32_put(b, 0x00);
695 mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b);
696
697 ret = mwChannel_create(chan);
698 if(ret) {
699 conf_state(conf, mwConference_ERROR);
700 } else {
701 conf_state(conf, mwConference_PENDING);
702 conf->channel = chan;
703 }
704
705 return ret;
706 }
707
708
mwConference_destroy(struct mwConference * conf,guint32 reason,const char * text)709 int mwConference_destroy(struct mwConference *conf,
710 guint32 reason, const char *text) {
711
712 struct mwServiceConference *srvc;
713 struct mwOpaque info = { 0, 0 };
714 int ret = 0;
715
716 g_return_val_if_fail(conf != NULL, -1);
717
718 srvc = conf->service;
719 g_return_val_if_fail(srvc != NULL, -1);
720
721 /* remove conference from the service */
722 srvc->confs = g_list_remove_all(srvc->confs, conf);
723
724 /* close the channel if applicable */
725 if(conf->channel) {
726 if(text && *text) {
727 info.len = strlen(text);
728 info.data = (guchar *) text;
729 }
730
731 ret = mwChannel_destroy(conf->channel, reason, &info);
732 }
733
734 /* free the conference */
735 conf_free(conf);
736
737 return ret;
738 }
739
740
mwConference_accept(struct mwConference * conf)741 int mwConference_accept(struct mwConference *conf) {
742 /* - if conference is not INVITED, return -1
743 - accept the conference channel
744 - send an empty JOIN message
745 */
746
747 struct mwChannel *chan;
748 int ret;
749
750 g_return_val_if_fail(conf != NULL, -1);
751 g_return_val_if_fail(conf->state == mwConference_INVITED, -1);
752
753 chan = conf->channel;
754 ret = mwChannel_accept(chan);
755
756 if(! ret)
757 ret = mwChannel_sendEncrypted(chan, msg_JOIN, NULL, FALSE);
758
759 return ret;
760 }
761
762
mwConference_invite(struct mwConference * conf,struct mwIdBlock * who,const char * text)763 int mwConference_invite(struct mwConference *conf,
764 struct mwIdBlock *who,
765 const char *text) {
766
767 struct mwPutBuffer *b;
768 struct mwOpaque o;
769 int ret;
770
771 g_return_val_if_fail(conf != NULL, -1);
772 g_return_val_if_fail(conf->channel != NULL, -1);
773 g_return_val_if_fail(who != NULL, -1);
774
775 b = mwPutBuffer_new();
776
777 mwIdBlock_put(b, who);
778 guint16_put(b, 0x00);
779 guint32_put(b, 0x00);
780 mwString_put(b, text);
781 mwString_put(b, who->user);
782
783 mwPutBuffer_finalize(&o, b);
784 ret = mwChannel_sendEncrypted(conf->channel, msg_INVITE, &o, FALSE);
785 mwOpaque_clear(&o);
786
787 return ret;
788 }
789
790
mwConference_sendText(struct mwConference * conf,const char * text)791 int mwConference_sendText(struct mwConference *conf, const char *text) {
792 struct mwPutBuffer *b;
793 struct mwOpaque o;
794 int ret;
795
796 g_return_val_if_fail(conf != NULL, -1);
797 g_return_val_if_fail(conf->channel != NULL, -1);
798
799 b = mwPutBuffer_new();
800
801 guint32_put(b, 0x01);
802 mwString_put(b, text);
803
804 mwPutBuffer_finalize(&o, b);
805 ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE);
806 mwOpaque_clear(&o);
807
808 return ret;
809 }
810
811
mwConference_sendTyping(struct mwConference * conf,gboolean typing)812 int mwConference_sendTyping(struct mwConference *conf, gboolean typing) {
813 struct mwPutBuffer *b;
814 struct mwOpaque o;
815 int ret;
816
817 g_return_val_if_fail(conf != NULL, -1);
818 g_return_val_if_fail(conf->channel != NULL, -1);
819 g_return_val_if_fail(conf->state == mwConference_OPEN, -1);
820
821 b = mwPutBuffer_new();
822
823 guint32_put(b, 0x02);
824 guint32_put(b, 0x01);
825 guint32_put(b, !typing);
826 mwOpaque_put(b, NULL);
827
828 mwPutBuffer_finalize(&o, b);
829 ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE);
830 mwOpaque_clear(&o);
831
832 return ret;
833 }
834
835
mwConference_setClientData(struct mwConference * conference,gpointer data,GDestroyNotify clear)836 void mwConference_setClientData(struct mwConference *conference,
837 gpointer data, GDestroyNotify clear) {
838
839 g_return_if_fail(conference != NULL);
840 mw_datum_set(&conference->client_data, data, clear);
841 }
842
843
mwConference_getClientData(struct mwConference * conference)844 gpointer mwConference_getClientData(struct mwConference *conference) {
845 g_return_val_if_fail(conference != NULL, NULL);
846 return mw_datum_get(&conference->client_data);
847 }
848
849
mwConference_removeClientData(struct mwConference * conference)850 void mwConference_removeClientData(struct mwConference *conference) {
851 g_return_if_fail(conference != NULL);
852 mw_datum_clear(&conference->client_data);
853 }
854
855
856 struct mwConferenceHandler *
mwServiceConference_getHandler(struct mwServiceConference * srvc)857 mwServiceConference_getHandler(struct mwServiceConference *srvc) {
858 g_return_val_if_fail(srvc != NULL, NULL);
859 return srvc->handler;
860 }
861
862
mwServiceConference_getConferences(struct mwServiceConference * srvc)863 GList *mwServiceConference_getConferences(struct mwServiceConference *srvc) {
864 g_return_val_if_fail(srvc != NULL, NULL);
865 return g_list_copy(srvc->confs);
866 }
867
868