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