Mercurial > pidgin
diff src/protocols/sametime/meanwhile/srvc_store.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 | 03cd74ca2562 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_store.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,608 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <glib/glist.h> + +#include "mw_channel.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_store.h" + + +#define PROTOCOL_TYPE 0x00000025 +#define PROTOCOL_VER 0x00000001 + + +enum storage_action { + action_load = 0x0004, + action_loaded = 0x0005, + action_save = 0x0006, + action_saved = 0x0007, +}; + + +struct mwStorageUnit { + /** key by which data is referenced in service + @see mwStorageKey */ + guint32 key; + + /** Data associated with key in service */ + struct mwOpaque data; +}; + + +struct mwStorageReq { + guint32 id; /**< unique id for this request */ + guint32 result_code; /**< result code for completed request */ + enum storage_action action; /**< load or save */ + struct mwStorageUnit *item; /**< the key/data pair */ + mwStorageCallback cb; /**< callback to notify upon completion */ + gpointer data; /**< user data to pass with callback */ + GDestroyNotify data_free; /**< optionally frees user data */ +}; + + +struct mwServiceStorage { + struct mwService service; + + /** collection of mwStorageReq */ + GList *pending; + + /** current service channel */ + struct mwChannel *channel; + + /** keep track of the counter */ + guint32 id_counter; +}; + + +static void request_get(struct mwGetBuffer *b, struct mwStorageReq *req) { + guint32 id, count, junk; + + if(mwGetBuffer_error(b)) return; + + guint32_get(b, &id); + guint32_get(b, &req->result_code); + + if(req->action == action_loaded) { + guint32_get(b, &count); + + if(count > 0) { + guint32_get(b, &junk); + guint32_get(b, &req->item->key); + + mwOpaque_clear(&req->item->data); + mwOpaque_get(b, &req->item->data); + } + } +} + + +static void request_put(struct mwPutBuffer *b, struct mwStorageReq *req) { + + guint32_put(b, req->id); + guint32_put(b, 1); + + if(req->action == action_save) { + guint32_put(b, 20 + req->item->data.len); /* ugh, offset garbage */ + guint32_put(b, req->item->key); + mwOpaque_put(b, &req->item->data); + + } else { + guint32_put(b, req->item->key); + } +} + + +static int request_send(struct mwChannel *chan, struct mwStorageReq *req) { + struct mwPutBuffer *b; + struct mwOpaque o = { 0, 0 }; + int ret; + + b = mwPutBuffer_new(); + request_put(b, req); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, req->action, &o); + mwOpaque_clear(&o); + + if(! ret) { + if(req->action == action_save) { + req->action = action_saved; + } else if(req->action == action_load) { + req->action = action_loaded; + } + } + + return ret; +} + + +static struct mwStorageReq *request_find(struct mwServiceStorage *srvc, + guint32 id) { + GList *l; + + for(l = srvc->pending; l; l = l->next) { + struct mwStorageReq *r = l->data; + if(r->id == id) return r; + } + + return NULL; +} + + +static const char *action_str(enum storage_action act) { + switch(act) { + case action_load: return "load"; + case action_loaded: return "loaded"; + case action_save: return "save"; + case action_saved: return "saved"; + default: return "UNKNOWN"; + } +} + + +static void request_trigger(struct mwServiceStorage *srvc, + struct mwStorageReq *req) { + + struct mwStorageUnit *item = req->item; + + g_message("storage request %s: key = 0x%x, result = 0x%x, length = %u", + action_str(req->action), + item->key, req->result_code, item->data.len); + + if(req->cb) + req->cb(srvc, req->result_code, item, req->data); +} + + +static void request_free(struct mwStorageReq *req) { + if(req->data_free) { + req->data_free(req->data); + req->data = NULL; + req->data_free = NULL; + } + + mwStorageUnit_free(req->item); + g_free(req); +} + + +static void request_remove(struct mwServiceStorage *srvc, + struct mwStorageReq *req) { + + srvc->pending = g_list_remove_all(srvc->pending, req); + request_free(req); +} + + +static const char *get_name(struct mwService *srvc) { + return "User Storage"; +} + + +static const char *get_desc(struct mwService *srvc) { + return "Stores user data and settings on the server"; +} + + +static struct mwChannel *make_channel(struct mwServiceStorage *srvc) { + struct mwSession *session; + struct mwChannelSet *cs; + struct mwChannel *chan; + + session = mwService_getSession(MW_SERVICE(srvc)); + cs = mwSession_getChannels(session); + chan = mwChannel_newOutgoing(cs); + + mwChannel_setService(chan, MW_SERVICE(srvc)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + return mwChannel_create(chan)? NULL: chan; +} + + +static void start(struct mwService *srvc) { + struct mwServiceStorage *srvc_store; + struct mwChannel *chan; + + g_return_if_fail(srvc != NULL); + srvc_store = (struct mwServiceStorage *) srvc; + + chan = make_channel(srvc_store); + if(chan) { + srvc_store->channel = chan; + } else { + mwService_stopped(srvc); + } +} + + +static void stop(struct mwService *srvc) { + + struct mwServiceStorage *srvc_store; + GList *l; + + g_return_if_fail(srvc != NULL); + srvc_store = (struct mwServiceStorage *) srvc; + + if(srvc_store->channel) { + mwChannel_destroy(srvc_store->channel, ERR_SUCCESS, NULL); + srvc_store->channel = NULL; + } + +#if 1 + /* the new way */ + /* remove pending requests. Sometimes we can crash the storage + service, and when that happens, we end up resending the killer + request over and over again, and the service never stays up */ + for(l = srvc_store->pending; l; l = l->next) + request_free(l->data); + + g_list_free(srvc_store->pending); + srvc_store->pending = NULL; + + srvc_store->id_counter = 0; + +#else + /* the old way */ + /* reset all of the started requests to their unstarted states */ + for(l = srvc_store->pending; l; l = l->next) { + struct mwStorageReq *req = l->data; + + if(req->action == action_loaded) { + req->action = action_load; + } else if(req->action == action_saved) { + req->action = action_save; + } + } +#endif + + mwService_stopped(srvc); +} + + +static void recv_channelAccept(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + struct mwServiceStorage *srvc_stor; + GList *l; + + g_return_if_fail(srvc != NULL); + srvc_stor = (struct mwServiceStorage *) srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc_stor->channel); + + /* send all pending requests */ + for(l = srvc_stor->pending; l; l = l->next) { + struct mwStorageReq *req = l->data; + + if(req->action == action_save || req->action == action_load) { + request_send(chan, req); + } + } + + mwService_started(srvc); +} + + +static void recv_channelDestroy(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwSession *session; + struct mwServiceStorage *srvc_stor; + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + + session = mwService_getSession(srvc); + g_return_if_fail(session != NULL); + + srvc_stor = (struct mwServiceStorage *) srvc; + srvc_stor->channel = NULL; + + mwService_stop(srvc); + mwSession_senseService(session, mwService_getType(srvc)); +} + + +static void recv(struct mwService *srvc, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + /* process into results, trigger callbacks */ + + struct mwGetBuffer *b; + struct mwServiceStorage *srvc_stor; + struct mwStorageReq *req; + guint32 id; + + g_return_if_fail(srvc != NULL); + srvc_stor = (struct mwServiceStorage *) srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc_stor->channel); + g_return_if_fail(data != NULL); + + b = mwGetBuffer_wrap(data); + + id = guint32_peek(b); + req = request_find(srvc_stor, id); + + if(! req) { + g_warning("couldn't find request 0x%x in storage service", id); + mwGetBuffer_free(b); + return; + } + + g_return_if_fail(req->action == type); + request_get(b, req); + + if(mwGetBuffer_error(b)) { + mw_debug_mailme(data, "storage request 0x%x, type: 0x%x", id, type); + + } else { + request_trigger(srvc_stor, req); + } + + mwGetBuffer_free(b); + request_remove(srvc_stor, req); +} + + +static void clear(struct mwService *srvc) { + struct mwServiceStorage *srvc_stor; + GList *l; + + srvc_stor = (struct mwServiceStorage *) srvc; + + for(l = srvc_stor->pending; l; l = l->next) + request_free(l->data); + + g_list_free(srvc_stor->pending); + srvc_stor->pending = NULL; + + srvc_stor->id_counter = 0; +} + + +struct mwServiceStorage *mwServiceStorage_new(struct mwSession *session) { + struct mwServiceStorage *srvc_store; + struct mwService *srvc; + + srvc_store = g_new0(struct mwServiceStorage, 1); + srvc = MW_SERVICE(srvc_store); + + mwService_init(srvc, session, SERVICE_STORAGE); + srvc->get_name = get_name; + srvc->get_desc = get_desc; + srvc->recv_accept = recv_channelAccept; + srvc->recv_destroy = recv_channelDestroy; + srvc->recv = recv; + srvc->start = start; + srvc->stop = stop; + srvc->clear = clear; + + return srvc_store; +} + + +struct mwStorageUnit *mwStorageUnit_new(guint32 key) { + struct mwStorageUnit *u; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newOpaque(guint32 key, + struct mwOpaque *data) { + struct mwStorageUnit *u; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + if(data) + mwOpaque_clone(&u->data, data); + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newBoolean(guint32 key, + gboolean val) { + + return mwStorageUnit_newInteger(key, (guint32) val); +} + + +struct mwStorageUnit *mwStorageUnit_newInteger(guint32 key, + guint32 val) { + struct mwStorageUnit *u; + struct mwPutBuffer *b; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + b = mwPutBuffer_new(); + guint32_put(b, val); + mwPutBuffer_finalize(&u->data, b); + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newString(guint32 key, + const char *str) { + struct mwStorageUnit *u; + struct mwPutBuffer *b; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + b = mwPutBuffer_new(); + mwString_put(b, str); + mwPutBuffer_finalize(&u->data, b); + + return u; +} + + +guint32 mwStorageUnit_getKey(struct mwStorageUnit *item) { + g_return_val_if_fail(item != NULL, 0x00); /* feh, unsafe */ + return item->key; +} + + +gboolean mwStorageUnit_asBoolean(struct mwStorageUnit *item, + gboolean val) { + + return !! mwStorageUnit_asInteger(item, (guint32) val); +} + + +guint32 mwStorageUnit_asInteger(struct mwStorageUnit *item, + guint32 val) { + struct mwGetBuffer *b; + guint32 v; + + g_return_val_if_fail(item != NULL, val); + + b = mwGetBuffer_wrap(&item->data); + + guint32_get(b, &v); + if(! mwGetBuffer_error(b)) val = v; + mwGetBuffer_free(b); + + return val; +} + + +char *mwStorageUnit_asString(struct mwStorageUnit *item) { + struct mwGetBuffer *b; + char *c = NULL; + + g_return_val_if_fail(item != NULL, NULL); + + b = mwGetBuffer_wrap(&item->data); + + mwString_get(b, &c); + + if(mwGetBuffer_error(b)) + g_debug("error obtaining string value from opaque"); + + mwGetBuffer_free(b); + + return c; +} + + +struct mwOpaque *mwStorageUnit_asOpaque(struct mwStorageUnit *item) { + g_return_val_if_fail(item != NULL, NULL); + return &item->data; +} + + +void mwStorageUnit_free(struct mwStorageUnit *item) { + if(! item) return; + + mwOpaque_clear(&item->data); + g_free(item); +} + + +static struct mwStorageReq *request_new(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify df) { + + struct mwStorageReq *req = g_new0(struct mwStorageReq, 1); + + req->id = ++srvc->id_counter; + req->item = item; + req->cb = cb; + req->data = data; + req->data_free = df; + + return req; +} + + +void mwServiceStorage_load(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify d_free) { + + /* - construct a request + - put request at end of pending + - if channel is open and connected + - compose the load message + - send message + - set request to sent + - else + - start service + */ + + struct mwStorageReq *req; + + req = request_new(srvc, item, cb, data, d_free); + req->action = action_load; + + srvc->pending = g_list_append(srvc->pending, req); + + if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) + request_send(srvc->channel, req); +} + + +void mwServiceStorage_save(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify d_free) { + + /* - construct a request + - put request at end of pending + - if channel is open and connected + - compose the save message + - send message + - set request to sent + - else + - start service + */ + + struct mwStorageReq *req; + + req = request_new(srvc, item, cb, data, d_free); + req->action = action_save; + + srvc->pending = g_list_append(srvc->pending, req); + + if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) + request_send(srvc->channel, req); +} +
