Fri, 20 Jan 2006 00:19:53 +0000
[gaim-migrate @ 15309]
Add .cvsignore actions to meanwhile so cvs shuts up about it.
| 12956 | 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/ghash.h> | |
| 22 | ||
| 23 | #include "mw_channel.h" | |
| 24 | #include "mw_common.h" | |
| 25 | #include "mw_debug.h" | |
| 26 | #include "mw_error.h" | |
| 27 | #include "mw_service.h" | |
| 28 | #include "mw_session.h" | |
| 29 | #include "mw_srvc_resolve.h" | |
| 30 | ||
| 31 | ||
| 32 | #define PROTOCOL_TYPE 0x00000015 | |
| 33 | #define PROTOCOL_VER 0x00000000 | |
| 34 | ||
| 35 | ||
| 36 | /** oddly, there is only one message type in this service */ | |
| 37 | #define RESOLVE_ACTION 0x02 | |
| 38 | ||
| 39 | ||
| 40 | struct mwServiceResolve { | |
| 41 | struct mwService service; | |
| 42 | ||
| 43 | struct mwChannel *channel; /**< channel for this service */ | |
| 44 | GHashTable *searches; /**< guint32:struct mw_search */ | |
| 45 | guint32 counter; /**< incremented to provide searche IDs */ | |
| 46 | }; | |
| 47 | ||
| 48 | ||
| 49 | /** structure representing an active search. keeps track of the ID, | |
| 50 | the handler, and the optional user data and cleanup */ | |
| 51 | struct mw_search { | |
| 52 | struct mwServiceResolve *service; | |
| 53 | guint32 id; | |
| 54 | mwResolveHandler handler; | |
| 55 | gpointer data; | |
| 56 | GDestroyNotify cleanup; | |
| 57 | }; | |
| 58 | ||
| 59 | ||
| 60 | static struct mw_search *search_new(struct mwServiceResolve *srvc, | |
| 61 | mwResolveHandler handler, | |
| 62 | gpointer data, GDestroyNotify cleanup) { | |
| 63 | ||
| 64 | struct mw_search *search = g_new0(struct mw_search, 1); | |
| 65 | ||
| 66 | search->service = srvc; | |
| 67 | search->handler = handler; | |
| 68 | ||
| 69 | /* we want search IDs that aren't SEARCH_ERROR */ | |
| 70 | do { | |
| 71 | search->id = srvc->counter++; | |
| 72 | } while(search->id == SEARCH_ERROR); | |
| 73 | ||
| 74 | search->data = data; | |
| 75 | search->cleanup = cleanup; | |
| 76 | ||
| 77 | return search; | |
| 78 | } | |
| 79 | ||
| 80 | ||
| 81 | /** called whenever a mw_search is removed from the searches table of | |
| 82 | the service */ | |
| 83 | static void search_free(struct mw_search *search) { | |
| 84 | g_return_if_fail(search != NULL); | |
| 85 | ||
| 86 | if(search->cleanup) | |
| 87 | search->cleanup(search->data); | |
| 88 | ||
| 89 | g_free(search); | |
| 90 | } | |
| 91 | ||
| 92 | ||
| 93 | static const char *get_name(struct mwService *srvc) { | |
| 94 | return "Identity Resolution"; | |
| 95 | } | |
| 96 | ||
| 97 | ||
| 98 | static const char *get_desc(struct mwService *srvc) { | |
| 99 | return "Resolves short IDs to full IDs"; | |
| 100 | } | |
| 101 | ||
| 102 | ||
| 103 | static struct mwChannel *make_channel(struct mwServiceResolve *srvc) { | |
| 104 | struct mwSession *session; | |
| 105 | struct mwChannelSet *cs; | |
| 106 | struct mwChannel *chan; | |
| 107 | ||
| 108 | session = mwService_getSession(MW_SERVICE(srvc)); | |
| 109 | cs = mwSession_getChannels(session); | |
| 110 | chan = mwChannel_newOutgoing(cs); | |
| 111 | ||
| 112 | mwChannel_setService(chan, MW_SERVICE(srvc)); | |
| 113 | mwChannel_setProtoType(chan, PROTOCOL_TYPE); | |
| 114 | mwChannel_setProtoVer(chan, PROTOCOL_VER); | |
| 115 | ||
| 116 | return mwChannel_create(chan)? NULL: chan; | |
| 117 | } | |
| 118 | ||
| 119 | ||
| 120 | static void start(struct mwServiceResolve *srvc) { | |
| 121 | struct mwChannel *chan; | |
| 122 | ||
| 123 | g_return_if_fail(srvc != NULL); | |
| 124 | ||
| 125 | chan = make_channel(srvc); | |
| 126 | if(chan) { | |
| 127 | srvc->channel = chan; | |
| 128 | } else { | |
| 129 | mwService_stopped(MW_SERVICE(srvc)); | |
| 130 | return; | |
| 131 | } | |
| 132 | ||
| 133 | /* semi-lazily create the searches table */ | |
| 134 | srvc->searches = g_hash_table_new_full(g_direct_hash, g_direct_equal, | |
| 135 | NULL, (GDestroyNotify) search_free); | |
| 136 | } | |
| 137 | ||
| 138 | ||
| 139 | static void stop(struct mwServiceResolve *srvc) { | |
| 140 | g_return_if_fail(srvc != NULL); | |
| 141 | ||
| 142 | if(srvc->channel) { | |
| 143 | mwChannel_destroy(srvc->channel, ERR_SUCCESS, NULL); | |
| 144 | srvc->channel = NULL; | |
| 145 | } | |
| 146 | ||
| 147 | /* destroy all the pending requests. */ | |
| 148 | g_hash_table_destroy(srvc->searches); | |
| 149 | srvc->searches = NULL; | |
| 150 | ||
| 151 | mwService_stopped(MW_SERVICE(srvc)); | |
| 152 | } | |
| 153 | ||
| 154 | ||
| 155 | static void clear(struct mwServiceResolve *srvc) { | |
| 156 | if(srvc->searches) { | |
| 157 | g_hash_table_destroy(srvc->searches); | |
| 158 | srvc->searches = NULL; | |
| 159 | } | |
| 160 | } | |
| 161 | ||
| 162 | ||
| 163 | static void recv_create(struct mwServiceResolve *srvc, | |
| 164 | struct mwChannel *chan, | |
| 165 | struct mwMsgChannelCreate *msg) { | |
| 166 | ||
| 167 | /* you serve me, not the other way around */ | |
| 168 | mwChannel_destroy(chan, ERR_FAILURE, NULL); | |
| 169 | } | |
| 170 | ||
| 171 | ||
| 172 | static void recv_accept(struct mwServiceResolve *srvc, | |
| 173 | struct mwChannel *chan, | |
| 174 | struct mwMsgChannelAccept *msg) { | |
| 175 | ||
| 176 | g_return_if_fail(srvc != NULL); | |
| 177 | g_return_if_fail(chan != NULL); | |
| 178 | g_return_if_fail(chan == srvc->channel); | |
| 179 | ||
| 180 | mwService_started(MW_SERVICE(srvc)); | |
| 181 | } | |
| 182 | ||
| 183 | ||
| 184 | static void recv_destroy(struct mwServiceResolve *srvc, | |
| 185 | struct mwChannel *chan, | |
| 186 | struct mwMsgChannelDestroy *msg) { | |
| 187 | ||
| 188 | struct mwSession *session; | |
| 189 | ||
| 190 | g_return_if_fail(srvc != NULL); | |
| 191 | g_return_if_fail(chan != NULL); | |
| 192 | g_return_if_fail(chan == srvc->channel); | |
| 193 | ||
| 194 | srvc->channel = NULL; | |
| 195 | mwService_stop(MW_SERVICE(srvc)); | |
| 196 | ||
| 197 | session = mwService_getSession(MW_SERVICE(srvc)); | |
| 198 | g_return_if_fail(session != NULL); | |
| 199 | ||
| 200 | mwSession_senseService(session, mwService_getType(MW_SERVICE(srvc))); | |
| 201 | } | |
| 202 | ||
| 203 | ||
| 204 | static GList *load_matches(struct mwGetBuffer *b, guint32 count) { | |
| 205 | GList *matches = NULL; | |
| 206 | ||
| 207 | while(count--) { | |
| 208 | struct mwResolveMatch *m = g_new0(struct mwResolveMatch, 1); | |
| 209 | ||
| 210 | mwString_get(b, &m->id); | |
| 211 | mwString_get(b, &m->name); | |
| 212 | mwString_get(b, &m->desc); | |
| 213 | guint32_get(b, &m->type); | |
| 214 | ||
| 215 | matches = g_list_append(matches, m); | |
| 216 | } | |
| 217 | ||
| 218 | return matches; | |
| 219 | } | |
| 220 | ||
| 221 | ||
| 222 | static GList *load_results(struct mwGetBuffer *b, guint32 count) { | |
| 223 | GList *results = NULL; | |
| 224 | ||
| 225 | while(count--) { | |
| 226 | struct mwResolveResult *r = g_new0(struct mwResolveResult, 1); | |
| 227 | guint32 junk, matches; | |
| 228 | ||
| 229 | guint32_get(b, &junk); | |
| 230 | guint32_get(b, &r->code); | |
| 231 | mwString_get(b, &r->name); | |
| 232 | ||
| 233 | guint32_get(b, &matches); | |
| 234 | r->matches = load_matches(b, matches); | |
| 235 | ||
| 236 | results = g_list_append(results, r); | |
| 237 | } | |
| 238 | ||
| 239 | return results; | |
| 240 | } | |
| 241 | ||
| 242 | ||
| 243 | static void free_matches(GList *matches) { | |
| 244 | for(; matches; matches = g_list_delete_link(matches, matches)) { | |
| 245 | struct mwResolveMatch *m = matches->data; | |
| 246 | g_free(m->id); | |
| 247 | g_free(m->name); | |
| 248 | g_free(m->desc); | |
| 249 | g_free(m); | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | ||
| 254 | static void free_results(GList *results) { | |
| 255 | for(; results; results = g_list_delete_link(results, results)) { | |
| 256 | struct mwResolveResult *r = results->data; | |
| 257 | g_free(r->name); | |
| 258 | free_matches(r->matches); | |
| 259 | g_free(r); | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | ||
| 264 | static void recv(struct mwServiceResolve *srvc, | |
| 265 | struct mwChannel *chan, | |
| 266 | guint16 type, struct mwOpaque *data) { | |
| 267 | ||
| 268 | struct mwGetBuffer *b; | |
| 269 | guint32 junk, id, code, count; | |
| 270 | struct mw_search *search; | |
| 271 | ||
| 272 | g_return_if_fail(srvc != NULL); | |
| 273 | g_return_if_fail(chan != NULL); | |
| 274 | g_return_if_fail(chan == srvc->channel); | |
| 275 | g_return_if_fail(data != NULL); | |
| 276 | ||
| 277 | if(type != RESOLVE_ACTION) { | |
| 278 | mw_mailme_opaque(data, "unknown message in resolve service: 0x%04x", type); | |
| 279 | return; | |
| 280 | } | |
| 281 | ||
| 282 | b = mwGetBuffer_wrap(data); | |
| 283 | guint32_get(b, &junk); | |
| 284 | guint32_get(b, &id); | |
| 285 | guint32_get(b, &code); | |
| 286 | guint32_get(b, &count); | |
| 287 | ||
| 288 | if(mwGetBuffer_error(b)) { | |
| 289 | g_warning("error parsing search result"); | |
| 290 | mwGetBuffer_free(b); | |
| 291 | return; | |
| 292 | } | |
| 293 | ||
| 294 | search = g_hash_table_lookup(srvc->searches, GUINT_TO_POINTER(id)); | |
| 295 | ||
| 296 | if(search) { | |
| 297 | GList *results = load_results(b, count); | |
| 298 | if(mwGetBuffer_error(b)) { | |
| 299 | g_warning("error parsing search results"); | |
| 300 | } else { | |
| 301 | g_debug("triggering handler"); | |
| 302 | search->handler(srvc, id, code, results, search->data); | |
| 303 | } | |
| 304 | free_results(results); | |
| 305 | g_hash_table_remove(srvc->searches, GUINT_TO_POINTER(id)); | |
| 306 | ||
| 307 | } else { | |
| 308 | g_debug("no search found: 0x%x", id); | |
| 309 | } | |
| 310 | ||
| 311 | mwGetBuffer_free(b); | |
| 312 | } | |
| 313 | ||
| 314 | ||
| 315 | struct mwServiceResolve *mwServiceResolve_new(struct mwSession *session) { | |
| 316 | struct mwServiceResolve *srvc_resolve; | |
| 317 | struct mwService *srvc; | |
| 318 | ||
| 319 | g_return_val_if_fail(session != NULL, NULL); | |
| 320 | ||
| 321 | srvc_resolve = g_new0(struct mwServiceResolve, 1); | |
| 322 | ||
| 323 | srvc = MW_SERVICE(srvc_resolve); | |
| 324 | ||
| 325 | mwService_init(srvc, session, mwService_RESOLVE); | |
| 326 | srvc->get_name = get_name; | |
| 327 | srvc->get_desc = get_desc; | |
| 328 | srvc->recv_create = (mwService_funcRecvCreate) recv_create; | |
| 329 | srvc->recv_accept = (mwService_funcRecvAccept) recv_accept; | |
| 330 | srvc->recv_destroy = (mwService_funcRecvDestroy) recv_destroy; | |
| 331 | srvc->recv = (mwService_funcRecv) recv; | |
| 332 | srvc->start = (mwService_funcStart) start; | |
| 333 | srvc->stop = (mwService_funcStop) stop; | |
| 334 | srvc->clear = (mwService_funcClear) clear; | |
| 335 | ||
| 336 | return srvc_resolve; | |
| 337 | } | |
| 338 | ||
| 339 | ||
| 340 | guint32 mwServiceResolve_resolve(struct mwServiceResolve *srvc, | |
| 341 | GList *queries, enum mwResolveFlag flags, | |
| 342 | mwResolveHandler handler, | |
| 343 | gpointer data, GDestroyNotify cleanup) { | |
| 344 | ||
| 345 | struct mw_search *search; | |
| 346 | struct mwPutBuffer *b; | |
| 347 | struct mwOpaque o = { 0, 0 }; | |
| 348 | int ret, count = 0; | |
| 349 | ||
| 350 | g_return_val_if_fail(srvc != NULL, SEARCH_ERROR); | |
| 351 | g_return_val_if_fail(handler != NULL, SEARCH_ERROR); | |
| 352 | ||
| 353 | count = g_list_length(queries); | |
| 354 | g_return_val_if_fail(count > 0, SEARCH_ERROR); | |
| 355 | ||
| 356 | search = search_new(srvc, handler, data, cleanup); | |
| 357 | ||
| 358 | b = mwPutBuffer_new(); | |
| 359 | guint32_put(b, 0x00); /* to be overwritten */ | |
| 360 | guint32_put(b, search->id); | |
| 361 | guint32_put(b, count); | |
| 362 | for(; queries; queries = queries->next) | |
| 363 | mwString_put(b, queries->data); | |
| 364 | guint32_put(b, flags); | |
| 365 | ||
| 366 | mwPutBuffer_finalize(&o, b); | |
| 367 | ||
| 368 | ret = mwChannel_send(srvc->channel, RESOLVE_ACTION, &o); | |
| 369 | if(ret) { | |
| 370 | search_free(search); | |
| 371 | return SEARCH_ERROR; | |
| 372 | ||
| 373 | } else { | |
| 374 | g_hash_table_insert(srvc->searches, | |
| 375 | GUINT_TO_POINTER(search->id), search); | |
| 376 | return search->id; | |
| 377 | } | |
| 378 | } | |
| 379 | ||
| 380 | ||
| 381 | void mwServiceResolve_cancelResolve(struct mwServiceResolve *srvc, | |
| 382 | guint32 id) { | |
| 383 | ||
| 384 | g_return_if_fail(srvc != NULL); | |
| 385 | g_return_if_fail(srvc->searches != NULL); | |
| 386 | ||
| 387 | g_hash_table_remove(srvc->searches, GUINT_TO_POINTER(id)); | |
| 388 | } | |
| 389 |