--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_dir.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,664 @@ + +/* + 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/ghash.h> + +#include "mw_channel.h" +#include "mw_common.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_dir.h" +#include "mw_util.h" + + +#define PROTOCOL_TYPE 0x0000001c +#define PROTOCOL_VER 0x00000005 + + +enum dir_action { + action_list = 0x0000, /**< list address books */ + action_open = 0x0001, /**< open an addressbook as a directory */ + action_close = 0x0002, /**< close a directory */ + action_search = 0x0003, /**< search an open directory */ +}; + + +struct mwServiceDirectory { + struct mwService service; + + struct mwDirectoryHandler *handler; + + struct mwChannel *channel; + + guint32 counter; /**< counter of request IDs */ + GHashTable *requests; /**< map of request ID:directory */ + GHashTable *books; /**< book->name:mwAddressBook */ +}; + + +struct mwAddressBook { + struct mwServiceDirectory *service; + + guint32 id; /**< id or type or something */ + char *name; /**< name of address book */ + GHashTable *dirs; /**< dir->id:mwDirectory */ +}; + + +struct mwDirectory { + struct mwServiceDirectory *service; + struct mwAddressBook *book; + + enum mwDirectoryState state; + + guint32 id; /**< id of directory, assigned by server */ + guint32 search_id; /**< id of current search, from srvc->counter++ */ + + mwSearchHandler handler; + struct mw_datum client_data; +}; + + +#define next_request_id(srvc) ( ++((srvc)->counter) ) + + +static guint32 map_request(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc = dir->service; + guint32 id = next_request_id(srvc); + + dir->search_id = id; + map_guint_insert(srvc->requests, id, dir); + + return id; +} + + +/** called when directory is removed from the service directory map */ +static void dir_free(struct mwDirectory *dir) { + map_guint_remove(dir->service->requests, dir->search_id); + g_free(dir); +} + + +/** remove the directory from the service list and its owning address + book, then frees the directory */ +static void dir_remove(struct mwDirectory *dir) { + struct mwAddressBook *book = dir->book; + map_guint_remove(book->dirs, dir->id); +} + + +__attribute__((used)) +static struct mwDirectory *dir_new(struct mwAddressBook *book, guint32 id) { + struct mwDirectory *dir = g_new0(struct mwDirectory, 1); + dir->service = book->service; + dir->book = book; + dir->id = id; + map_guint_insert(book->dirs, id, dir); + return dir; +} + + +/** called when book is removed from the service book map. Removed all + directories as well */ +static void book_free(struct mwAddressBook *book) { + g_hash_table_destroy(book->dirs); + g_free(book->name); +} + + +__attribute__((used)) +static void book_remove(struct mwAddressBook *book) { + struct mwServiceDirectory *srvc = book->service; + g_hash_table_remove(srvc->books, book->name); +} + + +static struct mwAddressBook *book_new(struct mwServiceDirectory *srvc, + const char *name, guint32 id) { + struct mwAddressBook *book = g_new0(struct mwAddressBook, 1); + book->service = srvc; + book->id = id; + book->name = g_strdup(name); + book->dirs = map_guint_new_full((GDestroyNotify) dir_free); + g_hash_table_insert(srvc->books, book->name, book); + return book; +} + + +static const char *getName(struct mwService *srvc) { + return "Address Book and Directory"; +} + + +static const char *getDesc(struct mwService *srvc) { + return "Address book directory service for user and group lookups"; +} + + +static struct mwChannel *make_channel(struct mwServiceDirectory *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 mwServiceDirectory *srvc) { + struct mwChannel *chan; + + chan = make_channel(srvc); + if(chan) { + srvc->channel = chan; + } else { + mwService_stopped(MW_SERVICE(srvc)); + return; + } +} + + +static void stop(struct mwServiceDirectory *srvc) { + /* XXX */ + + if(srvc->channel) { + mwChannel_destroy(srvc->channel, ERR_SUCCESS, NULL); + srvc->channel = NULL; + } +} + + +static void clear(struct mwServiceDirectory *srvc) { + struct mwDirectoryHandler *handler; + + if(srvc->books) { + g_hash_table_destroy(srvc->books); + srvc->books = NULL; + } + + /* clear the handler */ + handler = srvc->handler; + if(handler && handler->clear) + handler->clear(srvc); + srvc->handler = NULL; +} + + +static void recv_create(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* no way man, we call the shots around here */ + mwChannel_destroy(chan, ERR_FAILURE, NULL); +} + + +static void recv_accept(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + g_return_if_fail(srvc->channel != NULL); + g_return_if_fail(srvc->channel == chan); + + if(MW_SERVICE_IS_STARTING(srvc)) { + mwService_started(MW_SERVICE(srvc)); + + } else { + mwChannel_destroy(chan, ERR_FAILURE, NULL); + } +} + + +static void recv_destroy(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + srvc->channel = NULL; + mwService_stop(MW_SERVICE(srvc)); + /** @todo session sense service */ +} + + +static void recv_list(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + struct mwGetBuffer *b; + guint32 request, code, count; + gboolean foo_1; + guint16 foo_2; + + b = mwGetBuffer_wrap(data); + + guint32_get(b, &request); + guint32_get(b, &code); + guint32_get(b, &count); + + gboolean_get(b, &foo_1); + guint16_get(b, &foo_2); + + if(foo_1 || foo_2) { + mw_debug_mailme(data, "received strange address book list"); + mwGetBuffer_free(b); + return; + } + + while(!mwGetBuffer_error(b) && count--) { + guint32 id; + char *name = NULL; + + guint32_get(b, &id); + mwString_get(b, &name); + + book_new(srvc, name, id); + g_free(name); + } +} + + +static void recv_open(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + /* look up the directory associated with this request id, + mark it as open, and trigger the event */ +} + + +static void recv_search(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + /* look up the directory associated with this request id, + trigger the event */ +} + + +static void recv(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + guint16 msg_type, struct mwOpaque *data) { + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc->channel); + g_return_if_fail(data != NULL); + + switch(msg_type) { + case action_list: + recv_list(srvc, data); + break; + + case action_open: + recv_open(srvc, data); + break; + + case action_close: + ; /* I don't think we should receive these */ + break; + + case action_search: + recv_search(srvc, data); + break; + + default: + mw_debug_mailme(data, "msg type 0x%04x in directory service", msg_type); + } +} + + +struct mwServiceDirectory * +mwServiceDirectory_new(struct mwSession *session, + struct mwDirectoryHandler *handler) { + + struct mwServiceDirectory *srvc; + struct mwService *service; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(handler != NULL, NULL); + + srvc = g_new0(struct mwServiceDirectory, 1); + service = MW_SERVICE(srvc); + + mwService_init(service, session, SERVICE_DIRECTORY); + service->get_name = getName; + service->get_desc = getDesc; + service->start = (mwService_funcStart) start; + service->stop = (mwService_funcStop) stop; + service->clear = (mwService_funcClear) clear; + service->recv_create = (mwService_funcRecvCreate) recv_create; + service->recv_accept = (mwService_funcRecvAccept) recv_accept; + service->recv_destroy = (mwService_funcRecvDestroy) recv_destroy; + service->recv = (mwService_funcRecv) recv; + + srvc->handler = handler; + srvc->requests = map_guint_new(); + srvc->books = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify) book_free); + return srvc; +} + + +struct mwDirectoryHandler * +mwServiceDirectory_getHandler(struct mwServiceDirectory *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +int mwServiceDirectory_refreshAddressBooks(struct mwServiceDirectory *srvc) { + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, next_request_id(srvc)); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_list, &o); + mwOpaque_clear(&o); + + return ret; +} + + +GList *mwServiceDirectory_getAddressBooks(struct mwServiceDirectory *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(srvc->books != NULL, NULL); + + return map_collect_values(srvc->books); +} + + +GList *mwServiceDirectory_getDirectories(struct mwServiceDirectory *srvc) { + GList *bl, *ret = NULL; + + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(srvc->books != NULL, NULL); + + bl = map_collect_values(srvc->books); + for( ; bl; bl = g_list_delete_link(bl, bl)) { + struct mwAddressBook *book = bl->data; + ret = g_list_concat(ret, map_collect_values(book->dirs)); + } + + return ret; +} + + +GList *mwAddressBook_getDirectories(struct mwAddressBook *book) { + g_return_val_if_fail(book != NULL, NULL); + g_return_val_if_fail(book->dirs != NULL, NULL); + + return map_collect_values(book->dirs); +} + + +const char *mwAddressBook_getName(struct mwAddressBook *book) { + g_return_val_if_fail(book != NULL, NULL); + return book->name; +} + + +struct mwDirectory *mwDirectory_new(struct mwAddressBook *book) { + struct mwDirectory *dir; + + g_return_val_if_fail(book != NULL, NULL); + g_return_val_if_fail(book->service != NULL, NULL); + + dir = g_new0(struct mwDirectory, 1); + dir->service = book->service; + dir->book = book; + dir->state = mwDirectory_NEW; + + return dir; +} + + +enum mwDirectoryState mwDirectory_getState(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, mwDirectory_UNKNOWN); + return dir->state; +} + + +void mwDirectory_setClientData(struct mwDirectory *dir, + gpointer data, GDestroyNotify clear) { + + g_return_if_fail(dir != NULL); + mw_datum_set(&dir->client_data, data, clear); +} + + +gpointer mwDirectory_getClientData(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + return mw_datum_get(&dir->client_data); +} + + +void mwDirectory_removeClientData(struct mwDirectory *dir) { + g_return_if_fail(dir != NULL); + mw_datum_clear(&dir->client_data); +} + + +struct mwServiceDirectory *mwDirectory_getService(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + g_return_val_if_fail(dir->book != NULL, NULL); + return dir->book->service; +} + + +struct mwAddressBook *mwDirectory_getAddressBook(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + return dir->book; +} + + +static int dir_open(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + + /* unsure about these three bytes */ + gboolean_put(b, FALSE); + guint16_put(b, 0x0000); + + guint32_put(b, dir->book->id); + mwString_put(b, dir->book->name); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_open, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_open(struct mwDirectory *dir, mwSearchHandler cb) { + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(cb != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_NEW(dir), -1); + + dir->state = mwDirectory_PENDING; + dir->handler = cb; + + return dir_open(dir); +} + + +int mwDirectory_next(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0xffff); /* some magic? */ + guint32_put(b, 0x00000000); /* next results */ + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_previous(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0x0061); /* some magic? */ + guint32_put(b, 0x00000001); /* prev results */ + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_search(struct mwDirectory *dir, const char *query) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + g_return_val_if_fail(query != NULL, -1); + g_return_val_if_fail(*query != '\0', -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0x0061); /* some magic? */ + guint32_put(b, 0x00000008); /* seek results */ + mwString_put(b, query); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int dir_close(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, next_request_id(dir->service)); + guint32_put(b, dir->id); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_close, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_destroy(struct mwDirectory *dir) { + int ret = 0; + + g_return_val_if_fail(dir != NULL, -1); + + if(MW_DIRECTORY_IS_OPEN(dir) || MW_DIRECTORY_IS_PENDING(dir)) { + ret = dir_close(dir); + } + dir_remove(dir); + + return ret; +} +