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 <string.h>
23 
24 #include "mw_channel.h"
25 #include "mw_cipher.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_util.h"
32 
33 
34 /** the hash table key for a service, for mwSession::services */
35 #define SERVICE_KEY(srvc) mwService_getType(srvc)
36 
37 /** the hash table key for a cipher, for mwSession::ciphers */
38 #define CIPHER_KEY(ciph)  mwCipher_getType(ciph)
39 
40 
41 #define GPOINTER(val)  (GUINT_TO_POINTER((guint) (val)))
42 #define GUINT(val)     (GPOINTER_TO_UINT((val)))
43 
44 
45 struct mwSession {
46 
47   /** provides I/O and callback functions */
48   struct mwSessionHandler *handler;
49 
50   enum mwSessionState state;  /**< session state */
51   gpointer state_info;        /**< additional state info */
52 
53   /* input buffering for an incoming message */
54   guchar *buf;  /**< buffer for incoming message data */
55   gsize buf_len;       /**< length of buf */
56   gsize buf_used;      /**< offset to last-used byte of buf */
57 
58   struct mwLoginInfo login;      /**< login information */
59   struct mwUserStatus status;    /**< user status */
60   struct mwPrivacyInfo privacy;  /**< privacy list */
61 
62   /** the collection of channels */
63   struct mwChannelSet *channels;
64 
65   /** the collection of services, keyed to guint32 service id */
66   GHashTable *services;
67 
68   /** the collection of ciphers, keyed to guint16 cipher type */
69   GHashTable *ciphers;
70 
71   /** arbitrary key:value pairs */
72   GHashTable *attributes;
73 
74   /** optional user data */
75   struct mw_datum client_data;
76 };
77 
78 
property_set(struct mwSession * s,const char * key,gpointer val,GDestroyNotify clean)79 static void property_set(struct mwSession *s, const char *key,
80 			 gpointer val, GDestroyNotify clean) {
81 
82   g_hash_table_insert(s->attributes, g_strdup(key),
83 		      mw_datum_new(val, clean));
84 }
85 
86 
property_get(struct mwSession * s,const char * key)87 static gpointer property_get(struct mwSession *s, const char *key) {
88   struct mw_datum *p = g_hash_table_lookup(s->attributes, key);
89   return p? p->data: NULL;
90 }
91 
92 
property_del(struct mwSession * s,const char * key)93 static void property_del(struct mwSession *s, const char *key) {
94   g_hash_table_remove(s->attributes, key);
95 }
96 
97 
98 /**
99    set up the default properties for a newly created session
100 */
session_defaults(struct mwSession * s)101 static void session_defaults(struct mwSession *s) {
102   property_set(s, mwSession_CLIENT_VER_MAJOR,
103 	       GPOINTER(MW_PROTOCOL_VERSION_MAJOR), NULL);
104 
105   property_set(s, mwSession_CLIENT_VER_MINOR,
106 	       GPOINTER(MW_PROTOCOL_VERSION_MINOR), NULL);
107 
108   property_set(s, mwSession_CLIENT_TYPE_ID,
109 	       GPOINTER(mwLogin_MEANWHILE), NULL);
110 }
111 
112 
mwSession_new(struct mwSessionHandler * handler)113 struct mwSession *mwSession_new(struct mwSessionHandler *handler) {
114   struct mwSession *s;
115 
116   g_return_val_if_fail(handler != NULL, NULL);
117 
118   /* consider io_write and io_close to be absolute necessities */
119   g_return_val_if_fail(handler->io_write != NULL, NULL);
120   g_return_val_if_fail(handler->io_close != NULL, NULL);
121 
122   s = g_new0(struct mwSession, 1);
123 
124   s->state = mwSession_STOPPED;
125 
126   s->handler = handler;
127 
128   s->channels = mwChannelSet_new(s);
129   s->services = map_guint_new();
130   s->ciphers = map_guint_new();
131 
132   s->attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
133 					(GDestroyNotify) mw_datum_free);
134 
135   session_defaults(s);
136 
137   return s;
138 }
139 
140 
141 /** free and reset the session buffer */
session_buf_free(struct mwSession * s)142 static void session_buf_free(struct mwSession *s) {
143   g_return_if_fail(s != NULL);
144 
145   g_free(s->buf);
146   s->buf = NULL;
147   s->buf_len = 0;
148   s->buf_used = 0;
149 }
150 
151 
152 /** a polite string version of the session state enum */
state_str(enum mwSessionState state)153 static const char *state_str(enum mwSessionState state) {
154   switch(state) {
155   case mwSession_STARTING:      return "starting";
156   case mwSession_HANDSHAKE:     return "handshake sent";
157   case mwSession_HANDSHAKE_ACK: return "handshake acknowledged";
158   case mwSession_LOGIN:         return "login sent";
159   case mwSession_LOGIN_REDIR:   return "login redirected";
160   case mwSession_LOGIN_CONT:    return "forcing login";
161   case mwSession_LOGIN_ACK:     return "login acknowledged";
162   case mwSession_STARTED:       return "started";
163   case mwSession_STOPPING:      return "stopping";
164   case mwSession_STOPPED:       return "stopped";
165 
166   case mwSession_UNKNOWN:       /* fall-through */
167   default:                      return "UNKNOWN";
168   }
169 }
170 
171 
mwSession_free(struct mwSession * s)172 void mwSession_free(struct mwSession *s) {
173   struct mwSessionHandler *h;
174 
175   g_return_if_fail(s != NULL);
176 
177   if(! mwSession_isStopped(s)) {
178     g_debug("session is not stopped (state: %s), proceeding with free",
179 	    state_str(s->state));
180   }
181 
182   h = s->handler;
183   if(h && h->clear) h->clear(s);
184   s->handler = NULL;
185 
186   session_buf_free(s);
187 
188   mwChannelSet_free(s->channels);
189   g_hash_table_destroy(s->services);
190   g_hash_table_destroy(s->ciphers);
191   g_hash_table_destroy(s->attributes);
192 
193   mwLoginInfo_clear(&s->login);
194   mwUserStatus_clear(&s->status);
195   mwPrivacyInfo_clear(&s->privacy);
196 
197   g_free(s);
198 }
199 
200 
201 /** write data to the session handler */
io_write(struct mwSession * s,const guchar * buf,gsize len)202 static int io_write(struct mwSession *s, const guchar *buf, gsize len) {
203   g_return_val_if_fail(s != NULL, -1);
204   g_return_val_if_fail(s->handler != NULL, -1);
205   g_return_val_if_fail(s->handler->io_write != NULL, -1);
206 
207   return s->handler->io_write(s, buf, len);
208 }
209 
210 
211 /** close the session handler */
io_close(struct mwSession * s)212 static void io_close(struct mwSession *s) {
213   g_return_if_fail(s != NULL);
214   g_return_if_fail(s->handler != NULL);
215   g_return_if_fail(s->handler->io_close != NULL);
216 
217   s->handler->io_close(s);
218 }
219 
220 
state(struct mwSession * s,enum mwSessionState state,gpointer info)221 static void state(struct mwSession *s, enum mwSessionState state,
222 		  gpointer info) {
223 
224   struct mwSessionHandler *sh;
225 
226   g_return_if_fail(s != NULL);
227   g_return_if_fail(s->handler != NULL);
228 
229   if(mwSession_isState(s, state)) return;
230 
231   s->state = state;
232   s->state_info = info;
233 
234   switch(state) {
235   case mwSession_STOPPING:
236   case mwSession_STOPPED:
237     g_message("session state: %s (0x%08x)", state_str(state),
238 	      GPOINTER_TO_UINT(info));
239     break;
240 
241   case mwSession_LOGIN_REDIR:
242     g_message("session state: %s (%s)", state_str(state),
243 	      (char *)info);
244     break;
245 
246   default:
247     g_message("session state: %s", state_str(state));
248   }
249 
250   sh = s->handler;
251   if(sh && sh->on_stateChange)
252     sh->on_stateChange(s, state, info);
253 }
254 
255 
mwSession_start(struct mwSession * s)256 void mwSession_start(struct mwSession *s) {
257   struct mwMsgHandshake *msg;
258   int ret;
259 
260   g_return_if_fail(s != NULL);
261   g_return_if_fail(mwSession_isStopped(s));
262 
263   if(mwSession_isStarted(s) || mwSession_isStarting(s)) {
264     g_debug("attempted to start session that is already started/starting");
265     return;
266   }
267 
268   state(s, mwSession_STARTING, 0);
269 
270   msg = (struct mwMsgHandshake *) mwMessage_new(mwMessage_HANDSHAKE);
271   msg->major = GUINT(property_get(s, mwSession_CLIENT_VER_MAJOR));
272   msg->minor = GUINT(property_get(s, mwSession_CLIENT_VER_MINOR));
273   msg->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
274 
275   msg->loclcalc_addr = GUINT(property_get(s, mwSession_CLIENT_IP));
276 
277   if(msg->major >= 0x001e && msg->minor >= 0x001d) {
278     msg->unknown_a = 0x0100;
279     msg->local_host = property_get(s, mwSession_CLIENT_HOST);
280   }
281 
282   ret = mwSession_send(s, MW_MESSAGE(msg));
283   mwMessage_free(MW_MESSAGE(msg));
284 
285   if(ret) {
286     mwSession_stop(s, CONNECTION_BROKEN);
287   } else {
288     state(s, mwSession_HANDSHAKE, 0);
289   }
290 }
291 
292 
mwSession_stop(struct mwSession * s,guint32 reason)293 void mwSession_stop(struct mwSession *s, guint32 reason) {
294   GList *list, *l = NULL;
295   struct mwMsgChannelDestroy *msg;
296 
297   g_return_if_fail(s != NULL);
298 
299   if(mwSession_isStopped(s) || mwSession_isStopping(s)) {
300     g_debug("attempted to stop session that is already stopped/stopping");
301     return;
302   }
303 
304   state(s, mwSession_STOPPING, GUINT_TO_POINTER(reason));
305 
306   for(list = l = mwSession_getServices(s); l; l = l->next)
307     mwService_stop(MW_SERVICE(l->data));
308   g_list_free(list);
309 
310   msg = (struct mwMsgChannelDestroy *)
311     mwMessage_new(mwMessage_CHANNEL_DESTROY);
312 
313   msg->head.channel = MW_MASTER_CHANNEL_ID;
314   msg->reason = reason;
315 
316   /* don't care if this fails, we're closing the connection anyway */
317   mwSession_send(s, MW_MESSAGE(msg));
318   mwMessage_free(MW_MESSAGE(msg));
319 
320   session_buf_free(s);
321 
322   /* close the connection */
323   io_close(s);
324 
325   state(s, mwSession_STOPPED, GUINT_TO_POINTER(reason));
326 }
327 
328 
329 /** compose authentication information into an opaque based on the
330     password, encrypted via RC2/40 */
compose_auth_rc2_40(struct mwOpaque * auth,const char * pass)331 static void compose_auth_rc2_40(struct mwOpaque *auth, const char *pass) {
332   guchar iv[8], key[5];
333   struct mwOpaque a, b, z;
334   struct mwPutBuffer *p;
335 
336   /* get an IV and a random five-byte key */
337   mwIV_init(iv);
338   mwKeyRandom(key, 5);
339 
340   /* the opaque with the key */
341   a.len = 5;
342   a.data = key;
343 
344   /* the opaque to receive the encrypted pass */
345   b.len = 0;
346   b.data = NULL;
347 
348   /* the plain-text pass dressed up as an opaque */
349   z.len = strlen(pass);
350   z.data = (guchar *) pass;
351 
352   /* the opaque with the encrypted pass */
353   mwEncrypt(a.data, a.len, iv, &z, &b);
354 
355   /* an opaque containing the other two opaques */
356   p = mwPutBuffer_new();
357   mwOpaque_put(p, &a);
358   mwOpaque_put(p, &b);
359   mwPutBuffer_finalize(auth, p);
360 
361   /* this is the only one to clear, as the other uses a static buffer */
362   mwOpaque_clear(&b);
363 }
364 
365 
compose_auth_rc2_128(struct mwOpaque * auth,const char * pass,guint32 magic,struct mwOpaque * rkey)366 static void compose_auth_rc2_128(struct mwOpaque *auth, const char *pass,
367 				 guint32 magic, struct mwOpaque *rkey) {
368 
369   guchar iv[8];
370   struct mwOpaque a, b, c;
371   struct mwPutBuffer *p;
372 
373   struct mwMpi *private, *public;
374   struct mwMpi *remote;
375   struct mwMpi *shared;
376 
377   private = mwMpi_new();
378   public = mwMpi_new();
379   remote = mwMpi_new();
380   shared = mwMpi_new();
381 
382   mwIV_init(iv);
383 
384   mwMpi_randDHKeypair(private, public);
385   mwMpi_import(remote, rkey);
386   mwMpi_calculateDHShared(shared, remote, private);
387 
388   /* put the password in opaque a */
389   p = mwPutBuffer_new();
390   guint32_put(p, magic);
391   mwString_put(p, pass);
392   mwPutBuffer_finalize(&a, p);
393 
394   /* put the shared key in opaque b */
395   mwMpi_export(shared, &b);
396 
397   /* encrypt the password (a) using the shared key (b), put the result
398      in opaque c */
399   mwEncrypt(b.data+(b.len-16), 16, iv, &a, &c);
400 
401   /* don't need the shared key anymore, re-use opaque (b) as the
402      export of the public key */
403   mwOpaque_clear(&b);
404   mwMpi_export(public, &b);
405 
406   p = mwPutBuffer_new();
407   guint16_put(p, 0x0001);  /* XXX: unknown */
408   mwOpaque_put(p, &b);
409   mwOpaque_put(p, &c);
410   mwPutBuffer_finalize(auth, p);
411 
412   mwOpaque_clear(&a);
413   mwOpaque_clear(&b);
414   mwOpaque_clear(&c);
415 
416   mwMpi_free(private);
417   mwMpi_free(public);
418   mwMpi_free(remote);
419   mwMpi_free(shared);
420 }
421 
422 
423 /** handle the receipt of a handshake_ack message by sending the login
424     message */
HANDSHAKE_ACK_recv(struct mwSession * s,struct mwMsgHandshakeAck * msg)425 static void HANDSHAKE_ACK_recv(struct mwSession *s,
426 			       struct mwMsgHandshakeAck *msg) {
427   struct mwMsgLogin *log;
428   int ret;
429 
430   g_return_if_fail(s != NULL);
431   g_return_if_fail(msg != NULL);
432   g_return_if_fail(mwSession_isState(s, mwSession_HANDSHAKE) ||
433 		   mwSession_isState(s, mwSession_LOGIN_CONT));
434 
435   if(mwSession_isState(s, mwSession_LOGIN_CONT)) {
436     /* this is a login continuation, don't re-send the login. We
437        should receive a login ack in a moment */
438 
439     state(s, mwSession_HANDSHAKE_ACK, 0);
440     state(s, mwSession_LOGIN, 0);
441     return;
442 
443   } else {
444     state(s, mwSession_HANDSHAKE_ACK, 0);
445   }
446 
447   /* record the major/minor versions from the server */
448   property_set(s, mwSession_SERVER_VER_MAJOR, GPOINTER(msg->major), NULL);
449   property_set(s, mwSession_SERVER_VER_MINOR, GPOINTER(msg->minor), NULL);
450 
451   /* compose the login message */
452   log = (struct mwMsgLogin *) mwMessage_new(mwMessage_LOGIN);
453   log->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
454   log->name = g_strdup(property_get(s, mwSession_AUTH_USER_ID));
455 
456   /** @todo default to password for now. later use token optionally */
457   {
458     const char *pw;
459     pw = property_get(s, mwSession_AUTH_PASSWORD);
460 
461     if(msg->data.len >= 64) {
462       /* good login encryption */
463       log->auth_type = mwAuthType_RC2_128;
464       compose_auth_rc2_128(&log->auth_data, pw, msg->magic, &msg->data);
465 
466     } else {
467       /* BAD login encryption */
468       log->auth_type = mwAuthType_RC2_40;
469       compose_auth_rc2_40(&log->auth_data, pw);
470     }
471   }
472 
473   /* send the login message */
474   ret = mwSession_send(s, MW_MESSAGE(log));
475   mwMessage_free(MW_MESSAGE(log));
476 
477   if(! ret) {
478     /* sent login OK, set state appropriately */
479     state(s, mwSession_LOGIN, 0);
480   }
481 }
482 
483 
484 /** handle the receipt of a login_ack message. This completes the
485     startup sequence for the session */
LOGIN_ACK_recv(struct mwSession * s,struct mwMsgLoginAck * msg)486 static void LOGIN_ACK_recv(struct mwSession *s,
487 			   struct mwMsgLoginAck *msg) {
488   GList *ll, *l;
489 
490   g_return_if_fail(s != NULL);
491   g_return_if_fail(msg != NULL);
492   g_return_if_fail(mwSession_isState(s, mwSession_LOGIN));
493 
494   /* store the login information in the session */
495   mwLoginInfo_clear(&s->login);
496   mwLoginInfo_clone(&s->login, &msg->login);
497 
498   state(s, mwSession_LOGIN_ACK, 0);
499 
500   /* start up our services */
501   for(ll = l = mwSession_getServices(s); l; l = l->next) {
502     mwService_start(l->data);
503   }
504   g_list_free(ll);
505 
506   /* @todo any further startup stuff? */
507 
508   state(s, mwSession_STARTED, 0);
509 }
510 
511 
CHANNEL_CREATE_recv(struct mwSession * s,struct mwMsgChannelCreate * msg)512 static void CHANNEL_CREATE_recv(struct mwSession *s,
513 				struct mwMsgChannelCreate *msg) {
514   struct mwChannel *chan;
515   chan = mwChannel_newIncoming(s->channels, msg->channel);
516 
517   /* hand off to channel */
518   mwChannel_recvCreate(chan, msg);
519 }
520 
521 
CHANNEL_ACCEPT_recv(struct mwSession * s,struct mwMsgChannelAccept * msg)522 static void CHANNEL_ACCEPT_recv(struct mwSession *s,
523 				struct mwMsgChannelAccept *msg) {
524   struct mwChannel *chan;
525   chan = mwChannel_find(s->channels, msg->head.channel);
526 
527   g_return_if_fail(chan != NULL);
528 
529   /* hand off to channel */
530   mwChannel_recvAccept(chan, msg);
531 }
532 
533 
CHANNEL_DESTROY_recv(struct mwSession * s,struct mwMsgChannelDestroy * msg)534 static void CHANNEL_DESTROY_recv(struct mwSession *s,
535 				 struct mwMsgChannelDestroy *msg) {
536 
537   /* the server can indicate that we should close the session by
538      destroying the zero channel */
539   if(msg->head.channel == MW_MASTER_CHANNEL_ID) {
540     mwSession_stop(s, msg->reason);
541 
542   } else {
543     struct mwChannel *chan;
544     chan = mwChannel_find(s->channels, msg->head.channel);
545 
546     /* we don't have any such channel... so I guess we destroyed it.
547        This is to remove a warning from timing errors when two clients
548        both try to close a channel at about the same time. */
549     if(! chan) return;
550 
551     /* hand off to channel */
552     mwChannel_recvDestroy(chan, msg);
553   }
554 }
555 
556 
CHANNEL_SEND_recv(struct mwSession * s,struct mwMsgChannelSend * msg)557 static void CHANNEL_SEND_recv(struct mwSession *s,
558 			      struct mwMsgChannelSend *msg) {
559   struct mwChannel *chan;
560   chan = mwChannel_find(s->channels, msg->head.channel);
561 
562   /* if we don't have any such channel, we're certainly not going to
563      accept data from it */
564   if(! chan) return;
565 
566   /* hand off to channel */
567   mwChannel_recv(chan, msg);
568 }
569 
570 
SET_PRIVACY_LIST_recv(struct mwSession * s,struct mwMsgSetPrivacyList * msg)571 static void SET_PRIVACY_LIST_recv(struct mwSession *s,
572 				  struct mwMsgSetPrivacyList *msg) {
573   struct mwSessionHandler *sh = s->handler;
574 
575   g_info("SET_PRIVACY_LIST");
576 
577   mwPrivacyInfo_clear(&s->privacy);
578   mwPrivacyInfo_clone(&s->privacy, &msg->privacy);
579 
580   if(sh && sh->on_setPrivacyInfo)
581     sh->on_setPrivacyInfo(s);
582 }
583 
584 
SET_USER_STATUS_recv(struct mwSession * s,struct mwMsgSetUserStatus * msg)585 static void SET_USER_STATUS_recv(struct mwSession *s,
586 				 struct mwMsgSetUserStatus *msg) {
587   struct mwSessionHandler *sh = s->handler;
588 
589   mwUserStatus_clear(&s->status);
590   mwUserStatus_clone(&s->status, &msg->status);
591 
592   if(sh && sh->on_setUserStatus)
593     sh->on_setUserStatus(s);
594 }
595 
596 
SENSE_SERVICE_recv(struct mwSession * s,struct mwMsgSenseService * msg)597 static void SENSE_SERVICE_recv(struct mwSession *s,
598 			       struct mwMsgSenseService *msg) {
599   struct mwService *srvc;
600 
601   srvc = mwSession_getService(s, msg->service);
602   if(srvc) mwService_start(srvc);
603 }
604 
605 
ADMIN_recv(struct mwSession * s,struct mwMsgAdmin * msg)606 static void ADMIN_recv(struct mwSession *s, struct mwMsgAdmin *msg) {
607   struct mwSessionHandler *sh = s->handler;
608 
609   if(sh && sh->on_admin)
610     sh->on_admin(s, msg->text);
611 }
612 
613 
ANNOUNCE_recv(struct mwSession * s,struct mwMsgAnnounce * msg)614 static void ANNOUNCE_recv(struct mwSession *s, struct mwMsgAnnounce *msg) {
615   struct mwSessionHandler *sh = s->handler;
616 
617   if(sh && sh->on_announce)
618     sh->on_announce(s, &msg->sender, msg->may_reply, msg->text);
619 }
620 
621 
LOGIN_REDIRECT_recv(struct mwSession * s,struct mwMsgLoginRedirect * msg)622 static void LOGIN_REDIRECT_recv(struct mwSession *s,
623 				struct mwMsgLoginRedirect *msg) {
624 
625   state(s, mwSession_LOGIN_REDIR, msg->host);
626 }
627 
628 
629 #define CASE(var, type) \
630 case mwMessage_ ## var: \
631   var ## _recv(s, (struct type *) msg); \
632   break;
633 
634 
session_process(struct mwSession * s,const guchar * buf,gsize len)635 static void session_process(struct mwSession *s,
636 			    const guchar *buf, gsize len) {
637 
638   struct mwOpaque o = { .len = len, .data = (guchar *) buf };
639   struct mwGetBuffer *b;
640   struct mwMessage *msg;
641 
642   g_return_if_fail(s != NULL);
643   g_return_if_fail(buf != NULL);
644 
645   /* ignore zero-length messages */
646   if(len == 0) return;
647 
648   /* wrap up buf */
649   b = mwGetBuffer_wrap(&o);
650 
651   /* attempt to parse the message. */
652   msg = mwMessage_get(b);
653 
654   if(mwGetBuffer_error(b)) {
655     mw_mailme_opaque(&o, "parsing of message failed");
656   }
657 
658   mwGetBuffer_free(b);
659 
660   g_return_if_fail(msg != NULL);
661 
662   /* handle each of the appropriate incoming types of mwMessage */
663   switch(msg->type) {
664     CASE(HANDSHAKE_ACK, mwMsgHandshakeAck);
665     CASE(LOGIN_REDIRECT, mwMsgLoginRedirect);
666     CASE(LOGIN_ACK, mwMsgLoginAck);
667     CASE(CHANNEL_CREATE, mwMsgChannelCreate);
668     CASE(CHANNEL_DESTROY, mwMsgChannelDestroy);
669     CASE(CHANNEL_SEND, mwMsgChannelSend);
670     CASE(CHANNEL_ACCEPT, mwMsgChannelAccept);
671     CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList);
672     CASE(SET_USER_STATUS, mwMsgSetUserStatus);
673     CASE(SENSE_SERVICE, mwMsgSenseService);
674     CASE(ADMIN, mwMsgAdmin);
675     CASE(ANNOUNCE, mwMsgAnnounce);
676 
677   default:
678     g_warning("unknown message type 0x%04x, no handler", msg->type);
679   }
680 
681   mwMessage_free(msg);
682 }
683 
684 
685 #undef CASE
686 
687 
688 #define ADVANCE(b, n, count) { b += count; n -= count; }
689 
690 
691 /* handle input to complete an existing buffer */
session_recv_cont(struct mwSession * s,const guchar * b,gsize n)692 static gsize session_recv_cont(struct mwSession *s,
693 			       const guchar *b, gsize n) {
694 
695   /* determine how many bytes still required */
696   gsize x = s->buf_len - s->buf_used;
697 
698   /* g_message(" session_recv_cont: session = %p, b = %p, n = %u",
699 	    s, b, n); */
700 
701   if(n < x) {
702     /* not quite enough; still need some more */
703     memcpy(s->buf+s->buf_used, b, n);
704     s->buf_used += n;
705     return 0;
706 
707   } else {
708     /* enough to finish the buffer, at least */
709     memcpy(s->buf+s->buf_used, b, x);
710     ADVANCE(b, n, x);
711 
712     if(s->buf_len == 4) {
713       /* if only the length bytes were being buffered, we'll now try
714        to complete an actual message */
715 
716       struct mwOpaque o = { 4, s->buf };
717       struct mwGetBuffer *gb = mwGetBuffer_wrap(&o);
718       x = guint32_peek(gb);
719       mwGetBuffer_free(gb);
720 
721       if(n < x) {
722 	/* there isn't enough to meet the demands of the length, so
723 	   we'll buffer it for next time */
724 
725 	guchar *t;
726 	x += 4;
727 	t = (guchar *) g_malloc(x);
728 	memcpy(t, s->buf, 4);
729 	memcpy(t+4, b, n);
730 
731 	session_buf_free(s);
732 
733 	s->buf = t;
734 	s->buf_len = x;
735 	s->buf_used = n + 4;
736 	return 0;
737 
738       } else {
739 	/* there's enough (maybe more) for a full message. don't need
740 	   the old session buffer (which recall, was only the length
741 	   bytes) any more */
742 
743 	session_buf_free(s);
744 	session_process(s, b, x);
745 	ADVANCE(b, n, x);
746       }
747 
748     } else {
749       /* process the now-complete buffer. remember to skip the first
750 	 four bytes, since they're just the size count */
751       session_process(s, s->buf+4, s->buf_len-4);
752       session_buf_free(s);
753     }
754   }
755 
756   return n;
757 }
758 
759 
760 /* handle input when there's nothing previously buffered */
session_recv_empty(struct mwSession * s,const guchar * b,gsize n)761 static gsize session_recv_empty(struct mwSession *s,
762 				const guchar *b, gsize n) {
763 
764   struct mwOpaque o = { n, (guchar *) b };
765   struct mwGetBuffer *gb;
766   gsize x;
767 
768   if(n < 4) {
769     /* uh oh. less than four bytes means we've got an incomplete
770        length indicator. Have to buffer to get the rest of it. */
771     s->buf = (guchar *) g_malloc0(4);
772     memcpy(s->buf, b, n);
773     s->buf_len = 4;
774     s->buf_used = n;
775     return 0;
776   }
777 
778   /* peek at the length indicator. if it's a zero length message,
779      don't process, just skip it */
780   gb = mwGetBuffer_wrap(&o);
781   x = guint32_peek(gb);
782   mwGetBuffer_free(gb);
783   if(! x) return n - 4;
784 
785   if(n < (x + 4)) {
786     /* if the total amount of data isn't enough to cover the length
787        bytes and the length indicated by those bytes, then we'll need
788        to buffer. This is where the DOS mentioned below in
789        session_recv takes place */
790 
791     x += 4;
792     s->buf = (guchar *) g_malloc(x);
793     memcpy(s->buf, b, n);
794     s->buf_len = x;
795     s->buf_used = n;
796     return 0;
797 
798   } else {
799     /* advance past length bytes */
800     ADVANCE(b, n, 4);
801 
802     /* process and advance */
803     session_process(s, b, x);
804     ADVANCE(b, n, x);
805 
806     /* return left-over count */
807     return n;
808   }
809 }
810 
811 
session_recv(struct mwSession * s,const guchar * b,gsize n)812 static gsize session_recv(struct mwSession *s,
813 			  const guchar *b, gsize n) {
814 
815   /* This is messy and kind of confusing. I'd like to simplify it at
816      some point, but the constraints are as follows:
817 
818       - buffer up to a single full message on the session buffer
819       - buffer must contain the four length bytes
820       - the four length bytes indicate how much we'll need to buffer
821       - the four length bytes might not arrive all at once, so it's
822         possible that we'll need to buffer to get them.
823       - since our buffering includes the length bytes, we know we
824         still have an incomplete length if the buffer length is only
825         four. */
826 
827   /** @todo we should allow a compiled-in upper limit to message
828      sizes, and just drop messages over that size. However, to do that
829      we'd need to keep track of the size of a message and keep
830      dropping bytes until we'd fulfilled the entire length. eg: if we
831      receive a message size of 10MB, we need to pass up exactly 10MB
832      before it's safe to start processing the rest as a new
833      message. As it stands, a malicious packet from the server can run
834      us out of memory by indicating it's going to send us some
835      obscenely long message (even if it never actually sends it) */
836 
837   /* g_message(" session_recv: session = %p, b = %p, n = %u",
838 	    s, b, n); */
839 
840   if(s->buf_len == 0) {
841     while(n && (*b & 0x80)) {
842       /* keep-alive and series bytes are ignored */
843       ADVANCE(b, n, 1);
844     }
845   }
846 
847   if(n == 0) {
848     return 0;
849 
850   } else if(s->buf_len > 0) {
851     return session_recv_cont(s, b, n);
852 
853   } else {
854     return session_recv_empty(s, b, n);
855   }
856 }
857 
858 
859 #undef ADVANCE
860 
861 
mwSession_recv(struct mwSession * s,const guchar * buf,gsize n)862 void mwSession_recv(struct mwSession *s, const guchar *buf, gsize n) {
863   guchar *b = (guchar *) buf;
864   gsize remain = 0;
865 
866   g_return_if_fail(s != NULL);
867 
868   while(n > 0) {
869     remain = session_recv(s, b, n);
870     b += (n - remain);
871     n = remain;
872   }
873 }
874 
875 
mwSession_send(struct mwSession * s,struct mwMessage * msg)876 int mwSession_send(struct mwSession *s, struct mwMessage *msg) {
877   struct mwPutBuffer *b;
878   struct mwOpaque o;
879   int ret = 0;
880 
881   g_return_val_if_fail(s != NULL, -1);
882 
883   /* writing nothing is easy */
884   if(! msg) return 0;
885 
886   /* first we render the message into an opaque */
887   b = mwPutBuffer_new();
888   mwMessage_put(b, msg);
889   mwPutBuffer_finalize(&o, b);
890 
891   /* then we render the opaque into... another opaque! */
892   b = mwPutBuffer_new();
893   mwOpaque_put(b, &o);
894   mwOpaque_clear(&o);
895   mwPutBuffer_finalize(&o, b);
896 
897   /* then we use that opaque's data and length to write to the socket */
898   ret = io_write(s, o.data, o.len);
899   mwOpaque_clear(&o);
900 
901   /* ensure we could actually write the message */
902   if(! ret) {
903 
904     /* special case, as the server doesn't always respond to user
905        status messages. Thus, we trigger the event when we send the
906        messages as well as when we receive them */
907     if(msg->type == mwMessage_SET_USER_STATUS) {
908       SET_USER_STATUS_recv(s, (struct mwMsgSetUserStatus *) msg);
909     }
910   }
911 
912   return ret;
913 }
914 
915 
mwSession_sendKeepalive(struct mwSession * s)916 int mwSession_sendKeepalive(struct mwSession *s) {
917   const guchar b = 0x80;
918 
919   g_return_val_if_fail(s != NULL, -1);
920   return io_write(s, &b, 1);
921 }
922 
923 
mwSession_forceLogin(struct mwSession * s)924 int mwSession_forceLogin(struct mwSession *s) {
925   struct mwMsgLoginContinue *msg;
926   int ret;
927 
928   g_return_val_if_fail(s != NULL, -1);
929   g_return_val_if_fail(mwSession_isState(s, mwSession_LOGIN_REDIR), -1);
930 
931   state(s, mwSession_LOGIN_CONT, 0x00);
932 
933   msg = (struct mwMsgLoginContinue *)
934     mwMessage_new(mwMessage_LOGIN_CONTINUE);
935 
936   ret = mwSession_send(s, MW_MESSAGE(msg));
937   mwMessage_free(MW_MESSAGE(msg));
938 
939   return ret;
940 }
941 
942 
mwSession_sendAnnounce(struct mwSession * s,gboolean may_reply,const char * text,const GList * recipients)943 int mwSession_sendAnnounce(struct mwSession *s, gboolean may_reply,
944 			   const char *text, const GList *recipients) {
945 
946   struct mwMsgAnnounce *msg;
947   int ret;
948 
949   g_return_val_if_fail(s != NULL, -1);
950   g_return_val_if_fail(mwSession_isStarted(s), -1);
951 
952   msg = (struct mwMsgAnnounce *) mwMessage_new(mwMessage_ANNOUNCE);
953 
954   msg->recipients = (GList *) recipients;
955   msg->may_reply = may_reply;
956   msg->text = g_strdup(text);
957 
958   ret = mwSession_send(s, MW_MESSAGE(msg));
959 
960   msg->recipients = NULL;  /* don't kill our recipients param */
961   mwMessage_free(MW_MESSAGE(msg));
962 
963   return ret;
964 }
965 
966 
mwSession_getHandler(struct mwSession * s)967 struct mwSessionHandler *mwSession_getHandler(struct mwSession *s) {
968   g_return_val_if_fail(s != NULL, NULL);
969   return s->handler;
970 }
971 
972 
mwSession_getLoginInfo(struct mwSession * s)973 struct mwLoginInfo *mwSession_getLoginInfo(struct mwSession *s) {
974   g_return_val_if_fail(s != NULL, NULL);
975   return &s->login;
976 }
977 
978 
mwSession_setPrivacyInfo(struct mwSession * s,struct mwPrivacyInfo * privacy)979 int mwSession_setPrivacyInfo(struct mwSession *s,
980 			     struct mwPrivacyInfo *privacy) {
981 
982   struct mwMsgSetPrivacyList *msg;
983   int ret;
984 
985   g_return_val_if_fail(s != NULL, -1);
986   g_return_val_if_fail(privacy != NULL, -1);
987 
988   msg = (struct mwMsgSetPrivacyList *)
989     mwMessage_new(mwMessage_SET_PRIVACY_LIST);
990 
991   mwPrivacyInfo_clone(&msg->privacy, privacy);
992 
993   ret = mwSession_send(s, MW_MESSAGE(msg));
994   mwMessage_free(MW_MESSAGE(msg));
995 
996   return ret;
997 }
998 
999 
mwSession_getPrivacyInfo(struct mwSession * s)1000 struct mwPrivacyInfo *mwSession_getPrivacyInfo(struct mwSession *s) {
1001   g_return_val_if_fail(s != NULL, NULL);
1002   return &s->privacy;
1003 }
1004 
1005 
mwSession_setUserStatus(struct mwSession * s,struct mwUserStatus * stat)1006 int mwSession_setUserStatus(struct mwSession *s,
1007 			    struct mwUserStatus *stat) {
1008 
1009   struct mwMsgSetUserStatus *msg;
1010   int ret;
1011 
1012   g_return_val_if_fail(s != NULL, -1);
1013   g_return_val_if_fail(stat != NULL, -1);
1014 
1015   msg = (struct mwMsgSetUserStatus *)
1016     mwMessage_new(mwMessage_SET_USER_STATUS);
1017 
1018   mwUserStatus_clone(&msg->status, stat);
1019 
1020   ret = mwSession_send(s, MW_MESSAGE(msg));
1021   mwMessage_free(MW_MESSAGE(msg));
1022 
1023   return ret;
1024 }
1025 
1026 
mwSession_getUserStatus(struct mwSession * s)1027 struct mwUserStatus *mwSession_getUserStatus(struct mwSession *s) {
1028   g_return_val_if_fail(s != NULL, NULL);
1029   return &s->status;
1030 }
1031 
1032 
mwSession_getState(struct mwSession * s)1033 enum mwSessionState mwSession_getState(struct mwSession *s) {
1034   g_return_val_if_fail(s != NULL, mwSession_UNKNOWN);
1035   return s->state;
1036 }
1037 
1038 
mwSession_getStateInfo(struct mwSession * s)1039 gpointer mwSession_getStateInfo(struct mwSession *s) {
1040   g_return_val_if_fail(s != NULL, 0);
1041   return s->state_info;
1042 }
1043 
1044 
mwSession_getChannels(struct mwSession * session)1045 struct mwChannelSet *mwSession_getChannels(struct mwSession *session) {
1046   g_return_val_if_fail(session != NULL, NULL);
1047   return session->channels;
1048 }
1049 
1050 
mwSession_addService(struct mwSession * s,struct mwService * srv)1051 gboolean mwSession_addService(struct mwSession *s, struct mwService *srv) {
1052   g_return_val_if_fail(s != NULL, FALSE);
1053   g_return_val_if_fail(srv != NULL, FALSE);
1054   g_return_val_if_fail(s->services != NULL, FALSE);
1055 
1056   if(map_guint_lookup(s->services, SERVICE_KEY(srv))) {
1057     return FALSE;
1058 
1059   } else {
1060     map_guint_insert(s->services, SERVICE_KEY(srv), srv);
1061     if(mwSession_isState(s, mwSession_STARTED))
1062       mwSession_senseService(s, mwService_getType(srv));
1063     return TRUE;
1064   }
1065 }
1066 
1067 
mwSession_getService(struct mwSession * s,guint32 srv)1068 struct mwService *mwSession_getService(struct mwSession *s, guint32 srv) {
1069   g_return_val_if_fail(s != NULL, NULL);
1070   g_return_val_if_fail(s->services != NULL, NULL);
1071 
1072   return map_guint_lookup(s->services, srv);
1073 }
1074 
1075 
mwSession_removeService(struct mwSession * s,guint32 srv)1076 struct mwService *mwSession_removeService(struct mwSession *s, guint32 srv) {
1077   struct mwService *svc;
1078 
1079   g_return_val_if_fail(s != NULL, NULL);
1080   g_return_val_if_fail(s->services != NULL, NULL);
1081 
1082   svc = map_guint_lookup(s->services, srv);
1083   if(svc) map_guint_remove(s->services, srv);
1084   return svc;
1085 }
1086 
1087 
mwSession_getServices(struct mwSession * s)1088 GList *mwSession_getServices(struct mwSession *s) {
1089   g_return_val_if_fail(s != NULL, NULL);
1090   g_return_val_if_fail(s->services != NULL, NULL);
1091 
1092   return map_collect_values(s->services);
1093 }
1094 
1095 
mwSession_senseService(struct mwSession * s,guint32 srvc)1096 void mwSession_senseService(struct mwSession *s, guint32 srvc) {
1097   struct mwMsgSenseService *msg;
1098 
1099   g_return_if_fail(s != NULL);
1100   g_return_if_fail(srvc != 0x00);
1101   g_return_if_fail(mwSession_isStarted(s));
1102 
1103   msg = (struct mwMsgSenseService *)
1104     mwMessage_new(mwMessage_SENSE_SERVICE);
1105   msg->service = srvc;
1106 
1107   mwSession_send(s, MW_MESSAGE(msg));
1108   mwMessage_free(MW_MESSAGE(msg));
1109 }
1110 
1111 
mwSession_addCipher(struct mwSession * s,struct mwCipher * c)1112 gboolean mwSession_addCipher(struct mwSession *s, struct mwCipher *c) {
1113   g_return_val_if_fail(s != NULL, FALSE);
1114   g_return_val_if_fail(c != NULL, FALSE);
1115   g_return_val_if_fail(s->ciphers != NULL, FALSE);
1116 
1117   if(map_guint_lookup(s->ciphers, mwCipher_getType(c))) {
1118     g_message("cipher %s is already added, apparently",
1119 	      NSTR(mwCipher_getName(c)));
1120     return FALSE;
1121 
1122   } else {
1123     g_message("adding cipher %s", NSTR(mwCipher_getName(c)));
1124     map_guint_insert(s->ciphers, mwCipher_getType(c), c);
1125     return TRUE;
1126   }
1127 }
1128 
1129 
mwSession_getCipher(struct mwSession * s,guint16 c)1130 struct mwCipher *mwSession_getCipher(struct mwSession *s, guint16 c) {
1131   g_return_val_if_fail(s != NULL, NULL);
1132   g_return_val_if_fail(s->ciphers != NULL, NULL);
1133 
1134   return map_guint_lookup(s->ciphers, c);
1135 }
1136 
1137 
mwSession_removeCipher(struct mwSession * s,guint16 c)1138 struct mwCipher *mwSession_removeCipher(struct mwSession *s, guint16 c) {
1139   struct mwCipher *ciph;
1140 
1141   g_return_val_if_fail(s != NULL, NULL);
1142   g_return_val_if_fail(s->ciphers != NULL, NULL);
1143 
1144   ciph = map_guint_lookup(s->ciphers, c);
1145   if(ciph) map_guint_remove(s->ciphers, c);
1146   return ciph;
1147 }
1148 
1149 
mwSession_getCiphers(struct mwSession * s)1150 GList *mwSession_getCiphers(struct mwSession *s) {
1151   g_return_val_if_fail(s != NULL, NULL);
1152   g_return_val_if_fail(s->ciphers != NULL, NULL);
1153 
1154   return map_collect_values(s->ciphers);
1155 }
1156 
1157 
mwSession_setProperty(struct mwSession * s,const char * key,gpointer val,GDestroyNotify clean)1158 void mwSession_setProperty(struct mwSession *s, const char *key,
1159 			   gpointer val, GDestroyNotify clean) {
1160 
1161   g_return_if_fail(s != NULL);
1162   g_return_if_fail(s->attributes != NULL);
1163   g_return_if_fail(key != NULL);
1164 
1165   property_set(s, key, val, clean);
1166 }
1167 
1168 
mwSession_getProperty(struct mwSession * s,const char * key)1169 gpointer mwSession_getProperty(struct mwSession *s, const char *key) {
1170 
1171   g_return_val_if_fail(s != NULL, NULL);
1172   g_return_val_if_fail(s->attributes != NULL, NULL);
1173   g_return_val_if_fail(key != NULL, NULL);
1174 
1175   return property_get(s, key);
1176 }
1177 
1178 
mwSession_removeProperty(struct mwSession * s,const char * key)1179 void mwSession_removeProperty(struct mwSession *s, const char *key) {
1180   g_return_if_fail(s != NULL);
1181   g_return_if_fail(s->attributes != NULL);
1182   g_return_if_fail(key != NULL);
1183 
1184   property_del(s, key);
1185 }
1186 
1187 
mwSession_setClientData(struct mwSession * session,gpointer data,GDestroyNotify clear)1188 void mwSession_setClientData(struct mwSession *session,
1189 			     gpointer data, GDestroyNotify clear) {
1190 
1191   g_return_if_fail(session != NULL);
1192   mw_datum_set(&session->client_data, data, clear);
1193 }
1194 
1195 
mwSession_getClientData(struct mwSession * session)1196 gpointer mwSession_getClientData(struct mwSession *session) {
1197   g_return_val_if_fail(session != NULL, NULL);
1198   return mw_datum_get(&session->client_data);
1199 }
1200 
1201 
mwSession_removeClientData(struct mwSession * session)1202 void mwSession_removeClientData(struct mwSession *session) {
1203   g_return_if_fail(session != NULL);
1204   mw_datum_clear(&session->client_data);
1205 }
1206 
1207