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 <string.h>
24
25 #include "mw_channel.h"
26 #include "mw_debug.h"
27 #include "mw_error.h"
28 #include "mw_message.h"
29 #include "mw_service.h"
30 #include "mw_session.h"
31 #include "mw_srvc_im.h"
32 #include "mw_util.h"
33
34
35 #define PROTOCOL_TYPE 0x00001000
36 #define PROTOCOL_VER 0x00000003
37
38
39 /* data for the addtl blocks of channel creation */
40 #define mwImAddtlA_NORMAL 0x00000001
41
42 #define mwImAddtlB_NORMAL 0x00000001 /**< standard */
43 #define mwImAddtlB_PRECONF 0x00000019 /**< pre-conference chat */
44 #define mwImAddtlB_NOTESBUDDY 0x00033453 /**< notesbuddy */
45
46 #define mwImAddtlC_NORMAL 0x00000002
47
48
49 /* send-on-channel message type */
50 #define msg_MESSAGE 0x0064 /**< IM message */
51
52
53 #define BREAKUP 2048
54
55
56 /* which type of im? */
57 enum mwImType {
58 mwIm_TEXT = 0x00000001, /**< text message */
59 mwIm_DATA = 0x00000002, /**< status message (usually) */
60 };
61
62
63 /* which type of data im? */
64 enum mwImDataType {
65 mwImData_TYPING = 0x00000001, /**< common use typing indicator */
66 mwImData_SUBJECT = 0x00000003, /**< notesbuddy IM topic */
67 mwImData_HTML = 0x00000004, /**< notesbuddy HTML message */
68 mwImData_MIME = 0x00000005, /**< notesbuddy MIME message, w/image */
69 mwImData_TIMESTAMP = 0x00000006, /**< notesbuddy timestamp */
70
71 mwImData_INVITE = 0x0000000a, /**< Places invitation */
72
73 mwImData_MULTI_START = 0x00001388,
74 mwImData_MULTI_STOP = 0x00001389,
75 };
76
77
78 /** @todo might be appropriate to make a couple of hashtables to
79 reference conversations by channel and target */
80 struct mwServiceIm {
81 struct mwService service;
82
83 enum mwImClientType features;
84
85 struct mwImHandler *handler;
86 GList *convs; /**< list of struct im_convo */
87 };
88
89
90 struct mwConversation {
91 struct mwServiceIm *service; /**< owning service */
92 struct mwChannel *channel; /**< channel */
93 struct mwIdBlock target; /**< conversation target */
94
95 gboolean ext_id; /**< special treatment, external ID */
96
97 /** state of the conversation, based loosely on the state of its
98 underlying channel */
99 enum mwConversationState state;
100
101 enum mwImClientType features;
102
103 GString *multi; /**< buffer for multi-chunk message */
104 enum mwImSendType multi_type; /**< type of incoming multi-chunk message */
105
106 struct mw_datum client_data;
107 };
108
109
110 /** momentarily places a mwLoginInfo into a mwIdBlock */
login_into_id(struct mwIdBlock * to,struct mwLoginInfo * from)111 static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) {
112 to->user = from->user_id;
113 to->community = from->community;
114 }
115
116
convo_find_by_user(struct mwServiceIm * srvc,struct mwIdBlock * to)117 static struct mwConversation *convo_find_by_user(struct mwServiceIm *srvc,
118 struct mwIdBlock *to) {
119 GList *l;
120
121 for(l = srvc->convs; l; l = l->next) {
122 struct mwConversation *c = l->data;
123 if(mwIdBlock_equal(&c->target, to))
124 return c;
125 }
126
127 return NULL;
128 }
129
130
conv_state_str(enum mwConversationState state)131 static const char *conv_state_str(enum mwConversationState state) {
132 switch(state) {
133 case mwConversation_CLOSED:
134 return "closed";
135
136 case mwConversation_OPEN:
137 return "open";
138
139 case mwConversation_PENDING:
140 return "pending";
141
142 case mwConversation_UNKNOWN:
143 default:
144 return "UNKNOWN";
145 }
146 }
147
148
convo_set_state(struct mwConversation * conv,enum mwConversationState state)149 static void convo_set_state(struct mwConversation *conv,
150 enum mwConversationState state) {
151
152 g_return_if_fail(conv != NULL);
153
154 if(conv->state != state) {
155 g_info("setting conversation (%s, %s) state: %s",
156 NSTR(conv->target.user), NSTR(conv->target.community),
157 conv_state_str(state));
158 conv->state = state;
159 }
160 }
161
162
mwServiceIm_findConversation(struct mwServiceIm * srvc,struct mwIdBlock * to)163 struct mwConversation *mwServiceIm_findConversation(struct mwServiceIm *srvc,
164 struct mwIdBlock *to) {
165 g_return_val_if_fail(srvc != NULL, NULL);
166 g_return_val_if_fail(to != NULL, NULL);
167
168 return convo_find_by_user(srvc, to);
169 }
170
171
mwServiceIm_getConversation(struct mwServiceIm * srvc,struct mwIdBlock * to)172 struct mwConversation *mwServiceIm_getConversation(struct mwServiceIm *srvc,
173 struct mwIdBlock *to) {
174 struct mwConversation *c;
175
176 g_return_val_if_fail(srvc != NULL, NULL);
177 g_return_val_if_fail(to != NULL, NULL);
178
179 c = convo_find_by_user(srvc, to);
180 if(! c) {
181 c = g_new0(struct mwConversation, 1);
182 c->service = srvc;
183 mwIdBlock_clone(&c->target, to);
184 c->state = mwConversation_CLOSED;
185 c->features = srvc->features;
186
187 /* mark external users */
188 /* c->ext_id = g_str_has_prefix(to->user, "@E "); */
189
190 srvc->convs = g_list_prepend(srvc->convs, c);
191 }
192
193 return c;
194 }
195
196
convo_create_chan(struct mwConversation * c)197 static void convo_create_chan(struct mwConversation *c) {
198 struct mwSession *s;
199 struct mwChannelSet *cs;
200 struct mwChannel *chan;
201 struct mwLoginInfo *login;
202 struct mwPutBuffer *b;
203
204 /* we only should be calling this if there isn't a channel already
205 associated with the conversation */
206 g_return_if_fail(c != NULL);
207 g_return_if_fail(mwConversation_isPending(c));
208 g_return_if_fail(c->channel == NULL);
209
210 s = mwService_getSession(MW_SERVICE(c->service));
211 cs = mwSession_getChannels(s);
212
213 chan = mwChannel_newOutgoing(cs);
214 mwChannel_setService(chan, MW_SERVICE(c->service));
215 mwChannel_setProtoType(chan, PROTOCOL_TYPE);
216 mwChannel_setProtoVer(chan, PROTOCOL_VER);
217
218 /* offer all known ciphers */
219 mwChannel_populateSupportedCipherInstances(chan);
220
221 /* set the target */
222 login = mwChannel_getUser(chan);
223 login->user_id = g_strdup(c->target.user);
224 login->community = g_strdup(c->target.community);
225
226 /* compose the addtl create, with optional FANCY HTML! */
227 b = mwPutBuffer_new();
228 guint32_put(b, mwImAddtlA_NORMAL);
229 guint32_put(b, c->features);
230 mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b);
231
232 c->channel = mwChannel_create(chan)? NULL: chan;
233 if(c->channel) {
234 mwChannel_setServiceData(c->channel, c, NULL);
235 }
236 }
237
238
mwConversation_open(struct mwConversation * conv)239 void mwConversation_open(struct mwConversation *conv) {
240 g_return_if_fail(conv != NULL);
241 g_return_if_fail(mwConversation_isClosed(conv));
242
243 convo_set_state(conv, mwConversation_PENDING);
244 convo_create_chan(conv);
245 }
246
247
convo_opened(struct mwConversation * conv)248 static void convo_opened(struct mwConversation *conv) {
249 struct mwImHandler *h = NULL;
250
251 g_return_if_fail(conv != NULL);
252 g_return_if_fail(conv->service != NULL);
253
254 convo_set_state(conv, mwConversation_OPEN);
255 h = conv->service->handler;
256
257 g_return_if_fail(h != NULL);
258
259 if(h->conversation_opened)
260 h->conversation_opened(conv);
261 }
262
263
convo_free(struct mwConversation * conv)264 static void convo_free(struct mwConversation *conv) {
265 struct mwServiceIm *srvc;
266
267 mwConversation_removeClientData(conv);
268
269 srvc = conv->service;
270 srvc->convs = g_list_remove_all(srvc->convs, conv);
271
272 mwIdBlock_clear(&conv->target);
273 g_free(conv);
274 }
275
276
send_accept(struct mwConversation * c)277 static int send_accept(struct mwConversation *c) {
278
279 struct mwChannel *chan = c->channel;
280 struct mwSession *s = mwChannel_getSession(chan);
281 struct mwUserStatus *stat = mwSession_getUserStatus(s);
282
283 struct mwPutBuffer *b;
284 struct mwOpaque *o;
285
286 b = mwPutBuffer_new();
287 guint32_put(b, mwImAddtlA_NORMAL);
288 guint32_put(b, c->features);
289 guint32_put(b, mwImAddtlC_NORMAL);
290 mwUserStatus_put(b, stat);
291
292 o = mwChannel_getAddtlAccept(chan);
293 mwOpaque_clear(o);
294 mwPutBuffer_finalize(o, b);
295
296 return mwChannel_accept(chan);
297 }
298
299
recv_channelCreate(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelCreate * msg)300 static void recv_channelCreate(struct mwService *srvc,
301 struct mwChannel *chan,
302 struct mwMsgChannelCreate *msg) {
303
304 /* - ensure it's the right service,proto,and proto ver
305 - check the opaque for the right opaque junk
306 - if not, close channel
307 - compose & send a channel accept
308 */
309
310 struct mwServiceIm *srvc_im = (struct mwServiceIm *) srvc;
311 struct mwImHandler *handler;
312 struct mwSession *s;
313 struct mwUserStatus *stat;
314 guint32 x, y, z;
315 struct mwGetBuffer *b;
316 struct mwConversation *c = NULL;
317 struct mwIdBlock idb;
318
319 handler = srvc_im->handler;
320 s = mwChannel_getSession(chan);
321 stat = mwSession_getUserStatus(s);
322
323 /* ensure the appropriate service/proto/ver */
324 x = mwChannel_getServiceId(chan);
325 y = mwChannel_getProtoType(chan);
326 z = mwChannel_getProtoVer(chan);
327
328 if( (x != mwService_IM) || (y != PROTOCOL_TYPE) || (z != PROTOCOL_VER) ) {
329 g_warning("unacceptable service, proto, ver:"
330 " 0x%08x, 0x%08x, 0x%08x", x, y, z);
331 mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
332 return;
333 }
334
335 /* act upon the values in the addtl block */
336 b = mwGetBuffer_wrap(&msg->addtl);
337 guint32_get(b, &x);
338 guint32_get(b, &y);
339 z = (guint) mwGetBuffer_error(b);
340 mwGetBuffer_free(b);
341
342 if(z /* buffer error, BOOM! */ ) {
343 g_warning("bad/malformed addtl in IM service");
344 mwChannel_destroy(chan, ERR_FAILURE, NULL);
345 return;
346
347 } else if(x != mwImAddtlA_NORMAL) {
348 g_message("unknown params: 0x%08x, 0x%08x", x, y);
349 mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL);
350 return;
351
352 } else if(y == mwImAddtlB_PRECONF) {
353 if(! handler->place_invite) {
354 g_info("rejecting place-invite channel");
355 mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL);
356 return;
357
358 } else {
359 g_info("accepting place-invite channel");
360 }
361
362 } else if(y != mwImClient_PLAIN && y != srvc_im->features) {
363 /** reject what we do not understand */
364 mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL);
365 return;
366
367 } else if(stat->status == mwStatus_BUSY) {
368 g_info("rejecting IM channel due to DND status");
369 mwChannel_destroy(chan, ERR_CLIENT_USER_DND, NULL);
370 return;
371 }
372
373 login_into_id(&idb, mwChannel_getUser(chan));
374
375 #if 0
376 c = convo_find_by_user(srvc_im, &idb);
377 #endif
378
379 if(! c) {
380 c = g_new0(struct mwConversation, 1);
381 c->service = srvc_im;
382 srvc_im->convs = g_list_prepend(srvc_im->convs, c);
383 }
384
385 #if 0
386 /* we're going to re-associate any existing conversations with this
387 new channel. That means closing any existing ones */
388 if(c->channel) {
389 g_info("closing existing IM channel 0x%08x", mwChannel_getId(c->channel));
390 mwConversation_close(c, ERR_SUCCESS);
391 }
392 #endif
393
394 /* set up the conversation with this channel, target, and be fancy
395 if the other side requested it */
396 c->channel = chan;
397 mwIdBlock_clone(&c->target, &idb);
398 c->features = y;
399 convo_set_state(c, mwConversation_PENDING);
400 mwChannel_setServiceData(c->channel, c, NULL);
401
402 if(send_accept(c)) {
403 g_warning("sending IM channel accept failed");
404 mwConversation_free(c);
405
406 } else {
407 convo_opened(c);
408 }
409 }
410
411
recv_channelAccept(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelAccept * msg)412 static void recv_channelAccept(struct mwService *srvc, struct mwChannel *chan,
413 struct mwMsgChannelAccept *msg) {
414
415 struct mwConversation *conv;
416
417 conv = mwChannel_getServiceData(chan);
418 if(! conv) {
419 g_warning("received channel accept for non-existant conversation");
420 mwChannel_destroy(chan, ERR_FAILURE, NULL);
421 return;
422 }
423
424 convo_opened(conv);
425 }
426
427
recv_channelDestroy(struct mwService * srvc,struct mwChannel * chan,struct mwMsgChannelDestroy * msg)428 static void recv_channelDestroy(struct mwService *srvc, struct mwChannel *chan,
429 struct mwMsgChannelDestroy *msg) {
430
431 struct mwConversation *c;
432
433 c = mwChannel_getServiceData(chan);
434 g_return_if_fail(c != NULL);
435
436 c->channel = NULL;
437
438 if(mwChannel_isState(chan, mwChannel_ERROR)) {
439
440 /* checking for failure on the receiving end to accept html
441 messages. Fail-over to a non-html format on a new channel for
442 the convo */
443 if(c->features != mwImClient_PLAIN
444 && (msg->reason == ERR_IM_NOT_REGISTERED ||
445 msg->reason == ERR_SERVICE_NO_SUPPORT)) {
446
447 g_debug("falling back on a plaintext conversation");
448 c->features = mwImClient_PLAIN;
449 convo_create_chan(c);
450 return;
451 }
452 }
453
454 mwConversation_close(c, msg->reason);
455 }
456
457
convo_recv(struct mwConversation * conv,enum mwImSendType type,gconstpointer msg)458 static void convo_recv(struct mwConversation *conv, enum mwImSendType type,
459 gconstpointer msg) {
460
461 struct mwServiceIm *srvc;
462 struct mwImHandler *handler;
463
464 g_return_if_fail(conv != NULL);
465
466 srvc = conv->service;
467 g_return_if_fail(srvc != NULL);
468
469 handler = srvc->handler;
470 if(handler && handler->conversation_recv)
471 handler->conversation_recv(conv, type, msg);
472 }
473
474
convo_multi_start(struct mwConversation * conv)475 static void convo_multi_start(struct mwConversation *conv) {
476 g_return_if_fail(conv->multi == NULL);
477 conv->multi = g_string_new(NULL);
478 }
479
480
convo_multi_stop(struct mwConversation * conv)481 static void convo_multi_stop(struct mwConversation *conv) {
482
483 g_return_if_fail(conv->multi != NULL);
484
485 /* actually send it */
486 convo_recv(conv, conv->multi_type, conv->multi->str);
487
488 /* clear up the multi buffer */
489 g_string_free(conv->multi, TRUE);
490 conv->multi = NULL;
491 }
492
493
recv_text(struct mwServiceIm * srvc,struct mwChannel * chan,struct mwGetBuffer * b)494 static void recv_text(struct mwServiceIm *srvc, struct mwChannel *chan,
495 struct mwGetBuffer *b) {
496
497 struct mwConversation *c;
498 char *text = NULL;
499
500 mwString_get(b, &text);
501
502 if(! text) return;
503
504 c = mwChannel_getServiceData(chan);
505 if(c) {
506 if(c->multi) {
507 g_string_append(c->multi, text);
508
509 } else {
510 convo_recv(c, mwImSend_PLAIN, text);
511 }
512 }
513
514 g_free(text);
515 }
516
517
convo_invite(struct mwConversation * conv,struct mwOpaque * o)518 static void convo_invite(struct mwConversation *conv,
519 struct mwOpaque *o) {
520
521 struct mwServiceIm *srvc;
522 struct mwImHandler *handler;
523
524 struct mwGetBuffer *b;
525 char *title, *name, *msg;
526 char *unkn, *host;
527 guint16 with_who;
528
529 g_info("convo_invite");
530
531 srvc = conv->service;
532 handler = srvc->handler;
533
534 g_return_if_fail(handler != NULL);
535 g_return_if_fail(handler->place_invite != NULL);
536
537 b = mwGetBuffer_wrap(o);
538 mwGetBuffer_advance(b, 4);
539 mwString_get(b, &title);
540 mwString_get(b, &msg);
541 mwGetBuffer_advance(b, 19);
542 mwString_get(b, &name);
543
544 /* todo: add a mwString_skip */
545 mwString_get(b, &unkn);
546 mwString_get(b, &host);
547 g_free(unkn);
548 g_free(host);
549
550 /* hack. Sometimes incoming convo invitation channels won't have the
551 owner id block filled in */
552 guint16_get(b, &with_who);
553 if(with_who && !conv->target.user) {
554 char *login;
555 mwString_get(b, &conv->target.user);
556 mwString_get(b, &login); g_free(login);
557 mwString_get(b, &conv->target.community);
558 }
559
560 if(mwGetBuffer_error(b)) {
561 mw_mailme_opaque(o, "problem with place invite over IM service");
562 } else {
563 handler->place_invite(conv, msg, title, name);
564 }
565
566 mwGetBuffer_free(b);
567 g_free(msg);
568 g_free(title);
569 g_free(name);
570 }
571
572
recv_data(struct mwServiceIm * srvc,struct mwChannel * chan,struct mwGetBuffer * b)573 static void recv_data(struct mwServiceIm *srvc, struct mwChannel *chan,
574 struct mwGetBuffer *b) {
575
576 struct mwConversation *conv;
577 guint32 type, subtype;
578 struct mwOpaque o = { 0, 0 };
579 char *x;
580
581 guint32_get(b, &type);
582 guint32_get(b, &subtype);
583 mwOpaque_get(b, &o);
584
585 if(mwGetBuffer_error(b)) {
586 mwOpaque_clear(&o);
587 return;
588 }
589
590 conv = mwChannel_getServiceData(chan);
591 if(! conv) return;
592
593 switch(type) {
594 case mwImData_TYPING:
595 convo_recv(conv, mwImSend_TYPING, GINT_TO_POINTER(! subtype));
596 break;
597
598 case mwImData_HTML:
599 if(o.len) {
600 if(conv->multi) {
601 g_string_append_len(conv->multi, (char *) o.data, o.len);
602 conv->multi_type = mwImSend_HTML;
603
604 } else {
605 x = g_strndup((char *) o.data, o.len);
606 convo_recv(conv, mwImSend_HTML, x);
607 g_free(x);
608 }
609 }
610 break;
611
612 case mwImData_SUBJECT:
613 x = g_strndup((char *) o.data, o.len);
614 convo_recv(conv, mwImSend_SUBJECT, x);
615 g_free(x);
616 break;
617
618 case mwImData_MIME:
619 if(conv->multi) {
620 g_string_append_len(conv->multi, (char *) o.data, o.len);
621 conv->multi_type = mwImSend_MIME;
622
623 } else {
624 x = g_strndup((char *) o.data, o.len);
625 convo_recv(conv, mwImSend_MIME, x);
626 g_free(x);
627 }
628 break;
629
630 case mwImData_TIMESTAMP:
631 /* todo */
632 break;
633
634 case mwImData_INVITE:
635 convo_invite(conv, &o);
636 break;
637
638 case mwImData_MULTI_START:
639 convo_multi_start(conv);
640 break;
641
642 case mwImData_MULTI_STOP:
643 convo_multi_stop(conv);
644 break;
645
646 default:
647
648 mw_mailme_opaque(&o, "unknown data message type in IM service:"
649 " (0x%08x, 0x%08x)", type, subtype);
650 }
651
652 mwOpaque_clear(&o);
653 }
654
655
recv(struct mwService * srvc,struct mwChannel * chan,guint16 type,struct mwOpaque * data)656 static void recv(struct mwService *srvc, struct mwChannel *chan,
657 guint16 type, struct mwOpaque *data) {
658
659 /* - ensure message type is something we want
660 - parse message type into either mwIMText or mwIMData
661 - handle
662 */
663
664 struct mwGetBuffer *b;
665 guint32 mt;
666
667 g_return_if_fail(type == msg_MESSAGE);
668
669 b = mwGetBuffer_wrap(data);
670 guint32_get(b, &mt);
671
672 if(mwGetBuffer_error(b)) {
673 g_warning("failed to parse message for IM service");
674 mwGetBuffer_free(b);
675 return;
676 }
677
678 switch(mt) {
679 case mwIm_TEXT:
680 recv_text((struct mwServiceIm *) srvc, chan, b);
681 break;
682
683 case mwIm_DATA:
684 recv_data((struct mwServiceIm *) srvc, chan, b);
685 break;
686
687 default:
688 g_warning("unknown message type 0x%08x for IM service", mt);
689 }
690
691 if(mwGetBuffer_error(b))
692 g_warning("failed to parse message type 0x%08x for IM service", mt);
693
694 mwGetBuffer_free(b);
695 }
696
697
clear(struct mwServiceIm * srvc)698 static void clear(struct mwServiceIm *srvc) {
699 struct mwImHandler *h;
700
701 while(srvc->convs)
702 convo_free(srvc->convs->data);
703
704 h = srvc->handler;
705 if(h && h->clear)
706 h->clear(srvc);
707 srvc->handler = NULL;
708 }
709
710
name(struct mwService * srvc)711 static const char *name(struct mwService *srvc) {
712 return "Instant Messaging";
713 }
714
715
desc(struct mwService * srvc)716 static const char *desc(struct mwService *srvc) {
717 return "IM service with Standard and NotesBuddy features";
718 }
719
720
start(struct mwService * srvc)721 static void start(struct mwService *srvc) {
722 mwService_started(srvc);
723 }
724
725
stop(struct mwServiceIm * srvc)726 static void stop(struct mwServiceIm *srvc) {
727
728 while(srvc->convs)
729 mwConversation_free(srvc->convs->data);
730
731 mwService_stopped(MW_SERVICE(srvc));
732 }
733
734
mwServiceIm_new(struct mwSession * session,struct mwImHandler * hndl)735 struct mwServiceIm *mwServiceIm_new(struct mwSession *session,
736 struct mwImHandler *hndl) {
737
738 struct mwServiceIm *srvc_im;
739 struct mwService *srvc;
740
741 g_return_val_if_fail(session != NULL, NULL);
742 g_return_val_if_fail(hndl != NULL, NULL);
743
744 srvc_im = g_new0(struct mwServiceIm, 1);
745 srvc = MW_SERVICE(srvc_im);
746
747 mwService_init(srvc, session, mwService_IM);
748 srvc->recv_create = recv_channelCreate;
749 srvc->recv_accept = recv_channelAccept;
750 srvc->recv_destroy = recv_channelDestroy;
751 srvc->recv = recv;
752 srvc->clear = (mwService_funcClear) clear;
753 srvc->get_name = name;
754 srvc->get_desc = desc;
755 srvc->start = start;
756 srvc->stop = (mwService_funcStop) stop;
757
758 srvc_im->features = mwImClient_PLAIN;
759 srvc_im->handler = hndl;
760
761 return srvc_im;
762 }
763
764
mwServiceIm_getHandler(struct mwServiceIm * srvc)765 struct mwImHandler *mwServiceIm_getHandler(struct mwServiceIm *srvc) {
766 g_return_val_if_fail(srvc != NULL, NULL);
767 return srvc->handler;
768 }
769
770
mwServiceIm_supports(struct mwServiceIm * srvc,enum mwImSendType type)771 gboolean mwServiceIm_supports(struct mwServiceIm *srvc,
772 enum mwImSendType type) {
773
774 g_return_val_if_fail(srvc != NULL, FALSE);
775
776 switch(type) {
777 case mwImSend_PLAIN:
778 case mwImSend_TYPING:
779 return TRUE;
780
781 case mwImSend_SUBJECT:
782 case mwImSend_HTML:
783 case mwImSend_MIME:
784 case mwImSend_TIMESTAMP:
785 return srvc->features == mwImClient_NOTESBUDDY;
786
787 default:
788 return FALSE;
789 }
790 }
791
792
mwServiceIm_setClientType(struct mwServiceIm * srvc,enum mwImClientType type)793 void mwServiceIm_setClientType(struct mwServiceIm *srvc,
794 enum mwImClientType type) {
795
796 g_return_if_fail(srvc != NULL);
797 srvc->features = type;
798 }
799
800
mwServiceIm_getClientType(struct mwServiceIm * srvc)801 enum mwImClientType mwServiceIm_getClientType(struct mwServiceIm *srvc) {
802 g_return_val_if_fail(srvc != NULL, mwImClient_UNKNOWN);
803 return srvc->features;
804 }
805
806
convo_send_data(struct mwConversation * conv,guint32 type,guint32 subtype,struct mwOpaque * data)807 static int convo_send_data(struct mwConversation *conv,
808 guint32 type, guint32 subtype,
809 struct mwOpaque *data) {
810 struct mwPutBuffer *b;
811 struct mwOpaque o;
812 struct mwChannel *chan;
813 int ret;
814
815 chan = conv->channel;
816 g_return_val_if_fail(chan != NULL, -1);
817
818 b = mwPutBuffer_new();
819
820 guint32_put(b, mwIm_DATA);
821 guint32_put(b, type);
822 guint32_put(b, subtype);
823 mwOpaque_put(b, data);
824
825 mwPutBuffer_finalize(&o, b);
826
827 ret = mwChannel_sendEncrypted(chan, msg_MESSAGE, &o, !conv->ext_id);
828 mwOpaque_clear(&o);
829
830 return ret;
831 }
832
833
convo_send_multi_start(struct mwConversation * conv)834 static int convo_send_multi_start(struct mwConversation *conv) {
835 return convo_send_data(conv, mwImData_MULTI_START, 0x00, NULL);
836 }
837
838
convo_send_multi_stop(struct mwConversation * conv)839 static int convo_send_multi_stop(struct mwConversation *conv) {
840 return convo_send_data(conv, mwImData_MULTI_STOP, 0x00, NULL);
841 }
842
843
844 /* breaks up a large message into segments, sends a start_segment
845 message, then sends each segment in turn, then sends a stop_segment
846 message */
847 static int
convo_sendSegmented(struct mwConversation * conv,const char * message,int (* send)(struct mwConversation * conv,const char * msg))848 convo_sendSegmented(struct mwConversation *conv, const char *message,
849 int (*send)(struct mwConversation *conv,
850 const char *msg)) {
851 char *buf = (char *) message;
852 gsize len;
853 int ret = 0;
854
855 len = strlen(buf);
856 ret = convo_send_multi_start(conv);
857
858 while(len && !ret) {
859 char tail;
860 gsize seg;
861
862 seg = BREAKUP;
863 if(len < BREAKUP)
864 seg = len;
865
866 /* temporarily NUL-terminate this segment */
867 tail = buf[seg];
868 buf[seg] = 0x00;
869
870 ret = send(conv, buf);
871
872 /* restore this segment */
873 buf[seg] = tail;
874
875 buf += seg;
876 len -= seg;
877 }
878
879 if(! ret)
880 ret = convo_send_multi_stop(conv);
881
882 return ret;
883 }
884
885
convo_sendText(struct mwConversation * conv,const char * text)886 static int convo_sendText(struct mwConversation *conv, const char *text) {
887 struct mwPutBuffer *b;
888 struct mwOpaque o;
889 int ret;
890
891 b = mwPutBuffer_new();
892
893 guint32_put(b, mwIm_TEXT);
894 mwString_put(b, text);
895
896 mwPutBuffer_finalize(&o, b);
897 ret = mwChannel_sendEncrypted(conv->channel, msg_MESSAGE, &o, !conv->ext_id);
898 mwOpaque_clear(&o);
899
900 return ret;
901 }
902
903
convo_sendTyping(struct mwConversation * conv,gboolean typing)904 static int convo_sendTyping(struct mwConversation *conv, gboolean typing) {
905 return convo_send_data(conv, mwImData_TYPING, !typing, NULL);
906 }
907
908
convo_sendSubject(struct mwConversation * conv,const char * subject)909 static int convo_sendSubject(struct mwConversation *conv,
910 const char *subject) {
911 struct mwOpaque o;
912
913 o.len = strlen(subject);
914 o.data = (guchar *) subject;
915
916 return convo_send_data(conv, mwImData_SUBJECT, 0x00, &o);
917 }
918
919
convo_sendHtml(struct mwConversation * conv,const char * html)920 static int convo_sendHtml(struct mwConversation *conv, const char *html) {
921 struct mwOpaque o;
922
923 o.len = strlen(html);
924 o.data = (guchar *) html;
925
926 if(o.len > BREAKUP) {
927 return convo_sendSegmented(conv, html, convo_sendHtml);
928 } else {
929 return convo_send_data(conv, mwImData_HTML, 0x00, &o);
930 }
931 }
932
933
convo_sendMime(struct mwConversation * conv,const char * mime)934 static int convo_sendMime(struct mwConversation *conv, const char *mime) {
935 struct mwOpaque o;
936
937 o.len = strlen(mime);
938 o.data = (guchar *) mime;
939
940 if(o.len > BREAKUP) {
941 return convo_sendSegmented(conv, mime, convo_sendMime);
942 } else {
943 return convo_send_data(conv, mwImData_MIME, 0x00, &o);
944 }
945 }
946
947
mwConversation_send(struct mwConversation * conv,enum mwImSendType type,gconstpointer msg)948 int mwConversation_send(struct mwConversation *conv, enum mwImSendType type,
949 gconstpointer msg) {
950
951 g_return_val_if_fail(conv != NULL, -1);
952 g_return_val_if_fail(mwConversation_isOpen(conv), -1);
953 g_return_val_if_fail(conv->channel != NULL, -1);
954
955 switch(type) {
956 case mwImSend_PLAIN:
957 return convo_sendText(conv, msg);
958 case mwImSend_TYPING:
959 return convo_sendTyping(conv, GPOINTER_TO_INT(msg));
960 case mwImSend_SUBJECT:
961 return convo_sendSubject(conv, msg);
962 case mwImSend_HTML:
963 return convo_sendHtml(conv, msg);
964 case mwImSend_MIME:
965 return convo_sendMime(conv, msg);
966
967 default:
968 g_warning("unsupported IM Send Type, 0x%x", type);
969 return -1;
970 }
971 }
972
973
mwConversation_getState(struct mwConversation * conv)974 enum mwConversationState mwConversation_getState(struct mwConversation *conv) {
975 g_return_val_if_fail(conv != NULL, mwConversation_UNKNOWN);
976 return conv->state;
977 }
978
979
mwConversation_getService(struct mwConversation * conv)980 struct mwServiceIm *mwConversation_getService(struct mwConversation *conv) {
981 g_return_val_if_fail(conv != NULL, NULL);
982 return conv->service;
983 }
984
985
mwConversation_supports(struct mwConversation * conv,enum mwImSendType type)986 gboolean mwConversation_supports(struct mwConversation *conv,
987 enum mwImSendType type) {
988 g_return_val_if_fail(conv != NULL, FALSE);
989
990 switch(type) {
991 case mwImSend_PLAIN:
992 case mwImSend_TYPING:
993 return TRUE;
994
995 case mwImSend_SUBJECT:
996 case mwImSend_HTML:
997 case mwImSend_MIME:
998 return conv->features == mwImClient_NOTESBUDDY;
999
1000 default:
1001 return FALSE;
1002 }
1003 }
1004
1005
1006 enum mwImClientType
mwConversation_getClientType(struct mwConversation * conv)1007 mwConversation_getClientType(struct mwConversation *conv) {
1008 g_return_val_if_fail(conv != NULL, mwImClient_UNKNOWN);
1009 return conv->features;
1010 }
1011
1012
mwConversation_getTarget(struct mwConversation * conv)1013 struct mwIdBlock *mwConversation_getTarget(struct mwConversation *conv) {
1014 g_return_val_if_fail(conv != NULL, NULL);
1015 return &conv->target;
1016 }
1017
1018
mwConversation_getTargetInfo(struct mwConversation * conv)1019 struct mwLoginInfo *mwConversation_getTargetInfo(struct mwConversation *conv) {
1020 g_return_val_if_fail(conv != NULL, NULL);
1021 g_return_val_if_fail(conv->channel != NULL, NULL);
1022 return mwChannel_getUser(conv->channel);
1023 }
1024
1025
mwConversation_setClientData(struct mwConversation * conv,gpointer data,GDestroyNotify clean)1026 void mwConversation_setClientData(struct mwConversation *conv,
1027 gpointer data, GDestroyNotify clean) {
1028 g_return_if_fail(conv != NULL);
1029 mw_datum_set(&conv->client_data, data, clean);
1030 }
1031
1032
mwConversation_getClientData(struct mwConversation * conv)1033 gpointer mwConversation_getClientData(struct mwConversation *conv) {
1034 g_return_val_if_fail(conv != NULL, NULL);
1035 return mw_datum_get(&conv->client_data);
1036 }
1037
1038
mwConversation_removeClientData(struct mwConversation * conv)1039 void mwConversation_removeClientData(struct mwConversation *conv) {
1040 g_return_if_fail(conv != NULL);
1041 mw_datum_clear(&conv->client_data);
1042 }
1043
1044
mwConversation_close(struct mwConversation * conv,guint32 reason)1045 void mwConversation_close(struct mwConversation *conv, guint32 reason) {
1046 struct mwServiceIm *srvc;
1047 struct mwImHandler *h;
1048
1049 g_return_if_fail(conv != NULL);
1050
1051 convo_set_state(conv, mwConversation_CLOSED);
1052
1053 srvc = conv->service;
1054 g_return_if_fail(srvc != NULL);
1055
1056 h = srvc->handler;
1057 if(h && h->conversation_closed)
1058 h->conversation_closed(conv, reason);
1059
1060 if(conv->channel) {
1061 mwChannel_destroy(conv->channel, reason, NULL);
1062 conv->channel = NULL;
1063 }
1064 }
1065
1066
mwConversation_free(struct mwConversation * conv)1067 void mwConversation_free(struct mwConversation *conv) {
1068 g_return_if_fail(conv != NULL);
1069
1070 if(! mwConversation_isClosed(conv))
1071 mwConversation_close(conv, ERR_SUCCESS);
1072
1073 convo_free(conv);
1074 }
1075
1076