comparison src/protocols/sametime/meanwhile/session.c @ 10969:3ef77720e577

[gaim-migrate @ 12790] importing meanwhile library for use in the sametime plugin committer: Tailor Script <tailor@pidgin.im>
author Christopher O'Brien <siege@pidgin.im>
date Sun, 05 Jun 2005 02:50:13 +0000
parents
children 57fccea36e36
comparison
equal deleted inserted replaced
10968:e0d5038fbb7e 10969:3ef77720e577
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 guint32 state_info; /**< additional state info */
52
53 /* input buffering for an incoming message */
54 char *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
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
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
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 */
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
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 */
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 */
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
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(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 */
202 static int io_write(struct mwSession *s, const char *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 */
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
221 /** set the state of the session, and trigger the session handler's
222 on_stateChange function. Has no effect if the session is already
223 in the specified state (ignores additional state info)
224
225 @param s the session
226 @param state the state to set
227 @param info additional state info
228 */
229 static void state(struct mwSession *s, enum mwSessionState state,
230 guint32 info) {
231
232 struct mwSessionHandler *sh;
233
234 g_return_if_fail(s != NULL);
235 g_return_if_fail(s->handler != NULL);
236
237 if(mwSession_isState(s, state)) return;
238
239 s->state = state;
240 s->state_info = info;
241
242 if(info) {
243 g_message("session state: %s (0x%08x)", state_str(state), info);
244 } else {
245 g_message("session state: %s", state_str(state));
246 }
247
248 sh = s->handler;
249 if(sh->on_stateChange)
250 sh->on_stateChange(s, state, info);
251 }
252
253
254 void mwSession_start(struct mwSession *s) {
255 struct mwMsgHandshake *msg;
256 int ret;
257
258 g_return_if_fail(s != NULL);
259 g_return_if_fail(mwSession_isStopped(s));
260
261 state(s, mwSession_STARTING, 0);
262
263 msg = (struct mwMsgHandshake *) mwMessage_new(mwMessage_HANDSHAKE);
264 msg->major = GUINT(property_get(s, mwSession_CLIENT_VER_MAJOR));
265 msg->minor = GUINT(property_get(s, mwSession_CLIENT_VER_MINOR));
266 msg->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
267
268 ret = mwSession_send(s, MW_MESSAGE(msg));
269 mwMessage_free(MW_MESSAGE(msg));
270
271 if(ret) {
272 mwSession_stop(s, CONNECTION_BROKEN);
273 } else {
274 state(s, mwSession_HANDSHAKE, 0);
275 }
276 }
277
278
279 void mwSession_stop(struct mwSession *s, guint32 reason) {
280 GList *list, *l = NULL;
281 struct mwMsgChannelDestroy *msg;
282
283 g_return_if_fail(s != NULL);
284 g_return_if_fail(! mwSession_isStopping(s));
285 g_return_if_fail(! mwSession_isStopped(s));
286
287 state(s, mwSession_STOPPING, reason);
288
289 for(list = l = mwSession_getServices(s); l; l = l->next)
290 mwService_stop(MW_SERVICE(l->data));
291 g_list_free(list);
292
293 msg = (struct mwMsgChannelDestroy *)
294 mwMessage_new(mwMessage_CHANNEL_DESTROY);
295
296 msg->head.channel = MW_MASTER_CHANNEL_ID;
297 msg->reason = reason;
298
299 /* don't care if this fails, we're closing the connection anyway */
300 mwSession_send(s, MW_MESSAGE(msg));
301 mwMessage_free(MW_MESSAGE(msg));
302
303 session_buf_free(s);
304
305 /* close the connection */
306 io_close(s);
307
308 state(s, mwSession_STOPPED, reason);
309 }
310
311
312 /** compose authentication information into an opaque based on the
313 password */
314 static void compose_auth(struct mwOpaque *auth, const char *pass) {
315 char iv[8], key[5];
316 struct mwOpaque a, b, z;
317 struct mwPutBuffer *p;
318
319 /* get an IV and a random five-byte key */
320 mwIV_init((char *) iv);
321 rand_key((char *) key, 5);
322
323 /* the opaque with the key */
324 a.len = 5;
325 a.data = key;
326
327 /* the opaque to receive the encrypted pass */
328 b.len = 0;
329 b.data = NULL;
330
331 /* the plain-text pass dressed up as an opaque */
332 z.len = strlen(pass);
333 z.data = (char *) pass;
334
335 /* the opaque with the encrypted pass */
336 mwEncrypt(a.data, a.len, iv, &z, &b);
337
338 /* an opaque containing the other two opaques */
339 p = mwPutBuffer_new();
340 mwOpaque_put(p, &a);
341 mwOpaque_put(p, &b);
342 mwPutBuffer_finalize(auth, p);
343
344 /* this is the only one to clear, as the other uses a static buffer */
345 mwOpaque_clear(&b);
346 }
347
348
349 /** handle the receipt of a handshake_ack message by sending the login
350 message */
351 static void HANDSHAKE_ACK_recv(struct mwSession *s,
352 struct mwMsgHandshakeAck *msg) {
353 struct mwMsgLogin *log;
354 int ret;
355
356 g_return_if_fail(s != NULL);
357 g_return_if_fail(msg != NULL);
358 g_return_if_fail(mwSession_isState(s, mwSession_HANDSHAKE) ||
359 mwSession_isState(s, mwSession_LOGIN_CONT));
360
361 if(mwSession_isState(s, mwSession_LOGIN_CONT)) {
362 /* this is a login continuation, don't re-send the login. We
363 should receive a login ack in a moment */
364
365 state(s, mwSession_HANDSHAKE_ACK, 0);
366 state(s, mwSession_LOGIN, 0);
367 return;
368
369 } else {
370 state(s, mwSession_HANDSHAKE_ACK, 0);
371 }
372
373 /* record the major/minor versions from the server */
374 property_set(s, mwSession_SERVER_VER_MAJOR, GPOINTER(msg->major), NULL);
375 property_set(s, mwSession_SERVER_VER_MINOR, GPOINTER(msg->minor), NULL);
376
377 /* compose the login message */
378 log = (struct mwMsgLogin *) mwMessage_new(mwMessage_LOGIN);
379 log->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
380 log->name = g_strdup(property_get(s, mwSession_AUTH_USER_ID));
381
382 /** @todo default to password for now. later use token optionally */
383 log->auth_type = mwAuthType_ENCRYPT;
384 compose_auth(&log->auth_data, property_get(s, mwSession_AUTH_PASSWORD));
385
386 /* send the login message */
387 ret = mwSession_send(s, MW_MESSAGE(log));
388 mwMessage_free(MW_MESSAGE(log));
389
390 if(! ret) {
391 /* sent login OK, set state appropriately */
392 state(s, mwSession_LOGIN, 0);
393 }
394 }
395
396
397 /** handle the receipt of a login_ack message. This completes the
398 startup sequence for the session */
399 static void LOGIN_ACK_recv(struct mwSession *s,
400 struct mwMsgLoginAck *msg) {
401 GList *ll, *l;
402
403 g_return_if_fail(s != NULL);
404 g_return_if_fail(msg != NULL);
405 g_return_if_fail(mwSession_isState(s, mwSession_LOGIN));
406
407 /* store the login information in the session */
408 mwLoginInfo_clear(&s->login);
409 mwLoginInfo_clone(&s->login, &msg->login);
410
411 state(s, mwSession_LOGIN_ACK, 0);
412
413 /* start up our services */
414 for(ll = l = mwSession_getServices(s); l; l = l->next) {
415 mwService_start(l->data);
416 }
417 g_list_free(ll);
418
419 /* @todo any further startup stuff? */
420
421 state(s, mwSession_STARTED, 0);
422 }
423
424
425 static void CHANNEL_CREATE_recv(struct mwSession *s,
426 struct mwMsgChannelCreate *msg) {
427 struct mwChannel *chan;
428 chan = mwChannel_newIncoming(s->channels, msg->channel);
429
430 /* hand off to channel */
431 mwChannel_recvCreate(chan, msg);
432 }
433
434
435 static void CHANNEL_ACCEPT_recv(struct mwSession *s,
436 struct mwMsgChannelAccept *msg) {
437 struct mwChannel *chan;
438 chan = mwChannel_find(s->channels, msg->head.channel);
439
440 g_return_if_fail(chan != NULL);
441
442 /* hand off to channel */
443 mwChannel_recvAccept(chan, msg);
444 }
445
446
447 static void CHANNEL_DESTROY_recv(struct mwSession *s,
448 struct mwMsgChannelDestroy *msg) {
449
450 /* the server can indicate that we should close the session by
451 destroying the zero channel */
452 if(msg->head.channel == MW_MASTER_CHANNEL_ID) {
453 mwSession_stop(s, msg->reason);
454
455 } else {
456 struct mwChannel *chan;
457 chan = mwChannel_find(s->channels, msg->head.channel);
458
459 /* we don't have any such channel... so I guess we destroyed it.
460 This is to remove a warning from timing errors when two clients
461 both try to close a channel at about the same time. */
462 if(! chan) return;
463
464 /* hand off to channel */
465 mwChannel_recvDestroy(chan, msg);
466 }
467 }
468
469
470 static void CHANNEL_SEND_recv(struct mwSession *s,
471 struct mwMsgChannelSend *msg) {
472 struct mwChannel *chan;
473 chan = mwChannel_find(s->channels, msg->head.channel);
474
475 /* if we don't have any such channel, we're certainly not going to
476 accept data from it */
477 if(! chan) return;
478
479 /* hand off to channel */
480 mwChannel_recv(chan, msg);
481 }
482
483
484 static void SET_PRIVACY_LIST_recv(struct mwSession *s,
485 struct mwMsgSetPrivacyList *msg) {
486 struct mwSessionHandler *sh = s->handler;
487
488 g_info("SET_PRIVACY_LIST");
489
490 mwPrivacyInfo_clear(&s->privacy);
491 mwPrivacyInfo_clone(&s->privacy, &msg->privacy);
492
493 if(sh && sh->on_setPrivacyInfo)
494 sh->on_setPrivacyInfo(s);
495 }
496
497
498 static void SET_USER_STATUS_recv(struct mwSession *s,
499 struct mwMsgSetUserStatus *msg) {
500 struct mwSessionHandler *sh = s->handler;
501
502 mwUserStatus_clear(&s->status);
503 mwUserStatus_clone(&s->status, &msg->status);
504
505 if(sh && sh->on_setUserStatus)
506 sh->on_setUserStatus(s);
507 }
508
509
510 static void SENSE_SERVICE_recv(struct mwSession *s,
511 struct mwMsgSenseService *msg) {
512 struct mwService *srvc;
513
514 srvc = mwSession_getService(s, msg->service);
515 if(srvc) mwService_start(srvc);
516 }
517
518
519 static void ADMIN_recv(struct mwSession *s, struct mwMsgAdmin *msg) {
520 struct mwSessionHandler *sh = s->handler;
521
522 if(sh && sh->on_admin)
523 sh->on_admin(s, msg->text);
524 }
525
526
527 static void LOGIN_REDIRECT_recv(struct mwSession *s,
528 struct mwMsgLoginRedirect *msg) {
529 struct mwSessionHandler *sh = s->handler;
530
531 state(s, mwSession_LOGIN_REDIR, 0);
532
533 if(sh && sh->on_loginRedirect)
534 sh->on_loginRedirect(s, msg->host);
535 }
536
537
538 #define CASE(var, type) \
539 case mwMessage_ ## var: \
540 var ## _recv(s, (struct type *) msg); \
541 break;
542
543
544 static void session_process(struct mwSession *s,
545 const char *buf, gsize len) {
546
547 struct mwOpaque o = { len, (char *) buf };
548 struct mwGetBuffer *b;
549 struct mwMessage *msg;
550
551 g_assert(s != NULL);
552 g_return_if_fail(buf != NULL);
553
554 /* ignore zero-length messages */
555 if(len == 0) return;
556
557 /* wrap up buf */
558 b = mwGetBuffer_wrap(&o);
559
560 /* attempt to parse the message. */
561 msg = mwMessage_get(b);
562 g_return_if_fail(msg != NULL);
563
564 /* handle each of the appropriate incoming types of mwMessage */
565 switch(msg->type) {
566 CASE(HANDSHAKE_ACK, mwMsgHandshakeAck);
567 CASE(LOGIN_REDIRECT, mwMsgLoginRedirect);
568 CASE(LOGIN_ACK, mwMsgLoginAck);
569 CASE(CHANNEL_CREATE, mwMsgChannelCreate);
570 CASE(CHANNEL_DESTROY, mwMsgChannelDestroy);
571 CASE(CHANNEL_SEND, mwMsgChannelSend);
572 CASE(CHANNEL_ACCEPT, mwMsgChannelAccept);
573 CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList);
574 CASE(SET_USER_STATUS, mwMsgSetUserStatus);
575 CASE(SENSE_SERVICE, mwMsgSenseService);
576 CASE(ADMIN, mwMsgAdmin);
577
578 default:
579 g_warning("unknown message type 0x%04x, no handler", msg->type);
580 }
581
582 if(mwGetBuffer_error(b)) {
583 struct mwOpaque o = { .data = (char *) buf, .len = len };
584 mw_debug_mailme(&o, "parsing of message type 0x%04x failed", msg->type);
585 }
586
587 mwGetBuffer_free(b);
588 mwMessage_free(msg);
589 }
590
591
592 #undef CASE
593
594
595 #define ADVANCE(b, n, count) (b += count, n -= count)
596
597
598 /* handle input to complete an existing buffer */
599 static gsize session_recv_cont(struct mwSession *s, const char *b, gsize n) {
600
601 /* determine how many bytes still required */
602 gsize x = s->buf_len - s->buf_used;
603
604 /* g_message(" session_recv_cont: session = %p, b = %p, n = %u",
605 s, b, n); */
606
607 if(n < x) {
608 /* not quite enough; still need some more */
609 memcpy(s->buf+s->buf_used, b, n);
610 s->buf_used += n;
611 return 0;
612
613 } else {
614 /* enough to finish the buffer, at least */
615 memcpy(s->buf+s->buf_used, b, x);
616 ADVANCE(b, n, x);
617
618 if(s->buf_len == 4) {
619 /* if only the length bytes were being buffered, we'll now try
620 to complete an actual message */
621
622 struct mwOpaque o = { 4, s->buf };
623 struct mwGetBuffer *gb = mwGetBuffer_wrap(&o);
624 x = guint32_peek(gb);
625 mwGetBuffer_free(gb);
626
627 if(n < x) {
628 /* there isn't enough to meet the demands of the length, so
629 we'll buffer it for next time */
630
631 char *t;
632 x += 4;
633 t = (char *) g_malloc(x);
634 memcpy(t, s->buf, 4);
635 memcpy(t+4, b, n);
636
637 session_buf_free(s);
638
639 s->buf = t;
640 s->buf_len = x;
641 s->buf_used = n + 4;
642 return 0;
643
644 } else {
645 /* there's enough (maybe more) for a full message. don't need
646 the old session buffer (which recall, was only the length
647 bytes) any more */
648
649 session_buf_free(s);
650 session_process(s, b, x);
651 ADVANCE(b, n, x);
652 }
653
654 } else {
655 /* process the now-complete buffer. remember to skip the first
656 four bytes, since they're just the size count */
657 session_process(s, s->buf+4, s->buf_len-4);
658 session_buf_free(s);
659 }
660 }
661
662 return n;
663 }
664
665
666 /* handle input when there's nothing previously buffered */
667 static gsize session_recv_empty(struct mwSession *s, const char *b, gsize n) {
668 struct mwOpaque o = { n, (char *) b };
669 struct mwGetBuffer *gb;
670 gsize x;
671
672 if(n < 4) {
673 /* uh oh. less than four bytes means we've got an incomplete
674 length indicator. Have to buffer to get the rest of it. */
675 s->buf = (char *) g_malloc0(4);
676 memcpy(s->buf, b, n);
677 s->buf_len = 4;
678 s->buf_used = n;
679 return 0;
680 }
681
682 /* peek at the length indicator. if it's a zero length message,
683 don't process, just skip it */
684 gb = mwGetBuffer_wrap(&o);
685 x = guint32_peek(gb);
686 mwGetBuffer_free(gb);
687 if(! x) return n - 4;
688
689 if(n < (x + 4)) {
690 /* if the total amount of data isn't enough to cover the length
691 bytes and the length indicated by those bytes, then we'll need
692 to buffer. This is where the DOS mentioned below in
693 session_recv takes place */
694
695 x += 4;
696 s->buf = (char *) g_malloc(x);
697 memcpy(s->buf, b, n);
698 s->buf_len = x;
699 s->buf_used = n;
700 return 0;
701
702 } else {
703 /* advance past length bytes */
704 ADVANCE(b, n, 4);
705
706 /* process and advance */
707 session_process(s, b, x);
708 ADVANCE(b, n, x);
709
710 /* return left-over count */
711 return n;
712 }
713 }
714
715
716 static gsize session_recv(struct mwSession *s, const char *b, gsize n) {
717 /* This is messy and kind of confusing. I'd like to simplify it at
718 some point, but the constraints are as follows:
719
720 - buffer up to a single full message on the session buffer
721 - buffer must contain the four length bytes
722 - the four length bytes indicate how much we'll need to buffer
723 - the four length bytes might not arrive all at once, so it's
724 possible that we'll need to buffer to get them.
725 - since our buffering includes the length bytes, we know we
726 still have an incomplete length if the buffer length is only
727 four. */
728
729 /** @todo we should allow a compiled-in upper limit to message
730 sizes, and just drop messages over that size. However, to do that
731 we'd need to keep track of the size of a message and keep
732 dropping bytes until we'd fulfilled the entire length. eg: if we
733 receive a message size of 10MB, we need to pass up exactly 10MB
734 before it's safe to start processing the rest as a new
735 message. As it stands, a malicious packet from the server can run
736 us out of memory by indicating it's going to send us some
737 obscenely long message (even if it never actually sends it) */
738
739 /* g_message(" session_recv: session = %p, b = %p, n = %u",
740 s, b, n); */
741
742 if(n && (s->buf_len == 0) && (*b & 0x80)) {
743 /* keep-alive and series bytes are ignored */
744 ADVANCE(b, n, 1);
745 }
746
747 if(n == 0) {
748 return 0;
749
750 } else if(s->buf_len > 0) {
751 return session_recv_cont(s, b, n);
752
753 } else {
754 return session_recv_empty(s, b, n);
755 }
756 }
757
758
759 #undef ADVANCE
760
761
762 void mwSession_recv(struct mwSession *s, const char *buf, gsize n) {
763 char *b = (char *) buf;
764 gsize remain = 0;
765
766 g_return_if_fail(s != NULL);
767
768 /* g_message(" mwSession_recv: session = %p, b = %p, n = %u",
769 s, b, n); */
770
771 while(n > 0) {
772 remain = session_recv(s, b, n);
773 b += (n - remain);
774 n = remain;
775 }
776 }
777
778
779 int mwSession_send(struct mwSession *s, struct mwMessage *msg) {
780 struct mwPutBuffer *b;
781 struct mwOpaque o;
782 gsize len;
783 int ret = 0;
784
785 g_return_val_if_fail(s != NULL, -1);
786
787 /* writing nothing is easy */
788 if(! msg) return 0;
789
790 /* first we render the message into an opaque */
791 b = mwPutBuffer_new();
792 mwMessage_put(b, msg);
793 mwPutBuffer_finalize(&o, b);
794
795 /* then we render the opaque into... another opaque! */
796 b = mwPutBuffer_new();
797 mwOpaque_put(b, &o);
798 mwOpaque_clear(&o);
799 mwPutBuffer_finalize(&o, b);
800
801 /* then we use that opaque's data and length to write to the socket */
802 len = o.len;
803 ret = io_write(s, o.data, o.len);
804 mwOpaque_clear(&o);
805
806 /* ensure we could actually write the message */
807 if(! ret) {
808
809 /* special case, as the server doesn't always respond to user
810 status messages. Thus, we trigger the event when we send the
811 messages as well as when we receive them */
812 if(msg->type == mwMessage_SET_USER_STATUS) {
813 SET_USER_STATUS_recv(s, (struct mwMsgSetUserStatus *) msg);
814 }
815 }
816
817 return ret;
818 }
819
820
821 int mwSession_sendKeepalive(struct mwSession *s) {
822 const char b = 0x80;
823
824 g_return_val_if_fail(s != NULL, -1);
825 return io_write(s, &b, 1);
826 }
827
828
829 int mwSession_forceLogin(struct mwSession *s) {
830 struct mwMsgLoginContinue *msg;
831 int ret;
832
833 g_return_val_if_fail(s != NULL, -1);
834 g_return_val_if_fail(mwSession_isState(s, mwSession_LOGIN_REDIR), -1);
835
836 state(s, mwSession_LOGIN_CONT, 0x00);
837
838 msg = (struct mwMsgLoginContinue *)
839 mwMessage_new(mwMessage_LOGIN_CONTINUE);
840
841 ret = mwSession_send(s, MW_MESSAGE(msg));
842 mwMessage_free(MW_MESSAGE(msg));
843
844 return ret;
845 }
846
847
848 struct mwSessionHandler *mwSession_getHandler(struct mwSession *s) {
849 g_return_val_if_fail(s != NULL, NULL);
850 return s->handler;
851 }
852
853
854 struct mwLoginInfo *mwSession_getLoginInfo(struct mwSession *s) {
855 g_return_val_if_fail(s != NULL, NULL);
856 return &s->login;
857 }
858
859
860 int mwSession_setPrivacyInfo(struct mwSession *s,
861 struct mwPrivacyInfo *privacy) {
862
863 struct mwMsgSetPrivacyList *msg;
864 int ret;
865
866 g_return_val_if_fail(s != NULL, -1);
867 g_return_val_if_fail(privacy != NULL, -1);
868
869 msg = (struct mwMsgSetPrivacyList *)
870 mwMessage_new(mwMessage_SET_PRIVACY_LIST);
871
872 mwPrivacyInfo_clone(&msg->privacy, privacy);
873
874 ret = mwSession_send(s, MW_MESSAGE(msg));
875 mwMessage_free(MW_MESSAGE(msg));
876
877 return ret;
878 }
879
880
881 struct mwPrivacyInfo *mwSession_getPrivacyInfo(struct mwSession *s) {
882 g_return_val_if_fail(s != NULL, NULL);
883 return &s->privacy;
884 }
885
886
887 int mwSession_setUserStatus(struct mwSession *s,
888 struct mwUserStatus *stat) {
889
890 struct mwMsgSetUserStatus *msg;
891 int ret;
892
893 g_return_val_if_fail(s != NULL, -1);
894 g_return_val_if_fail(stat != NULL, -1);
895
896 msg = (struct mwMsgSetUserStatus *)
897 mwMessage_new(mwMessage_SET_USER_STATUS);
898
899 mwUserStatus_clone(&msg->status, stat);
900
901 ret = mwSession_send(s, MW_MESSAGE(msg));
902 mwMessage_free(MW_MESSAGE(msg));
903
904 return ret;
905 }
906
907
908 struct mwUserStatus *mwSession_getUserStatus(struct mwSession *s) {
909 g_return_val_if_fail(s != NULL, NULL);
910 return &s->status;
911 }
912
913
914 enum mwSessionState mwSession_getState(struct mwSession *s) {
915 g_return_val_if_fail(s != NULL, mwSession_UNKNOWN);
916 return s->state;
917 }
918
919
920 guint32 mwSession_getStateInfo(struct mwSession *s) {
921 g_return_val_if_fail(s != NULL, 0);
922 return s->state_info;
923 }
924
925
926 struct mwChannelSet *mwSession_getChannels(struct mwSession *session) {
927 g_return_val_if_fail(session != NULL, NULL);
928 return session->channels;
929 }
930
931
932 gboolean mwSession_addService(struct mwSession *s, struct mwService *srv) {
933 g_return_val_if_fail(s != NULL, FALSE);
934 g_return_val_if_fail(srv != NULL, FALSE);
935 g_return_val_if_fail(s->services != NULL, FALSE);
936
937 if(map_guint_lookup(s->services, SERVICE_KEY(srv))) {
938 return FALSE;
939
940 } else {
941 map_guint_insert(s->services, SERVICE_KEY(srv), srv);
942 if(mwSession_isState(s, mwSession_STARTED))
943 mwSession_senseService(s, mwService_getType(srv));
944 return TRUE;
945 }
946 }
947
948
949 struct mwService *mwSession_getService(struct mwSession *s, guint32 srv) {
950 g_return_val_if_fail(s != NULL, NULL);
951 g_return_val_if_fail(s->services != NULL, NULL);
952
953 return map_guint_lookup(s->services, srv);
954 }
955
956
957 struct mwService *mwSession_removeService(struct mwSession *s, guint32 srv) {
958 struct mwService *svc;
959
960 g_return_val_if_fail(s != NULL, NULL);
961 g_return_val_if_fail(s->services != NULL, NULL);
962
963 svc = map_guint_lookup(s->services, srv);
964 if(svc) map_guint_remove(s->services, srv);
965 return svc;
966 }
967
968
969 GList *mwSession_getServices(struct mwSession *s) {
970 g_return_val_if_fail(s != NULL, NULL);
971 g_return_val_if_fail(s->services != NULL, NULL);
972
973 return map_collect_values(s->services);
974 }
975
976
977 void mwSession_senseService(struct mwSession *s, guint32 srvc) {
978 struct mwMsgSenseService *msg;
979
980 g_return_if_fail(s != NULL);
981 g_return_if_fail(srvc != 0x00);
982 g_return_if_fail(mwSession_isStarted(s));
983
984 msg = (struct mwMsgSenseService *)
985 mwMessage_new(mwMessage_SENSE_SERVICE);
986 msg->service = srvc;
987
988 mwSession_send(s, MW_MESSAGE(msg));
989 mwMessage_free(MW_MESSAGE(msg));
990 }
991
992
993 gboolean mwSession_addCipher(struct mwSession *s, struct mwCipher *c) {
994 g_return_val_if_fail(s != NULL, FALSE);
995 g_return_val_if_fail(c != NULL, FALSE);
996 g_return_val_if_fail(s->ciphers != NULL, FALSE);
997
998 if(map_guint_lookup(s->ciphers, mwCipher_getType(c))) {
999 g_message("cipher %s is already added, apparently",
1000 NSTR(mwCipher_getName(c)));
1001 return FALSE;
1002
1003 } else {
1004 g_message("adding cipher %s", NSTR(mwCipher_getName(c)));
1005 map_guint_insert(s->ciphers, mwCipher_getType(c), c);
1006 return TRUE;
1007 }
1008 }
1009
1010
1011 struct mwCipher *mwSession_getCipher(struct mwSession *s, guint16 c) {
1012 g_return_val_if_fail(s != NULL, NULL);
1013 g_return_val_if_fail(s->ciphers != NULL, NULL);
1014
1015 return map_guint_lookup(s->ciphers, c);
1016 }
1017
1018
1019 struct mwCipher *mwSession_removeCipher(struct mwSession *s, guint16 c) {
1020 struct mwCipher *ciph;
1021
1022 g_return_val_if_fail(s != NULL, NULL);
1023 g_return_val_if_fail(s->ciphers != NULL, NULL);
1024
1025 ciph = map_guint_lookup(s->ciphers, c);
1026 if(ciph) map_guint_remove(s->ciphers, c);
1027 return ciph;
1028 }
1029
1030
1031 GList *mwSession_getCiphers(struct mwSession *s) {
1032 g_return_val_if_fail(s != NULL, NULL);
1033 g_return_val_if_fail(s->ciphers != NULL, NULL);
1034
1035 return map_collect_values(s->ciphers);
1036 }
1037
1038
1039 void mwSession_setProperty(struct mwSession *s, const char *key,
1040 gpointer val, GDestroyNotify clean) {
1041
1042 g_return_if_fail(s != NULL);
1043 g_return_if_fail(s->attributes != NULL);
1044 g_return_if_fail(key != NULL);
1045
1046 property_set(s, key, val, clean);
1047 }
1048
1049
1050 gpointer mwSession_getProperty(struct mwSession *s, const char *key) {
1051
1052 g_return_val_if_fail(s != NULL, NULL);
1053 g_return_val_if_fail(s->attributes != NULL, NULL);
1054 g_return_val_if_fail(key != NULL, NULL);
1055
1056 return property_get(s, key);
1057 }
1058
1059
1060 void mwSession_removeProperty(struct mwSession *s, const char *key) {
1061 g_return_if_fail(s != NULL);
1062 g_return_if_fail(s->attributes != NULL);
1063 g_return_if_fail(key != NULL);
1064
1065 property_del(s, key);
1066 }
1067
1068
1069 void mwSession_setClientData(struct mwSession *session,
1070 gpointer data, GDestroyNotify clear) {
1071
1072 g_return_if_fail(session != NULL);
1073 mw_datum_set(&session->client_data, data, clear);
1074 }
1075
1076
1077 gpointer mwSession_getClientData(struct mwSession *session) {
1078 g_return_val_if_fail(session != NULL, NULL);
1079 return mw_datum_get(&session->client_data);
1080 }
1081
1082
1083 void mwSession_removeClientData(struct mwSession *session) {
1084 g_return_if_fail(session != NULL);
1085 mw_datum_clear(&session->client_data);
1086 }
1087