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