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