Mercurial > pidgin.yaz
annotate src/protocols/sametime/meanwhile/srvc_store.c @ 12311:a2ebf585d8c6
[gaim-migrate @ 14615]
should work fine, but I have to go home now. This ought to clean up all the signedness warnings, start to remove some unnecessary features (server-stored status messages), and add a useful feature (pretending to be an official client when the server is blocking unofficial clients)
committer: Tailor Script <tailor@pidgin.im>
author | Christopher O'Brien <siege@pidgin.im> |
---|---|
date | Sun, 04 Dec 2005 04:09:30 +0000 |
parents | 0110fc7c6a8a |
children |
rev | line source |
---|---|
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/glist.h> | |
22 | |
23 #include "mw_channel.h" | |
24 #include "mw_debug.h" | |
25 #include "mw_error.h" | |
26 #include "mw_message.h" | |
27 #include "mw_service.h" | |
28 #include "mw_session.h" | |
29 #include "mw_srvc_store.h" | |
30 | |
31 | |
32 #define PROTOCOL_TYPE 0x00000025 | |
33 #define PROTOCOL_VER 0x00000001 | |
34 | |
35 | |
36 enum storage_action { | |
37 action_load = 0x0004, | |
38 action_loaded = 0x0005, | |
39 action_save = 0x0006, | |
40 action_saved = 0x0007, | |
41 }; | |
42 | |
43 | |
44 struct mwStorageUnit { | |
45 /** key by which data is referenced in service | |
46 @see mwStorageKey */ | |
47 guint32 key; | |
48 | |
49 /** Data associated with key in service */ | |
50 struct mwOpaque data; | |
51 }; | |
52 | |
53 | |
54 struct mwStorageReq { | |
55 guint32 id; /**< unique id for this request */ | |
56 guint32 result_code; /**< result code for completed request */ | |
57 enum storage_action action; /**< load or save */ | |
58 struct mwStorageUnit *item; /**< the key/data pair */ | |
59 mwStorageCallback cb; /**< callback to notify upon completion */ | |
60 gpointer data; /**< user data to pass with callback */ | |
61 GDestroyNotify data_free; /**< optionally frees user data */ | |
62 }; | |
63 | |
64 | |
65 struct mwServiceStorage { | |
66 struct mwService service; | |
67 | |
68 /** collection of mwStorageReq */ | |
69 GList *pending; | |
70 | |
71 /** current service channel */ | |
72 struct mwChannel *channel; | |
73 | |
74 /** keep track of the counter */ | |
75 guint32 id_counter; | |
76 }; | |
77 | |
78 | |
79 static void request_get(struct mwGetBuffer *b, struct mwStorageReq *req) { | |
80 guint32 id, count, junk; | |
81 | |
82 if(mwGetBuffer_error(b)) return; | |
83 | |
84 guint32_get(b, &id); | |
85 guint32_get(b, &req->result_code); | |
86 | |
87 if(req->action == action_loaded) { | |
88 guint32_get(b, &count); | |
89 | |
90 if(count > 0) { | |
91 guint32_get(b, &junk); | |
92 guint32_get(b, &req->item->key); | |
93 | |
94 mwOpaque_clear(&req->item->data); | |
95 mwOpaque_get(b, &req->item->data); | |
96 } | |
97 } | |
98 } | |
99 | |
100 | |
101 static void request_put(struct mwPutBuffer *b, struct mwStorageReq *req) { | |
102 | |
103 guint32_put(b, req->id); | |
104 guint32_put(b, 1); | |
105 | |
106 if(req->action == action_save) { | |
107 guint32_put(b, 20 + req->item->data.len); /* ugh, offset garbage */ | |
108 guint32_put(b, req->item->key); | |
109 mwOpaque_put(b, &req->item->data); | |
110 | |
111 } else { | |
112 guint32_put(b, req->item->key); | |
113 } | |
114 } | |
115 | |
116 | |
117 static int request_send(struct mwChannel *chan, struct mwStorageReq *req) { | |
118 struct mwPutBuffer *b; | |
119 struct mwOpaque o = { 0, 0 }; | |
120 int ret; | |
121 | |
122 b = mwPutBuffer_new(); | |
123 request_put(b, req); | |
124 | |
125 mwPutBuffer_finalize(&o, b); | |
126 ret = mwChannel_send(chan, req->action, &o); | |
127 mwOpaque_clear(&o); | |
128 | |
129 if(! ret) { | |
130 if(req->action == action_save) { | |
131 req->action = action_saved; | |
132 } else if(req->action == action_load) { | |
133 req->action = action_loaded; | |
134 } | |
135 } | |
136 | |
137 return ret; | |
138 } | |
139 | |
140 | |
141 static struct mwStorageReq *request_find(struct mwServiceStorage *srvc, | |
142 guint32 id) { | |
143 GList *l; | |
144 | |
145 for(l = srvc->pending; l; l = l->next) { | |
146 struct mwStorageReq *r = l->data; | |
147 if(r->id == id) return r; | |
148 } | |
149 | |
150 return NULL; | |
151 } | |
152 | |
153 | |
154 static const char *action_str(enum storage_action act) { | |
155 switch(act) { | |
156 case action_load: return "load"; | |
157 case action_loaded: return "loaded"; | |
158 case action_save: return "save"; | |
159 case action_saved: return "saved"; | |
160 default: return "UNKNOWN"; | |
161 } | |
162 } | |
163 | |
164 | |
165 static void request_trigger(struct mwServiceStorage *srvc, | |
166 struct mwStorageReq *req) { | |
167 | |
168 struct mwStorageUnit *item = req->item; | |
169 | |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
170 g_message("storage request %s: key = 0x%x, result = 0x%x, length = %u", |
10969 | 171 action_str(req->action), |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
172 item->key, req->result_code, (guint) item->data.len); |
10969 | 173 |
174 if(req->cb) | |
175 req->cb(srvc, req->result_code, item, req->data); | |
176 } | |
177 | |
178 | |
179 static void request_free(struct mwStorageReq *req) { | |
180 if(req->data_free) { | |
181 req->data_free(req->data); | |
182 req->data = NULL; | |
183 req->data_free = NULL; | |
184 } | |
185 | |
186 mwStorageUnit_free(req->item); | |
187 g_free(req); | |
188 } | |
189 | |
190 | |
191 static void request_remove(struct mwServiceStorage *srvc, | |
192 struct mwStorageReq *req) { | |
193 | |
194 srvc->pending = g_list_remove_all(srvc->pending, req); | |
195 request_free(req); | |
196 } | |
197 | |
198 | |
199 static const char *get_name(struct mwService *srvc) { | |
200 return "User Storage"; | |
201 } | |
202 | |
203 | |
204 static const char *get_desc(struct mwService *srvc) { | |
205 return "Stores user data and settings on the server"; | |
206 } | |
207 | |
208 | |
209 static struct mwChannel *make_channel(struct mwServiceStorage *srvc) { | |
210 struct mwSession *session; | |
211 struct mwChannelSet *cs; | |
212 struct mwChannel *chan; | |
213 | |
214 session = mwService_getSession(MW_SERVICE(srvc)); | |
215 cs = mwSession_getChannels(session); | |
216 chan = mwChannel_newOutgoing(cs); | |
217 | |
218 mwChannel_setService(chan, MW_SERVICE(srvc)); | |
219 mwChannel_setProtoType(chan, PROTOCOL_TYPE); | |
220 mwChannel_setProtoVer(chan, PROTOCOL_VER); | |
221 | |
222 return mwChannel_create(chan)? NULL: chan; | |
223 } | |
224 | |
225 | |
226 static void start(struct mwService *srvc) { | |
227 struct mwServiceStorage *srvc_store; | |
228 struct mwChannel *chan; | |
229 | |
230 g_return_if_fail(srvc != NULL); | |
231 srvc_store = (struct mwServiceStorage *) srvc; | |
232 | |
233 chan = make_channel(srvc_store); | |
234 if(chan) { | |
235 srvc_store->channel = chan; | |
236 } else { | |
237 mwService_stopped(srvc); | |
238 } | |
239 } | |
240 | |
241 | |
242 static void stop(struct mwService *srvc) { | |
243 | |
244 struct mwServiceStorage *srvc_store; | |
245 GList *l; | |
246 | |
247 g_return_if_fail(srvc != NULL); | |
248 srvc_store = (struct mwServiceStorage *) srvc; | |
249 | |
250 if(srvc_store->channel) { | |
251 mwChannel_destroy(srvc_store->channel, ERR_SUCCESS, NULL); | |
252 srvc_store->channel = NULL; | |
253 } | |
254 | |
255 #if 1 | |
256 /* the new way */ | |
257 /* remove pending requests. Sometimes we can crash the storage | |
258 service, and when that happens, we end up resending the killer | |
259 request over and over again, and the service never stays up */ | |
260 for(l = srvc_store->pending; l; l = l->next) | |
261 request_free(l->data); | |
262 | |
263 g_list_free(srvc_store->pending); | |
264 srvc_store->pending = NULL; | |
265 | |
266 srvc_store->id_counter = 0; | |
267 | |
268 #else | |
269 /* the old way */ | |
270 /* reset all of the started requests to their unstarted states */ | |
271 for(l = srvc_store->pending; l; l = l->next) { | |
272 struct mwStorageReq *req = l->data; | |
273 | |
274 if(req->action == action_loaded) { | |
275 req->action = action_load; | |
276 } else if(req->action == action_saved) { | |
277 req->action = action_save; | |
278 } | |
279 } | |
280 #endif | |
281 | |
282 mwService_stopped(srvc); | |
283 } | |
284 | |
285 | |
286 static void recv_channelAccept(struct mwService *srvc, | |
287 struct mwChannel *chan, | |
288 struct mwMsgChannelAccept *msg) { | |
289 | |
290 struct mwServiceStorage *srvc_stor; | |
291 GList *l; | |
292 | |
293 g_return_if_fail(srvc != NULL); | |
294 srvc_stor = (struct mwServiceStorage *) srvc; | |
295 | |
296 g_return_if_fail(chan != NULL); | |
297 g_return_if_fail(chan == srvc_stor->channel); | |
298 | |
299 /* send all pending requests */ | |
300 for(l = srvc_stor->pending; l; l = l->next) { | |
301 struct mwStorageReq *req = l->data; | |
302 | |
303 if(req->action == action_save || req->action == action_load) { | |
304 request_send(chan, req); | |
305 } | |
306 } | |
307 | |
308 mwService_started(srvc); | |
309 } | |
310 | |
311 | |
312 static void recv_channelDestroy(struct mwService *srvc, | |
313 struct mwChannel *chan, | |
314 struct mwMsgChannelDestroy *msg) { | |
315 | |
316 struct mwSession *session; | |
317 struct mwServiceStorage *srvc_stor; | |
318 | |
319 g_return_if_fail(srvc != NULL); | |
320 g_return_if_fail(chan != NULL); | |
321 | |
322 session = mwService_getSession(srvc); | |
323 g_return_if_fail(session != NULL); | |
324 | |
325 srvc_stor = (struct mwServiceStorage *) srvc; | |
326 srvc_stor->channel = NULL; | |
327 | |
328 mwService_stop(srvc); | |
329 mwSession_senseService(session, mwService_getType(srvc)); | |
330 } | |
331 | |
332 | |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
333 static void recv(struct mwService *srvc, struct mwChannel *chan, |
10969 | 334 guint16 type, struct mwOpaque *data) { |
335 | |
336 /* process into results, trigger callbacks */ | |
337 | |
338 struct mwGetBuffer *b; | |
339 struct mwServiceStorage *srvc_stor; | |
340 struct mwStorageReq *req; | |
341 guint32 id; | |
342 | |
343 g_return_if_fail(srvc != NULL); | |
344 srvc_stor = (struct mwServiceStorage *) srvc; | |
345 | |
346 g_return_if_fail(chan != NULL); | |
347 g_return_if_fail(chan == srvc_stor->channel); | |
348 g_return_if_fail(data != NULL); | |
349 | |
350 b = mwGetBuffer_wrap(data); | |
351 | |
352 id = guint32_peek(b); | |
353 req = request_find(srvc_stor, id); | |
354 | |
355 if(! req) { | |
356 g_warning("couldn't find request 0x%x in storage service", id); | |
357 mwGetBuffer_free(b); | |
358 return; | |
359 } | |
360 | |
361 g_return_if_fail(req->action == type); | |
362 request_get(b, req); | |
363 | |
364 if(mwGetBuffer_error(b)) { | |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
365 mw_mailme_opaque(data, "storage request 0x%x, type: 0x%x", id, type); |
10969 | 366 |
367 } else { | |
368 request_trigger(srvc_stor, req); | |
369 } | |
370 | |
371 mwGetBuffer_free(b); | |
372 request_remove(srvc_stor, req); | |
373 } | |
374 | |
375 | |
376 static void clear(struct mwService *srvc) { | |
377 struct mwServiceStorage *srvc_stor; | |
378 GList *l; | |
379 | |
380 srvc_stor = (struct mwServiceStorage *) srvc; | |
381 | |
382 for(l = srvc_stor->pending; l; l = l->next) | |
383 request_free(l->data); | |
384 | |
385 g_list_free(srvc_stor->pending); | |
386 srvc_stor->pending = NULL; | |
387 | |
388 srvc_stor->id_counter = 0; | |
389 } | |
390 | |
391 | |
392 struct mwServiceStorage *mwServiceStorage_new(struct mwSession *session) { | |
393 struct mwServiceStorage *srvc_store; | |
394 struct mwService *srvc; | |
395 | |
396 srvc_store = g_new0(struct mwServiceStorage, 1); | |
397 srvc = MW_SERVICE(srvc_store); | |
398 | |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
399 mwService_init(srvc, session, mwService_STORAGE); |
10969 | 400 srvc->get_name = get_name; |
401 srvc->get_desc = get_desc; | |
402 srvc->recv_accept = recv_channelAccept; | |
403 srvc->recv_destroy = recv_channelDestroy; | |
11943
0110fc7c6a8a
[gaim-migrate @ 14234]
Christopher O'Brien <siege@pidgin.im>
parents:
11588
diff
changeset
|
404 srvc->recv = recv; |
10969 | 405 srvc->start = start; |
406 srvc->stop = stop; | |
407 srvc->clear = clear; | |
408 | |
409 return srvc_store; | |
410 } | |
411 | |
412 | |
413 struct mwStorageUnit *mwStorageUnit_new(guint32 key) { | |
414 struct mwStorageUnit *u; | |
415 | |
416 u = g_new0(struct mwStorageUnit, 1); | |
417 u->key = key; | |
418 | |
419 return u; | |
420 } | |
421 | |
422 | |
423 struct mwStorageUnit *mwStorageUnit_newOpaque(guint32 key, | |
424 struct mwOpaque *data) { | |
425 struct mwStorageUnit *u; | |
426 | |
427 u = g_new0(struct mwStorageUnit, 1); | |
428 u->key = key; | |
429 | |
430 if(data) | |
431 mwOpaque_clone(&u->data, data); | |
432 | |
433 return u; | |
434 } | |
435 | |
436 | |
437 struct mwStorageUnit *mwStorageUnit_newBoolean(guint32 key, | |
438 gboolean val) { | |
439 | |
440 return mwStorageUnit_newInteger(key, (guint32) val); | |
441 } | |
442 | |
443 | |
444 struct mwStorageUnit *mwStorageUnit_newInteger(guint32 key, | |
445 guint32 val) { | |
446 struct mwStorageUnit *u; | |
447 struct mwPutBuffer *b; | |
448 | |
449 u = g_new0(struct mwStorageUnit, 1); | |
450 u->key = key; | |
451 | |
452 b = mwPutBuffer_new(); | |
453 guint32_put(b, val); | |
454 mwPutBuffer_finalize(&u->data, b); | |
455 | |
456 return u; | |
457 } | |
458 | |
459 | |
460 struct mwStorageUnit *mwStorageUnit_newString(guint32 key, | |
461 const char *str) { | |
462 struct mwStorageUnit *u; | |
463 struct mwPutBuffer *b; | |
464 | |
465 u = g_new0(struct mwStorageUnit, 1); | |
466 u->key = key; | |
467 | |
468 b = mwPutBuffer_new(); | |
469 mwString_put(b, str); | |
470 mwPutBuffer_finalize(&u->data, b); | |
471 | |
472 return u; | |
473 } | |
474 | |
475 | |
476 guint32 mwStorageUnit_getKey(struct mwStorageUnit *item) { | |
477 g_return_val_if_fail(item != NULL, 0x00); /* feh, unsafe */ | |
478 return item->key; | |
479 } | |
480 | |
481 | |
482 gboolean mwStorageUnit_asBoolean(struct mwStorageUnit *item, | |
483 gboolean val) { | |
484 | |
485 return !! mwStorageUnit_asInteger(item, (guint32) val); | |
486 } | |
487 | |
488 | |
489 guint32 mwStorageUnit_asInteger(struct mwStorageUnit *item, | |
490 guint32 val) { | |
491 struct mwGetBuffer *b; | |
492 guint32 v; | |
493 | |
494 g_return_val_if_fail(item != NULL, val); | |
495 | |
496 b = mwGetBuffer_wrap(&item->data); | |
497 | |
498 guint32_get(b, &v); | |
499 if(! mwGetBuffer_error(b)) val = v; | |
500 mwGetBuffer_free(b); | |
501 | |
502 return val; | |
503 } | |
504 | |
505 | |
506 char *mwStorageUnit_asString(struct mwStorageUnit *item) { | |
507 struct mwGetBuffer *b; | |
508 char *c = NULL; | |
509 | |
510 g_return_val_if_fail(item != NULL, NULL); | |
511 | |
512 b = mwGetBuffer_wrap(&item->data); | |
513 | |
514 mwString_get(b, &c); | |
515 | |
516 if(mwGetBuffer_error(b)) | |
517 g_debug("error obtaining string value from opaque"); | |
518 | |
519 mwGetBuffer_free(b); | |
520 | |
521 return c; | |
522 } | |
523 | |
524 | |
525 struct mwOpaque *mwStorageUnit_asOpaque(struct mwStorageUnit *item) { | |
526 g_return_val_if_fail(item != NULL, NULL); | |
527 return &item->data; | |
528 } | |
529 | |
530 | |
531 void mwStorageUnit_free(struct mwStorageUnit *item) { | |
532 if(! item) return; | |
533 | |
534 mwOpaque_clear(&item->data); | |
535 g_free(item); | |
536 } | |
537 | |
538 | |
539 static struct mwStorageReq *request_new(struct mwServiceStorage *srvc, | |
540 struct mwStorageUnit *item, | |
541 mwStorageCallback cb, | |
542 gpointer data, GDestroyNotify df) { | |
543 | |
544 struct mwStorageReq *req = g_new0(struct mwStorageReq, 1); | |
545 | |
546 req->id = ++srvc->id_counter; | |
547 req->item = item; | |
548 req->cb = cb; | |
549 req->data = data; | |
550 req->data_free = df; | |
551 | |
552 return req; | |
553 } | |
554 | |
555 | |
556 void mwServiceStorage_load(struct mwServiceStorage *srvc, | |
557 struct mwStorageUnit *item, | |
558 mwStorageCallback cb, | |
559 gpointer data, GDestroyNotify d_free) { | |
560 | |
561 /* - construct a request | |
562 - put request at end of pending | |
563 - if channel is open and connected | |
564 - compose the load message | |
565 - send message | |
566 - set request to sent | |
567 - else | |
568 - start service | |
569 */ | |
570 | |
571 struct mwStorageReq *req; | |
572 | |
573 req = request_new(srvc, item, cb, data, d_free); | |
574 req->action = action_load; | |
575 | |
576 srvc->pending = g_list_append(srvc->pending, req); | |
577 | |
578 if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) | |
579 request_send(srvc->channel, req); | |
580 } | |
581 | |
582 | |
583 void mwServiceStorage_save(struct mwServiceStorage *srvc, | |
584 struct mwStorageUnit *item, | |
585 mwStorageCallback cb, | |
586 gpointer data, GDestroyNotify d_free) { | |
587 | |
588 /* - construct a request | |
589 - put request at end of pending | |
590 - if channel is open and connected | |
591 - compose the save message | |
592 - send message | |
593 - set request to sent | |
594 - else | |
595 - start service | |
596 */ | |
597 | |
598 struct mwStorageReq *req; | |
599 | |
600 req = request_new(srvc, item, cb, data, d_free); | |
601 req->action = action_save; | |
602 | |
603 srvc->pending = g_list_append(srvc->pending, req); | |
604 | |
605 if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) | |
606 request_send(srvc->channel, req); | |
607 } | |
608 |