10969
|
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
|
|
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 */
|
|
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 */
|
|
107 static char *conf_generate_name(const char *user) {
|
|
108 guint a, b;
|
|
109 char c[16]; /* limited space. Used only to hold sprintf output */
|
|
110 char *ret;
|
|
111
|
|
112 user = user? user: "";
|
|
113
|
|
114 srand(clock());
|
|
115 a = ((rand() & 0xff) << 8) | (rand() & 0xff);
|
|
116 b = time(NULL);
|
|
117 sprintf(c, "(%08x,%04x)", b, a);
|
|
118
|
|
119 ret = g_strconcat(user, c, NULL);
|
|
120
|
|
121 g_debug("generated random conference name: '%s'", ret);
|
|
122 return ret;
|
|
123 }
|
|
124
|
|
125
|
|
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 */
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
260 static void recv_channelAccept(struct mwService *srvc,
|
|
261 struct mwChannel *chan,
|
|
262 struct mwMsgChannelAccept *msg) {
|
|
263
|
|
264 ;
|
|
265 }
|
|
266
|
|
267
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
569 static const char *name(struct mwService *srvc) {
|
|
570 return "Basic Conferencing";
|
|
571 }
|
|
572
|
|
573
|
|
574 static const char *desc(struct mwService *srvc) {
|
|
575 return "Multi-user plain-text conferencing";
|
|
576 }
|
|
577
|
|
578
|
|
579 static void start(struct mwService *srvc) {
|
|
580 mwService_started(srvc);
|
|
581 }
|
|
582
|
|
583
|
|
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 *
|
|
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, SERVICE_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
|
|
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 *
|
|
636 mwConference_getService(struct mwConference *conf) {
|
|
637 g_return_val_if_fail(conf != NULL, NULL);
|
|
638 return conf->service;
|
|
639 }
|
|
640
|
|
641
|
|
642 const char *mwConference_getName(struct mwConference *conf) {
|
|
643 g_return_val_if_fail(conf != NULL, NULL);
|
|
644 return conf->name;
|
|
645 }
|
|
646
|
|
647
|
|
648 const char *mwConference_getTitle(struct mwConference *conf) {
|
|
649 g_return_val_if_fail(conf != NULL, NULL);
|
|
650 return conf->title;
|
|
651 }
|
|
652
|
|
653
|
|
654 GList *mwConference_memebers(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
|
|
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_assert(session != NULL);
|
|
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 /* offer all known ciphers */
|
|
687 mwChannel_populateSupportedCipherInstances(chan);
|
|
688
|
|
689 b = mwPutBuffer_new();
|
|
690 mwString_put(b, conf->name);
|
|
691 mwString_put(b, conf->title);
|
|
692 guint32_put(b, 0x00);
|
|
693 mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b);
|
|
694
|
|
695 ret = mwChannel_create(chan);
|
|
696 if(ret) {
|
|
697 conf_state(conf, mwConference_ERROR);
|
|
698 } else {
|
|
699 conf_state(conf, mwConference_PENDING);
|
|
700 conf->channel = chan;
|
|
701 }
|
|
702
|
|
703 return ret;
|
|
704 }
|
|
705
|
|
706
|
|
707 int mwConference_destroy(struct mwConference *conf,
|
|
708 guint32 reason, const char *text) {
|
|
709
|
|
710 struct mwServiceConference *srvc;
|
|
711 struct mwOpaque info = { 0, 0 };
|
|
712 int ret = 0;
|
|
713
|
|
714 g_return_val_if_fail(conf != NULL, -1);
|
|
715
|
|
716 srvc = conf->service;
|
|
717 g_return_val_if_fail(srvc != NULL, -1);
|
|
718
|
|
719 /* remove conference from the service */
|
|
720 srvc->confs = g_list_remove_all(srvc->confs, conf);
|
|
721
|
|
722 /* close the channel if applicable */
|
|
723 if(conf->channel) {
|
|
724 if(text && *text) {
|
|
725 info.len = strlen(text);
|
|
726 info.data = (char *) text;
|
|
727 }
|
|
728
|
|
729 ret = mwChannel_destroy(conf->channel, reason, &info);
|
|
730 }
|
|
731
|
|
732 /* free the conference */
|
|
733 conf_free(conf);
|
|
734
|
|
735 return ret;
|
|
736 }
|
|
737
|
|
738
|
|
739 int mwConference_accept(struct mwConference *conf) {
|
|
740 /* - if conference is not INVITED, return -1
|
|
741 - accept the conference channel
|
|
742 - send an empty JOIN message
|
|
743 */
|
|
744
|
|
745 struct mwChannel *chan;
|
|
746 int ret;
|
|
747
|
|
748 g_return_val_if_fail(conf != NULL, -1);
|
|
749 g_return_val_if_fail(conf->state == mwConference_INVITED, -1);
|
|
750
|
|
751 chan = conf->channel;
|
|
752 ret = mwChannel_accept(chan);
|
|
753
|
|
754 if(! ret)
|
|
755 ret = mwChannel_sendEncrypted(chan, msg_JOIN, NULL, FALSE);
|
|
756
|
|
757 return ret;
|
|
758 }
|
|
759
|
|
760
|
|
761 int mwConference_invite(struct mwConference *conf,
|
|
762 struct mwIdBlock *who,
|
|
763 const char *text) {
|
|
764
|
|
765 struct mwPutBuffer *b;
|
|
766 struct mwOpaque o;
|
|
767 int ret;
|
|
768
|
|
769 g_return_val_if_fail(conf != NULL, -1);
|
|
770 g_return_val_if_fail(conf->channel != NULL, -1);
|
|
771 g_return_val_if_fail(who != NULL, -1);
|
|
772
|
|
773 b = mwPutBuffer_new();
|
|
774
|
|
775 mwIdBlock_put(b, who);
|
|
776 guint16_put(b, 0x00);
|
|
777 guint32_put(b, 0x00);
|
|
778 mwString_put(b, text);
|
|
779 mwString_put(b, who->user);
|
|
780
|
|
781 mwPutBuffer_finalize(&o, b);
|
|
782 ret = mwChannel_sendEncrypted(conf->channel, msg_INVITE, &o, FALSE);
|
|
783 mwOpaque_clear(&o);
|
|
784
|
|
785 return ret;
|
|
786 }
|
|
787
|
|
788
|
|
789 int mwConference_sendText(struct mwConference *conf, const char *text) {
|
|
790 struct mwPutBuffer *b;
|
|
791 struct mwOpaque o;
|
|
792 int ret;
|
|
793
|
|
794 g_return_val_if_fail(conf != NULL, -1);
|
|
795 g_return_val_if_fail(conf->channel != NULL, -1);
|
|
796
|
|
797 b = mwPutBuffer_new();
|
|
798
|
|
799 guint32_put(b, 0x01);
|
|
800 mwString_put(b, text);
|
|
801
|
|
802 mwPutBuffer_finalize(&o, b);
|
|
803 ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE);
|
|
804 mwOpaque_clear(&o);
|
|
805
|
|
806 return ret;
|
|
807 }
|
|
808
|
|
809
|
|
810 int mwConference_sendTyping(struct mwConference *conf, gboolean typing) {
|
|
811 struct mwPutBuffer *b;
|
|
812 struct mwOpaque o;
|
|
813 int ret;
|
|
814
|
|
815 g_return_val_if_fail(conf != NULL, -1);
|
|
816 g_return_val_if_fail(conf->channel != NULL, -1);
|
|
817 g_return_val_if_fail(conf->state == mwConference_OPEN, -1);
|
|
818
|
|
819 b = mwPutBuffer_new();
|
|
820
|
|
821 guint32_put(b, 0x02);
|
|
822 guint32_put(b, 0x01);
|
|
823 guint32_put(b, !typing);
|
|
824 mwOpaque_put(b, NULL);
|
|
825
|
|
826 mwPutBuffer_finalize(&o, b);
|
|
827 ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE);
|
|
828 mwOpaque_clear(&o);
|
|
829
|
|
830 return ret;
|
|
831 }
|
|
832
|
|
833
|
|
834 void mwConference_setClientData(struct mwConference *conference,
|
|
835 gpointer data, GDestroyNotify clear) {
|
|
836
|
|
837 g_return_if_fail(conference != NULL);
|
|
838 mw_datum_set(&conference->client_data, data, clear);
|
|
839 }
|
|
840
|
|
841
|
|
842 gpointer mwConference_getClientData(struct mwConference *conference) {
|
|
843 g_return_val_if_fail(conference != NULL, NULL);
|
|
844 return mw_datum_get(&conference->client_data);
|
|
845 }
|
|
846
|
|
847
|
|
848 void mwConference_removeClientData(struct mwConference *conference) {
|
|
849 g_return_if_fail(conference != NULL);
|
|
850 mw_datum_clear(&conference->client_data);
|
|
851 }
|
|
852
|
|
853
|
|
854 struct mwConferenceHandler *
|
|
855 mwServiceConference_getHandler(struct mwServiceConference *srvc) {
|
|
856 g_return_val_if_fail(srvc != NULL, NULL);
|
|
857 return srvc->handler;
|
|
858 }
|
|
859
|
|
860
|
|
861 GList *mwServiceConference_getConferences(struct mwServiceConference *srvc) {
|
|
862 g_return_val_if_fail(srvc != NULL, NULL);
|
|
863 return g_list_copy(srvc->confs);
|
|
864 }
|
|
865
|