|
10969
|
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
|