Mercurial > pidgin
annotate src/protocols/sametime/sametime.c @ 11620:fbc4eeab2227
[gaim-migrate @ 13894]
this lets you leave a highlighted tab by control-tab (forward) or
control-shift-tab (backwards). its not 100% intuitive though, because it
leaves the tab highlighed, which means that in the case of 1 highlighted
tab, the current one, you will leave the tab on the first control-tab,
then immediately return to it on the second one. For this reason, removing
the highlighting of current tabs would be a better permanent solution.
In talking with Tim however, he suggested we do both, on the off chance we
change our minds about the tab highlighting and go back to the
autoswitching.
committer: Tailor Script <tailor@pidgin.im>
| author | Luke Schierer <lschiere@pidgin.im> |
|---|---|
| date | Thu, 06 Oct 2005 15:01:08 +0000 |
| parents | 726264a8f48f |
| children | 3a05b53a589e |
| rev | line source |
|---|---|
| 10977 | 1 |
| 2 /* | |
| 3 Meanwhile Protocol Plugin for Gaim | |
| 4 Adds Lotus Sametime support to Gaim using the Meanwhile library | |
| 5 | |
| 6 Copyright (C) 2004 Christopher (siege) O'Brien <siege@preoccupied.net> | |
| 7 | |
| 8 This program is free software; you can redistribute it and/or modify | |
| 9 it under the terms of the GNU General Public License as published by | |
| 10 the Free Software Foundation; either version 2 of the License, or (at | |
| 11 your option) any later version. | |
| 12 | |
| 13 This program is distributed in the hope that it will be useful, but | |
| 14 WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 16 General Public License for more details. | |
| 17 | |
| 18 You should have received a copy of the GNU General Public License | |
| 19 along with this program; if not, write to the Free Software | |
| 20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
| 21 USA. | |
| 22 */ | |
| 23 | |
| 24 #include <stdlib.h> | |
| 25 | |
| 26 #include <internal.h> | |
| 27 #include <gaim.h> | |
| 28 #include <config.h> | |
| 29 | |
| 30 #include <account.h> | |
| 31 #include <accountopt.h> | |
| 32 #include <conversation.h> | |
| 33 #include <debug.h> | |
| 34 #include <ft.h> | |
| 35 #include <imgstore.h> | |
| 36 #include <mime.h> | |
| 37 #include <notify.h> | |
| 38 #include <plugin.h> | |
| 39 #include <privacy.h> | |
| 40 #include <prpl.h> | |
| 41 #include <request.h> | |
| 42 #include <util.h> | |
| 43 #include <version.h> | |
| 44 | |
| 45 #include <glib.h> | |
| 46 #include <glib/ghash.h> | |
| 47 #include <glib/glist.h> | |
| 48 | |
| 49 #include <mw_cipher.h> | |
| 50 #include <mw_common.h> | |
| 51 #include <mw_error.h> | |
| 52 #include <mw_service.h> | |
| 53 #include <mw_session.h> | |
| 54 #include <mw_srvc_aware.h> | |
| 55 #include <mw_srvc_conf.h> | |
| 56 #include <mw_srvc_ft.h> | |
| 57 #include <mw_srvc_im.h> | |
| 58 #include <mw_srvc_resolve.h> | |
| 59 #include <mw_srvc_store.h> | |
| 60 #include <mw_st_list.h> | |
| 61 | |
| 62 #include "sametime.h" | |
| 63 | |
| 64 | |
| 65 /* considering that there's no display of this information for prpls, | |
| 66 I don't know why I even bother providing these. Oh valiant reader, | |
| 67 I do it all for you. */ | |
| 68 /* scratch that, I just added it to the prpl options panel */ | |
| 69 #define PLUGIN_ID "prpl-meanwhile" | |
| 70 #define PLUGIN_NAME "Sametime" | |
| 71 #define PLUGIN_SUMMARY "Sametime Protocol Plugin" | |
| 72 #define PLUGIN_DESC "Open implementation of a Lotus Sametime client" | |
| 73 #define PLUGIN_AUTHOR "Christopher (siege) O'Brien <siege@preoccupied.net>" | |
| 74 #define PLUGIN_HOMEPAGE "http://meanwhile.sourceforge.net/" | |
| 75 | |
| 76 | |
| 77 /* plugin preference names */ | |
| 78 #define MW_PRPL_OPT_BASE "/plugins/prpl/meanwhile" | |
| 79 #define MW_PRPL_OPT_BLIST_ACTION MW_PRPL_OPT_BASE "/blist_action" | |
| 80 #define MW_PRPL_OPT_PSYCHIC MW_PRPL_OPT_BASE "/psychic" | |
| 81 #define MW_PRPL_OPT_FORCE_LOGIN MW_PRPL_OPT_BASE "/force_login" | |
| 82 #define MW_PRPL_OPT_SAVE_DYNAMIC MW_PRPL_OPT_BASE "/save_dynamic" | |
| 83 | |
| 84 | |
| 85 /* stages of connecting-ness */ | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
86 #define MW_CONNECT_STEPS 10 |
| 10977 | 87 |
| 88 | |
| 89 /* stages of conciousness */ | |
| 90 #define MW_STATE_OFFLINE "offline" | |
| 91 #define MW_STATE_ACTIVE "available" | |
| 92 #define MW_STATE_AWAY "away" | |
| 93 #define MW_STATE_BUSY "busy" | |
| 94 #define MW_STATE_IDLE "idle" | |
| 95 #define MW_STATE_UNKNOWN "unknown" | |
| 96 #define MW_STATE_BUDHA "enlightened" | |
| 97 | |
| 98 #define MW_STATE_MESSAGE "message" | |
| 99 | |
| 100 | |
| 101 /* keys to get/set chat information */ | |
| 102 #define CHAT_KEY_CREATOR "chat.creator" | |
| 103 #define CHAT_KEY_NAME "chat.name" | |
| 104 #define CHAT_KEY_TOPIC "chat.topic" | |
| 105 #define CHAT_KEY_INVITE "chat.invite" | |
| 106 | |
| 107 | |
| 108 /* key for associating a mwLoginType with a buddy */ | |
| 109 #define BUDDY_KEY_CLIENT "meanwhile.client" | |
| 110 | |
| 111 /* store the remote alias so that we can re-create it easily */ | |
| 112 #define BUDDY_KEY_NAME "meanwhile.shortname" | |
| 113 | |
| 114 /* enum mwSametimeUserType */ | |
| 115 #define BUDDY_KEY_TYPE "meanwhile.type" | |
| 116 | |
| 117 | |
| 118 /* key for the real group name for a meanwhile group */ | |
| 119 #define GROUP_KEY_NAME "meanwhile.group" | |
| 120 | |
| 121 /* enum mwSametimeGroupType */ | |
| 122 #define GROUP_KEY_TYPE "meanwhile.type" | |
| 123 | |
| 124 /* NAB group owning account */ | |
| 125 #define GROUP_KEY_OWNER "meanwhile.account" | |
| 126 | |
| 127 /* key gtk blist uses to indicate a collapsed group */ | |
| 128 #define GROUP_KEY_COLLAPSED "collapsed" | |
| 129 | |
| 130 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
131 /* verification replacement */ |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
132 #define mwSession_NO_SECRET "meanwhile.no_secret" |
| 10977 | 133 |
| 134 | |
| 135 /* keys to get/set gaim plugin information */ | |
| 136 #define MW_KEY_HOST "server" | |
| 137 #define MW_KEY_PORT "port" | |
| 138 #define MW_KEY_ACTIVE_MSG "active_msg" | |
| 139 #define MW_KEY_AWAY_MSG "away_msg" | |
| 140 #define MW_KEY_BUSY_MSG "busy_msg" | |
| 141 #define MW_KEY_MSG_PROMPT "msg_prompt" | |
| 142 #define MW_KEY_INVITE "conf_invite" | |
| 143 | |
| 144 | |
| 145 /** number of seconds from the first blist change before a save to the | |
| 146 storage service occurs. */ | |
| 147 #define BLIST_SAVE_SECONDS 15 | |
| 148 | |
| 149 | |
| 150 /** blist storage option, local only */ | |
| 151 #define BLIST_CHOICE_NONE 1 | |
| 152 | |
| 153 /** blist storage option, load from server */ | |
| 154 #define BLIST_CHOICE_LOAD 2 | |
| 155 | |
| 156 /** blist storage option, load and save to server */ | |
| 157 #define BLIST_CHOICE_SAVE 3 | |
| 158 | |
| 159 /** blist storage option, server only */ | |
| 160 #define BLIST_CHOICE_SERVER 4 | |
| 161 | |
| 162 | |
| 163 /** the default blist storage option */ | |
| 164 #define BLIST_CHOICE_DEFAULT BLIST_CHOICE_SAVE | |
| 165 | |
| 166 | |
| 167 /* testing for the above */ | |
| 168 #define BLIST_CHOICE_IS(n) (gaim_prefs_get_int(MW_PRPL_OPT_BLIST_ACTION)==(n)) | |
| 169 #define BLIST_CHOICE_IS_NONE() BLIST_CHOICE_IS(BLIST_CHOICE_NONE) | |
| 170 #define BLIST_CHOICE_IS_LOAD() BLIST_CHOICE_IS(BLIST_CHOICE_LOAD) | |
| 171 #define BLIST_CHOICE_IS_SAVE() BLIST_CHOICE_IS(BLIST_CHOICE_SAVE) | |
| 172 | |
| 173 | |
| 174 /* debugging output */ | |
| 175 #define DEBUG_ERROR(a...) gaim_debug_error(G_LOG_DOMAIN, a) | |
| 176 #define DEBUG_INFO(a...) gaim_debug_info(G_LOG_DOMAIN, a) | |
| 177 #define DEBUG_MISC(a...) gaim_debug_misc(G_LOG_DOMAIN, a) | |
| 178 #define DEBUG_WARN(a...) gaim_debug_warning(G_LOG_DOMAIN, a) | |
| 179 | |
| 180 | |
| 181 /** ensure non-null strings */ | |
| 182 #ifndef NSTR | |
| 183 # define NSTR(str) ((str)? (str): "(null)") | |
| 184 #endif | |
| 185 | |
| 186 | |
| 187 /** calibrates distinct secure channel nomenclature */ | |
| 188 static const unsigned char no_secret[] = { | |
| 189 0x2d, 0x2d, 0x20, 0x73, 0x69, 0x65, 0x67, 0x65, | |
| 190 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x6a, | |
| 191 0x65, 0x6e, 0x6e, 0x69, 0x20, 0x61, 0x6e, 0x64, | |
| 192 0x20, 0x7a, 0x6f, 0x65, 0x20, 0x2d, 0x2d, 0x00, | |
| 193 }; | |
| 194 | |
| 195 | |
| 196 /** handler IDs from g_log_set_handler in mw_plugin_init */ | |
| 197 static guint log_handler[2] = { 0, 0 }; | |
| 198 | |
| 199 | |
| 200 /** the gaim plugin data. | |
| 201 available as gc->proto_data and mwSession_getClientData */ | |
| 202 struct mwGaimPluginData { | |
| 203 struct mwSession *session; | |
| 204 | |
| 205 struct mwServiceAware *srvc_aware; | |
| 206 struct mwServiceConference *srvc_conf; | |
| 207 struct mwServiceFileTransfer *srvc_ft; | |
| 208 struct mwServiceIm *srvc_im; | |
| 209 struct mwServiceResolve *srvc_resolve; | |
| 210 struct mwServiceStorage *srvc_store; | |
| 211 | |
| 212 /** map of GaimGroup:mwAwareList and mwAwareList:GaimGroup */ | |
| 213 GHashTable *group_list_map; | |
| 214 | |
| 215 /** event id for the buddy list save callback */ | |
| 216 guint save_event; | |
| 217 | |
| 218 /** socket fd */ | |
| 219 int socket; | |
| 220 | |
| 221 GaimConnection *gc; | |
| 222 }; | |
| 223 | |
| 224 | |
| 225 /* blist and aware functions */ | |
| 226 | |
| 227 static void blist_export(GaimConnection *gc, struct mwSametimeList *stlist); | |
| 228 | |
| 229 static void blist_store(struct mwGaimPluginData *pd); | |
| 230 | |
| 231 static void blist_schedule(struct mwGaimPluginData *pd); | |
| 232 | |
| 233 static void blist_import(GaimConnection *gc, struct mwSametimeList *stlist); | |
| 234 | |
| 235 static void buddy_add(struct mwGaimPluginData *pd, GaimBuddy *buddy); | |
| 236 | |
| 237 static GaimBuddy * | |
| 238 buddy_ensure(GaimConnection *gc, GaimGroup *group, | |
| 239 struct mwSametimeUser *stuser); | |
| 240 | |
| 241 static void group_add(struct mwGaimPluginData *pd, GaimGroup *group); | |
| 242 | |
| 243 static GaimGroup * | |
| 244 group_ensure(GaimConnection *gc, struct mwSametimeGroup *stgroup); | |
| 245 | |
| 246 static struct mwAwareList * | |
| 247 list_ensure(struct mwGaimPluginData *pd, GaimGroup *group); | |
| 248 | |
| 249 | |
| 250 /* session functions */ | |
| 251 | |
| 252 static struct mwSession * | |
| 253 gc_to_session(GaimConnection *gc); | |
| 254 | |
| 255 static GaimConnection *session_to_gc(struct mwSession *session); | |
| 256 | |
| 257 | |
| 258 /* conference functions */ | |
| 259 | |
| 260 static struct mwConference * | |
| 261 conf_find_by_id(struct mwGaimPluginData *pd, int id); | |
| 262 | |
| 263 | |
| 264 /* conversation functions */ | |
| 265 | |
| 266 struct convo_msg { | |
| 267 enum mwImSendType type; | |
| 268 gpointer data; | |
| 269 GDestroyNotify clear; | |
| 270 }; | |
| 271 | |
| 272 | |
| 273 struct convo_data { | |
| 274 struct mwConversation *conv; | |
| 275 GList *queue; /**< outgoing message queue, list of convo_msg */ | |
| 276 }; | |
| 277 | |
| 278 static void convo_data_new(struct mwConversation *conv); | |
| 279 | |
| 280 static void convo_data_free(struct convo_data *conv); | |
| 281 | |
| 282 static void convo_features(struct mwConversation *conv); | |
| 283 | |
| 284 static GaimConversation *convo_get_gconv(struct mwConversation *conv); | |
| 285 | |
| 286 | |
| 287 /* resolved id */ | |
| 288 | |
| 289 struct resolved_id { | |
| 290 char *id; | |
| 291 char *name; | |
| 292 }; | |
| 293 | |
| 294 | |
| 295 /* ----- session ------ */ | |
| 296 | |
| 297 | |
| 298 /** resolves a mwSession from a GaimConnection */ | |
| 299 static struct mwSession *gc_to_session(GaimConnection *gc) { | |
| 300 struct mwGaimPluginData *pd; | |
| 301 | |
| 302 g_return_val_if_fail(gc != NULL, NULL); | |
| 303 | |
| 304 pd = gc->proto_data; | |
| 305 g_return_val_if_fail(pd != NULL, NULL); | |
| 306 | |
| 307 return pd->session; | |
| 308 } | |
| 309 | |
| 310 | |
| 311 /** resolves a GaimConnection from a mwSession */ | |
| 312 static GaimConnection *session_to_gc(struct mwSession *session) { | |
| 313 struct mwGaimPluginData *pd; | |
| 314 | |
| 315 g_return_val_if_fail(session != NULL, NULL); | |
| 316 | |
| 317 pd = mwSession_getClientData(session); | |
| 318 g_return_val_if_fail(pd != NULL, NULL); | |
| 319 | |
| 320 return pd->gc; | |
| 321 } | |
| 322 | |
| 323 | |
| 324 static int mw_session_io_write(struct mwSession *session, | |
| 325 const char *buf, gsize len) { | |
| 326 struct mwGaimPluginData *pd; | |
| 327 int ret = 0; | |
| 328 | |
| 329 pd = mwSession_getClientData(session); | |
| 330 | |
| 331 /* socket was already closed. */ | |
| 332 if(pd->socket == 0) | |
| 333 return 1; | |
| 334 | |
| 335 while(len) { | |
| 336 ret = write(pd->socket, buf, len); | |
| 337 if(ret <= 0) break; | |
| 338 len -= ret; | |
| 339 } | |
| 340 | |
| 341 if(len > 0) { | |
| 342 DEBUG_ERROR("write returned %i, %i bytes left unwritten\n", ret, len); | |
| 343 gaim_connection_error(pd->gc, "Connection closed (writing)"); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
344 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
345 #if 0 |
| 10977 | 346 close(pd->socket); |
| 347 pd->socket = 0; | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
348 #endif |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
349 |
| 10977 | 350 return -1; |
| 351 } | |
| 352 | |
| 353 return 0; | |
| 354 } | |
| 355 | |
| 356 | |
| 357 static void mw_session_io_close(struct mwSession *session) { | |
| 358 struct mwGaimPluginData *pd; | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
359 GaimConnection *gc; |
| 10977 | 360 |
| 361 pd = mwSession_getClientData(session); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
362 g_return_if_fail(pd != NULL); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
363 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
364 gc = pd->gc; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
365 |
| 10977 | 366 if(pd->socket) { |
| 367 close(pd->socket); | |
| 368 pd->socket = 0; | |
| 369 } | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
370 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
371 if(gc->inpa) { |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
372 gaim_input_remove(gc->inpa); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
373 gc->inpa = 0; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
374 } |
| 10977 | 375 } |
| 376 | |
| 377 | |
| 378 static void mw_session_clear(struct mwSession *session) { | |
| 379 ; /* nothing for now */ | |
| 380 } | |
| 381 | |
| 382 | |
| 383 /* ----- aware list ----- */ | |
| 384 | |
| 385 | |
| 386 static void blist_resolve_alias_cb(struct mwServiceResolve *srvc, | |
| 387 guint32 id, guint32 code, GList *results, | |
| 388 gpointer data) { | |
| 389 struct mwResolveResult *result; | |
| 390 struct mwResolveMatch *match; | |
| 391 | |
| 392 g_return_if_fail(results != NULL); | |
| 393 | |
| 394 result = results->data; | |
| 395 g_return_if_fail(result != NULL); | |
| 396 g_return_if_fail(result->matches != NULL); | |
| 397 | |
| 398 match = result->matches->data; | |
| 399 g_return_if_fail(match != NULL); | |
| 400 | |
| 401 gaim_blist_server_alias_buddy(data, match->name); | |
| 402 gaim_blist_node_set_string(data, BUDDY_KEY_NAME, match->name); | |
| 403 } | |
| 404 | |
| 405 | |
| 406 static void mw_aware_list_on_aware(struct mwAwareList *list, | |
| 407 struct mwAwareSnapshot *aware) { | |
| 408 | |
| 409 GaimConnection *gc; | |
| 410 struct mwGaimPluginData *pd; | |
| 411 | |
| 412 time_t idle = 0; | |
| 413 guint stat = aware->status.status; | |
| 414 | |
| 415 const char *id = aware->id.user; | |
| 416 | |
| 417 gc = mwAwareList_getClientData(list); | |
| 418 pd = gc->proto_data; | |
| 419 | |
| 420 switch(stat) { | |
| 421 case mwStatus_IDLE: | |
| 422 idle = -1; | |
| 423 break; | |
| 424 | |
| 425 case mwStatus_AWAY: | |
| 426 case mwStatus_BUSY: | |
| 427 /* need to let gaim know that these are 'unavailable' states */ | |
| 428 | |
| 429 /* XXX */ | |
| 430 /* stat |= UC_UNAVAILABLE; */ | |
| 431 | |
| 432 break; | |
| 433 } | |
| 434 | |
| 435 if(aware->group) { | |
| 436 GaimAccount *acct; | |
| 437 GaimGroup *group; | |
| 438 GaimBuddy *buddy; | |
| 439 GaimBlistNode *bnode; | |
| 440 | |
| 441 acct = gaim_connection_get_account(gc); | |
| 442 group = g_hash_table_lookup(pd->group_list_map, list); | |
| 443 buddy = gaim_find_buddy_in_group(acct, id, group); | |
| 444 bnode = (GaimBlistNode *) buddy; | |
| 445 | |
| 446 if(! buddy) { | |
| 447 struct mwServiceResolve *srvc; | |
| 448 GList *query; | |
| 449 | |
| 450 buddy = gaim_buddy_new(acct, id, NULL); | |
| 451 gaim_blist_add_buddy(buddy, NULL, group, NULL); | |
| 452 | |
| 453 bnode = (GaimBlistNode *) buddy; | |
| 454 | |
| 455 /* mark buddy as transient if preferences do not indicate that | |
| 456 we should save the buddy between gaim sessions */ | |
| 457 if(! gaim_prefs_get_bool(MW_PRPL_OPT_SAVE_DYNAMIC)) | |
| 458 bnode->flags |= GAIM_BLIST_NODE_FLAG_NO_SAVE; | |
| 459 | |
| 460 srvc = pd->srvc_resolve; | |
| 461 query = g_list_append(NULL, (char *) id); | |
| 462 | |
| 463 mwServiceResolve_resolve(srvc, query, mwResolveFlag_USERS, | |
| 464 blist_resolve_alias_cb, buddy, NULL); | |
| 465 } | |
| 466 | |
| 467 gaim_blist_node_set_int(bnode, BUDDY_KEY_TYPE, mwSametimeUser_NORMAL); | |
| 468 } | |
| 469 | |
| 470 /* XXX */ | |
| 471 /* serv_got_update(gc, id, aware->online, 0, 0, idle, stat); */ | |
| 472 } | |
| 473 | |
| 474 | |
| 475 static void mw_aware_list_on_attrib(struct mwAwareList *list, | |
| 476 struct mwAwareIdBlock *id, | |
| 477 struct mwAwareAttribute *attrib) { | |
| 478 | |
| 479 ; /* nothing. We'll get attribute data as we need it */ | |
| 480 } | |
| 481 | |
| 482 | |
| 483 static void mw_aware_list_clear(struct mwAwareList *list) { | |
| 484 ; /* nothing for now */ | |
| 485 } | |
| 486 | |
| 487 | |
| 488 static struct mwAwareListHandler mw_aware_list_handler = { | |
| 489 .on_aware = mw_aware_list_on_aware, | |
| 490 .on_attrib = mw_aware_list_on_attrib, | |
| 491 .clear = mw_aware_list_clear, | |
| 492 }; | |
| 493 | |
| 494 | |
| 495 /** Ensures that an Aware List is associated with the given group, and | |
| 496 returns that list. */ | |
| 497 static struct mwAwareList * | |
| 498 list_ensure(struct mwGaimPluginData *pd, GaimGroup *group) { | |
| 499 | |
| 500 struct mwAwareList *list; | |
| 501 | |
| 502 g_return_val_if_fail(pd != NULL, NULL); | |
| 503 g_return_val_if_fail(group != NULL, NULL); | |
| 504 | |
| 505 list = g_hash_table_lookup(pd->group_list_map, group); | |
| 506 if(! list) { | |
| 507 list = mwAwareList_new(pd->srvc_aware, &mw_aware_list_handler); | |
| 508 mwAwareList_setClientData(list, pd->gc, NULL); | |
| 509 | |
| 510 mwAwareList_watchAttributes(list, | |
| 511 mwAttribute_AV_PREFS_SET, | |
| 512 mwAttribute_MICROPHONE, | |
| 513 mwAttribute_SPEAKERS, | |
| 514 mwAttribute_VIDEO_CAMERA, | |
| 515 mwAttribute_FILE_TRANSFER, | |
| 516 NULL); | |
| 517 | |
| 518 g_hash_table_replace(pd->group_list_map, group, list); | |
| 519 g_hash_table_insert(pd->group_list_map, list, group); | |
| 520 } | |
| 521 | |
| 522 return list; | |
| 523 } | |
| 524 | |
| 525 | |
| 526 static void blist_export(GaimConnection *gc, struct mwSametimeList *stlist) { | |
| 527 /* - find the account for this connection | |
| 528 - iterate through the buddy list | |
| 529 - add each buddy matching this account to the stlist | |
| 530 */ | |
| 531 | |
| 532 GaimAccount *acct; | |
| 533 GaimBuddyList *blist; | |
| 534 GaimBlistNode *gn, *cn, *bn; | |
| 535 GaimGroup *grp; | |
| 536 GaimBuddy *bdy; | |
| 537 | |
| 538 struct mwSametimeGroup *stg = NULL; | |
| 539 struct mwIdBlock idb = { NULL, NULL }; | |
| 540 | |
| 541 acct = gaim_connection_get_account(gc); | |
| 542 g_return_if_fail(acct != NULL); | |
| 543 | |
| 544 blist = gaim_get_blist(); | |
| 545 g_return_if_fail(blist != NULL); | |
| 546 | |
| 547 for(gn = blist->root; gn; gn = gn->next) { | |
| 548 const char *owner; | |
| 549 const char *gname; | |
| 550 enum mwSametimeGroupType gtype; | |
| 551 gboolean gopen; | |
| 552 | |
| 553 if(! GAIM_BLIST_NODE_IS_GROUP(gn)) continue; | |
| 554 grp = (GaimGroup *) gn; | |
| 555 | |
| 556 /* the group's type (normal or dynamic) */ | |
| 557 gtype = gaim_blist_node_get_int(gn, GROUP_KEY_TYPE); | |
| 558 if(! gtype) gtype = mwSametimeGroup_NORMAL; | |
| 559 | |
| 560 /* if it's a normal group with none of our people in it, skip it */ | |
| 561 if(gtype == mwSametimeGroup_NORMAL && !gaim_group_on_account(grp, acct)) | |
| 562 continue; | |
| 563 | |
| 564 /* if the group has an owner and we're not it, skip it */ | |
| 565 owner = gaim_blist_node_get_string(gn, GROUP_KEY_OWNER); | |
| 566 if(owner && strcmp(owner, gaim_account_get_username(acct))) | |
| 567 continue; | |
| 568 | |
| 569 /* the group's actual name may be different from the gaim group's | |
| 570 name. Find whichever is there */ | |
| 571 gname = gaim_blist_node_get_string(gn, GROUP_KEY_NAME); | |
| 572 if(! gname) gname = grp->name; | |
| 573 | |
| 574 /* we save this, but never actually honor it */ | |
| 575 gopen = ! gaim_blist_node_get_bool(gn, GROUP_KEY_COLLAPSED); | |
| 576 | |
| 577 stg = mwSametimeGroup_new(stlist, gtype, gname); | |
| 578 mwSametimeGroup_setAlias(stg, grp->name); | |
| 579 mwSametimeGroup_setOpen(stg, gopen); | |
| 580 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
581 /* don't attempt to put buddies in a dynamic group, it breaks |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
582 other clients */ |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
583 if(gtype == mwSametimeGroup_DYNAMIC) |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
584 continue; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
585 |
| 10977 | 586 for(cn = gn->child; cn; cn = cn->next) { |
| 587 if(! GAIM_BLIST_NODE_IS_CONTACT(cn)) continue; | |
| 588 | |
| 589 for(bn = cn->child; bn; bn = bn->next) { | |
| 590 if(! GAIM_BLIST_NODE_IS_BUDDY(bn)) continue; | |
| 591 if(! GAIM_BLIST_NODE_SHOULD_SAVE(bn)) continue; | |
| 592 | |
| 593 bdy = (GaimBuddy *) bn; | |
| 594 | |
| 595 if(bdy->account == acct) { | |
| 596 struct mwSametimeUser *stu; | |
| 597 enum mwSametimeUserType utype; | |
| 598 | |
| 599 idb.user = bdy->name; | |
| 600 | |
| 601 utype = gaim_blist_node_get_int(bn, BUDDY_KEY_TYPE); | |
| 602 if(! utype) utype = mwSametimeUser_NORMAL; | |
| 603 | |
| 604 stu = mwSametimeUser_new(stg, utype, &idb); | |
| 605 mwSametimeUser_setShortName(stu, bdy->server_alias); | |
| 606 mwSametimeUser_setAlias(stu, bdy->alias); | |
| 607 } | |
| 608 } | |
| 609 } | |
| 610 } | |
| 611 } | |
| 612 | |
| 613 | |
| 614 static void blist_store(struct mwGaimPluginData *pd) { | |
| 615 | |
| 616 struct mwSametimeList *stlist; | |
| 617 struct mwServiceStorage *srvc; | |
| 618 struct mwStorageUnit *unit; | |
| 619 | |
| 620 GaimConnection *gc; | |
| 621 | |
| 622 struct mwPutBuffer *b; | |
| 623 struct mwOpaque *o; | |
| 624 | |
| 625 g_return_if_fail(pd != NULL); | |
| 626 | |
| 627 srvc = pd->srvc_store; | |
| 628 g_return_if_fail(srvc != NULL); | |
| 629 | |
| 630 gc = pd->gc; | |
| 631 | |
| 632 /* check if we should do this, according to user prefs */ | |
| 633 if(! BLIST_CHOICE_IS_SAVE()) { | |
| 634 DEBUG_INFO("preferences indicate not to save remote blist\n"); | |
| 635 return; | |
| 636 | |
| 637 } else if(MW_SERVICE_IS_DEAD(srvc)) { | |
| 638 DEBUG_INFO("aborting save of blist: storage service is not alive\n"); | |
| 639 return; | |
| 640 | |
| 641 } else { | |
| 642 DEBUG_INFO("saving remote blist\n"); | |
| 643 } | |
| 644 | |
| 645 /* create and export to a list object */ | |
| 646 stlist = mwSametimeList_new(); | |
| 647 blist_export(gc, stlist); | |
| 648 | |
| 649 /* write it to a buffer */ | |
| 650 b = mwPutBuffer_new(); | |
| 651 mwSametimeList_put(b, stlist); | |
| 652 mwSametimeList_free(stlist); | |
| 653 | |
| 654 /* put the buffer contents into a storage unit */ | |
| 655 unit = mwStorageUnit_new(mwStore_AWARE_LIST); | |
| 656 o = mwStorageUnit_asOpaque(unit); | |
| 657 mwPutBuffer_finalize(o, b); | |
| 658 | |
| 659 /* save the storage unit to the service */ | |
| 660 mwServiceStorage_save(srvc, unit, NULL, NULL, NULL); | |
| 661 } | |
| 662 | |
| 663 | |
| 664 static gboolean blist_save_cb(gpointer data) { | |
| 665 struct mwGaimPluginData *pd = data; | |
| 666 | |
| 667 blist_store(pd); | |
| 668 pd->save_event = 0; | |
| 669 return FALSE; | |
| 670 } | |
| 671 | |
| 672 | |
| 673 /** schedules the buddy list to be saved to the server */ | |
| 674 static void blist_schedule(struct mwGaimPluginData *pd) { | |
| 675 if(pd->save_event) return; | |
| 676 | |
| 677 pd->save_event = gaim_timeout_add(BLIST_SAVE_SECONDS * 1000, | |
| 678 blist_save_cb, pd); | |
| 679 } | |
| 680 | |
| 681 | |
| 682 /** Actually add a buddy to the aware service, and schedule the buddy | |
| 683 list to be saved to the server */ | |
| 684 static void buddy_add(struct mwGaimPluginData *pd, | |
| 685 GaimBuddy *buddy) { | |
| 686 | |
| 687 struct mwAwareIdBlock idb = { mwAware_USER, (char *) buddy->name, NULL }; | |
| 688 struct mwAwareList *list; | |
| 689 | |
| 690 GaimGroup *group; | |
| 691 GList *add; | |
| 692 | |
| 693 add = g_list_prepend(NULL, &idb); | |
| 694 | |
| 695 group = gaim_find_buddys_group(buddy); | |
| 696 list = list_ensure(pd, group); | |
| 697 | |
| 698 if(mwAwareList_addAware(list, add)) { | |
| 699 gaim_blist_remove_buddy(buddy); | |
| 700 } | |
| 701 | |
| 702 blist_schedule(pd); | |
| 703 | |
| 704 g_list_free(add); | |
| 705 } | |
| 706 | |
| 707 | |
| 708 /** ensure that a GaimBuddy exists in the group with data | |
| 709 appropriately matching the st user entry from the st list */ | |
| 710 static GaimBuddy *buddy_ensure(GaimConnection *gc, GaimGroup *group, | |
| 711 struct mwSametimeUser *stuser) { | |
| 712 | |
| 713 struct mwGaimPluginData *pd = gc->proto_data; | |
| 714 GaimBuddy *buddy; | |
| 715 GaimAccount *acct = gaim_connection_get_account(gc); | |
| 716 | |
| 717 const char *id = mwSametimeUser_getUser(stuser); | |
| 718 const char *name = mwSametimeUser_getShortName(stuser); | |
| 719 const char *alias = mwSametimeUser_getAlias(stuser); | |
| 720 enum mwSametimeUserType type = mwSametimeUser_getType(stuser); | |
| 721 | |
| 722 g_return_val_if_fail(id != NULL, NULL); | |
| 723 g_return_val_if_fail(strlen(id) > 0, NULL); | |
| 724 | |
| 725 buddy = gaim_find_buddy_in_group(acct, id, group); | |
| 726 if(! buddy) { | |
| 727 buddy = gaim_buddy_new(acct, id, alias); | |
| 728 | |
| 729 gaim_blist_add_buddy(buddy, NULL, group, NULL); | |
| 730 buddy_add(pd, buddy); | |
| 731 } | |
| 732 | |
| 733 gaim_blist_alias_buddy(buddy, alias); | |
| 734 gaim_blist_server_alias_buddy(buddy, name); | |
| 735 gaim_blist_node_set_string((GaimBlistNode *) buddy, BUDDY_KEY_NAME, name); | |
| 736 gaim_blist_node_set_int((GaimBlistNode *) buddy, BUDDY_KEY_TYPE, type); | |
| 737 | |
| 738 return buddy; | |
| 739 } | |
| 740 | |
| 741 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
742 /** add aware watch for a dynamic group */ |
| 10977 | 743 static void group_add(struct mwGaimPluginData *pd, |
| 744 GaimGroup *group) { | |
| 745 | |
| 746 struct mwAwareIdBlock idb = { mwAware_GROUP, NULL, NULL }; | |
| 747 struct mwAwareList *list; | |
| 748 const char *n; | |
| 749 GList *add; | |
| 750 | |
| 751 n = gaim_blist_node_get_string((GaimBlistNode *) group, GROUP_KEY_NAME); | |
| 752 if(! n) n = group->name; | |
| 753 | |
| 754 idb.user = (char *) n; | |
| 755 add = g_list_prepend(NULL, &idb); | |
| 756 | |
| 757 list = list_ensure(pd, group); | |
| 758 mwAwareList_addAware(list, add); | |
| 759 g_list_free(add); | |
| 760 } | |
| 761 | |
| 762 | |
| 763 /** ensure that a GaimGroup exists in the blist with data | |
| 764 appropriately matching the st group entry from the st list */ | |
| 765 static GaimGroup *group_ensure(GaimConnection *gc, | |
| 766 struct mwSametimeGroup *stgroup) { | |
| 767 GaimAccount *acct; | |
| 768 GaimGroup *group; | |
| 769 GaimBlistNode *gn; | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
770 const char *name, *alias, *owner; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
771 enum mwSametimeGroupType type; |
| 10977 | 772 |
| 773 acct = gaim_connection_get_account(gc); | |
| 774 owner = gaim_account_get_username(acct); | |
| 775 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
776 name = mwSametimeGroup_getName(stgroup); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
777 alias = mwSametimeGroup_getAlias(stgroup); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
778 type = mwSametimeGroup_getType(stgroup); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
779 |
| 10977 | 780 group = gaim_find_group(alias); |
| 781 if(! group) { | |
| 782 group = gaim_group_new(alias); | |
| 783 gaim_blist_add_group(group, NULL); | |
| 784 } | |
| 785 | |
| 786 gn = (GaimBlistNode *) group; | |
| 787 | |
| 788 gaim_blist_node_set_string(gn, GROUP_KEY_NAME, name); | |
| 789 gaim_blist_node_set_int(gn, GROUP_KEY_TYPE, type); | |
| 790 | |
| 791 if(type == mwSametimeGroup_DYNAMIC) { | |
| 792 gaim_blist_node_set_string(gn, GROUP_KEY_OWNER, owner); | |
| 793 group_add(gc->proto_data, group); | |
| 794 } | |
| 795 | |
| 796 return group; | |
| 797 } | |
| 798 | |
| 799 | |
| 800 /** merge the entries from a st list into the gaim blist */ | |
| 801 static void blist_import(GaimConnection *gc, struct mwSametimeList *stlist) { | |
| 802 struct mwSametimeGroup *stgroup; | |
| 803 struct mwSametimeUser *stuser; | |
| 804 | |
| 805 GaimGroup *group; | |
| 806 GaimBuddy *buddy; | |
| 807 | |
| 808 GList *gl, *gtl, *ul, *utl; | |
| 809 | |
| 810 gl = gtl = mwSametimeList_getGroups(stlist); | |
| 811 for(; gl; gl = gl->next) { | |
| 812 | |
| 813 stgroup = (struct mwSametimeGroup *) gl->data; | |
| 814 group = group_ensure(gc, stgroup); | |
| 815 | |
| 816 ul = utl = mwSametimeGroup_getUsers(stgroup); | |
| 817 for(; ul; ul = ul->next) { | |
| 818 | |
| 819 stuser = (struct mwSametimeUser *) ul->data; | |
| 820 buddy = buddy_ensure(gc, group, stuser); | |
| 821 } | |
| 822 g_list_free(utl); | |
| 823 } | |
| 824 g_list_free(gtl); | |
| 825 } | |
| 826 | |
| 827 | |
| 828 /** callback passed to the storage service when it's told to load the | |
| 829 st list */ | |
| 830 static void fetch_blist_cb(struct mwServiceStorage *srvc, | |
| 831 guint32 result, struct mwStorageUnit *item, | |
| 832 gpointer data) { | |
| 833 | |
| 834 struct mwGaimPluginData *pd = data; | |
| 835 struct mwSametimeList *stlist; | |
| 836 struct mwSession *s; | |
| 837 | |
| 838 struct mwGetBuffer *b; | |
| 839 | |
| 840 g_return_if_fail(result == ERR_SUCCESS); | |
| 841 | |
| 842 /* check our preferences for loading */ | |
| 843 if(BLIST_CHOICE_IS_NONE()) { | |
| 844 DEBUG_INFO("preferences indicate not to load remote buddy list\n"); | |
| 845 return; | |
| 846 } | |
| 847 | |
| 848 b = mwGetBuffer_wrap(mwStorageUnit_asOpaque(item)); | |
| 849 | |
| 850 stlist = mwSametimeList_new(); | |
| 851 mwSametimeList_get(b, stlist); | |
| 852 | |
| 853 s = mwService_getSession(MW_SERVICE(srvc)); | |
| 854 blist_import(pd->gc, stlist); | |
| 855 | |
| 856 mwSametimeList_free(stlist); | |
| 857 } | |
| 858 | |
| 859 | |
| 860 /** callback passed to the storage service when it's told to load one | |
| 861 of the default status messages */ | |
| 862 static void fetch_msg_cb(struct mwServiceStorage *srvc, | |
| 863 guint32 result, struct mwStorageUnit *item, | |
| 864 gpointer data) { | |
| 865 | |
| 866 struct mwGaimPluginData *pd = data; | |
| 867 GaimConnection *gc; | |
| 868 GaimAccount *acct; | |
| 869 struct mwSession *session; | |
| 870 char *msg, *m; | |
| 871 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
872 /* it's no big deal if these entries don't exist on the server */ |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
873 if(result != ERR_SUCCESS) return; |
| 10977 | 874 |
| 875 g_return_if_fail(pd != NULL); | |
| 876 | |
| 877 gc = pd->gc; | |
| 878 g_return_if_fail(gc != NULL); | |
| 879 | |
| 880 acct = gaim_connection_get_account(gc); | |
| 881 g_return_if_fail(acct != NULL); | |
| 882 | |
| 883 session = pd->session; | |
| 884 g_return_if_fail(session != NULL); | |
| 885 | |
| 886 m = msg = mwStorageUnit_asString(item); | |
| 887 | |
| 888 /* only load the first (non-empty) line of the collection of | |
| 889 status messages */ | |
| 890 if(m && *m) { | |
| 891 while(*m && isspace(*m)) m++; | |
| 892 if(*m) { | |
| 893 char *tail; | |
| 894 | |
| 895 tail = strchr(m, '\r'); | |
| 896 if(tail) *tail = '\0'; | |
| 897 tail = strchr(m, '\n'); | |
| 898 if(tail) *tail = '\0'; | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 switch(mwStorageUnit_getKey(item)) { | |
| 903 case mwStore_AWAY_MESSAGES: | |
| 904 DEBUG_INFO("setting away message to \"%s\"\n", NSTR(m)); | |
| 905 gaim_account_set_string(acct, MW_KEY_AWAY_MSG, m); | |
| 906 break; | |
| 907 | |
| 908 case mwStore_BUSY_MESSAGES: | |
| 909 DEBUG_INFO("setting busy message to \"%s\"\n", NSTR(m)); | |
| 910 gaim_account_set_string(acct, MW_KEY_BUSY_MSG, m); | |
| 911 break; | |
| 912 | |
| 913 case mwStore_ACTIVE_MESSAGES: | |
| 914 DEBUG_INFO("setting active message to \"%s\"\n", NSTR(m)); | |
| 915 gaim_account_set_string(acct, MW_KEY_ACTIVE_MSG, m); | |
| 916 break; | |
| 917 | |
| 918 default: | |
| 919 g_free(msg); | |
| 920 g_return_if_reached(); | |
| 921 } | |
| 922 | |
| 923 g_free(msg); | |
| 924 msg = NULL; | |
| 925 | |
| 926 #if 0 | |
| 927 /* XXX */ | |
| 928 if(!gc->away_state || !strcmp(gc->away_state, MW_STATE_ACTIVE)) { | |
| 929 msg = MW_STATE_ACTIVE; | |
| 930 } else if(gc->away_state && !strcmp(gc->away_state, MW_STATE_AWAY)) { | |
| 931 msg = MW_STATE_AWAY; | |
| 932 } else if(gc->away_state && !strcmp(gc->away_state, MW_STATE_BUSY)) { | |
| 933 msg = MW_STATE_BUSY; | |
| 934 } | |
| 935 | |
| 936 if(msg) | |
| 937 serv_set_away(gc, msg, NULL); | |
| 938 #endif | |
| 939 } | |
| 940 | |
| 941 | |
| 942 /** signal triggered when a conversation is opened in Gaim */ | |
| 943 static void conversation_created_cb(GaimConversation *g_conv, | |
| 944 struct mwGaimPluginData *pd) { | |
| 945 | |
| 946 /* we need to tell the IM service to negotiate features for the | |
| 947 conversation right away, otherwise it'll wait until the first | |
| 948 message is sent before offering NotesBuddy features. Therefore | |
| 949 whenever Gaim creates a conversation, we'll immediately open the | |
| 950 channel to the other side and figure out what the target can | |
| 951 handle. Unfortunately, this makes us vulnerable to Psychic Mode, | |
| 952 whereas a more lazy negotiation based on the first message | |
| 953 isn't */ | |
| 954 | |
| 955 GaimConnection *gc; | |
| 956 struct mwIdBlock who = { 0, 0 }; | |
| 957 struct mwConversation *conv; | |
| 958 | |
| 959 gc = gaim_conversation_get_gc(g_conv); | |
| 960 if(pd->gc != gc) | |
| 961 return; /* not ours */ | |
| 962 | |
| 11338 | 963 if(gaim_conversation_get_type(g_conv) != GAIM_CONV_TYPE_IM) |
| 10977 | 964 return; /* wrong type */ |
| 965 | |
| 966 who.user = (char *) gaim_conversation_get_name(g_conv); | |
| 967 conv = mwServiceIm_getConversation(pd->srvc_im, &who); | |
| 968 | |
| 969 convo_features(conv); | |
| 970 | |
| 971 if(mwConversation_isClosed(conv)) | |
| 972 mwConversation_open(conv); | |
| 973 } | |
| 974 | |
| 975 | |
| 976 static void blist_menu_nab(GaimBlistNode *node, gpointer data) { | |
| 977 struct mwGaimPluginData *pd = data; | |
| 978 GaimConnection *gc; | |
| 979 | |
| 980 GaimGroup *group = (GaimGroup *) node; | |
| 981 | |
| 982 GString *str; | |
| 983 char *tmp; | |
| 984 | |
| 985 g_return_if_fail(pd != NULL); | |
| 986 | |
| 987 gc = pd->gc; | |
| 988 g_return_if_fail(gc != NULL); | |
| 989 | |
| 990 g_return_if_fail(GAIM_BLIST_NODE_IS_GROUP(node)); | |
| 991 | |
| 992 str = g_string_new(NULL); | |
| 993 | |
| 994 tmp = (char *) gaim_blist_node_get_string(node, GROUP_KEY_NAME); | |
| 995 | |
| 996 g_string_append_printf(str, "<b>Group Title:</b> %s<br>", group->name); | |
| 997 g_string_append_printf(str, "<b>Notes Group ID:</b> %s<br>", tmp); | |
| 998 | |
| 999 tmp = g_strdup_printf("Info for Group %s", group->name); | |
| 1000 | |
| 1001 gaim_notify_formatted(gc, tmp, "Notes Address Book Information", | |
| 1002 NULL, str->str, NULL, NULL); | |
| 1003 | |
| 1004 g_free(tmp); | |
| 1005 g_string_free(str, TRUE); | |
| 1006 } | |
| 1007 | |
| 1008 | |
| 1009 /** The normal blist menu prpl function doesn't get called for groups, | |
| 1010 so we use the blist-node-extended-menu signal to trigger this | |
| 1011 handler */ | |
| 1012 static void blist_node_menu_cb(GaimBlistNode *node, | |
| 1013 GList **menu, struct mwGaimPluginData *pd) { | |
| 1014 GaimBlistNodeAction *act; | |
| 1015 | |
| 1016 if(GAIM_BLIST_NODE_IS_GROUP(node)) { | |
| 1017 const char *owner; | |
| 1018 GaimAccount *acct; | |
| 1019 | |
| 1020 owner = gaim_blist_node_get_string(node, GROUP_KEY_OWNER); | |
| 1021 if(! owner) return; | |
| 1022 | |
| 1023 acct = gaim_accounts_find(owner, PLUGIN_ID); | |
| 1024 if(! acct) return; | |
| 1025 if(! gaim_account_is_connected(acct)) return; | |
| 1026 if(acct != gaim_connection_get_account(pd->gc)) return; | |
| 1027 | |
| 1028 act = gaim_blist_node_action_new("Get Notes Address Book Info", | |
| 1029 blist_menu_nab, pd, NULL); | |
| 1030 | |
| 1031 *menu = g_list_append(*menu, NULL); | |
| 1032 *menu = g_list_append(*menu, act); | |
| 1033 } | |
| 1034 } | |
| 1035 | |
| 1036 | |
| 1037 /** Last thing to happen from a started session */ | |
| 1038 static void services_starting(struct mwGaimPluginData *pd) { | |
| 1039 | |
| 1040 GaimConnection *gc; | |
| 1041 GaimAccount *acct; | |
| 1042 struct mwStorageUnit *unit; | |
| 1043 GaimBuddyList *blist; | |
| 1044 GaimBlistNode *l; | |
| 1045 | |
| 1046 gc = pd->gc; | |
| 1047 acct = gaim_connection_get_account(gc); | |
| 1048 | |
| 1049 /* grab the buddy list from the server */ | |
| 1050 unit = mwStorageUnit_new(mwStore_AWARE_LIST); | |
| 1051 mwServiceStorage_load(pd->srvc_store, unit, fetch_blist_cb, pd, NULL); | |
| 1052 | |
| 1053 /* fetch the away/busy/active messages from the server */ | |
| 1054 unit = mwStorageUnit_new(mwStore_AWAY_MESSAGES); | |
| 1055 mwServiceStorage_load(pd->srvc_store, unit, fetch_msg_cb, pd, NULL); | |
| 1056 | |
| 1057 unit = mwStorageUnit_new(mwStore_BUSY_MESSAGES); | |
| 1058 mwServiceStorage_load(pd->srvc_store, unit, fetch_msg_cb, pd, NULL); | |
| 1059 | |
| 1060 unit = mwStorageUnit_new(mwStore_ACTIVE_MESSAGES); | |
| 1061 mwServiceStorage_load(pd->srvc_store, unit, fetch_msg_cb, pd, NULL); | |
| 1062 | |
| 1063 /* start watching for new conversations */ | |
| 1064 gaim_signal_connect(gaim_conversations_get_handle(), | |
| 1065 "conversation-created", gc, | |
| 1066 GAIM_CALLBACK(conversation_created_cb), pd); | |
| 1067 | |
| 1068 /* watch for group extended menu items */ | |
| 1069 gaim_signal_connect(gaim_blist_get_handle(), | |
| 1070 "blist-node-extended-menu", gc, | |
| 1071 GAIM_CALLBACK(blist_node_menu_cb), pd); | |
| 1072 | |
| 1073 /* find all the NAB groups and subscribe to them */ | |
| 1074 blist = gaim_get_blist(); | |
| 1075 for(l = blist->root; l; l = l->next) { | |
| 1076 GaimGroup *group = (GaimGroup *) l; | |
| 1077 enum mwSametimeGroupType gt; | |
| 1078 const char *owner; | |
| 1079 | |
| 1080 if(! GAIM_BLIST_NODE_IS_GROUP(l)) continue; | |
| 1081 | |
| 1082 /* if the group is ownerless, or has an owner and we're not it, | |
| 1083 skip it */ | |
| 1084 owner = gaim_blist_node_get_string(l, GROUP_KEY_OWNER); | |
| 1085 if(!owner || strcmp(owner, gaim_account_get_username(acct))) | |
| 1086 continue; | |
| 1087 | |
| 1088 gt = gaim_blist_node_get_int(l, GROUP_KEY_TYPE); | |
| 1089 if(gt == mwSametimeGroup_DYNAMIC) | |
| 1090 group_add(pd, group); | |
| 1091 } | |
| 1092 | |
| 1093 /* set the aware attributes */ | |
| 1094 /* indicate we understand what AV prefs are, but don't support any */ | |
| 1095 mwServiceAware_setAttributeBoolean(pd->srvc_aware, | |
| 1096 mwAttribute_AV_PREFS_SET, TRUE); | |
| 1097 mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_MICROPHONE); | |
| 1098 mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_SPEAKERS); | |
| 1099 mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_VIDEO_CAMERA); | |
| 1100 | |
| 1101 /* ... but we can do file transfers! */ | |
| 1102 mwServiceAware_setAttributeBoolean(pd->srvc_aware, | |
| 1103 mwAttribute_FILE_TRANSFER, TRUE); | |
| 1104 } | |
| 1105 | |
| 1106 | |
| 1107 /** called from mw_session_stateChange when the session's state is | |
| 1108 mwSession_STARTED. Any finalizing of start-up stuff should go | |
| 1109 here */ | |
| 1110 static void session_started(struct mwGaimPluginData *pd) { | |
| 1111 | |
| 1112 /* XXX setup status */ | |
| 1113 | |
| 1114 /* use our services to do neat things */ | |
| 1115 services_starting(pd); | |
| 1116 } | |
| 1117 | |
| 1118 | |
| 1119 static void mw_session_stateChange(struct mwSession *session, | |
| 1120 enum mwSessionState state, guint32 info) { | |
| 1121 struct mwGaimPluginData *pd; | |
| 1122 GaimConnection *gc; | |
| 1123 char *msg = NULL; | |
| 1124 | |
| 1125 pd = mwSession_getClientData(session); | |
| 1126 gc = pd->gc; | |
| 1127 | |
| 1128 switch(state) { | |
| 1129 case mwSession_STARTING: | |
| 1130 msg = _("Sending Handshake"); | |
| 1131 gaim_connection_update_progress(gc, msg, 2, MW_CONNECT_STEPS); | |
| 1132 break; | |
| 1133 | |
| 1134 case mwSession_HANDSHAKE: | |
| 1135 msg = _("Waiting for Handshake Acknowledgement"); | |
| 1136 gaim_connection_update_progress(gc, msg, 3, MW_CONNECT_STEPS); | |
| 1137 break; | |
| 1138 | |
| 1139 case mwSession_HANDSHAKE_ACK: | |
| 1140 msg = _("Handshake Acknowledged, Sending Login"); | |
| 1141 gaim_connection_update_progress(gc, msg, 4, MW_CONNECT_STEPS); | |
| 1142 break; | |
| 1143 | |
| 1144 case mwSession_LOGIN: | |
| 1145 msg = _("Waiting for Login Acknowledgement"); | |
| 1146 gaim_connection_update_progress(gc, msg, 5, MW_CONNECT_STEPS); | |
| 1147 break; | |
| 1148 | |
| 1149 case mwSession_LOGIN_REDIR: | |
| 1150 msg = _("Login Redirected"); | |
| 1151 gaim_connection_update_progress(gc, msg, 6, MW_CONNECT_STEPS); | |
| 1152 break; | |
| 1153 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1154 case mwSession_LOGIN_CONT: |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1155 msg = _("Forcing Login"); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1156 gaim_connection_update_progress(gc, msg, 7, MW_CONNECT_STEPS); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1157 |
| 10977 | 1158 case mwSession_LOGIN_ACK: |
| 1159 msg = _("Login Acknowledged"); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1160 gaim_connection_update_progress(gc, msg, 8, MW_CONNECT_STEPS); |
| 10977 | 1161 break; |
| 1162 | |
| 1163 case mwSession_STARTED: | |
| 1164 msg = _("Connected to Sametime Community Server"); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1165 gaim_connection_update_progress(gc, msg, 9, MW_CONNECT_STEPS); |
| 10977 | 1166 gaim_connection_set_state(gc, GAIM_CONNECTED); |
| 1167 /* XXX serv_finish_login(gc); */ | |
| 1168 | |
| 1169 session_started(pd); | |
| 1170 break; | |
| 1171 | |
| 1172 case mwSession_STOPPING: | |
| 1173 if(info & ERR_FAILURE) { | |
| 1174 msg = mwError(info); | |
| 1175 gaim_connection_error(gc, msg); | |
| 1176 g_free(msg); | |
| 1177 } | |
| 1178 break; | |
| 1179 | |
| 1180 case mwSession_STOPPED: | |
| 1181 break; | |
| 1182 | |
| 1183 case mwSession_UNKNOWN: | |
| 1184 default: | |
| 1185 DEBUG_WARN("session in unknown state\n"); | |
| 1186 } | |
| 1187 } | |
| 1188 | |
| 1189 | |
| 1190 static void mw_session_setPrivacyInfo(struct mwSession *session) { | |
| 1191 struct mwGaimPluginData *pd; | |
| 1192 GaimConnection *gc; | |
| 1193 GaimAccount *acct; | |
| 1194 struct mwPrivacyInfo *privacy; | |
| 1195 GSList *l, **ll; | |
| 1196 guint count; | |
| 1197 | |
| 1198 DEBUG_INFO("privacy information set from server\n"); | |
| 1199 | |
| 1200 g_return_if_fail(session != NULL); | |
| 1201 | |
| 1202 pd = mwSession_getClientData(session); | |
| 1203 g_return_if_fail(pd != NULL); | |
| 1204 | |
| 1205 gc = pd->gc; | |
| 1206 g_return_if_fail(gc != NULL); | |
| 1207 | |
| 1208 acct = gaim_connection_get_account(gc); | |
| 1209 g_return_if_fail(acct != NULL); | |
| 1210 | |
| 1211 privacy = mwSession_getPrivacyInfo(session); | |
| 1212 count = privacy->count; | |
| 1213 | |
| 1214 ll = (privacy->deny)? &acct->deny: &acct->permit; | |
| 1215 for(l = *ll; l; l = l->next) g_free(l->data); | |
| 1216 g_slist_free(*ll); | |
| 1217 l = *ll = NULL; | |
| 1218 | |
| 1219 while(count--) { | |
| 1220 struct mwUserItem *u = privacy->users + count; | |
| 1221 l = g_slist_prepend(l, g_strdup(u->id)); | |
| 1222 } | |
| 1223 *ll = l; | |
| 1224 } | |
| 1225 | |
| 1226 | |
| 1227 static void mw_session_setUserStatus(struct mwSession *session) { | |
| 1228 struct mwGaimPluginData *pd; | |
| 1229 GaimConnection *gc; | |
| 1230 struct mwAwareIdBlock idb = { mwAware_USER, NULL, NULL }; | |
| 1231 struct mwUserStatus *stat; | |
| 1232 | |
| 1233 g_return_if_fail(session != NULL); | |
| 1234 | |
| 1235 pd = mwSession_getClientData(session); | |
| 1236 g_return_if_fail(pd != NULL); | |
| 1237 | |
| 1238 gc = pd->gc; | |
| 1239 g_return_if_fail(gc != NULL); | |
| 1240 | |
| 1241 idb.user = mwSession_getProperty(session, mwSession_AUTH_USER_ID); | |
| 1242 stat = mwSession_getUserStatus(session); | |
| 1243 | |
| 1244 /* trigger an update of our own status if we're in the buddy list */ | |
| 1245 mwServiceAware_setStatus(pd->srvc_aware, &idb, stat); | |
| 1246 } | |
| 1247 | |
| 1248 | |
| 1249 static void mw_session_admin(struct mwSession *session, | |
| 1250 const char *text) { | |
| 1251 | |
| 1252 GaimConnection *gc = session_to_gc(session); | |
| 1253 g_return_if_fail(gc != NULL); | |
| 1254 | |
| 1255 /** @todo Admin alerts should probably be in a conversation window | |
| 1256 rather than a gaim_notify_message. Or in some sort of updating | |
| 1257 dialog, or something. */ | |
| 1258 | |
| 1259 gaim_notify_message(gc, GAIM_NOTIFY_MSG_INFO, _("Admin Alert"), | |
| 1260 text, NULL, NULL, NULL); | |
| 1261 } | |
| 1262 | |
| 1263 | |
| 1264 /** called from read_cb, attempts to read available data from sock and | |
| 1265 pass it to the session, passing back the return code from the read | |
| 1266 call for handling in read_cb */ | |
| 1267 static int read_recv(struct mwSession *session, int sock) { | |
| 1268 char buf[BUF_LEN]; | |
| 1269 int len; | |
| 1270 | |
| 1271 len = read(sock, buf, BUF_LEN); | |
| 1272 if(len > 0) mwSession_recv(session, buf, len); | |
| 1273 | |
| 1274 return len; | |
| 1275 } | |
| 1276 | |
| 1277 | |
| 1278 /** callback triggered from gaim_input_add, watches the socked for | |
| 1279 available data to be processed by the session */ | |
| 1280 static void read_cb(gpointer data, gint source, | |
| 1281 GaimInputCondition cond) { | |
| 1282 | |
| 1283 struct mwGaimPluginData *pd = data; | |
| 1284 int ret = 0, err = 0; | |
| 1285 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1286 if(! cond) return; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1287 |
| 10977 | 1288 g_return_if_fail(pd != NULL); |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1289 g_return_if_fail(cond & GAIM_INPUT_READ); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1290 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1291 ret = read_recv(pd->session, pd->socket); |
| 10977 | 1292 |
| 1293 /* normal operation ends here */ | |
| 1294 if(ret > 0) return; | |
| 1295 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1296 /* fetch the global error value */ |
| 10977 | 1297 err = errno; |
| 1298 | |
| 1299 /* read problem occured if we're here, so we'll need to take care of | |
| 1300 it and clean up internal state */ | |
| 1301 | |
| 1302 if(pd->socket) { | |
| 1303 close(pd->socket); | |
| 1304 pd->socket = 0; | |
| 1305 } | |
| 1306 | |
| 1307 if(pd->gc->inpa) { | |
| 1308 gaim_input_remove(pd->gc->inpa); | |
| 1309 pd->gc->inpa = 0; | |
| 1310 } | |
| 1311 | |
| 1312 if(! ret) { | |
| 1313 DEBUG_INFO("connection reset\n"); | |
| 1314 gaim_connection_error(pd->gc, "Connection reset"); | |
| 1315 | |
| 1316 } else if(ret < 0) { | |
| 1317 char *msg = strerror(err); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1318 |
| 10977 | 1319 DEBUG_INFO("error in read callback: %s\n", msg); |
| 1320 | |
| 1321 msg = g_strdup_printf("Error reading from socket: %s", msg); | |
| 1322 gaim_connection_error(pd->gc, msg); | |
| 1323 g_free(msg); | |
| 1324 } | |
| 1325 } | |
| 1326 | |
| 1327 | |
| 1328 /** Callback passed to gaim_proxy_connect when an account is logged | |
| 1329 in, and if the session logging in receives a redirect message */ | |
| 1330 static void connect_cb(gpointer data, gint source, | |
| 1331 GaimInputCondition cond) { | |
| 1332 | |
| 1333 struct mwGaimPluginData *pd = data; | |
| 1334 GaimConnection *gc = pd->gc; | |
| 1335 | |
| 1336 if(! g_list_find(gaim_connections_get_all(), pd->gc)) { | |
| 1337 close(source); | |
| 1338 g_return_if_reached(); | |
| 1339 } | |
| 1340 | |
| 1341 if(source < 0) { | |
| 1342 /* connection failed */ | |
| 1343 | |
| 1344 if(pd->socket) { | |
| 1345 /* this is a redirect connect, force login on existing socket */ | |
| 1346 mwSession_forceLogin(pd->session); | |
| 1347 | |
| 1348 } else { | |
| 1349 /* this is a regular connect, error out */ | |
| 1350 gaim_connection_error(pd->gc, "Unable to connect to host"); | |
| 1351 } | |
| 1352 | |
| 1353 return; | |
| 1354 } | |
| 1355 | |
| 1356 if(pd->socket) { | |
| 1357 /* stop any existing login attempt */ | |
| 1358 mwSession_stop(pd->session, ERR_SUCCESS); | |
| 1359 } | |
| 1360 | |
| 1361 pd->socket = source; | |
| 1362 gc->inpa = gaim_input_add(source, GAIM_INPUT_READ, read_cb, pd); | |
| 1363 | |
| 1364 mwSession_start(pd->session); | |
| 1365 } | |
| 1366 | |
| 1367 | |
| 1368 static void mw_session_loginRedirect(struct mwSession *session, | |
| 1369 const char *host) { | |
| 1370 | |
| 1371 struct mwGaimPluginData *pd; | |
| 1372 GaimConnection *gc; | |
| 1373 GaimAccount *account; | |
| 1374 guint port; | |
| 1375 | |
| 1376 pd = mwSession_getClientData(session); | |
| 1377 gc = pd->gc; | |
| 1378 account = gaim_connection_get_account(gc); | |
| 1379 port = gaim_account_get_int(account, "port", MW_PLUGIN_DEFAULT_PORT); | |
| 1380 | |
| 1381 if(gaim_prefs_get_bool(MW_PRPL_OPT_FORCE_LOGIN) || | |
| 1382 gaim_proxy_connect(account, host, port, connect_cb, pd)) { | |
| 1383 | |
| 1384 mwSession_forceLogin(session); | |
| 1385 } | |
| 1386 } | |
| 1387 | |
| 1388 | |
| 1389 static struct mwSessionHandler mw_session_handler = { | |
| 1390 .io_write = mw_session_io_write, | |
| 1391 .io_close = mw_session_io_close, | |
| 1392 .clear = mw_session_clear, | |
| 1393 .on_stateChange = mw_session_stateChange, | |
| 1394 .on_setPrivacyInfo = mw_session_setPrivacyInfo, | |
| 1395 .on_setUserStatus = mw_session_setUserStatus, | |
| 1396 .on_admin = mw_session_admin, | |
| 1397 .on_loginRedirect = mw_session_loginRedirect, | |
| 1398 }; | |
| 1399 | |
| 1400 | |
| 1401 static void mw_aware_on_attrib(struct mwServiceAware *srvc, | |
| 1402 struct mwAwareAttribute *attrib) { | |
| 1403 | |
| 1404 ; /** @todo handle server attributes. There may be some stuff we | |
| 1405 actually want to look for, but I'm not aware of anything right | |
| 1406 now.*/ | |
| 1407 } | |
| 1408 | |
| 1409 | |
| 1410 static void mw_aware_clear(struct mwServiceAware *srvc) { | |
| 1411 ; /* nothing for now */ | |
| 1412 } | |
| 1413 | |
| 1414 | |
| 1415 static struct mwAwareHandler mw_aware_handler = { | |
| 1416 .on_attrib = mw_aware_on_attrib, | |
| 1417 .clear = mw_aware_clear, | |
| 1418 }; | |
| 1419 | |
| 1420 | |
| 1421 static struct mwServiceAware *mw_srvc_aware_new(struct mwSession *s) { | |
| 1422 struct mwServiceAware *srvc; | |
| 1423 srvc = mwServiceAware_new(s, &mw_aware_handler); | |
| 1424 return srvc; | |
| 1425 }; | |
| 1426 | |
| 1427 | |
| 1428 static void mw_conf_invited(struct mwConference *conf, | |
| 1429 struct mwLoginInfo *inviter, | |
| 1430 const char *invitation) { | |
| 1431 | |
| 1432 struct mwServiceConference *srvc; | |
| 1433 struct mwSession *session; | |
| 1434 struct mwGaimPluginData *pd; | |
| 1435 GaimConnection *gc; | |
| 1436 | |
| 1437 char *c_inviter, *c_name, *c_topic, *c_invitation; | |
| 1438 GHashTable *ht; | |
| 1439 | |
| 1440 srvc = mwConference_getService(conf); | |
| 1441 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1442 pd = mwSession_getClientData(session); | |
| 1443 gc = pd->gc; | |
| 1444 | |
| 1445 ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); | |
| 1446 | |
| 1447 c_inviter = g_strdup(inviter->user_id); | |
| 1448 g_hash_table_insert(ht, CHAT_KEY_CREATOR, c_inviter); | |
| 1449 | |
| 1450 c_name = g_strdup(mwConference_getName(conf)); | |
| 1451 g_hash_table_insert(ht, CHAT_KEY_NAME, c_name); | |
| 1452 | |
| 1453 c_topic = g_strdup(mwConference_getTitle(conf)); | |
| 1454 g_hash_table_insert(ht, CHAT_KEY_TOPIC, c_topic); | |
| 1455 | |
| 1456 c_invitation = g_strdup(invitation); | |
| 1457 g_hash_table_insert(ht, CHAT_KEY_INVITE, c_invitation); | |
| 1458 | |
| 1459 DEBUG_INFO("received invitation from '%s' to join ('%s','%s'): '%s'\n", | |
| 1460 NSTR(c_inviter), NSTR(c_name), | |
| 1461 NSTR(c_topic), NSTR(c_invitation)); | |
| 1462 | |
| 1463 serv_got_chat_invite(gc, c_topic, c_inviter, c_invitation, ht); | |
| 1464 } | |
| 1465 | |
| 1466 | |
| 1467 /* The following mess helps us relate a mwConference to a GaimConvChat | |
| 1468 in the various forms by which either may be indicated */ | |
| 1469 | |
| 1470 #define CONF_TO_ID(conf) (GPOINTER_TO_INT(conf)) | |
| 1471 #define ID_TO_CONF(pd, id) (conf_find_by_id((pd), (id))) | |
| 1472 | |
| 1473 #define CHAT_TO_ID(chat) (gaim_conv_chat_get_id(chat)) | |
| 1474 #define ID_TO_CHAT(id) (gaim_find_chat(id)) | |
| 1475 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1476 #define CHAT_TO_CONF(pd, chat) (ID_TO_CONF((pd), CHAT_TO_ID(chat))) |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
1477 #define CONF_TO_CHAT(conf) (ID_TO_CHAT(CONF_TO_ID(conf))) |
| 10977 | 1478 |
| 1479 | |
| 1480 static struct mwConference * | |
| 1481 conf_find_by_id(struct mwGaimPluginData *pd, int id) { | |
| 1482 | |
| 1483 struct mwServiceConference *srvc = pd->srvc_conf; | |
| 1484 struct mwConference *conf = NULL; | |
| 1485 GList *l, *ll; | |
| 1486 | |
| 1487 ll = mwServiceConference_getConferences(srvc); | |
| 1488 for(l = ll; l; l = l->next) { | |
| 1489 struct mwConference *c = l->data; | |
| 1490 GaimConvChat *h = mwConference_getClientData(c); | |
| 1491 | |
| 1492 if(CHAT_TO_ID(h) == id) { | |
| 1493 conf = c; | |
| 1494 break; | |
| 1495 } | |
| 1496 } | |
| 1497 g_list_free(ll); | |
| 1498 | |
| 1499 return conf; | |
| 1500 } | |
| 1501 | |
| 1502 | |
| 1503 static void mw_conf_opened(struct mwConference *conf, GList *members) { | |
| 1504 struct mwServiceConference *srvc; | |
| 1505 struct mwSession *session; | |
| 1506 struct mwGaimPluginData *pd; | |
| 1507 GaimConnection *gc; | |
| 1508 GaimConversation *g_conf; | |
| 1509 | |
| 1510 const char *n = mwConference_getName(conf); | |
| 1511 | |
| 1512 DEBUG_INFO("conf %s opened, %u initial members\n", | |
| 1513 NSTR(n), g_list_length(members)); | |
| 1514 | |
| 1515 srvc = mwConference_getService(conf); | |
| 1516 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1517 pd = mwSession_getClientData(session); | |
| 1518 gc = pd->gc; | |
| 1519 | |
| 1520 g_conf = serv_got_joined_chat(gc, CONF_TO_ID(conf), | |
| 1521 mwConference_getTitle(conf)); | |
| 1522 | |
| 1523 mwConference_setClientData(conf, GAIM_CONV_CHAT(g_conf), NULL); | |
| 1524 | |
| 1525 for(; members; members = members->next) { | |
| 1526 struct mwLoginInfo *peer = members->data; | |
| 1527 gaim_conv_chat_add_user(GAIM_CONV_CHAT(g_conf), peer->user_id, | |
| 1528 NULL, GAIM_CBFLAGS_NONE, FALSE); | |
| 1529 } | |
| 1530 } | |
| 1531 | |
| 1532 | |
| 1533 static void mw_conf_closed(struct mwConference *conf, guint32 reason) { | |
| 1534 struct mwServiceConference *srvc; | |
| 1535 struct mwSession *session; | |
| 1536 struct mwGaimPluginData *pd; | |
| 1537 GaimConnection *gc; | |
| 1538 | |
| 1539 const char *n = mwConference_getName(conf); | |
| 1540 char *msg = mwError(reason); | |
| 1541 | |
| 1542 DEBUG_INFO("conf %s closed, 0x%08x\n", NSTR(n), reason); | |
| 1543 | |
| 1544 srvc = mwConference_getService(conf); | |
| 1545 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1546 pd = mwSession_getClientData(session); | |
| 1547 gc = pd->gc; | |
| 1548 | |
| 1549 serv_got_chat_left(gc, CONF_TO_ID(conf)); | |
| 1550 | |
| 1551 gaim_notify_error(gc, "Conference Closed", NULL, msg); | |
| 1552 g_free(msg); | |
| 1553 } | |
| 1554 | |
| 1555 | |
| 1556 static void mw_conf_peer_joined(struct mwConference *conf, | |
| 1557 struct mwLoginInfo *peer) { | |
| 1558 | |
| 1559 struct mwServiceConference *srvc; | |
| 1560 struct mwSession *session; | |
| 1561 struct mwGaimPluginData *pd; | |
| 1562 GaimConnection *gc; | |
| 1563 GaimConvChat *g_conf; | |
| 1564 | |
| 1565 const char *n = mwConference_getName(conf); | |
| 1566 | |
| 1567 DEBUG_INFO("%s joined conf %s\n", NSTR(peer->user_id), NSTR(n)); | |
| 1568 | |
| 1569 srvc = mwConference_getService(conf); | |
| 1570 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1571 pd = mwSession_getClientData(session); | |
| 1572 gc = pd->gc; | |
| 1573 | |
| 1574 g_conf = mwConference_getClientData(conf); | |
| 1575 g_return_if_fail(g_conf != NULL); | |
| 1576 | |
| 1577 gaim_conv_chat_add_user(g_conf, peer->user_id, | |
| 1578 NULL, GAIM_CBFLAGS_NONE, TRUE); | |
| 1579 } | |
| 1580 | |
| 1581 | |
| 1582 static void mw_conf_peer_parted(struct mwConference *conf, | |
| 1583 struct mwLoginInfo *peer) { | |
| 1584 | |
| 1585 struct mwServiceConference *srvc; | |
| 1586 struct mwSession *session; | |
| 1587 struct mwGaimPluginData *pd; | |
| 1588 GaimConnection *gc; | |
| 1589 GaimConvChat *g_conf; | |
| 1590 | |
| 1591 const char *n = mwConference_getName(conf); | |
| 1592 | |
| 1593 DEBUG_INFO("%s left conf %s\n", NSTR(peer->user_id), NSTR(n)); | |
| 1594 | |
| 1595 srvc = mwConference_getService(conf); | |
| 1596 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1597 pd = mwSession_getClientData(session); | |
| 1598 gc = pd->gc; | |
| 1599 | |
| 1600 g_conf = mwConference_getClientData(conf); | |
| 1601 g_return_if_fail(g_conf != NULL); | |
| 1602 | |
| 1603 gaim_conv_chat_remove_user(g_conf, peer->user_id, NULL); | |
| 1604 } | |
| 1605 | |
| 1606 | |
| 1607 static void mw_conf_text(struct mwConference *conf, | |
| 1608 struct mwLoginInfo *who, const char *text) { | |
| 1609 | |
| 1610 struct mwServiceConference *srvc; | |
| 1611 struct mwSession *session; | |
| 1612 struct mwGaimPluginData *pd; | |
| 1613 GaimConnection *gc; | |
| 1614 char *esc; | |
| 1615 | |
| 1616 srvc = mwConference_getService(conf); | |
| 1617 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1618 pd = mwSession_getClientData(session); | |
| 1619 gc = pd->gc; | |
| 1620 | |
| 1621 esc = g_markup_escape_text(text, -1); | |
| 1622 serv_got_chat_in(gc, CONF_TO_ID(conf), who->user_id, 0, esc, time(NULL)); | |
| 1623 g_free(esc); | |
| 1624 } | |
| 1625 | |
| 1626 | |
| 1627 static void mw_conf_typing(struct mwConference *conf, | |
| 1628 struct mwLoginInfo *who, gboolean typing) { | |
| 1629 | |
| 1630 /* gaim really has no good way to expose this to the user. */ | |
| 1631 | |
| 1632 const char *n = mwConference_getName(conf); | |
| 1633 const char *w = who->user_id; | |
| 1634 | |
| 1635 if(typing) { | |
| 1636 DEBUG_INFO("%s in conf %s: <typing>\n", NSTR(w), NSTR(n)); | |
| 1637 | |
| 1638 } else { | |
| 1639 DEBUG_INFO("%s in conf %s: <stopped typing>\n", NSTR(w), NSTR(n)); | |
| 1640 } | |
| 1641 } | |
| 1642 | |
| 1643 | |
| 1644 static void mw_conf_clear(struct mwServiceConference *srvc) { | |
| 1645 ; | |
| 1646 } | |
| 1647 | |
| 1648 | |
| 1649 static struct mwConferenceHandler mw_conference_handler = { | |
| 1650 .on_invited = mw_conf_invited, | |
| 1651 .conf_opened = mw_conf_opened, | |
| 1652 .conf_closed = mw_conf_closed, | |
| 1653 .on_peer_joined = mw_conf_peer_joined, | |
| 1654 .on_peer_parted = mw_conf_peer_parted, | |
| 1655 .on_text = mw_conf_text, | |
| 1656 .on_typing = mw_conf_typing, | |
| 1657 .clear = mw_conf_clear, | |
| 1658 }; | |
| 1659 | |
| 1660 | |
| 1661 static struct mwServiceConference *mw_srvc_conf_new(struct mwSession *s) { | |
| 1662 struct mwServiceConference *srvc; | |
| 1663 srvc = mwServiceConference_new(s, &mw_conference_handler); | |
| 1664 return srvc; | |
| 1665 } | |
| 1666 | |
| 1667 | |
| 1668 static void ft_incoming_cancel(GaimXfer *xfer) { | |
| 1669 /* incoming transfer rejected or canceled in-progress */ | |
| 1670 struct mwFileTransfer *ft = xfer->data; | |
| 1671 if(ft) mwFileTransfer_reject(ft); | |
| 1672 } | |
| 1673 | |
| 1674 | |
| 1675 static void ft_incoming_init(GaimXfer *xfer) { | |
| 1676 /* incoming transfer accepted */ | |
| 1677 | |
| 1678 /* - accept the mwFileTransfer | |
| 1679 - open/create the local FILE "wb" | |
| 1680 - stick the FILE's fp in xfer->dest_fp | |
| 1681 */ | |
| 1682 | |
| 1683 struct mwFileTransfer *ft; | |
| 1684 FILE *fp; | |
| 1685 | |
| 1686 ft = xfer->data; | |
| 1687 | |
| 1688 fp = g_fopen(xfer->local_filename, "wb"); | |
| 1689 if(! fp) { | |
| 1690 mwFileTransfer_cancel(ft); | |
| 1691 return; | |
| 1692 } | |
| 1693 | |
| 1694 xfer->dest_fp = fp; | |
| 1695 mwFileTransfer_accept(ft); | |
| 1696 } | |
| 1697 | |
| 1698 | |
| 1699 static void mw_ft_offered(struct mwFileTransfer *ft) { | |
| 1700 /* | |
| 1701 - create a gaim ft object | |
| 1702 - offer it | |
| 1703 */ | |
| 1704 | |
| 1705 struct mwServiceFileTransfer *srvc; | |
| 1706 struct mwSession *session; | |
| 1707 struct mwGaimPluginData *pd; | |
| 1708 GaimConnection *gc; | |
| 1709 GaimAccount *acct; | |
| 1710 const char *who; | |
| 1711 GaimXfer *xfer; | |
| 1712 | |
| 1713 /* @todo add some safety checks */ | |
| 1714 srvc = mwFileTransfer_getService(ft); | |
| 1715 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1716 pd = mwSession_getClientData(session); | |
| 1717 gc = pd->gc; | |
| 1718 acct = gaim_connection_get_account(gc); | |
| 1719 | |
| 1720 who = mwFileTransfer_getUser(ft)->user; | |
| 1721 | |
| 1722 DEBUG_INFO("file transfer %p offered\n", ft); | |
| 1723 DEBUG_INFO(" from: %s\n", NSTR(who)); | |
| 1724 DEBUG_INFO(" file: %s\n", NSTR(mwFileTransfer_getFileName(ft))); | |
| 1725 DEBUG_INFO(" size: %u\n", mwFileTransfer_getFileSize(ft)); | |
| 1726 DEBUG_INFO(" text: %s\n", NSTR(mwFileTransfer_getMessage(ft))); | |
| 1727 | |
| 1728 xfer = gaim_xfer_new(acct, GAIM_XFER_RECEIVE, who); | |
| 1729 | |
| 1730 gaim_xfer_ref(xfer); | |
| 1731 mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) gaim_xfer_unref); | |
| 1732 xfer->data = ft; | |
| 1733 | |
| 1734 gaim_xfer_set_init_fnc(xfer, ft_incoming_init); | |
| 1735 gaim_xfer_set_cancel_recv_fnc(xfer, ft_incoming_cancel); | |
| 1736 gaim_xfer_set_request_denied_fnc(xfer, ft_incoming_cancel); | |
| 1737 | |
| 1738 gaim_xfer_set_filename(xfer, mwFileTransfer_getFileName(ft)); | |
| 1739 gaim_xfer_set_size(xfer, mwFileTransfer_getFileSize(ft)); | |
| 1740 gaim_xfer_set_message(xfer, mwFileTransfer_getMessage(ft)); | |
| 1741 | |
| 1742 gaim_xfer_request(xfer); | |
| 1743 } | |
| 1744 | |
| 1745 | |
| 1746 static void ft_send(struct mwFileTransfer *ft, FILE *fp) { | |
| 1747 char buf[BUF_LONG]; | |
| 1748 struct mwOpaque o = { .data = buf, .len = BUF_LONG }; | |
| 1749 guint32 rem; | |
| 1750 GaimXfer *xfer; | |
| 1751 | |
| 1752 xfer = mwFileTransfer_getClientData(ft); | |
| 1753 | |
| 1754 rem = mwFileTransfer_getRemaining(ft); | |
| 1755 if(rem < BUF_LONG) o.len = rem; | |
| 1756 | |
| 1757 if(fread(buf, (size_t) o.len, 1, fp)) { | |
| 1758 | |
| 1759 /* calculate progress first. update is displayed upon ack */ | |
| 1760 xfer->bytes_sent += o.len; | |
| 1761 xfer->bytes_remaining -= o.len; | |
| 1762 | |
| 1763 /* ... send data second */ | |
| 1764 mwFileTransfer_send(ft, &o); | |
| 1765 | |
| 1766 } else { | |
| 1767 int err = errno; | |
| 1768 DEBUG_WARN("problem reading from file %s: %s", | |
| 1769 NSTR(mwFileTransfer_getFileName(ft)), strerror(err)); | |
| 1770 | |
| 1771 mwFileTransfer_cancel(ft); | |
| 1772 } | |
| 1773 } | |
| 1774 | |
| 1775 | |
| 1776 static gboolean ft_idle_cb(struct mwFileTransfer *ft) { | |
| 1777 GaimXfer *xfer = mwFileTransfer_getClientData(ft); | |
| 1778 g_return_val_if_fail(xfer != NULL, FALSE); | |
| 1779 | |
| 1780 xfer->watcher = 0; | |
| 1781 ft_send(ft, xfer->dest_fp); | |
| 1782 | |
| 1783 return FALSE; | |
| 1784 } | |
| 1785 | |
| 1786 | |
| 1787 static void mw_ft_opened(struct mwFileTransfer *ft) { | |
| 1788 /* | |
| 1789 - get gaim ft from client data in ft | |
| 1790 - set the state to active | |
| 1791 */ | |
| 1792 | |
| 1793 GaimXfer *xfer; | |
| 1794 | |
| 1795 xfer = mwFileTransfer_getClientData(ft); | |
| 1796 | |
| 1797 if(! xfer) { | |
| 1798 mwFileTransfer_cancel(ft); | |
| 1799 mwFileTransfer_free(ft); | |
| 1800 g_return_if_reached(); | |
| 1801 } | |
| 1802 | |
| 1803 gaim_xfer_update_progress(xfer); | |
| 1804 | |
| 1805 if(gaim_xfer_get_type(xfer) == GAIM_XFER_SEND) { | |
| 1806 xfer->watcher = g_idle_add((GSourceFunc)ft_idle_cb, ft); | |
| 1807 xfer->dest_fp = g_fopen(xfer->local_filename, "rb"); | |
| 1808 } | |
| 1809 } | |
| 1810 | |
| 1811 | |
| 1812 static void mw_ft_closed(struct mwFileTransfer *ft, guint32 code) { | |
| 1813 /* | |
| 1814 - get gaim ft from client data in ft | |
| 1815 - indicate rejection/cancelation/completion | |
| 1816 - free the file transfer itself | |
| 1817 */ | |
| 1818 | |
| 1819 GaimXfer *xfer; | |
| 1820 | |
| 1821 xfer = mwFileTransfer_getClientData(ft); | |
| 1822 if(xfer) { | |
| 1823 xfer->data = NULL; | |
| 1824 | |
| 1825 if(mwFileTransfer_isDone(ft)) { | |
| 1826 gaim_xfer_set_completed(xfer, TRUE); | |
| 1827 gaim_xfer_end(xfer); | |
| 1828 | |
| 1829 } else if(mwFileTransfer_isCancelLocal(ft)) { | |
| 1830 /* calling gaim_xfer_cancel_local is redundant, since that's | |
| 1831 probably what triggered this function to be called */ | |
| 1832 ; | |
| 1833 | |
| 1834 } else if(mwFileTransfer_isCancelRemote(ft)) { | |
| 1835 /* steal the reference for the xfer */ | |
| 1836 mwFileTransfer_setClientData(ft, NULL, NULL); | |
| 1837 gaim_xfer_cancel_remote(xfer); | |
| 1838 | |
| 1839 /* drop the stolen reference */ | |
| 1840 gaim_xfer_unref(xfer); | |
| 1841 return; | |
| 1842 } | |
| 1843 } | |
| 1844 | |
| 1845 mwFileTransfer_free(ft); | |
| 1846 } | |
| 1847 | |
| 1848 | |
| 1849 static void mw_ft_recv(struct mwFileTransfer *ft, | |
| 1850 struct mwOpaque *data) { | |
| 1851 /* | |
| 1852 - get gaim ft from client data in ft | |
| 1853 - update transfered percentage | |
| 1854 - if done, destroy the ft, disassociate from gaim ft | |
| 1855 */ | |
| 1856 | |
| 1857 GaimXfer *xfer; | |
| 1858 FILE *fp; | |
| 1859 | |
| 1860 xfer = mwFileTransfer_getClientData(ft); | |
| 1861 g_return_if_fail(xfer != NULL); | |
| 1862 | |
| 1863 fp = xfer->dest_fp; | |
| 1864 g_return_if_fail(fp != NULL); | |
| 1865 | |
| 1866 /* we must collect and save our precious data */ | |
| 1867 fwrite(data->data, 1, data->len, fp); | |
| 1868 | |
| 1869 /* update the progress */ | |
| 1870 xfer->bytes_sent += data->len; | |
| 1871 xfer->bytes_remaining -= data->len; | |
| 1872 gaim_xfer_update_progress(xfer); | |
| 1873 | |
| 1874 /* let the other side know we got it, and to send some more */ | |
| 1875 mwFileTransfer_ack(ft); | |
| 1876 } | |
| 1877 | |
| 1878 | |
| 1879 static void mw_ft_ack(struct mwFileTransfer *ft) { | |
| 1880 GaimXfer *xfer; | |
| 1881 | |
| 1882 xfer = mwFileTransfer_getClientData(ft); | |
| 1883 g_return_if_fail(xfer != NULL); | |
| 1884 g_return_if_fail(xfer->watcher == 0); | |
| 1885 | |
| 1886 gaim_xfer_update_progress(xfer); | |
| 1887 | |
| 1888 if(mwFileTransfer_isOpen(ft)) | |
| 1889 xfer->watcher = g_idle_add((GSourceFunc)ft_idle_cb, ft); | |
| 1890 } | |
| 1891 | |
| 1892 | |
| 1893 static void mw_ft_clear(struct mwServiceFileTransfer *srvc) { | |
| 1894 ; | |
| 1895 } | |
| 1896 | |
| 1897 | |
| 1898 static struct mwFileTransferHandler mw_ft_handler = { | |
| 1899 .ft_offered = mw_ft_offered, | |
| 1900 .ft_opened = mw_ft_opened, | |
| 1901 .ft_closed = mw_ft_closed, | |
| 1902 .ft_recv = mw_ft_recv, | |
| 1903 .ft_ack = mw_ft_ack, | |
| 1904 .clear = mw_ft_clear, | |
| 1905 }; | |
| 1906 | |
| 1907 | |
| 1908 static struct mwServiceFileTransfer *mw_srvc_ft_new(struct mwSession *s) { | |
| 1909 struct mwServiceFileTransfer *srvc; | |
| 1910 GHashTable *ft_map; | |
| 1911 | |
| 1912 ft_map = g_hash_table_new(g_direct_hash, g_direct_equal); | |
| 1913 | |
| 1914 srvc = mwServiceFileTransfer_new(s, &mw_ft_handler); | |
| 1915 mwService_setClientData(MW_SERVICE(srvc), ft_map, | |
| 1916 (GDestroyNotify) g_hash_table_destroy); | |
| 1917 | |
| 1918 return srvc; | |
| 1919 } | |
| 1920 | |
| 1921 | |
| 1922 static void convo_data_free(struct convo_data *cd) { | |
| 1923 GList *l; | |
| 1924 | |
| 1925 /* clean the queue */ | |
| 1926 for(l = cd->queue; l; l = g_list_delete_link(l, l)) { | |
| 1927 struct convo_msg *m = l->data; | |
| 1928 if(m->clear) m->clear(m->data); | |
| 1929 g_free(m); | |
| 1930 } | |
| 1931 | |
| 1932 g_free(cd); | |
| 1933 } | |
| 1934 | |
| 1935 | |
| 1936 /** allocates a convo_data structure and associates it with the | |
| 1937 conversation in the client data slot */ | |
| 1938 static void convo_data_new(struct mwConversation *conv) { | |
| 1939 struct convo_data *cd; | |
| 1940 | |
| 1941 g_return_if_fail(conv != NULL); | |
| 1942 | |
| 1943 if(mwConversation_getClientData(conv)) | |
| 1944 return; | |
| 1945 | |
| 1946 cd = g_new0(struct convo_data, 1); | |
| 1947 cd->conv = conv; | |
| 1948 | |
| 1949 mwConversation_setClientData(conv, cd, (GDestroyNotify) convo_data_free); | |
| 1950 } | |
| 1951 | |
| 1952 | |
| 1953 static GaimConversation *convo_get_gconv(struct mwConversation *conv) { | |
| 1954 struct mwServiceIm *srvc; | |
| 1955 struct mwSession *session; | |
| 1956 struct mwGaimPluginData *pd; | |
| 1957 GaimConnection *gc; | |
| 1958 GaimAccount *acct; | |
| 1959 | |
| 1960 struct mwIdBlock *idb; | |
| 1961 | |
| 1962 srvc = mwConversation_getService(conv); | |
| 1963 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 1964 pd = mwSession_getClientData(session); | |
| 1965 gc = pd->gc; | |
| 1966 acct = gaim_connection_get_account(gc); | |
| 1967 | |
| 1968 idb = mwConversation_getTarget(conv); | |
| 1969 | |
| 11338 | 1970 return gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM,idb->user, acct); |
| 10977 | 1971 } |
| 1972 | |
| 1973 | |
| 1974 static void convo_queue(struct mwConversation *conv, | |
| 1975 enum mwImSendType type, gconstpointer data) { | |
| 1976 | |
| 1977 struct convo_data *cd; | |
| 1978 struct convo_msg *m; | |
| 1979 | |
| 1980 convo_data_new(conv); | |
| 1981 cd = mwConversation_getClientData(conv); | |
| 1982 | |
| 1983 m = g_new0(struct convo_msg, 1); | |
| 1984 m->type = type; | |
| 1985 | |
| 1986 switch(type) { | |
| 1987 case mwImSend_PLAIN: | |
| 1988 m->data = g_strdup(data); | |
| 1989 m->clear = g_free; | |
| 1990 break; | |
| 1991 | |
| 1992 case mwImSend_TYPING: | |
| 1993 default: | |
| 1994 m->data = (gpointer) data; | |
| 1995 m->clear = NULL; | |
| 1996 } | |
| 1997 | |
| 1998 cd->queue = g_list_append(cd->queue, m); | |
| 1999 } | |
| 2000 | |
| 2001 | |
| 2002 /* Does what it takes to get an error displayed for a conversation */ | |
| 2003 static void convo_error(struct mwConversation *conv, guint32 err) { | |
| 2004 GaimConversation *gconv; | |
| 2005 char *tmp, *text; | |
| 2006 struct mwIdBlock *idb; | |
| 2007 | |
| 2008 idb = mwConversation_getTarget(conv); | |
| 2009 | |
| 2010 tmp = mwError(err); | |
| 2011 text = g_strconcat("Unable to send message: ", tmp, NULL); | |
| 2012 | |
| 2013 gconv = convo_get_gconv(conv); | |
| 2014 if(gconv && !gaim_conv_present_error(idb->user, gconv->account, text)) { | |
| 2015 | |
| 2016 g_free(text); | |
| 2017 text = g_strdup_printf("Unable to send message to %s:", | |
| 2018 (idb->user)? idb->user: "(unknown)"); | |
| 2019 gaim_notify_error(gaim_account_get_connection(gconv->account), | |
| 2020 NULL, text, tmp); | |
| 2021 } | |
| 2022 | |
| 2023 g_free(tmp); | |
| 2024 g_free(text); | |
| 2025 } | |
| 2026 | |
| 2027 | |
| 2028 static void convo_queue_send(struct mwConversation *conv) { | |
| 2029 struct convo_data *cd; | |
| 2030 GList *l; | |
| 2031 | |
| 2032 cd = mwConversation_getClientData(conv); | |
| 2033 | |
| 2034 for(l = cd->queue; l; l = g_list_delete_link(l, l)) { | |
| 2035 struct convo_msg *m = l->data; | |
| 2036 | |
| 2037 mwConversation_send(conv, m->type, m->data); | |
| 2038 | |
| 2039 if(m->clear) m->clear(m->data); | |
| 2040 g_free(m); | |
| 2041 } | |
| 2042 | |
| 2043 cd->queue = NULL; | |
| 2044 } | |
| 2045 | |
| 2046 | |
| 2047 /** called when a mw conversation leaves a gaim conversation to | |
| 2048 inform the gaim conversation that it's unsafe to offer any *cool* | |
| 2049 features. */ | |
| 2050 static void convo_nofeatures(struct mwConversation *conv) { | |
| 2051 GaimConversation *gconv; | |
| 2052 GaimConnection *gc; | |
| 2053 | |
| 2054 gconv = convo_get_gconv(conv); | |
| 2055 if(! gconv) return; | |
| 2056 | |
| 2057 gc = gaim_conversation_get_gc(gconv); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2058 if(! gc) return; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2059 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2060 gaim_conversation_set_features(gconv, gc->flags); |
| 10977 | 2061 } |
| 2062 | |
| 2063 | |
| 2064 /** called when a mw conversation and gaim conversation come together, | |
| 2065 to inform the gaim conversation of what features to offer the | |
| 2066 user */ | |
| 2067 static void convo_features(struct mwConversation *conv) { | |
| 2068 GaimConversation *gconv; | |
| 2069 GaimConnectionFlags feat; | |
| 2070 | |
| 2071 gconv = convo_get_gconv(conv); | |
| 2072 if(! gconv) return; | |
| 2073 | |
| 2074 feat = gaim_conversation_get_features(gconv); | |
| 2075 | |
| 2076 if(mwConversation_isOpen(conv)) { | |
| 2077 if(mwConversation_supports(conv, mwImSend_HTML)) { | |
| 2078 feat |= GAIM_CONNECTION_HTML; | |
| 2079 } else { | |
| 2080 feat &= ~GAIM_CONNECTION_HTML; | |
| 2081 } | |
| 2082 | |
| 2083 if(mwConversation_supports(conv, mwImSend_MIME)) { | |
| 2084 feat &= ~GAIM_CONNECTION_NO_IMAGES; | |
| 2085 } else { | |
| 2086 feat |= GAIM_CONNECTION_NO_IMAGES; | |
| 2087 } | |
| 2088 | |
| 2089 DEBUG_INFO("conversation features set to 0x%04x\n", feat); | |
| 2090 gaim_conversation_set_features(gconv, feat); | |
| 2091 | |
| 2092 } else { | |
| 2093 convo_nofeatures(conv); | |
| 2094 } | |
| 2095 } | |
| 2096 | |
| 2097 | |
| 2098 /** triggered from mw_conversation_opened if the appropriate plugin | |
| 2099 preference is set. This will open a window for the conversation | |
| 2100 before the first message is sent. */ | |
| 2101 static void convo_do_psychic(struct mwConversation *conv) { | |
| 2102 struct mwServiceIm *srvc; | |
| 2103 struct mwSession *session; | |
| 2104 struct mwGaimPluginData *pd; | |
| 2105 GaimConnection *gc; | |
| 2106 GaimAccount *acct; | |
| 2107 | |
| 2108 struct mwIdBlock *idb; | |
| 2109 | |
| 2110 GaimConversation *gconv; | |
| 2111 | |
| 2112 srvc = mwConversation_getService(conv); | |
| 2113 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 2114 pd = mwSession_getClientData(session); | |
| 2115 gc = pd->gc; | |
| 2116 acct = gaim_connection_get_account(gc); | |
| 2117 | |
| 2118 idb = mwConversation_getTarget(conv); | |
| 2119 | |
| 11338 | 2120 gconv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, idb->user, acct); |
| 10977 | 2121 if(! gconv) { |
| 11338 | 2122 gconv = gaim_conversation_new(GAIM_CONV_TYPE_IM, acct, idb->user); |
| 10977 | 2123 } |
| 2124 | |
| 2125 g_return_if_fail(gconv != NULL); | |
| 2126 | |
| 11547 | 2127 |
| 2128 #if 0 | |
| 2129 GaimConvWindow *win; | |
| 10977 | 2130 win = gaim_conversation_get_window(gconv); |
| 2131 g_return_if_fail(win != NULL); | |
| 2132 | |
| 2133 gaim_conv_window_show(win); | |
| 11547 | 2134 #endif |
| 10977 | 2135 } |
| 2136 | |
| 2137 | |
| 2138 static void mw_conversation_opened(struct mwConversation *conv) { | |
| 2139 struct mwServiceIm *srvc; | |
| 2140 struct mwSession *session; | |
| 2141 struct mwGaimPluginData *pd; | |
| 2142 GaimConnection *gc; | |
| 2143 GaimAccount *acct; | |
| 2144 | |
| 2145 struct convo_dat *cd; | |
| 2146 | |
| 2147 srvc = mwConversation_getService(conv); | |
| 2148 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 2149 pd = mwSession_getClientData(session); | |
| 2150 gc = pd->gc; | |
| 2151 acct = gaim_connection_get_account(gc); | |
| 2152 | |
| 2153 /* set up the queue */ | |
| 2154 cd = mwConversation_getClientData(conv); | |
| 2155 if(cd) { | |
| 2156 convo_queue_send(conv); | |
| 2157 | |
| 2158 if(! convo_get_gconv(conv)) { | |
| 2159 mwConversation_free(conv); | |
| 2160 return; | |
| 2161 } | |
| 2162 | |
| 2163 } else { | |
| 2164 convo_data_new(conv); | |
| 2165 | |
| 2166 if(gaim_prefs_get_bool(MW_PRPL_OPT_PSYCHIC)) { | |
| 2167 convo_do_psychic(conv); | |
| 2168 } | |
| 2169 } | |
| 2170 | |
| 2171 { /* record the client key for the buddy */ | |
| 2172 GaimBuddy *buddy; | |
| 2173 struct mwLoginInfo *info; | |
| 2174 info = mwConversation_getTargetInfo(conv); | |
| 2175 | |
| 2176 buddy = gaim_find_buddy(acct, info->user_id); | |
| 2177 if(buddy) { | |
| 2178 gaim_blist_node_set_int((GaimBlistNode *) buddy, | |
| 2179 BUDDY_KEY_CLIENT, info->type); | |
| 2180 } | |
| 2181 } | |
| 2182 | |
| 2183 convo_features(conv); | |
| 2184 } | |
| 2185 | |
| 2186 | |
| 2187 static void mw_conversation_closed(struct mwConversation *conv, | |
| 2188 guint32 reason) { | |
| 2189 | |
| 2190 struct convo_data *cd; | |
| 2191 | |
| 2192 g_return_if_fail(conv != NULL); | |
| 2193 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2194 /* if there's a error code and a non-typing message in the queue, |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2195 print an error message to the conversation */ |
| 10977 | 2196 cd = mwConversation_getClientData(conv); |
| 2197 if(reason && cd && cd->queue) { | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2198 GList *l; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2199 for(l = cd->queue; l; l = l->next) { |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2200 struct convo_msg *m = l->data; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2201 if(m->type != mwImSend_TYPING) { |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2202 convo_error(conv, reason); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2203 break; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2204 } |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2205 } |
| 10977 | 2206 } |
| 2207 | |
| 2208 #if 0 | |
| 2209 /* don't do this, to prevent the occasional weird sending of | |
| 2210 formatted messages as plaintext when the other end closes the | |
| 2211 conversation after we've begun composing the message */ | |
| 2212 convo_nofeatures(conv); | |
| 2213 #endif | |
| 2214 | |
| 2215 mwConversation_removeClientData(conv); | |
| 2216 } | |
| 2217 | |
| 2218 | |
| 2219 static void im_recv_text(struct mwConversation *conv, | |
| 2220 struct mwGaimPluginData *pd, | |
| 2221 const char *msg) { | |
| 2222 | |
| 2223 struct mwIdBlock *idb; | |
| 2224 char *txt, *esc; | |
| 2225 | |
| 2226 idb = mwConversation_getTarget(conv); | |
| 2227 txt = gaim_utf8_try_convert(msg); | |
| 2228 esc = g_markup_escape_text(txt, -1); | |
| 2229 | |
| 2230 serv_got_im(pd->gc, idb->user, esc, 0, time(NULL)); | |
| 2231 | |
| 2232 g_free(txt); | |
| 2233 g_free(esc); | |
| 2234 } | |
| 2235 | |
| 2236 | |
| 2237 static void im_recv_typing(struct mwConversation *conv, | |
| 2238 struct mwGaimPluginData *pd, | |
| 2239 gboolean typing) { | |
| 2240 | |
| 2241 struct mwIdBlock *idb; | |
| 2242 idb = mwConversation_getTarget(conv); | |
| 2243 | |
| 2244 serv_got_typing(pd->gc, idb->user, 0, | |
| 2245 typing? GAIM_TYPING: GAIM_NOT_TYPING); | |
| 2246 } | |
| 2247 | |
| 2248 | |
| 2249 static void im_recv_html(struct mwConversation *conv, | |
| 2250 struct mwGaimPluginData *pd, | |
| 2251 const char *msg) { | |
| 2252 | |
| 2253 struct mwIdBlock *idb; | |
| 2254 char *txt; | |
| 2255 | |
| 2256 idb = mwConversation_getTarget(conv); | |
| 2257 txt = gaim_utf8_try_convert(msg); | |
| 2258 | |
| 2259 serv_got_im(pd->gc, idb->user, txt, 0, time(NULL)); | |
| 2260 | |
| 2261 g_free(txt); | |
| 2262 } | |
| 2263 | |
| 2264 | |
| 2265 static void im_recv_subj(struct mwConversation *conv, | |
| 2266 struct mwGaimPluginData *pd, | |
| 2267 const char *subj) { | |
| 2268 | |
| 2269 /** @todo somehow indicate receipt of a conversation subject. It | |
| 2270 would also be nice if we added a /topic command for the | |
| 2271 protocol */ | |
| 2272 ; | |
| 2273 } | |
| 2274 | |
| 2275 | |
| 2276 /** generate "cid:908@20582notesbuddy" from "<908@20582notesbuddy>" */ | |
| 2277 static char *make_cid(const char *cid) { | |
| 2278 gsize n; | |
| 2279 char *c, *d; | |
| 2280 | |
| 2281 g_return_val_if_fail(cid != NULL, NULL); | |
| 2282 | |
| 2283 n = strlen(cid); | |
| 2284 g_return_val_if_fail(n > 2, NULL); | |
| 2285 | |
| 2286 c = g_strndup(cid+1, n-2); | |
| 2287 d = g_strdup_printf("cid:%s", c); | |
| 2288 | |
| 2289 g_free(c); | |
| 2290 return d; | |
| 2291 } | |
| 2292 | |
| 2293 | |
| 2294 static void im_recv_mime(struct mwConversation *conv, | |
| 2295 struct mwGaimPluginData *pd, | |
| 2296 const char *data) { | |
| 2297 | |
| 2298 struct mwIdBlock *idb; | |
| 2299 | |
| 2300 GHashTable *img_by_cid; | |
| 2301 GList *images; | |
| 2302 | |
| 2303 GString *str; | |
| 2304 | |
| 2305 GaimMimeDocument *doc; | |
| 2306 const GList *parts; | |
| 2307 | |
| 2308 idb = mwConversation_getTarget(conv); | |
| 2309 | |
| 2310 img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); | |
| 2311 images = NULL; | |
| 2312 | |
| 2313 /* don't want the contained string to ever be NULL */ | |
| 2314 str = g_string_new(""); | |
| 2315 | |
| 2316 doc = gaim_mime_document_parse(data); | |
| 2317 | |
| 2318 /* handle all the MIME parts */ | |
| 2319 parts = gaim_mime_document_get_parts(doc); | |
| 2320 for(; parts; parts = parts->next) { | |
| 2321 GaimMimePart *part = parts->data; | |
| 2322 const char *type; | |
| 2323 | |
| 2324 type = gaim_mime_part_get_field(part, "content-type"); | |
| 2325 DEBUG_INFO("MIME part Content-Type: %s\n", NSTR(type)); | |
| 2326 | |
| 2327 if(! type) { | |
| 2328 ; /* feh */ | |
| 2329 | |
| 2330 } else if(g_str_has_prefix(type, "image")) { | |
| 2331 /* put images into the image store */ | |
| 2332 | |
| 11183 | 2333 guchar *d_dat; |
| 10977 | 2334 gsize d_len; |
| 2335 char *cid; | |
| 2336 int img; | |
| 2337 | |
| 2338 /* obtain and unencode the data */ | |
| 2339 gaim_mime_part_get_data_decoded(part, &d_dat, &d_len); | |
| 2340 | |
| 2341 /* look up the content id */ | |
| 2342 cid = (char *) gaim_mime_part_get_field(part, "Content-ID"); | |
| 2343 cid = make_cid(cid); | |
| 2344 | |
| 2345 /* add image to the gaim image store */ | |
| 2346 img = gaim_imgstore_add(d_dat, d_len, cid); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2347 g_free(d_dat); |
| 10977 | 2348 |
| 2349 /* map the cid to the image store identifier */ | |
| 2350 g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img)); | |
| 2351 | |
| 2352 /* recall the image for dereferencing later */ | |
| 2353 images = g_list_append(images, GINT_TO_POINTER(img)); | |
| 2354 | |
| 2355 } else if(g_str_has_prefix(type, "text")) { | |
| 2356 | |
| 2357 /* concatenate all the text parts together */ | |
| 11183 | 2358 guchar *data; |
| 11132 | 2359 char *txt; |
| 10977 | 2360 gsize len; |
| 2361 | |
| 2362 gaim_mime_part_get_data_decoded(part, &data, &len); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2363 |
| 11132 | 2364 txt = gaim_utf8_try_convert((const char *)data); |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2365 g_free(data); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2366 |
| 10977 | 2367 g_string_append(str, txt); |
| 2368 g_free(txt); | |
| 2369 } | |
| 2370 } | |
| 2371 | |
| 2372 gaim_mime_document_free(doc); | |
| 2373 | |
| 2374 { /* replace each IMG tag's SRC attribute with an ID attribute. This | |
| 2375 actually modifies the contents of str */ | |
| 2376 GData *attribs; | |
| 2377 char *start, *end; | |
| 2378 char *tmp = str->str; | |
| 2379 | |
| 2380 while(*tmp && gaim_markup_find_tag("img", tmp, (const char **) &start, | |
| 2381 (const char **) &end, &attribs)) { | |
| 2382 | |
| 2383 char *alt, *align, *border, *src; | |
| 2384 int img; | |
| 2385 | |
| 2386 alt = g_datalist_get_data(&attribs, "alt"); | |
| 2387 align = g_datalist_get_data(&attribs, "align"); | |
| 2388 border = g_datalist_get_data(&attribs, "border"); | |
| 2389 src = g_datalist_get_data(&attribs, "src"); | |
| 2390 | |
| 2391 img = GPOINTER_TO_INT(g_hash_table_lookup(img_by_cid, src)); | |
| 2392 if(img) { | |
| 2393 GString *atstr; | |
| 2394 gsize len = (end - start); | |
| 2395 gsize mov; | |
| 2396 | |
| 2397 atstr = g_string_new(""); | |
| 2398 if(alt) g_string_append_printf(atstr, " alt=\"%s\"", alt); | |
| 2399 if(align) g_string_append_printf(atstr, " align=\"%s\"", align); | |
| 2400 if(border) g_string_append_printf(atstr, " border=\"%s\"", border); | |
| 2401 | |
| 2402 mov = g_snprintf(start, len, "<img%s id=\"%i\"", atstr->str, img); | |
| 2403 while(mov < len) start[mov++] = ' '; | |
| 2404 | |
| 2405 g_string_free(atstr, TRUE); | |
| 2406 } | |
| 2407 | |
| 2408 g_datalist_clear(&attribs); | |
| 2409 tmp = end + 1; | |
| 2410 } | |
| 2411 } | |
| 2412 | |
| 2413 /* actually display the message */ | |
| 2414 serv_got_im(pd->gc, idb->user, str->str, 0, time(NULL)); | |
| 2415 | |
| 2416 g_string_free(str, TRUE); | |
| 2417 | |
| 2418 /* clean up the cid table */ | |
| 2419 g_hash_table_destroy(img_by_cid); | |
| 2420 | |
| 2421 /* dereference all the imgages */ | |
| 2422 while(images) { | |
| 2423 gaim_imgstore_unref(GPOINTER_TO_INT(images->data)); | |
| 2424 images = g_list_delete_link(images, images); | |
| 2425 } | |
| 2426 } | |
| 2427 | |
| 2428 | |
| 2429 static void mw_conversation_recv(struct mwConversation *conv, | |
| 2430 enum mwImSendType type, | |
| 2431 gconstpointer msg) { | |
| 2432 struct mwServiceIm *srvc; | |
| 2433 struct mwSession *session; | |
| 2434 struct mwGaimPluginData *pd; | |
| 2435 | |
| 2436 srvc = mwConversation_getService(conv); | |
| 2437 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 2438 pd = mwSession_getClientData(session); | |
| 2439 | |
| 2440 switch(type) { | |
| 2441 case mwImSend_PLAIN: | |
| 2442 im_recv_text(conv, pd, msg); | |
| 2443 break; | |
| 2444 | |
| 2445 case mwImSend_TYPING: | |
| 2446 im_recv_typing(conv, pd, !! msg); | |
| 2447 break; | |
| 2448 | |
| 2449 case mwImSend_HTML: | |
| 2450 im_recv_html(conv, pd, msg); | |
| 2451 break; | |
| 2452 | |
| 2453 case mwImSend_SUBJECT: | |
| 2454 im_recv_subj(conv, pd, msg); | |
| 2455 break; | |
| 2456 | |
| 2457 case mwImSend_MIME: | |
| 2458 im_recv_mime(conv, pd, msg); | |
| 2459 break; | |
| 2460 | |
| 2461 default: | |
| 2462 DEBUG_INFO("conversation received strange type, 0x%04x\n", type); | |
| 2463 ; /* erm... */ | |
| 2464 } | |
| 2465 } | |
| 2466 | |
| 2467 | |
| 2468 #if 0 | |
| 2469 /* this will be appropriate when meanwhile supports the Place service */ | |
| 2470 static void mw_place_invite(struct mwConversation *conv, | |
| 2471 const char *message, | |
| 2472 const char *title, const char *name) { | |
| 2473 struct mwServiceIm *srvc; | |
| 2474 struct mwSession *session; | |
| 2475 struct mwGaimPluginData *pd; | |
| 2476 | |
| 2477 struct mwIdBlock *idb; | |
| 2478 GHashTable *ht; | |
| 2479 | |
| 2480 srvc = mwConversation_getService(conv); | |
| 2481 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 2482 pd = mwSession_getClientData(session); | |
| 2483 | |
| 2484 idb = mwConversation_getTarget(conv); | |
| 2485 | |
| 2486 ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); | |
| 2487 g_hash_table_insert(ht, CHAT_KEY_CREATOR, g_strdup(idb->user)); | |
| 2488 g_hash_table_insert(ht, CHAT_KEY_NAME, g_strdup(name)); | |
| 2489 g_hash_table_insert(ht, CHAT_KEY_TOPIC, g_strdup(title)); | |
| 2490 g_hash_table_insert(ht, CHAT_KEY_INVITE, g_strdup(message)); | |
| 2491 | |
| 2492 serv_got_chat_invite(pd->gc, title, idb->user, message, ht); | |
| 2493 } | |
| 2494 #endif | |
| 2495 | |
| 2496 | |
| 2497 static void mw_im_clear(struct mwServiceIm *srvc) { | |
| 2498 ; | |
| 2499 } | |
| 2500 | |
| 2501 | |
| 2502 static struct mwImHandler mw_im_handler = { | |
| 2503 .conversation_opened = mw_conversation_opened, | |
| 2504 .conversation_closed = mw_conversation_closed, | |
| 2505 .conversation_recv = mw_conversation_recv, | |
| 2506 .place_invite = NULL, /* = mw_place_invite, */ | |
| 2507 .clear = mw_im_clear, | |
| 2508 }; | |
| 2509 | |
| 2510 | |
| 2511 static struct mwServiceIm *mw_srvc_im_new(struct mwSession *s) { | |
| 2512 struct mwServiceIm *srvc; | |
| 2513 srvc = mwServiceIm_new(s, &mw_im_handler); | |
| 2514 mwServiceIm_setClientType(srvc, mwImClient_NOTESBUDDY); | |
| 2515 return srvc; | |
| 2516 } | |
| 2517 | |
| 2518 | |
| 2519 static struct mwServiceResolve *mw_srvc_resolve_new(struct mwSession *s) { | |
| 2520 struct mwServiceResolve *srvc; | |
| 2521 srvc = mwServiceResolve_new(s); | |
| 2522 return srvc; | |
| 2523 } | |
| 2524 | |
| 2525 | |
| 2526 static struct mwServiceStorage *mw_srvc_store_new(struct mwSession *s) { | |
| 2527 struct mwServiceStorage *srvc; | |
| 2528 srvc = mwServiceStorage_new(s); | |
| 2529 return srvc; | |
| 2530 } | |
| 2531 | |
| 2532 | |
| 2533 /** allocate and associate a mwGaimPluginData with a GaimConnection */ | |
| 2534 static struct mwGaimPluginData *mwGaimPluginData_new(GaimConnection *gc) { | |
| 2535 struct mwGaimPluginData *pd; | |
| 2536 | |
| 2537 g_return_val_if_fail(gc != NULL, NULL); | |
| 2538 | |
| 2539 pd = g_new0(struct mwGaimPluginData, 1); | |
| 2540 pd->gc = gc; | |
| 2541 pd->session = mwSession_new(&mw_session_handler); | |
| 2542 pd->srvc_aware = mw_srvc_aware_new(pd->session); | |
| 2543 pd->srvc_conf = mw_srvc_conf_new(pd->session); | |
| 2544 pd->srvc_ft = mw_srvc_ft_new(pd->session); | |
| 2545 pd->srvc_im = mw_srvc_im_new(pd->session); | |
| 2546 pd->srvc_resolve = mw_srvc_resolve_new(pd->session); | |
| 2547 pd->srvc_store = mw_srvc_store_new(pd->session); | |
| 2548 pd->group_list_map = g_hash_table_new(g_direct_hash, g_direct_equal); | |
| 2549 | |
| 2550 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_aware)); | |
| 2551 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_conf)); | |
| 2552 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_ft)); | |
| 2553 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_im)); | |
| 2554 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_resolve)); | |
| 2555 mwSession_addService(pd->session, MW_SERVICE(pd->srvc_store)); | |
| 2556 | |
| 2557 mwSession_addCipher(pd->session, mwCipher_new_RC2_40(pd->session)); | |
| 2558 | |
| 2559 mwSession_setClientData(pd->session, pd, NULL); | |
| 2560 gc->proto_data = pd; | |
| 2561 | |
| 2562 return pd; | |
| 2563 } | |
| 2564 | |
| 2565 | |
| 2566 static void mwGaimPluginData_free(struct mwGaimPluginData *pd) { | |
| 2567 g_return_if_fail(pd != NULL); | |
| 2568 | |
| 2569 pd->gc->proto_data = NULL; | |
| 2570 | |
| 2571 mwSession_removeService(pd->session, SERVICE_AWARE); | |
| 2572 mwSession_removeService(pd->session, SERVICE_CONFERENCE); | |
| 2573 mwSession_removeService(pd->session, SERVICE_IM); | |
| 2574 mwSession_removeService(pd->session, SERVICE_RESOLVE); | |
| 2575 mwSession_removeService(pd->session, SERVICE_STORAGE); | |
| 2576 | |
| 2577 mwService_free(MW_SERVICE(pd->srvc_aware)); | |
| 2578 mwService_free(MW_SERVICE(pd->srvc_conf)); | |
| 2579 mwService_free(MW_SERVICE(pd->srvc_im)); | |
| 2580 mwService_free(MW_SERVICE(pd->srvc_resolve)); | |
| 2581 mwService_free(MW_SERVICE(pd->srvc_store)); | |
| 2582 | |
| 2583 mwCipher_free(mwSession_getCipher(pd->session, mwCipher_RC2_40)); | |
| 2584 | |
| 2585 mwSession_free(pd->session); | |
| 2586 | |
| 2587 g_hash_table_destroy(pd->group_list_map); | |
| 2588 | |
| 2589 g_free(pd); | |
| 2590 } | |
| 2591 | |
| 2592 | |
| 2593 static const char *mw_prpl_list_icon(GaimAccount *a, GaimBuddy *b) { | |
| 2594 /* my little green dude is a chopped up version of the aim running | |
| 2595 guy. First, cut off the head and store someplace safe. Then, | |
| 2596 take the left-half side of the body and throw it away. Make a | |
| 2597 copy of the remaining body, and flip it horizontally. Now attach | |
| 2598 the two pieces into an X shape, and drop the head back on the | |
| 2599 top, being careful to center it. Then, just change the color | |
| 2600 saturation to bring the red down a bit, and voila! */ | |
| 2601 | |
| 2602 /* then, throw all of that away and use sodipodi to make a new | |
| 2603 icon. You know, LIKE A REAL MAN. */ | |
| 2604 | |
| 2605 return "meanwhile"; | |
| 2606 } | |
| 2607 | |
| 2608 | |
| 2609 static void mw_prpl_list_emblems(GaimBuddy *b, | |
| 2610 const char **se, const char **sw, | |
| 2611 const char **nw, const char **ne) { | |
| 11536 | 2612 GaimPresence *presence = gaim_buddy_get_presence(b); |
| 2613 GaimStatus *status = gaim_presence_get_active_status(presence); | |
| 10977 | 2614 |
| 2615 if(! GAIM_BUDDY_IS_ONLINE(b)) { | |
| 2616 *se = "offline"; | |
| 11536 | 2617 } else if(!gaim_presence_is_available(presence) && |
| 2618 !strcmp(gaim_status_get_id(status), MW_STATE_AWAY)) { | |
| 10977 | 2619 *se = "away"; |
| 11536 | 2620 } else if(!gaim_presence_is_available(presence) && |
| 2621 !strcmp(gaim_status_get_id(status), MW_STATE_BUSY)) { | |
| 10977 | 2622 *se = "dnd"; |
| 2623 } | |
| 2624 } | |
| 2625 | |
| 2626 | |
| 2627 static char *mw_prpl_status_text(GaimBuddy *b) { | |
| 2628 GaimConnection *gc; | |
| 2629 struct mwGaimPluginData *pd; | |
| 2630 struct mwAwareIdBlock t = { mwAware_USER, b->name, NULL }; | |
| 2631 const char *ret; | |
| 2632 | |
| 2633 gc = b->account->gc; | |
| 2634 pd = gc->proto_data; | |
| 2635 | |
| 2636 ret = mwServiceAware_getText(pd->srvc_aware, &t); | |
| 2637 return (ret)? g_strdup(ret): NULL; | |
| 2638 } | |
| 2639 | |
| 2640 | |
| 2641 static const char *status_text(GaimBuddy *b) { | |
| 11536 | 2642 GaimPresence *presence = gaim_buddy_get_presence(b); |
| 2643 GaimStatus *status = gaim_presence_get_active_status(presence); | |
| 2644 | |
| 2645 return gaim_status_get_name(status); | |
| 2646 | |
| 2647 /* I left this here in case it's more accurate than the status name. | |
| 2648 * Stu. */ | |
| 2649 #if 0 | |
| 2650 | |
| 10977 | 2651 guint status = b->uc; |
| 2652 | |
| 2653 if(! GAIM_BUDDY_IS_ONLINE(b) ) { | |
| 2654 return MW_STATE_OFFLINE; | |
| 2655 | |
| 2656 } else if(status == (mwStatus_AWAY /* XXX | UC_UNAVAILABLE */)) { | |
| 2657 return MW_STATE_AWAY; | |
| 2658 | |
| 2659 } else if(status == (mwStatus_BUSY /* XXX | UC_UNAVAILABLE */)) { | |
| 2660 return MW_STATE_BUSY; | |
| 2661 | |
| 2662 } else if(status == mwStatus_IDLE) { | |
| 2663 return MW_STATE_IDLE; | |
| 2664 | |
| 2665 } else if(status == mwStatus_ACTIVE) { | |
| 2666 return MW_STATE_ACTIVE; | |
| 2667 | |
| 2668 } else { | |
| 2669 return MW_STATE_UNKNOWN; | |
| 2670 } | |
| 11536 | 2671 #endif |
| 10977 | 2672 } |
| 2673 | |
| 2674 | |
| 2675 static gboolean user_supports(struct mwServiceAware *srvc, | |
| 2676 const char *who, guint32 feature) { | |
| 2677 | |
| 2678 const struct mwAwareAttribute *attr; | |
| 2679 struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; | |
| 2680 | |
| 2681 attr = mwServiceAware_getAttribute(srvc, &idb, feature); | |
| 2682 return (attr != NULL) && mwAwareAttribute_asBoolean(attr); | |
| 2683 } | |
| 2684 | |
| 2685 | |
| 2686 char *user_supports_text(struct mwServiceAware *srvc, const char *who) { | |
| 2687 char *feat[] = {NULL, NULL, NULL, NULL, NULL}; | |
| 2688 char **f = feat; | |
| 2689 | |
| 2690 if(user_supports(srvc, who, mwAttribute_AV_PREFS_SET)) { | |
| 2691 gboolean mic, speak, video; | |
| 2692 | |
| 2693 mic = user_supports(srvc, who, mwAttribute_MICROPHONE); | |
| 2694 speak = user_supports(srvc, who, mwAttribute_SPEAKERS); | |
| 2695 video = user_supports(srvc, who, mwAttribute_VIDEO_CAMERA); | |
| 2696 | |
| 2697 if(mic) *f++ = "Microphone"; | |
| 2698 if(speak) *f++ = "Speakers"; | |
| 2699 if(video) *f++ = "Video Camera"; | |
| 2700 } | |
| 2701 | |
| 2702 if(user_supports(srvc, who, mwAttribute_FILE_TRANSFER)) | |
| 2703 *f++ = "File Transfer"; | |
| 2704 | |
| 2705 return (*feat)? g_strjoinv(", ", feat): NULL; | |
| 2706 /* jenni loves siege */ | |
| 2707 } | |
| 2708 | |
| 2709 | |
| 2710 static char *mw_prpl_tooltip_text(GaimBuddy *b) { | |
| 2711 GaimConnection *gc; | |
| 2712 struct mwGaimPluginData *pd; | |
| 2713 struct mwAwareIdBlock idb = { mwAware_USER, b->name, NULL }; | |
| 2714 | |
| 2715 GString *str; | |
| 2716 const char *tmp; | |
| 2717 | |
| 2718 gc = b->account->gc; | |
| 2719 pd = gc->proto_data; | |
| 2720 | |
| 2721 str = g_string_new(NULL); | |
| 2722 | |
| 2723 tmp = status_text(b); | |
| 2724 g_string_append_printf(str, "\n<b>Status</b>: %s", tmp); | |
| 2725 | |
| 2726 tmp = mwServiceAware_getText(pd->srvc_aware, &idb); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2727 if(tmp) { |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2728 tmp = g_markup_escape_text(tmp, -1); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2729 g_string_append_printf(str, "\n<b>Message</b>: %s", tmp); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2730 g_free((char *) tmp); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2731 } |
| 10977 | 2732 |
| 2733 tmp = user_supports_text(pd->srvc_aware, b->name); | |
| 2734 if(tmp) { | |
| 2735 g_string_append_printf(str, "\n<b>Supports</b>: %s", tmp); | |
| 2736 g_free((char *) tmp); | |
| 2737 } | |
| 2738 | |
| 2739 tmp = str->str; | |
| 2740 g_string_free(str, FALSE); | |
| 2741 return (char *) tmp; | |
| 2742 } | |
| 2743 | |
| 2744 | |
| 2745 static GList *mw_prpl_status_types(GaimAccount *acct) { | |
| 2746 GList *types = NULL; | |
| 2747 GaimStatusType *type; | |
| 2748 | |
| 2749 type = gaim_status_type_new(GAIM_STATUS_OFFLINE, MW_STATE_OFFLINE, | |
| 2750 _("Offline"), TRUE); | |
| 2751 types = g_list_append(types, type); | |
| 2752 | |
| 2753 type = gaim_status_type_new(GAIM_STATUS_AVAILABLE, MW_STATE_ACTIVE, | |
| 2754 _("Active"), TRUE); | |
| 2755 gaim_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"), | |
| 2756 gaim_value_new(GAIM_TYPE_STRING)); | |
| 2757 types = g_list_append(types, type); | |
| 2758 | |
| 2759 type = gaim_status_type_new(GAIM_STATUS_AWAY, MW_STATE_AWAY, | |
| 2760 _("Away"), TRUE); | |
| 2761 gaim_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"), | |
| 2762 gaim_value_new(GAIM_TYPE_STRING)); | |
| 2763 types = g_list_append(types, type); | |
| 2764 | |
| 2765 type = gaim_status_type_new(GAIM_STATUS_UNAVAILABLE, MW_STATE_BUSY, | |
| 2766 _("Do Not Disturb"), TRUE); | |
| 2767 gaim_status_type_add_attr(type, MW_STATE_MESSAGE, _("Message"), | |
| 2768 gaim_value_new(GAIM_TYPE_STRING)); | |
| 2769 types = g_list_append(types, type); | |
| 2770 | |
| 2771 return types; | |
| 2772 } | |
| 2773 | |
| 2774 | |
| 2775 static void conf_create_prompt_cancel(GaimBuddy *buddy, | |
| 2776 GaimRequestFields *fields) { | |
| 2777 ; | |
| 2778 } | |
| 2779 | |
| 2780 | |
| 2781 static void conf_create_prompt_join(GaimBuddy *buddy, | |
| 2782 GaimRequestFields *fields) { | |
| 2783 GaimAccount *acct; | |
| 2784 GaimConnection *gc; | |
| 2785 struct mwGaimPluginData *pd; | |
| 2786 struct mwServiceConference *srvc; | |
| 2787 | |
| 2788 GaimRequestField *f; | |
| 2789 | |
| 2790 const char *topic, *invite; | |
| 2791 struct mwConference *conf; | |
| 2792 struct mwIdBlock idb = { NULL, NULL }; | |
| 2793 | |
| 2794 acct = buddy->account; | |
| 2795 gc = gaim_account_get_connection(acct); | |
| 2796 pd = gc->proto_data; | |
| 2797 srvc = pd->srvc_conf; | |
| 2798 | |
| 2799 f = gaim_request_fields_get_field(fields, CHAT_KEY_TOPIC); | |
| 2800 topic = gaim_request_field_string_get_value(f); | |
| 2801 | |
| 2802 f = gaim_request_fields_get_field(fields, CHAT_KEY_INVITE); | |
| 2803 invite = gaim_request_field_string_get_value(f); | |
| 2804 | |
| 2805 conf = mwConference_new(srvc, topic); | |
| 2806 mwConference_open(conf); | |
| 2807 | |
| 2808 idb.user = buddy->name; | |
| 2809 mwConference_invite(conf, &idb, invite); | |
| 2810 } | |
| 2811 | |
| 2812 | |
| 2813 static void blist_menu_conf_create(GaimBuddy *buddy, const char *msg) { | |
| 2814 | |
| 2815 GaimRequestFields *fields; | |
| 2816 GaimRequestFieldGroup *g; | |
| 2817 GaimRequestField *f; | |
| 2818 | |
| 2819 GaimAccount *acct; | |
| 2820 GaimConnection *gc; | |
| 2821 | |
| 2822 char *msgA, *msgB; | |
| 2823 | |
| 2824 g_return_if_fail(buddy != NULL); | |
| 2825 | |
| 2826 acct = buddy->account; | |
| 2827 g_return_if_fail(acct != NULL); | |
| 2828 | |
| 2829 gc = gaim_account_get_connection(acct); | |
| 2830 g_return_if_fail(gc != NULL); | |
| 2831 | |
| 2832 fields = gaim_request_fields_new(); | |
| 2833 | |
| 2834 g = gaim_request_field_group_new(NULL); | |
| 2835 gaim_request_fields_add_group(fields, g); | |
| 2836 | |
| 2837 f = gaim_request_field_string_new(CHAT_KEY_TOPIC, "Topic", NULL, FALSE); | |
| 2838 gaim_request_field_group_add_field(g, f); | |
| 2839 | |
| 2840 f = gaim_request_field_string_new(CHAT_KEY_INVITE, "Message", msg, FALSE); | |
| 2841 gaim_request_field_group_add_field(g, f); | |
| 2842 | |
| 2843 msgA = ("Create conference with user"); | |
| 2844 msgB = ("Please enter a topic for the new conference, and an invitation" | |
| 2845 " message to be sent to %s"); | |
| 2846 msgB = g_strdup_printf(msgB, buddy->name); | |
| 2847 | |
| 2848 gaim_request_fields(gc, "New Conference", | |
| 2849 msgA, msgB, fields, | |
| 2850 "Create", G_CALLBACK(conf_create_prompt_join), | |
| 2851 "Cancel", G_CALLBACK(conf_create_prompt_cancel), | |
| 2852 buddy); | |
| 2853 g_free(msgB); | |
| 2854 } | |
| 2855 | |
| 2856 | |
| 2857 static void conf_select_prompt_cancel(GaimBuddy *buddy, | |
| 2858 GaimRequestFields *fields) { | |
| 2859 ; | |
| 2860 } | |
| 2861 | |
| 2862 | |
| 2863 static void conf_select_prompt_invite(GaimBuddy *buddy, | |
| 2864 GaimRequestFields *fields) { | |
| 2865 GaimRequestField *f; | |
| 2866 const GList *l; | |
| 2867 const char *msg; | |
| 2868 | |
| 2869 f = gaim_request_fields_get_field(fields, CHAT_KEY_INVITE); | |
| 2870 msg = gaim_request_field_string_get_value(f); | |
| 2871 | |
| 2872 f = gaim_request_fields_get_field(fields, "conf"); | |
| 2873 l = gaim_request_field_list_get_selected(f); | |
| 2874 | |
| 2875 if(l) { | |
| 2876 gpointer d = gaim_request_field_list_get_data(f, l->data); | |
| 2877 | |
| 2878 if(GPOINTER_TO_INT(d) == 0x01) { | |
| 2879 blist_menu_conf_create(buddy, msg); | |
| 2880 | |
| 2881 } else { | |
| 2882 struct mwIdBlock idb = { buddy->name, NULL }; | |
| 2883 mwConference_invite(d, &idb, msg); | |
| 2884 } | |
| 2885 } | |
| 2886 } | |
| 2887 | |
| 2888 | |
| 2889 static void blist_menu_conf_list(GaimBuddy *buddy, | |
| 2890 GList *confs) { | |
| 2891 | |
| 2892 GaimRequestFields *fields; | |
| 2893 GaimRequestFieldGroup *g; | |
| 2894 GaimRequestField *f; | |
| 2895 | |
| 2896 GaimAccount *acct; | |
| 2897 GaimConnection *gc; | |
| 2898 | |
| 2899 char *msgA, *msgB; | |
| 2900 | |
| 2901 acct = buddy->account; | |
| 2902 g_return_if_fail(acct != NULL); | |
| 2903 | |
| 2904 gc = gaim_account_get_connection(acct); | |
| 2905 g_return_if_fail(gc != NULL); | |
| 2906 | |
| 2907 fields = gaim_request_fields_new(); | |
| 2908 | |
| 2909 g = gaim_request_field_group_new(NULL); | |
| 2910 gaim_request_fields_add_group(fields, g); | |
| 2911 | |
| 2912 f = gaim_request_field_list_new("conf", "Available Conferences"); | |
| 2913 gaim_request_field_list_set_multi_select(f, FALSE); | |
| 2914 for(; confs; confs = confs->next) { | |
| 2915 struct mwConference *c = confs->data; | |
| 2916 gaim_request_field_list_add(f, mwConference_getTitle(c), c); | |
| 2917 } | |
| 2918 gaim_request_field_list_add(f, "Create New Conference...", | |
| 2919 GINT_TO_POINTER(0x01)); | |
| 2920 gaim_request_field_group_add_field(g, f); | |
| 2921 | |
| 2922 f = gaim_request_field_string_new(CHAT_KEY_INVITE, "Message", NULL, FALSE); | |
| 2923 gaim_request_field_group_add_field(g, f); | |
| 2924 | |
| 2925 msgA = "Invite user to a conference"; | |
| 2926 msgB = ("Select a conference from the list below to send an invite to" | |
| 2927 " user %s. Select \"Create New Conference\" if you'd like to" | |
| 2928 " create a new conference to invite this user to."); | |
| 2929 msgB = g_strdup_printf(msgB, buddy->name); | |
| 2930 | |
| 2931 gaim_request_fields(gc, "Invite to Conference", | |
| 2932 msgA, msgB, fields, | |
| 2933 "Invite", G_CALLBACK(conf_select_prompt_invite), | |
| 2934 "Cancel", G_CALLBACK(conf_select_prompt_cancel), | |
| 2935 buddy); | |
| 2936 g_free(msgB); | |
| 2937 } | |
| 2938 | |
| 2939 | |
| 2940 static void blist_menu_conf(GaimBlistNode *node, gpointer data) { | |
| 2941 GaimBuddy *buddy = (GaimBuddy *) node; | |
| 2942 GaimAccount *acct; | |
| 2943 GaimConnection *gc; | |
| 2944 struct mwGaimPluginData *pd; | |
| 2945 GList *l; | |
| 2946 | |
| 2947 g_return_if_fail(node != NULL); | |
| 2948 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(node)); | |
| 2949 | |
| 2950 acct = buddy->account; | |
| 2951 g_return_if_fail(acct != NULL); | |
| 2952 | |
| 2953 gc = gaim_account_get_connection(acct); | |
| 2954 g_return_if_fail(gc != NULL); | |
| 2955 | |
| 2956 pd = gc->proto_data; | |
| 2957 g_return_if_fail(pd != NULL); | |
| 2958 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
2959 /* |
| 10977 | 2960 - get a list of all conferences on this session |
| 2961 - if none, prompt to create one, and invite buddy to it | |
| 2962 - else, prompt to select a conference or create one | |
| 2963 */ | |
| 2964 | |
| 2965 l = mwServiceConference_getConferences(pd->srvc_conf); | |
| 2966 if(l) { | |
| 2967 blist_menu_conf_list(buddy, l); | |
| 2968 g_list_free(l); | |
| 2969 | |
| 2970 } else { | |
| 2971 blist_menu_conf_create(buddy, NULL); | |
| 2972 } | |
| 2973 } | |
| 2974 | |
| 2975 | |
| 2976 static GList *mw_prpl_blist_node_menu(GaimBlistNode *node) { | |
| 2977 GList *l = NULL; | |
| 2978 GaimBlistNodeAction *act; | |
| 2979 | |
| 2980 if(! GAIM_BLIST_NODE_IS_BUDDY(node)) | |
| 2981 return l; | |
| 2982 | |
| 2983 l = g_list_append(l, NULL); | |
| 2984 | |
| 2985 act = gaim_blist_node_action_new("Invite to Conference...", | |
| 2986 blist_menu_conf, NULL, NULL); | |
| 2987 l = g_list_append(l, act); | |
| 2988 | |
| 2989 /** note: this never gets called for a GaimGroup, have to use the | |
| 2990 blist-node-extended-menu signal for that. The function | |
| 2991 blist_node_menu_cb is assigned to this signal in the function | |
| 2992 services_starting */ | |
| 2993 | |
| 2994 return l; | |
| 2995 } | |
| 2996 | |
| 2997 | |
| 2998 static GList *mw_prpl_chat_info(GaimConnection *gc) { | |
| 2999 GList *l = NULL; | |
| 3000 struct proto_chat_entry *pce; | |
| 3001 | |
| 3002 pce = g_new0(struct proto_chat_entry, 1); | |
| 3003 pce->label = "Topic:"; | |
| 3004 pce->identifier = CHAT_KEY_TOPIC; | |
| 3005 l = g_list_append(l, pce); | |
| 3006 | |
| 3007 return l; | |
| 3008 } | |
| 3009 | |
| 3010 | |
| 3011 static GHashTable *mw_prpl_chat_info_defaults(GaimConnection *gc, | |
| 3012 const char *name) { | |
| 3013 GHashTable *table; | |
| 3014 | |
| 3015 g_return_val_if_fail(gc != NULL, NULL); | |
| 3016 | |
| 3017 table = g_hash_table_new_full(g_str_hash, g_str_equal, | |
| 3018 NULL, g_free); | |
| 3019 | |
| 3020 g_hash_table_insert(table, CHAT_KEY_NAME, g_strdup(name)); | |
| 3021 g_hash_table_insert(table, CHAT_KEY_INVITE, NULL); | |
| 3022 | |
| 3023 return table; | |
| 3024 } | |
| 3025 | |
| 3026 | |
| 3027 static void mw_prpl_login(GaimAccount *acct, GaimStatus *stat); | |
| 3028 | |
| 3029 | |
| 3030 static void prompt_host_cancel_cb(GaimConnection *gc) { | |
| 3031 gaim_connection_error(gc, "No Sametime Community Server specified"); | |
| 3032 } | |
| 3033 | |
| 3034 | |
| 3035 static void prompt_host_ok_cb(GaimConnection *gc, const char *host) { | |
| 3036 if(host && *host) { | |
| 3037 GaimAccount *acct; | |
| 3038 GaimPresence *pres; | |
| 3039 GaimStatus *stat; | |
| 3040 | |
| 3041 acct = gaim_connection_get_account(gc); | |
| 3042 gaim_account_set_string(acct, MW_KEY_HOST, host); | |
| 3043 | |
| 3044 pres = gaim_account_get_presence(acct); | |
| 3045 stat = gaim_presence_get_active_status(pres); | |
| 3046 | |
| 3047 mw_prpl_login(acct, stat); | |
| 3048 | |
| 3049 } else { | |
| 3050 prompt_host_cancel_cb(gc); | |
| 3051 } | |
| 3052 } | |
| 3053 | |
| 3054 | |
| 3055 static void prompt_host(GaimConnection *gc) { | |
| 3056 GaimAccount *acct; | |
| 3057 char *msg; | |
| 3058 | |
| 3059 acct = gaim_connection_get_account(gc); | |
| 3060 msg = ("No host or IP address has been configured for the" | |
| 3061 " Meanwhile account %s. Please enter one below to" | |
| 3062 " continue logging in."); | |
| 3063 msg = g_strdup_printf(msg, NSTR(gaim_account_get_username(acct))); | |
| 3064 | |
| 3065 gaim_request_input(gc, "Meanwhile Connection Setup", | |
| 3066 "No Sametime Community Server Specified", msg, | |
| 3067 MW_PLUGIN_DEFAULT_HOST, FALSE, FALSE, NULL, | |
| 3068 "Connect", G_CALLBACK(prompt_host_ok_cb), | |
| 3069 "Cancel", G_CALLBACK(prompt_host_cancel_cb), | |
| 3070 gc); | |
| 3071 | |
| 3072 g_free(msg); | |
| 3073 } | |
| 3074 | |
| 3075 | |
| 3076 static void mw_prpl_login(GaimAccount *account, GaimStatus *stat) { | |
| 3077 GaimConnection *gc; | |
| 3078 struct mwGaimPluginData *pd; | |
| 3079 | |
| 3080 char *user, *pass, *host; | |
| 3081 guint port; | |
| 3082 | |
| 3083 gc = gaim_account_get_connection(account); | |
| 3084 pd = mwGaimPluginData_new(gc); | |
| 3085 | |
| 3086 /* while we do support images, the default is to not offer it */ | |
| 3087 gc->flags |= GAIM_CONNECTION_NO_IMAGES; | |
| 3088 | |
| 3089 user = g_strdup(gaim_account_get_username(account)); | |
| 3090 pass = (char *) gaim_account_get_password(account); | |
| 3091 | |
| 3092 host = strrchr(user, ':'); | |
| 3093 if(host) { | |
| 3094 /* annoying user split from 1.2.0, need to undo it */ | |
| 3095 *host++ = '\0'; | |
| 3096 gaim_account_set_string(account, MW_KEY_HOST, host); | |
| 3097 gaim_account_set_username(account, user); | |
| 3098 | |
| 3099 } else { | |
| 3100 host = (char *) gaim_account_get_string(account, MW_KEY_HOST, | |
| 3101 MW_PLUGIN_DEFAULT_HOST); | |
| 3102 } | |
| 3103 | |
| 3104 if(! host || ! *host) { | |
| 3105 /* somehow, we don't have a host to connect to. Well, we need one | |
| 3106 to actually continue, so let's ask the user directly. */ | |
| 3107 prompt_host(gc); | |
| 3108 return; | |
| 3109 } | |
| 3110 | |
| 3111 port = gaim_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT); | |
| 3112 | |
| 3113 DEBUG_INFO("user: '%s'\n", user); | |
| 3114 DEBUG_INFO("host: '%s'\n", host); | |
| 3115 DEBUG_INFO("port: %u\n", port); | |
| 3116 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3117 mwSession_setProperty(pd->session, mwSession_NO_SECRET, |
| 10977 | 3118 (char *) no_secret, NULL); |
| 3119 mwSession_setProperty(pd->session, mwSession_AUTH_USER_ID, user, g_free); | |
| 3120 mwSession_setProperty(pd->session, mwSession_AUTH_PASSWORD, pass, NULL); | |
| 3121 mwSession_setProperty(pd->session, mwSession_CLIENT_TYPE_ID, | |
| 3122 GUINT_TO_POINTER(MW_CLIENT_TYPE_ID), NULL); | |
| 3123 | |
| 3124 gaim_connection_update_progress(gc, "Connecting", 1, MW_CONNECT_STEPS); | |
| 3125 | |
| 3126 if(gaim_proxy_connect(account, host, port, connect_cb, pd)) { | |
| 3127 gaim_connection_error(gc, "Unable to connect to host"); | |
| 3128 } | |
| 3129 } | |
| 3130 | |
| 3131 | |
| 3132 static void mw_prpl_close(GaimConnection *gc) { | |
| 3133 struct mwGaimPluginData *pd; | |
| 3134 | |
| 3135 g_return_if_fail(gc != NULL); | |
| 3136 | |
| 3137 pd = gc->proto_data; | |
| 3138 g_return_if_fail(pd != NULL); | |
| 3139 | |
| 3140 /* get rid of the blist save timeout */ | |
| 3141 if(pd->save_event) { | |
| 3142 gaim_timeout_remove(pd->save_event); | |
| 3143 pd->save_event = 0; | |
| 3144 blist_store(pd); | |
| 3145 } | |
| 3146 | |
| 3147 /* stop the session */ | |
| 3148 mwSession_stop(pd->session, 0x00); | |
| 3149 | |
| 3150 /* no longer necessary */ | |
| 3151 gc->proto_data = NULL; | |
| 3152 | |
| 3153 /* stop watching the socket */ | |
| 3154 if(gc->inpa) { | |
| 3155 gaim_input_remove(gc->inpa); | |
| 3156 gc->inpa = 0; | |
| 3157 } | |
| 3158 | |
| 3159 /* clean up the rest */ | |
| 3160 mwGaimPluginData_free(pd); | |
| 3161 } | |
| 3162 | |
| 3163 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3164 /** generates a random-ish content id string */ |
| 10977 | 3165 static char *im_mime_content_id() { |
| 3166 const char *c = "%03x@%05xmeanwhile"; | |
| 3167 srand(time(0) ^ rand()); | |
| 3168 return g_strdup_printf(c, rand() & 0xfff, rand() & 0xfffff); | |
| 3169 } | |
| 3170 | |
| 3171 | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3172 /** generates a multipart/related content type with a random-ish |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3173 boundary value */ |
| 10977 | 3174 static char *im_mime_content_type() { |
| 3175 const char *c = "multipart/related; boundary=related_MW%03x_%04x"; | |
| 3176 srand(time(0) ^ rand()); | |
| 3177 return g_strdup_printf(c, rand() & 0xfff, rand() & 0xffff); | |
| 3178 } | |
| 3179 | |
| 3180 | |
| 3181 static const char *im_mime_img_content_type(GaimStoredImage *img) { | |
| 3182 const char *fn = gaim_imgstore_get_filename(img); | |
| 3183 | |
| 3184 fn = strrchr(fn, '.'); | |
| 3185 if(! fn) { | |
| 3186 return "image"; | |
| 3187 | |
| 3188 } else if(! strcmp(".png", fn)) { | |
| 3189 return "image/png"; | |
| 3190 | |
| 3191 } else if(! strcmp(".jpg", fn)) { | |
| 3192 return "image/jpeg"; | |
| 3193 | |
| 3194 } else if(! strcmp(".jpeg", fn)) { | |
| 3195 return "image/jpeg"; | |
| 3196 | |
| 3197 } else if(! strcmp(".gif", fn)) { | |
| 3198 return "image/gif"; | |
| 3199 | |
| 3200 } else { | |
| 3201 return "image"; | |
| 3202 } | |
| 3203 } | |
| 3204 | |
| 3205 | |
| 3206 static char *im_mime_img_content_disp(GaimStoredImage *img) { | |
| 3207 const char *fn = gaim_imgstore_get_filename(img); | |
| 3208 return g_strdup_printf("attachment; filename=\"%s\"", fn); | |
| 3209 } | |
| 3210 | |
| 3211 | |
| 3212 static char *im_mime_convert(const char *message) { | |
| 3213 GString *str; | |
| 3214 GaimMimeDocument *doc; | |
| 3215 GaimMimePart *part; | |
| 3216 | |
| 3217 GData *attr; | |
| 3218 char *tmp, *start, *end; | |
| 3219 | |
| 3220 str = g_string_new(NULL); | |
| 3221 | |
| 3222 doc = gaim_mime_document_new(); | |
| 3223 | |
| 3224 gaim_mime_document_set_field(doc, "Mime-Version", "1.0"); | |
| 3225 gaim_mime_document_set_field(doc, "Content-Disposition", "inline"); | |
| 3226 | |
| 3227 tmp = im_mime_content_type(); | |
| 3228 gaim_mime_document_set_field(doc, "Content-Type", tmp); | |
| 3229 g_free(tmp); | |
| 3230 | |
| 3231 tmp = (char *) message; | |
| 3232 while(*tmp && gaim_markup_find_tag("img", tmp, (const char **) &start, | |
| 3233 (const char **) &end, &attr)) { | |
| 3234 char *id; | |
| 3235 GaimStoredImage *img = NULL; | |
| 3236 | |
| 3237 gsize len = (start - tmp); | |
| 3238 | |
| 3239 /* append the in-between-tags text */ | |
| 3240 if(len) g_string_append_len(str, tmp, len); | |
| 3241 | |
| 3242 /* find the imgstore data by the id tag */ | |
| 3243 id = g_datalist_get_data(&attr, "id"); | |
| 3244 if(id && *id) | |
| 3245 img = gaim_imgstore_get(atoi(id)); | |
| 3246 | |
| 3247 if(img) { | |
| 3248 char *cid; | |
| 3249 gpointer data; | |
| 3250 size_t size; | |
| 3251 | |
| 3252 part = gaim_mime_part_new(doc); | |
| 3253 | |
| 3254 data = im_mime_img_content_disp(img); | |
| 3255 gaim_mime_part_set_field(part, "Content-Disposition", data); | |
| 3256 g_free(data); | |
| 3257 | |
| 3258 cid = im_mime_content_id(); | |
| 3259 data = g_strdup_printf("<%s>", cid); | |
| 3260 gaim_mime_part_set_field(part, "Content-ID", data); | |
| 3261 g_free(data); | |
| 3262 | |
| 3263 gaim_mime_part_set_field(part, "Content-transfer-encoding", "base64"); | |
| 3264 gaim_mime_part_set_field(part, "Content-Type", | |
| 3265 im_mime_img_content_type(img)); | |
| 3266 | |
| 3267 | |
| 3268 /* obtain and base64 encode the image data, and put it in the | |
| 3269 mime part */ | |
| 3270 data = gaim_imgstore_get_data(img); | |
| 3271 size = gaim_imgstore_get_size(img); | |
| 3272 data = gaim_base64_encode(data, (gsize) size); | |
| 3273 gaim_mime_part_set_data(part, data); | |
| 3274 g_free(data); | |
| 3275 | |
| 3276 /* append the modified tag */ | |
| 3277 g_string_append_printf(str, "<img src=\"cid:%s\">", cid); | |
| 3278 g_free(cid); | |
| 3279 | |
| 3280 } else { | |
| 3281 /* append the literal image tag, since we couldn't find a | |
| 3282 relative imgstore object */ | |
| 3283 gsize len = (end - start) + 1; | |
| 3284 g_string_append_len(str, start, len); | |
| 3285 } | |
| 3286 | |
| 3287 g_datalist_clear(&attr); | |
| 3288 tmp = end + 1; | |
| 3289 } | |
| 3290 | |
| 3291 /* append left-overs */ | |
| 3292 g_string_append(str, tmp); | |
| 3293 | |
| 3294 part = gaim_mime_part_new(doc); | |
| 3295 gaim_mime_part_set_field(part, "Content-Type", "text/html"); | |
| 3296 gaim_mime_part_set_field(part, "Content-Disposition", "inline"); | |
| 3297 gaim_mime_part_set_field(part, "Content-Transfer-Encoding", "8bit"); | |
| 3298 | |
| 3299 gaim_mime_part_set_data(part, str->str); | |
| 3300 g_string_free(str, TRUE); | |
| 3301 | |
| 3302 str = g_string_new(NULL); | |
| 3303 gaim_mime_document_write(doc, str); | |
| 3304 tmp = str->str; | |
| 3305 g_string_free(str, FALSE); | |
| 3306 | |
| 3307 return tmp; | |
| 3308 } | |
| 3309 | |
| 3310 | |
| 3311 static int mw_prpl_send_im(GaimConnection *gc, | |
| 3312 const char *name, | |
| 3313 const char *message, | |
| 3314 GaimConvImFlags flags) { | |
| 3315 | |
| 3316 struct mwGaimPluginData *pd; | |
| 3317 struct mwIdBlock who = { (char *) name, NULL }; | |
| 3318 struct mwConversation *conv; | |
| 3319 | |
| 3320 g_return_val_if_fail(gc != NULL, 0); | |
| 3321 pd = gc->proto_data; | |
| 3322 | |
| 3323 g_return_val_if_fail(pd != NULL, 0); | |
| 3324 | |
| 3325 conv = mwServiceIm_getConversation(pd->srvc_im, &who); | |
| 3326 | |
| 3327 /* this detection of features to determine how to send the message | |
| 3328 (plain, html, or mime) is flawed because the other end of the | |
| 3329 conversation could close their channel at any time, rendering any | |
| 3330 existing formatting in an outgoing message innapropriate. The end | |
| 3331 result is that it may be possible that the other side of the | |
| 3332 conversation will receive a plaintext message with html contents, | |
| 3333 which is bad. I'm not sure how to fix this correctly. */ | |
| 3334 | |
| 3335 if(strstr(message, "<img ") || strstr(message, "<IMG ")) | |
| 3336 flags |= GAIM_CONV_IM_IMAGES; | |
| 3337 | |
| 3338 if(mwConversation_isOpen(conv)) { | |
| 3339 char *msg = NULL; | |
| 3340 int ret; | |
| 3341 | |
| 3342 if((flags & GAIM_CONV_IM_IMAGES) && | |
| 3343 mwConversation_supports(conv, mwImSend_MIME)) { | |
| 3344 | |
| 3345 msg = im_mime_convert(message); | |
| 3346 ret = mwConversation_send(conv, mwImSend_MIME, msg); | |
| 3347 | |
| 3348 } else if(mwConversation_supports(conv, mwImSend_HTML)) { | |
| 3349 | |
| 3350 /* need to do this to get the \n to <br> conversion */ | |
| 3351 msg = gaim_strdup_withhtml(message); | |
| 3352 ret = mwConversation_send(conv, mwImSend_HTML, msg); | |
| 3353 | |
| 3354 } else { | |
| 3355 ret = mwConversation_send(conv, mwImSend_PLAIN, message); | |
| 3356 } | |
| 3357 | |
| 3358 g_free(msg); | |
| 3359 return !ret; | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3360 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3361 } else { |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3362 char *msg; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3363 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3364 /* queue up the message safely as plain text */ |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3365 msg = gaim_markup_strip_html(message); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3366 convo_queue(conv, mwImSend_PLAIN, msg); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3367 g_free(msg); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3368 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3369 if(! mwConversation_isPending(conv)) |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3370 mwConversation_open(conv); |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3371 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3372 return 1; |
| 10977 | 3373 } |
| 3374 } | |
| 3375 | |
| 3376 | |
| 3377 static int mw_prpl_send_typing(GaimConnection *gc, const char *name, | |
| 3378 int typing) { | |
| 3379 | |
| 3380 struct mwGaimPluginData *pd; | |
| 3381 struct mwIdBlock who = { (char *) name, NULL }; | |
| 3382 struct mwConversation *conv; | |
| 3383 | |
| 3384 gpointer t = GINT_TO_POINTER(!! typing); | |
| 3385 | |
| 3386 g_return_val_if_fail(gc != NULL, 0); | |
| 3387 pd = gc->proto_data; | |
| 3388 | |
| 3389 g_return_val_if_fail(pd != NULL, 0); | |
| 3390 | |
| 3391 conv = mwServiceIm_getConversation(pd->srvc_im, &who); | |
| 3392 | |
| 3393 if(mwConversation_isOpen(conv)) | |
| 3394 return ! mwConversation_send(conv, mwImSend_TYPING, t); | |
| 3395 | |
| 3396 if(typing) { | |
| 3397 /* let's only open a channel for typing, not for not-typing. | |
| 3398 Otherwise two users in psychic mode will continually open | |
| 3399 conversations to each other, never able to get rid of them, as | |
| 3400 when the other person closes, it psychicaly opens again */ | |
| 3401 | |
| 3402 convo_queue(conv, mwImSend_TYPING, t); | |
| 3403 | |
| 3404 if(! mwConversation_isPending(conv)) | |
| 3405 mwConversation_open(conv); | |
| 3406 } | |
| 3407 | |
| 3408 return 1; | |
| 3409 } | |
| 3410 | |
| 3411 | |
| 3412 static void mw_prpl_get_info(GaimConnection *gc, const char *who) { | |
| 3413 | |
| 3414 struct mwGaimPluginData *pd; | |
| 3415 struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; | |
| 3416 | |
| 3417 GaimAccount *acct; | |
| 3418 GaimBuddy *b; | |
| 3419 | |
| 3420 GString *str; | |
| 3421 const char *tmp; | |
| 3422 guint32 type; | |
| 3423 | |
| 3424 pd = gc->proto_data; | |
| 3425 | |
| 3426 acct = gaim_connection_get_account(gc); | |
| 3427 b = gaim_find_buddy(acct, who); | |
| 3428 | |
| 3429 g_return_if_fail(b != NULL); | |
| 3430 | |
| 3431 str = g_string_new(NULL); | |
| 3432 | |
| 3433 g_string_append_printf(str, "<b>User ID:</b> %s<br>", b->name); | |
| 3434 | |
| 3435 if(b->server_alias) { | |
| 3436 g_string_append_printf(str, "<b>Full Name:</b> %s<br>", | |
| 3437 b->server_alias); | |
| 3438 } | |
| 3439 | |
| 3440 type = gaim_blist_node_get_int((GaimBlistNode *) b, BUDDY_KEY_CLIENT); | |
| 3441 if(type) { | |
| 3442 g_string_append(str, "<b>Last Known Client:</b> "); | |
| 3443 | |
| 3444 tmp = mwLoginType_getName(type); | |
| 3445 if(tmp) { | |
| 3446 g_string_append(str, tmp); | |
| 3447 g_string_append(str, "<br>"); | |
| 3448 | |
| 3449 } else { | |
| 3450 g_string_append_printf(str, "Unknown (0x%04x)<br>", type); | |
| 3451 } | |
| 3452 } | |
| 3453 | |
| 3454 tmp = user_supports_text(pd->srvc_aware, who); | |
| 3455 if(tmp) { | |
| 3456 g_string_append_printf(str, "<b>Supports:</b> %s<br>", tmp); | |
| 3457 g_free((char *) tmp); | |
| 3458 } | |
| 3459 | |
| 3460 tmp = status_text(b); | |
| 3461 g_string_append_printf(str, "<b>Status:</b> %s", tmp); | |
| 3462 | |
| 3463 g_string_append(str, "<hr>"); | |
| 3464 | |
| 3465 tmp = mwServiceAware_getText(pd->srvc_aware, &idb); | |
| 3466 g_string_append(str, tmp); | |
| 3467 | |
| 3468 /* @todo emit a signal to allow a plugin to override the display of | |
| 3469 this notification, so that it can create its own */ | |
| 3470 | |
|
11533
c9b815aeddc1
[gaim-migrate @ 13782]
Richard Laager <rlaager@wiktel.com>
parents:
11531
diff
changeset
|
3471 gaim_notify_userinfo(gc, who, str->str, NULL, NULL); |
| 10977 | 3472 |
| 3473 g_string_free(str, TRUE); | |
| 3474 } | |
| 3475 | |
| 3476 | |
| 3477 #if 0 | |
| 3478 static void mw_prpl_set_away(GaimConnection *gc, | |
| 3479 const char *state, | |
| 3480 const char *message) { | |
| 3481 GaimAccount *acct; | |
| 3482 struct mwSession *session; | |
| 3483 struct mwUserStatus stat; | |
| 3484 | |
| 3485 acct = gaim_connection_get_account(gc); | |
| 3486 g_return_if_fail(acct != NULL); | |
| 3487 | |
| 3488 session = gc_to_session(gc); | |
| 3489 g_return_if_fail(session != NULL); | |
| 3490 | |
| 3491 /* get a working copy of the current status */ | |
| 3492 mwUserStatus_clone(&stat, mwSession_getUserStatus(session)); | |
| 3493 | |
| 3494 /* determine the state */ | |
| 3495 if(state) { | |
| 3496 if(! strcmp(state, GAIM_AWAY_CUSTOM)) { | |
| 3497 if(message) { | |
| 3498 stat.status = mwStatus_AWAY; | |
| 3499 } else { | |
| 3500 stat.status = mwStatus_ACTIVE; | |
| 3501 } | |
| 3502 | |
| 3503 } else if(! strcmp(state, MW_STATE_AWAY)) { | |
| 3504 stat.status = mwStatus_AWAY; | |
| 3505 | |
| 3506 } else if(! strcmp(state, MW_STATE_BUSY)) { | |
| 3507 stat.status = mwStatus_BUSY; | |
| 3508 | |
| 3509 } else if(! strcmp(state, MW_STATE_ACTIVE)) { | |
| 3510 stat.status = mwStatus_ACTIVE; | |
| 3511 } | |
| 3512 | |
| 3513 } else { | |
| 3514 stat.status = mwStatus_ACTIVE; | |
| 3515 } | |
| 3516 | |
| 3517 /* determine the message */ | |
| 3518 if(! message) { | |
| 3519 switch(stat.status) { | |
| 3520 case mwStatus_AWAY: | |
| 3521 message = gaim_account_get_string(acct, MW_KEY_AWAY_MSG, | |
| 3522 MW_PLUGIN_DEFAULT_AWAY_MSG); | |
| 3523 break; | |
| 3524 | |
| 3525 case mwStatus_BUSY: | |
| 3526 message = gaim_account_get_string(acct, MW_KEY_BUSY_MSG, | |
| 3527 MW_PLUGIN_DEFAULT_BUSY_MSG); | |
| 3528 break; | |
| 3529 | |
| 3530 case mwStatus_ACTIVE: | |
| 3531 message = gaim_account_get_string(acct, MW_KEY_ACTIVE_MSG, | |
| 3532 MW_PLUGIN_DEFAULT_ACTIVE_MSG); | |
| 3533 stat.time = 0; | |
| 3534 break; | |
| 3535 } | |
| 3536 } | |
| 3537 | |
| 3538 if(message) { | |
| 3539 /* all the possible non-NULL values of message up to this point | |
| 3540 are const, so we don't need to free them */ | |
| 3541 message = gaim_markup_strip_html(message); | |
| 3542 } | |
| 3543 | |
| 3544 /* out with the old */ | |
| 3545 g_free(stat.desc); | |
| 3546 g_free(gc->away); | |
| 3547 | |
| 3548 /* in with the new */ | |
| 3549 stat.desc = (char *) message; | |
| 3550 gc->away = g_strdup(message); | |
| 3551 | |
| 3552 mwSession_setUserStatus(session, &stat); | |
| 3553 mwUserStatus_clear(&stat); | |
| 3554 } | |
| 3555 #endif | |
| 3556 | |
| 3557 | |
| 3558 static void mw_prpl_set_idle(GaimConnection *gc, int time) { | |
| 3559 struct mwSession *session; | |
| 3560 struct mwUserStatus stat; | |
| 3561 | |
| 3562 session = gc_to_session(gc); | |
| 3563 g_return_if_fail(session != NULL); | |
| 3564 | |
| 3565 mwUserStatus_clone(&stat, mwSession_getUserStatus(session)); | |
| 3566 | |
| 3567 if(time > 0 && stat.status == mwStatus_ACTIVE) { | |
| 3568 stat.status = mwStatus_IDLE; | |
| 3569 | |
| 3570 } else if(time == 0 && stat.status == mwStatus_IDLE) { | |
| 3571 stat.status = mwStatus_ACTIVE; | |
| 3572 } | |
| 3573 | |
| 3574 /** @todo actually put the idle time in the user status */ | |
| 3575 | |
| 3576 mwSession_setUserStatus(session, &stat); | |
| 3577 mwUserStatus_clear(&stat); | |
| 3578 } | |
| 3579 | |
| 3580 | |
| 3581 static void add_resolved_done(const char *id, const char *name, | |
| 3582 GaimBuddy *buddy) { | |
| 3583 GaimAccount *acct; | |
| 3584 GaimConnection *gc; | |
| 3585 struct mwGaimPluginData *pd; | |
| 3586 | |
| 3587 g_return_if_fail(id != NULL); | |
| 3588 | |
| 3589 g_return_if_fail(buddy != NULL); | |
| 3590 acct = buddy->account; | |
| 3591 | |
| 3592 g_return_if_fail(acct != NULL); | |
| 3593 gc = gaim_account_get_connection(acct); | |
| 3594 | |
| 3595 g_return_if_fail(gc != NULL); | |
| 3596 pd = gc->proto_data; | |
| 3597 | |
| 3598 gaim_blist_rename_buddy(buddy, id); | |
| 3599 | |
| 3600 gaim_blist_server_alias_buddy(buddy, name); | |
| 3601 gaim_blist_node_set_string((GaimBlistNode *) buddy, BUDDY_KEY_NAME, name); | |
| 3602 | |
| 3603 buddy_add(pd, buddy); | |
| 3604 } | |
| 3605 | |
| 3606 | |
| 3607 static void multi_resolved_cleanup(GaimRequestFields *fields) { | |
| 3608 | |
| 3609 GaimRequestField *f; | |
| 3610 const GList *l; | |
| 3611 | |
| 3612 f = gaim_request_fields_get_field(fields, "user"); | |
| 3613 l = gaim_request_field_list_get_items(f); | |
| 3614 | |
| 3615 for(; l; l = l->next) { | |
| 3616 const char *i = l->data; | |
| 3617 struct resolved_id *res; | |
| 3618 | |
| 3619 res = gaim_request_field_list_get_data(f, i); | |
| 3620 | |
| 3621 g_free(res->id); | |
| 3622 g_free(res->name); | |
| 3623 g_free(res); | |
| 3624 } | |
| 3625 } | |
| 3626 | |
| 3627 | |
| 3628 static void multi_resolved_cancel(GaimBuddy *buddy, | |
| 3629 GaimRequestFields *fields) { | |
| 3630 GaimConnection *gc; | |
| 3631 struct mwGaimPluginData *pd; | |
| 3632 | |
| 3633 gc = gaim_account_get_connection(buddy->account); | |
| 3634 pd = gc->proto_data; | |
| 3635 | |
| 3636 gaim_blist_remove_buddy(buddy); | |
| 3637 multi_resolved_cleanup(fields); | |
| 3638 | |
| 3639 blist_schedule(pd); | |
| 3640 } | |
| 3641 | |
| 3642 | |
| 3643 static void multi_resolved_cb(GaimBuddy *buddy, | |
| 3644 GaimRequestFields *fields) { | |
| 3645 GaimRequestField *f; | |
| 3646 const GList *l; | |
| 3647 | |
| 3648 f = gaim_request_fields_get_field(fields, "user"); | |
| 3649 l = gaim_request_field_list_get_selected(f); | |
| 3650 | |
| 3651 if(l) { | |
| 3652 const char *i = l->data; | |
| 3653 struct resolved_id *res; | |
| 3654 | |
| 3655 res = gaim_request_field_list_get_data(f, i); | |
| 3656 | |
| 3657 add_resolved_done(res->id, res->name, buddy); | |
| 3658 multi_resolved_cleanup(fields); | |
| 3659 | |
| 3660 } else { | |
| 3661 multi_resolved_cancel(buddy, fields); | |
| 3662 } | |
| 3663 } | |
| 3664 | |
| 3665 | |
| 3666 static void multi_resolved_query(struct mwResolveResult *result, | |
| 3667 GaimBuddy *buddy) { | |
| 3668 GaimRequestFields *fields; | |
| 3669 GaimRequestFieldGroup *g; | |
| 3670 GaimRequestField *f; | |
| 3671 GList *l; | |
| 3672 char *msgA, *msgB; | |
| 3673 | |
| 3674 GaimAccount *acct; | |
| 3675 GaimConnection *gc; | |
| 3676 | |
| 3677 g_return_if_fail(buddy != NULL); | |
| 3678 | |
| 3679 acct = buddy->account; | |
| 3680 g_return_if_fail(acct != NULL); | |
| 3681 | |
| 3682 gc = gaim_account_get_connection(acct); | |
| 3683 g_return_if_fail(gc != NULL); | |
| 3684 | |
| 3685 fields = gaim_request_fields_new(); | |
| 3686 | |
| 3687 g = gaim_request_field_group_new(NULL); | |
| 3688 | |
| 3689 /* note that Gaim segfaults if you don't add the group to the fields | |
| 3690 before you add a required field to the group. Feh. */ | |
| 3691 gaim_request_fields_add_group(fields, g); | |
| 3692 | |
| 3693 f = gaim_request_field_list_new("user", "Possible Matches"); | |
| 3694 gaim_request_field_list_set_multi_select(f, FALSE); | |
| 3695 gaim_request_field_set_required(f, TRUE); | |
| 3696 | |
| 3697 for(l = result->matches; l; l = l->next) { | |
| 3698 struct mwResolveMatch *match = l->data; | |
| 3699 struct resolved_id *res = g_new0(struct resolved_id, 1); | |
| 3700 char *label; | |
| 3701 | |
| 3702 res->id = g_strdup(match->id); | |
| 3703 res->name = g_strdup(match->name); | |
| 3704 | |
| 3705 /* fixes bug 1178603 by making the selection label a combination | |
| 3706 of the full name and the user id. Problems arrise when multiple | |
| 3707 entries have identical labels */ | |
| 3708 label = g_strdup_printf("%s (%s)", NSTR(res->name), NSTR(res->id)); | |
| 3709 gaim_request_field_list_add(f, label, res); | |
| 3710 g_free(label); | |
| 3711 } | |
| 3712 | |
| 3713 gaim_request_field_group_add_field(g, f); | |
| 3714 | |
| 3715 msgA = ("An ambiguous user ID was entered"); | |
| 3716 msgB = ("The identifier '%s' may possibly refer to any of the following" | |
| 3717 " users. Please select the correct user from the list below to" | |
| 3718 " add them to your buddy list."); | |
| 3719 msgB = g_strdup_printf(msgB, result->name); | |
| 3720 | |
| 3721 gaim_request_fields(gc, "Select User to Add", | |
| 3722 msgA, msgB, fields, | |
| 3723 "Add User", G_CALLBACK(multi_resolved_cb), | |
| 3724 "Cancel", G_CALLBACK(multi_resolved_cancel), | |
| 3725 buddy); | |
| 3726 g_free(msgB); | |
| 3727 } | |
| 3728 | |
| 3729 | |
| 3730 static void add_buddy_resolved(struct mwServiceResolve *srvc, | |
| 3731 guint32 id, guint32 code, GList *results, | |
| 3732 gpointer b) { | |
| 3733 | |
| 3734 struct mwResolveResult *res = NULL; | |
| 3735 GaimBuddy *buddy = b; | |
| 3736 GaimConnection *gc; | |
| 3737 struct mwGaimPluginData *pd; | |
| 3738 | |
| 3739 gc = gaim_account_get_connection(buddy->account); | |
| 3740 pd = gc->proto_data; | |
| 3741 | |
| 3742 if(results) | |
| 3743 res = results->data; | |
| 3744 | |
| 3745 if(!code && res && res->matches) { | |
| 3746 if(g_list_length(res->matches) == 1) { | |
| 3747 struct mwResolveMatch *match = res->matches->data; | |
| 3748 | |
| 3749 DEBUG_INFO("searched for %s, got only %s\n", | |
| 3750 NSTR(res->name), NSTR(match->id)); | |
| 3751 | |
| 3752 /* only one? that might be the right one! */ | |
| 3753 if(strcmp(res->name, match->id)) { | |
| 3754 /* uh oh, the single result isn't identical to the search | |
| 3755 term, better safe then sorry, so let's make sure it's who | |
| 3756 the user meant to add */ | |
| 3757 multi_resolved_query(res, buddy); | |
| 3758 | |
| 3759 } else { | |
| 3760 /* same person, add 'em */ | |
| 3761 add_resolved_done(match->id, match->name, buddy); | |
| 3762 } | |
| 3763 | |
| 3764 } else { | |
| 3765 /* prompt user if more than one match was returned */ | |
| 3766 multi_resolved_query(res, buddy); | |
| 3767 } | |
| 3768 | |
| 3769 return; | |
| 3770 } | |
| 3771 | |
| 3772 /* fall-through indicates that we couldn't find a matching user in | |
| 3773 the resolve service (ether error or zero results), so we remove | |
| 3774 this buddy */ | |
| 3775 | |
| 3776 DEBUG_INFO("no such buddy in community\n"); | |
| 3777 gaim_blist_remove_buddy(buddy); | |
| 3778 blist_schedule(pd); | |
| 3779 | |
| 3780 if(res && res->name) { | |
| 3781 /* compose and display an error message */ | |
| 3782 char *msgA, *msgB; | |
| 3783 | |
| 3784 msgA = "Unable to add user: user not found"; | |
| 3785 | |
| 3786 msgB = ("The identifier '%s' did not match any users in your" | |
| 3787 " Sametime community. This entry has been removed from" | |
| 3788 " your buddy list."); | |
| 3789 msgB = g_strdup_printf(msgB, NSTR(res->name)); | |
| 3790 | |
| 3791 gaim_notify_error(gc, "Unable to add user", msgA, msgB); | |
| 3792 | |
| 3793 g_free(msgB); | |
| 3794 } | |
| 3795 } | |
| 3796 | |
| 3797 | |
| 3798 static void mw_prpl_add_buddy(GaimConnection *gc, | |
| 3799 GaimBuddy *buddy, | |
| 3800 GaimGroup *group) { | |
| 3801 | |
| 3802 struct mwGaimPluginData *pd; | |
| 3803 struct mwServiceResolve *srvc; | |
| 3804 GList *query; | |
| 3805 enum mwResolveFlag flags; | |
| 3806 guint32 req; | |
| 3807 | |
| 3808 pd = gc->proto_data; | |
| 3809 srvc = pd->srvc_resolve; | |
| 3810 | |
| 3811 query = g_list_prepend(NULL, buddy->name); | |
| 3812 flags = mwResolveFlag_FIRST | mwResolveFlag_USERS; | |
| 3813 | |
| 3814 req = mwServiceResolve_resolve(srvc, query, flags, add_buddy_resolved, | |
| 3815 buddy, NULL); | |
| 3816 g_list_free(query); | |
| 3817 | |
| 3818 if(req == SEARCH_ERROR) { | |
| 3819 gaim_blist_remove_buddy(buddy); | |
| 3820 blist_schedule(pd); | |
| 3821 } | |
| 3822 } | |
| 3823 | |
| 3824 | |
| 3825 static void foreach_add_buddies(GaimGroup *group, GList *buddies, | |
| 3826 struct mwGaimPluginData *pd) { | |
| 3827 | |
| 3828 struct mwAwareList *list; | |
| 3829 | |
| 3830 list = list_ensure(pd, group); | |
| 3831 mwAwareList_addAware(list, buddies); | |
| 3832 g_list_free(buddies); | |
| 3833 } | |
| 3834 | |
| 3835 | |
| 3836 static void mw_prpl_add_buddies(GaimConnection *gc, | |
| 3837 GList *buddies, | |
| 3838 GList *groups) { | |
| 3839 | |
| 3840 struct mwGaimPluginData *pd; | |
| 3841 GHashTable *group_sets; | |
| 3842 struct mwAwareIdBlock *idbs, *idb; | |
| 3843 | |
| 3844 pd = gc->proto_data; | |
| 3845 | |
| 3846 /* map GaimGroup:GList of mwAwareIdBlock */ | |
| 3847 group_sets = g_hash_table_new(g_direct_hash, g_direct_equal); | |
| 3848 | |
| 3849 /* bunch of mwAwareIdBlock allocated at once, free'd at once */ | |
| 3850 idb = idbs = g_new(struct mwAwareIdBlock, g_list_length(buddies)); | |
| 3851 | |
| 3852 /* first pass collects mwAwareIdBlock lists for each group */ | |
| 3853 for(; buddies; buddies = buddies->next) { | |
| 3854 GaimBuddy *b = buddies->data; | |
| 3855 GaimGroup *g; | |
| 3856 const char *fn; | |
| 3857 GList *l; | |
| 3858 | |
| 3859 /* nab the saved server alias and stick it on the buddy */ | |
| 3860 fn = gaim_blist_node_get_string((GaimBlistNode *) b, BUDDY_KEY_NAME); | |
| 3861 gaim_blist_server_alias_buddy(b, fn); | |
| 3862 | |
| 3863 /* convert GaimBuddy into a mwAwareIdBlock */ | |
| 3864 idb->type = mwAware_USER; | |
| 3865 idb->user = (char *) b->name; | |
| 3866 idb->community = NULL; | |
| 3867 | |
| 3868 /* put idb into the list associated with the buddy's group */ | |
| 3869 g = gaim_find_buddys_group(b); | |
| 3870 l = g_hash_table_lookup(group_sets, g); | |
| 3871 l = g_list_prepend(l, idb++); | |
| 3872 g_hash_table_insert(group_sets, g, l); | |
| 3873 } | |
| 3874 | |
| 3875 /* each group's buddies get added in one shot, and schedule the blist | |
| 3876 for saving */ | |
| 3877 g_hash_table_foreach(group_sets, (GHFunc) foreach_add_buddies, pd); | |
| 3878 blist_schedule(pd); | |
| 3879 | |
| 3880 /* cleanup */ | |
| 3881 g_hash_table_destroy(group_sets); | |
| 3882 g_free(idbs); | |
| 3883 } | |
| 3884 | |
| 3885 | |
| 3886 static void mw_prpl_remove_buddy(GaimConnection *gc, | |
| 3887 GaimBuddy *buddy, GaimGroup *group) { | |
| 3888 | |
| 3889 struct mwGaimPluginData *pd; | |
| 3890 struct mwAwareIdBlock idb = { mwAware_USER, buddy->name, NULL }; | |
| 3891 struct mwAwareList *list; | |
| 3892 | |
| 3893 GList *rem = g_list_prepend(NULL, &idb); | |
| 3894 | |
| 3895 pd = gc->proto_data; | |
| 3896 group = gaim_find_buddys_group(buddy); | |
| 3897 list = list_ensure(pd, group); | |
| 3898 | |
| 3899 mwAwareList_removeAware(list, rem); | |
| 3900 blist_schedule(pd); | |
| 3901 | |
| 3902 g_list_free(rem); | |
| 3903 } | |
| 3904 | |
| 3905 | |
| 3906 static void privacy_fill(struct mwPrivacyInfo *priv, | |
| 3907 GSList *members) { | |
| 3908 | |
| 3909 struct mwUserItem *u; | |
| 3910 guint count; | |
| 3911 | |
| 3912 count = g_slist_length(members); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3913 DEBUG_INFO("privacy_fill: %u members\n", count); |
| 10977 | 3914 |
| 3915 priv->count = count; | |
| 3916 priv->users = g_new0(struct mwUserItem, count); | |
| 3917 | |
| 3918 while(count--) { | |
| 3919 u = priv->users + count; | |
| 3920 u->id = members->data; | |
| 3921 members = members->next; | |
| 3922 } | |
| 3923 } | |
| 3924 | |
| 3925 | |
| 3926 static void mw_prpl_set_permit_deny(GaimConnection *gc) { | |
| 3927 GaimAccount *acct; | |
| 3928 struct mwGaimPluginData *pd; | |
| 3929 struct mwSession *session; | |
| 3930 | |
| 3931 struct mwPrivacyInfo privacy = { | |
| 3932 .deny = FALSE, | |
| 3933 .count = 0, | |
| 3934 .users = NULL, | |
| 3935 }; | |
| 3936 | |
| 3937 g_return_if_fail(gc != NULL); | |
| 3938 | |
| 3939 acct = gaim_connection_get_account(gc); | |
| 3940 g_return_if_fail(acct != NULL); | |
| 3941 | |
| 3942 pd = gc->proto_data; | |
| 3943 g_return_if_fail(pd != NULL); | |
| 3944 | |
| 3945 session = pd->session; | |
| 3946 g_return_if_fail(session != NULL); | |
| 3947 | |
| 3948 switch(acct->perm_deny) { | |
| 3949 case GAIM_PRIVACY_DENY_USERS: | |
| 3950 DEBUG_INFO("GAIM_PRIVACY_DENY_USERS\n"); | |
| 3951 privacy_fill(&privacy, acct->deny); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3952 privacy.deny = TRUE; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3953 break; |
| 10977 | 3954 |
| 3955 case GAIM_PRIVACY_ALLOW_ALL: | |
| 3956 DEBUG_INFO("GAIM_PRIVACY_ALLOW_ALL\n"); | |
| 3957 privacy.deny = TRUE; | |
| 3958 break; | |
| 3959 | |
| 3960 case GAIM_PRIVACY_ALLOW_USERS: | |
| 3961 DEBUG_INFO("GAIM_PRIVACY_ALLOW_USERS\n"); | |
| 3962 privacy_fill(&privacy, acct->permit); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3963 privacy.deny = FALSE; |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3964 break; |
| 10977 | 3965 |
| 3966 case GAIM_PRIVACY_DENY_ALL: | |
| 3967 DEBUG_INFO("GAIM_PRIVACY_DENY_ALL\n"); | |
| 3968 privacy.deny = FALSE; | |
| 3969 break; | |
| 3970 | |
| 3971 default: | |
| 3972 DEBUG_INFO("acct->perm_deny is 0x%x\n", acct->perm_deny); | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
3973 return; |
| 10977 | 3974 } |
| 3975 | |
| 3976 mwSession_setPrivacyInfo(session, &privacy); | |
| 3977 g_free(privacy.users); | |
| 3978 } | |
| 3979 | |
| 3980 | |
| 3981 static void mw_prpl_add_permit(GaimConnection *gc, const char *name) { | |
| 3982 mw_prpl_set_permit_deny(gc); | |
| 3983 } | |
| 3984 | |
| 3985 | |
| 3986 static void mw_prpl_add_deny(GaimConnection *gc, const char *name) { | |
| 3987 mw_prpl_set_permit_deny(gc); | |
| 3988 } | |
| 3989 | |
| 3990 | |
| 3991 static void mw_prpl_rem_permit(GaimConnection *gc, const char *name) { | |
| 3992 mw_prpl_set_permit_deny(gc); | |
| 3993 } | |
| 3994 | |
| 3995 | |
| 3996 static void mw_prpl_rem_deny(GaimConnection *gc, const char *name) { | |
| 3997 mw_prpl_set_permit_deny(gc); | |
| 3998 } | |
| 3999 | |
| 4000 | |
| 4001 static struct mwConference *conf_find(struct mwServiceConference *srvc, | |
| 4002 const char *name) { | |
| 4003 GList *l, *ll; | |
| 4004 struct mwConference *conf = NULL; | |
| 4005 | |
| 4006 ll = mwServiceConference_getConferences(srvc); | |
| 4007 for(l = ll; l; l = l->next) { | |
| 4008 struct mwConference *c = l->data; | |
| 4009 if(! strcmp(name, mwConference_getName(c))) { | |
| 4010 conf = c; | |
| 4011 break; | |
| 4012 } | |
| 4013 } | |
| 4014 g_list_free(ll); | |
| 4015 | |
| 4016 return conf; | |
| 4017 } | |
| 4018 | |
| 4019 | |
| 4020 static void mw_prpl_join_chat(GaimConnection *gc, | |
| 4021 GHashTable *components) { | |
| 4022 | |
| 4023 struct mwGaimPluginData *pd; | |
| 4024 struct mwServiceConference *srvc; | |
| 4025 struct mwConference *conf = NULL; | |
| 4026 char *c, *t; | |
| 4027 | |
| 4028 pd = gc->proto_data; | |
| 4029 srvc = pd->srvc_conf; | |
| 4030 | |
| 4031 c = g_hash_table_lookup(components, CHAT_KEY_NAME); | |
| 4032 t = g_hash_table_lookup(components, CHAT_KEY_TOPIC); | |
| 4033 | |
| 4034 if(c) conf = conf_find(srvc, c); | |
| 4035 | |
| 4036 if(conf) { | |
| 4037 DEBUG_INFO("accepting conference invitation\n"); | |
| 4038 mwConference_accept(conf); | |
| 4039 | |
| 4040 } else { | |
| 4041 DEBUG_INFO("creating new conference\n"); | |
| 4042 conf = mwConference_new(srvc, t); | |
| 4043 mwConference_open(conf); | |
| 4044 } | |
| 4045 } | |
| 4046 | |
| 4047 | |
| 4048 static void mw_prpl_reject_chat(GaimConnection *gc, | |
| 4049 GHashTable *components) { | |
| 4050 | |
| 4051 struct mwGaimPluginData *pd; | |
| 4052 struct mwServiceConference *srvc; | |
| 4053 char *c; | |
| 4054 | |
| 4055 pd = gc->proto_data; | |
| 4056 srvc = pd->srvc_conf; | |
| 4057 | |
| 4058 c = g_hash_table_lookup(components, CHAT_KEY_NAME); | |
| 4059 if(c) { | |
| 4060 struct mwConference *conf = conf_find(srvc, c); | |
| 4061 if(conf) mwConference_reject(conf, ERR_SUCCESS, "Declined"); | |
| 4062 } | |
| 4063 } | |
| 4064 | |
| 4065 | |
| 4066 static char *mw_prpl_get_chat_name(GHashTable *components) { | |
| 4067 return g_hash_table_lookup(components, CHAT_KEY_NAME); | |
| 4068 } | |
| 4069 | |
| 4070 | |
| 4071 static void mw_prpl_chat_invite(GaimConnection *gc, | |
| 4072 int id, | |
| 4073 const char *invitation, | |
| 4074 const char *who) { | |
| 4075 | |
| 4076 struct mwGaimPluginData *pd; | |
| 4077 struct mwConference *conf; | |
| 4078 struct mwIdBlock idb = { (char *) who, NULL }; | |
| 4079 | |
| 4080 pd = gc->proto_data; | |
| 4081 | |
| 4082 g_return_if_fail(pd != NULL); | |
| 4083 conf = ID_TO_CONF(pd, id); | |
| 4084 | |
| 4085 g_return_if_fail(conf != NULL); | |
| 4086 | |
| 4087 mwConference_invite(conf, &idb, invitation); | |
| 4088 } | |
| 4089 | |
| 4090 | |
| 4091 static void mw_prpl_chat_leave(GaimConnection *gc, | |
| 4092 int id) { | |
| 4093 | |
| 4094 struct mwGaimPluginData *pd; | |
| 4095 struct mwConference *conf; | |
| 4096 | |
| 4097 pd = gc->proto_data; | |
| 4098 | |
| 4099 g_return_if_fail(pd != NULL); | |
| 4100 conf = ID_TO_CONF(pd, id); | |
| 4101 | |
| 4102 g_return_if_fail(conf != NULL); | |
| 4103 | |
| 4104 mwConference_destroy(conf, ERR_SUCCESS, "Leaving"); | |
| 4105 } | |
| 4106 | |
| 4107 | |
| 4108 static void mw_prpl_chat_whisper(GaimConnection *gc, | |
| 4109 int id, | |
| 4110 const char *who, | |
| 4111 const char *message) { | |
| 4112 | |
| 4113 mw_prpl_send_im(gc, who, message, 0); | |
| 4114 } | |
| 4115 | |
| 4116 | |
| 4117 static int mw_prpl_chat_send(GaimConnection *gc, | |
| 4118 int id, | |
| 4119 const char *message) { | |
| 4120 | |
| 4121 struct mwGaimPluginData *pd; | |
| 4122 struct mwConference *conf; | |
| 4123 | |
| 4124 pd = gc->proto_data; | |
| 4125 | |
| 4126 g_return_val_if_fail(pd != NULL, 0); | |
| 4127 conf = ID_TO_CONF(pd, id); | |
| 4128 | |
| 4129 g_return_val_if_fail(conf != NULL, 0); | |
| 4130 | |
| 4131 return ! mwConference_sendText(conf, message); | |
| 4132 } | |
| 4133 | |
| 4134 | |
| 4135 static void mw_prpl_keepalive(GaimConnection *gc) { | |
| 4136 struct mwSession *session; | |
| 4137 | |
| 4138 g_return_if_fail(gc != NULL); | |
| 4139 | |
| 4140 session = gc_to_session(gc); | |
| 4141 g_return_if_fail(session != NULL); | |
| 4142 | |
| 4143 mwSession_sendKeepalive(session); | |
| 4144 } | |
| 4145 | |
| 4146 | |
| 4147 static void mw_prpl_alias_buddy(GaimConnection *gc, | |
| 4148 const char *who, | |
| 4149 const char *alias) { | |
| 4150 | |
| 4151 struct mwGaimPluginData *pd = gc->proto_data; | |
| 4152 g_return_if_fail(pd != NULL); | |
| 4153 | |
| 4154 /* it's a change to the buddy list, so we've gotta reflect that in | |
| 4155 the server copy */ | |
| 4156 | |
| 4157 blist_schedule(pd); | |
| 4158 } | |
| 4159 | |
| 4160 | |
| 4161 static void mw_prpl_group_buddy(GaimConnection *gc, | |
| 4162 const char *who, | |
| 4163 const char *old_group, | |
| 4164 const char *new_group) { | |
| 4165 | |
| 4166 struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; | |
| 4167 GList *gl = g_list_prepend(NULL, &idb); | |
| 4168 | |
| 4169 struct mwGaimPluginData *pd = gc->proto_data; | |
| 4170 GaimGroup *group; | |
| 4171 struct mwAwareList *list; | |
| 4172 | |
| 4173 /* add who to new_group's aware list */ | |
| 4174 group = gaim_find_group(new_group); | |
| 4175 list = list_ensure(pd, group); | |
| 4176 mwAwareList_addAware(list, gl); | |
| 4177 | |
| 4178 /* remove who from old_group's aware list */ | |
| 4179 group = gaim_find_group(old_group); | |
| 4180 list = list_ensure(pd, group); | |
| 4181 mwAwareList_removeAware(list, gl); | |
| 4182 | |
| 4183 g_list_free(gl); | |
| 4184 | |
| 4185 /* schedule the changes to be saved */ | |
| 4186 blist_schedule(pd); | |
| 4187 } | |
| 4188 | |
| 4189 | |
| 4190 static void mw_prpl_rename_group(GaimConnection *gc, | |
| 4191 const char *old, | |
| 4192 GaimGroup *group, | |
| 4193 GList *buddies) { | |
| 4194 | |
| 4195 struct mwGaimPluginData *pd = gc->proto_data; | |
| 4196 g_return_if_fail(pd != NULL); | |
| 4197 | |
| 4198 /* it's a change in the buddy list, so we've gotta reflect that in | |
| 4199 the server copy. Also, having this function should prevent all | |
| 4200 those buddies from being removed and re-added. We don't really | |
| 4201 give a crap what the group is named in Gaim other than to record | |
| 4202 that as the group name/alias */ | |
| 4203 | |
| 4204 blist_schedule(pd); | |
| 4205 } | |
| 4206 | |
| 4207 | |
| 4208 static void mw_prpl_buddy_free(GaimBuddy *buddy) { | |
| 4209 /* I don't think we have any cleanup for buddies yet */ | |
| 4210 ; | |
| 4211 } | |
| 4212 | |
| 4213 | |
| 4214 static void mw_prpl_convo_closed(GaimConnection *gc, const char *who) { | |
| 4215 struct mwGaimPluginData *pd = gc->proto_data; | |
| 4216 struct mwServiceIm *srvc; | |
| 4217 struct mwConversation *conv; | |
| 4218 struct mwIdBlock idb = { (char *) who, NULL }; | |
| 4219 | |
| 4220 g_return_if_fail(pd != NULL); | |
| 4221 | |
| 4222 srvc = pd->srvc_im; | |
| 4223 g_return_if_fail(srvc != NULL); | |
| 4224 | |
| 4225 conv = mwServiceIm_findConversation(srvc, &idb); | |
| 4226 if(! conv) return; | |
| 4227 | |
| 4228 if(mwConversation_isOpen(conv)) | |
| 4229 mwConversation_free(conv); | |
| 4230 } | |
| 4231 | |
| 4232 | |
| 4233 static const char *mw_prpl_normalize(const GaimAccount *account, | |
| 4234 const char *id) { | |
| 4235 | |
| 4236 /* code elsewhere assumes that the return value points to different | |
| 4237 memory than the passed value, but it won't free the normalized | |
| 4238 data. wtf? */ | |
| 4239 | |
| 4240 static char buf[BUF_LEN]; | |
| 4241 strncpy(buf, id, sizeof(buf)); | |
| 4242 return buf; | |
| 4243 } | |
| 4244 | |
| 4245 | |
| 4246 static void mw_prpl_remove_group(GaimConnection *gc, GaimGroup *group) { | |
| 4247 struct mwGaimPluginData *pd; | |
| 4248 struct mwAwareList *list; | |
| 4249 | |
| 4250 pd = gc->proto_data; | |
| 4251 g_return_if_fail(pd != NULL); | |
| 4252 g_return_if_fail(pd->group_list_map != NULL); | |
| 4253 | |
| 4254 list = g_hash_table_lookup(pd->group_list_map, group); | |
| 4255 | |
| 4256 if(list) { | |
| 4257 g_hash_table_remove(pd->group_list_map, list); | |
| 4258 g_hash_table_remove(pd->group_list_map, group); | |
| 4259 mwAwareList_free(list); | |
| 4260 | |
| 4261 blist_schedule(pd); | |
| 4262 } | |
| 4263 } | |
| 4264 | |
| 4265 | |
| 4266 static gboolean mw_prpl_can_receive_file(GaimConnection *gc, | |
| 4267 const char *who) { | |
| 4268 struct mwGaimPluginData *pd; | |
| 4269 struct mwServiceAware *srvc; | |
| 4270 GaimAccount *acct; | |
| 4271 | |
| 4272 g_return_val_if_fail(gc != NULL, FALSE); | |
| 4273 | |
| 4274 pd = gc->proto_data; | |
| 4275 g_return_val_if_fail(pd != NULL, FALSE); | |
| 4276 | |
| 4277 srvc = pd->srvc_aware; | |
| 4278 g_return_val_if_fail(srvc != NULL, FALSE); | |
| 4279 | |
| 4280 acct = gaim_connection_get_account(gc); | |
| 4281 g_return_val_if_fail(acct != NULL, FALSE); | |
| 4282 | |
| 4283 return gaim_find_buddy(acct, who) && | |
| 4284 user_supports(srvc, who, mwAttribute_FILE_TRANSFER); | |
| 4285 } | |
| 4286 | |
| 4287 | |
| 4288 static void ft_outgoing_init(GaimXfer *xfer) { | |
| 4289 GaimAccount *acct; | |
| 4290 GaimConnection *gc; | |
| 4291 | |
| 4292 struct mwGaimPluginData *pd; | |
| 4293 struct mwServiceFileTransfer *srvc; | |
| 4294 struct mwFileTransfer *ft; | |
| 4295 | |
| 4296 const char *filename; | |
| 4297 gsize filesize; | |
| 4298 FILE *fp; | |
| 4299 | |
| 4300 struct mwIdBlock idb = { NULL, NULL }; | |
| 4301 | |
| 4302 DEBUG_INFO("ft_outgoing_init\n"); | |
| 4303 | |
| 4304 acct = gaim_xfer_get_account(xfer); | |
| 4305 gc = gaim_account_get_connection(acct); | |
| 4306 pd = gc->proto_data; | |
| 4307 srvc = pd->srvc_ft; | |
| 4308 | |
| 4309 filename = gaim_xfer_get_local_filename(xfer); | |
| 4310 filesize = gaim_xfer_get_size(xfer); | |
| 4311 idb.user = xfer->who; | |
| 4312 | |
| 4313 /* test that we can actually send the file */ | |
| 4314 fp = g_fopen(filename, "rb"); | |
| 4315 if(! fp) { | |
| 4316 char *msg = g_strdup_printf("Error reading %s: \n%s\n", | |
| 4317 filename, strerror(errno)); | |
| 4318 gaim_xfer_error(gaim_xfer_get_type(xfer), acct, xfer->who, msg); | |
| 4319 g_free(msg); | |
| 4320 return; | |
| 4321 } | |
| 4322 fclose(fp); | |
| 4323 | |
| 4324 { | |
| 4325 char *tmp = strrchr(filename, G_DIR_SEPARATOR); | |
| 4326 if(tmp++) filename = tmp; | |
| 4327 } | |
| 4328 | |
| 4329 ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize); | |
| 4330 | |
| 4331 gaim_xfer_ref(xfer); | |
| 4332 mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) gaim_xfer_unref); | |
| 4333 xfer->data = ft; | |
| 4334 | |
| 4335 mwFileTransfer_offer(ft); | |
| 4336 } | |
| 4337 | |
| 4338 | |
| 4339 static void ft_outgoing_cancel(GaimXfer *xfer) { | |
| 4340 struct mwFileTransfer *ft = xfer->data; | |
| 4341 if(ft) mwFileTransfer_cancel(ft); | |
| 4342 } | |
| 4343 | |
| 4344 | |
| 4345 static void mw_prpl_send_file(GaimConnection *gc, | |
| 4346 const char *who, const char *file) { | |
| 4347 | |
| 4348 GaimAccount *acct; | |
| 4349 GaimXfer *xfer; | |
| 4350 | |
| 4351 DEBUG_INFO("mw_prpl_send_file\n"); | |
| 4352 | |
| 4353 acct = gaim_connection_get_account(gc); | |
| 4354 | |
| 4355 xfer = gaim_xfer_new(acct, GAIM_XFER_SEND, who); | |
| 4356 gaim_xfer_set_init_fnc(xfer, ft_outgoing_init); | |
| 4357 gaim_xfer_set_cancel_send_fnc(xfer, ft_outgoing_cancel); | |
| 4358 | |
| 4359 if(file) { | |
| 4360 DEBUG_INFO("file != NULL\n"); | |
| 4361 gaim_xfer_request_accepted(xfer, file); | |
| 4362 | |
| 4363 } else { | |
| 4364 DEBUG_INFO("file == NULL\n"); | |
| 4365 gaim_xfer_request(xfer); | |
| 4366 } | |
| 4367 } | |
| 4368 | |
| 4369 | |
| 4370 static GaimPluginProtocolInfo mw_prpl_info = { | |
| 4371 .options = OPT_PROTO_IM_IMAGE, | |
| 4372 .user_splits = NULL, /*< set in mw_plugin_init */ | |
| 4373 .protocol_options = NULL, /*< set in mw_plugin_init */ | |
| 4374 .icon_spec = NO_BUDDY_ICONS, | |
| 4375 .list_icon = mw_prpl_list_icon, | |
| 4376 .list_emblems = mw_prpl_list_emblems, | |
| 4377 .status_text = mw_prpl_status_text, | |
| 4378 .tooltip_text = mw_prpl_tooltip_text, | |
| 4379 .status_types = mw_prpl_status_types, | |
| 4380 .blist_node_menu = mw_prpl_blist_node_menu, | |
| 4381 .chat_info = mw_prpl_chat_info, | |
| 4382 .chat_info_defaults = mw_prpl_chat_info_defaults, | |
| 4383 .login = mw_prpl_login, | |
| 4384 .close = mw_prpl_close, | |
| 4385 .send_im = mw_prpl_send_im, | |
| 4386 .set_info = NULL, | |
| 4387 .send_typing = mw_prpl_send_typing, | |
| 4388 .get_info = mw_prpl_get_info, | |
| 4389 .set_idle = mw_prpl_set_idle, | |
| 4390 .change_passwd = NULL, | |
| 4391 .add_buddy = mw_prpl_add_buddy, | |
| 4392 .add_buddies = mw_prpl_add_buddies, | |
| 4393 .remove_buddy = mw_prpl_remove_buddy, | |
| 4394 .remove_buddies = NULL, | |
| 4395 .add_permit = mw_prpl_add_permit, | |
| 4396 .add_deny = mw_prpl_add_deny, | |
| 4397 .rem_permit = mw_prpl_rem_permit, | |
| 4398 .rem_deny = mw_prpl_rem_deny, | |
| 4399 .set_permit_deny = mw_prpl_set_permit_deny, | |
| 4400 .join_chat = mw_prpl_join_chat, | |
| 4401 .reject_chat = mw_prpl_reject_chat, | |
| 4402 .get_chat_name = mw_prpl_get_chat_name, | |
| 4403 .chat_invite = mw_prpl_chat_invite, | |
| 4404 .chat_leave = mw_prpl_chat_leave, | |
| 4405 .chat_whisper = mw_prpl_chat_whisper, | |
| 4406 .chat_send = mw_prpl_chat_send, | |
| 4407 .keepalive = mw_prpl_keepalive, | |
| 4408 .register_user = NULL, | |
| 4409 .get_cb_info = NULL, | |
| 4410 .get_cb_away = NULL, | |
| 4411 .alias_buddy = mw_prpl_alias_buddy, | |
| 4412 .group_buddy = mw_prpl_group_buddy, | |
| 4413 .rename_group = mw_prpl_rename_group, | |
| 4414 .buddy_free = mw_prpl_buddy_free, | |
| 4415 .convo_closed = mw_prpl_convo_closed, | |
| 4416 .normalize = mw_prpl_normalize, | |
| 4417 .set_buddy_icon = NULL, | |
| 4418 .remove_group = mw_prpl_remove_group, | |
| 4419 .get_cb_real_name = NULL, | |
| 4420 .set_chat_topic = NULL, | |
| 4421 .find_blist_chat = NULL, | |
| 4422 .roomlist_get_list = NULL, | |
| 4423 .roomlist_expand_category = NULL, | |
| 4424 .can_receive_file = mw_prpl_can_receive_file, | |
| 4425 .send_file = mw_prpl_send_file, | |
| 4426 }; | |
| 4427 | |
| 4428 | |
| 4429 static GaimPluginPrefFrame * | |
| 4430 mw_plugin_get_plugin_pref_frame(GaimPlugin *plugin) { | |
| 4431 GaimPluginPrefFrame *frame; | |
| 4432 GaimPluginPref *pref; | |
| 4433 | |
| 4434 frame = gaim_plugin_pref_frame_new(); | |
| 4435 | |
| 4436 pref = gaim_plugin_pref_new_with_label("Remotely Stored Buddy List"); | |
| 4437 gaim_plugin_pref_frame_add(frame, pref); | |
| 4438 | |
| 4439 | |
| 4440 pref = gaim_plugin_pref_new_with_name(MW_PRPL_OPT_BLIST_ACTION); | |
| 4441 gaim_plugin_pref_set_label(pref, "Buddy List Storage Mode"); | |
| 4442 | |
| 4443 gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_CHOICE); | |
| 4444 gaim_plugin_pref_add_choice(pref, "Local Buddy List Only", | |
| 4445 GINT_TO_POINTER(BLIST_CHOICE_NONE)); | |
| 4446 gaim_plugin_pref_add_choice(pref, "Merge List from Server", | |
| 4447 GINT_TO_POINTER(BLIST_CHOICE_LOAD)); | |
| 4448 gaim_plugin_pref_add_choice(pref, "Merge and Save List to Server", | |
| 4449 GINT_TO_POINTER(BLIST_CHOICE_SAVE)); | |
| 4450 | |
| 4451 #if 0 | |
| 4452 /* possible ways to handle: | |
| 4453 - mark all buddies as NO_SAVE | |
| 4454 - load server list, delete all local buddies not in server list | |
| 4455 */ | |
| 4456 gaim_plugin_pref_add_choice(pref, "Server Buddy List Only", | |
| 4457 GINT_TO_POINTER(BLIST_CHOISE_SERVER)); | |
| 4458 #endif | |
| 4459 | |
| 4460 gaim_plugin_pref_frame_add(frame, pref); | |
| 4461 | |
| 4462 pref = gaim_plugin_pref_new_with_label("General Options"); | |
| 4463 gaim_plugin_pref_frame_add(frame, pref); | |
| 4464 | |
| 4465 pref = gaim_plugin_pref_new_with_name(MW_PRPL_OPT_FORCE_LOGIN); | |
| 4466 gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_NONE); | |
| 4467 gaim_plugin_pref_set_label(pref, "Force Login (Ignore Login Redirects)"); | |
| 4468 gaim_plugin_pref_frame_add(frame, pref); | |
| 4469 | |
| 4470 pref = gaim_plugin_pref_new_with_name(MW_PRPL_OPT_PSYCHIC); | |
| 4471 gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_NONE); | |
| 4472 gaim_plugin_pref_set_label(pref, "Enable Psychic Mode"); | |
| 4473 gaim_plugin_pref_frame_add(frame, pref); | |
| 4474 | |
| 4475 pref = gaim_plugin_pref_new_with_name(MW_PRPL_OPT_SAVE_DYNAMIC); | |
| 4476 gaim_plugin_pref_set_type(pref, GAIM_PLUGIN_PREF_NONE); | |
| 4477 gaim_plugin_pref_set_label(pref, "Save NAB group members locally"); | |
| 4478 gaim_plugin_pref_frame_add(frame, pref); | |
| 4479 | |
| 4480 return frame; | |
| 4481 } | |
| 4482 | |
| 4483 | |
| 4484 static GaimPluginUiInfo mw_plugin_ui_info = { | |
| 4485 .get_plugin_pref_frame = mw_plugin_get_plugin_pref_frame, | |
| 4486 }; | |
| 4487 | |
| 4488 | |
| 4489 static void status_msg_action_cb(GaimConnection *gc, | |
| 4490 GaimRequestFields *fields) { | |
| 4491 GaimAccount *acct; | |
| 4492 GaimRequestField *f; | |
| 4493 const char *msg; | |
| 4494 /* gboolean prompt; */ | |
| 4495 | |
| 4496 struct mwGaimPluginData *pd; | |
| 4497 struct mwServiceStorage *srvc; | |
| 4498 struct mwStorageUnit *unit; | |
| 4499 | |
| 4500 pd = gc->proto_data; | |
| 4501 srvc = pd->srvc_store; | |
| 4502 | |
| 4503 acct = gaim_connection_get_account(gc); | |
| 4504 | |
| 4505 f = gaim_request_fields_get_field(fields, "active"); | |
| 4506 msg = gaim_request_field_string_get_value(f); | |
| 4507 gaim_account_set_string(acct, MW_KEY_ACTIVE_MSG, msg); | |
| 4508 unit = mwStorageUnit_newString(mwStore_ACTIVE_MESSAGES, msg); | |
| 4509 mwServiceStorage_save(srvc, unit, NULL, NULL, NULL); | |
| 4510 | |
| 4511 f = gaim_request_fields_get_field(fields, "away"); | |
| 4512 msg = gaim_request_field_string_get_value(f); | |
| 4513 gaim_account_set_string(acct, MW_KEY_AWAY_MSG, msg); | |
| 4514 unit = mwStorageUnit_newString(mwStore_AWAY_MESSAGES, msg); | |
| 4515 mwServiceStorage_save(srvc, unit, NULL, NULL, NULL); | |
| 4516 | |
| 4517 f = gaim_request_fields_get_field(fields, "busy"); | |
| 4518 msg = gaim_request_field_string_get_value(f); | |
| 4519 gaim_account_set_string(acct, MW_KEY_BUSY_MSG, msg); | |
| 4520 unit = mwStorageUnit_newString(mwStore_BUSY_MESSAGES, msg); | |
| 4521 mwServiceStorage_save(srvc, unit, NULL, NULL, NULL); | |
| 4522 | |
| 4523 #if 0 | |
| 4524 /** @todo not yet used. It should be possible to prompt the user for | |
| 4525 a message (ala the Sametime Connect client) when changing to one | |
| 4526 of the default states, and that preference is here */ | |
| 4527 f = gaim_request_fields_get_field(fields, "prompt"); | |
| 4528 prompt = gaim_request_field_bool_get_value(f); | |
| 4529 gaim_account_set_bool(acct, MW_KEY_MSG_PROMPT, prompt); | |
| 4530 #endif | |
| 4531 | |
| 4532 #if 0 | |
| 4533 /* XXX */ | |
| 4534 /* need to propagate the message change if we're in any of those | |
| 4535 default states */ | |
| 4536 msg = NULL; | |
| 4537 if(!gc->away_state || !strcmp(gc->away_state, MW_STATE_ACTIVE)) { | |
| 4538 msg = MW_STATE_ACTIVE; | |
| 4539 } else if(gc->away_state && !strcmp(gc->away_state, MW_STATE_AWAY)) { | |
| 4540 msg = MW_STATE_AWAY; | |
| 4541 } else if(gc->away_state && !strcmp(gc->away_state, MW_STATE_BUSY)) { | |
| 4542 msg = MW_STATE_BUSY; | |
| 4543 } | |
| 4544 if(msg) | |
| 4545 serv_set_away(gc, msg, NULL); | |
| 4546 #endif | |
| 4547 } | |
| 4548 | |
| 4549 | |
| 4550 /** Prompt for messages for the three default status types. These | |
| 4551 values should be mirrored as strings in the storage service */ | |
| 4552 static void status_msg_action(GaimPluginAction *act) { | |
| 4553 GaimConnection *gc; | |
| 4554 GaimAccount *acct; | |
| 4555 | |
| 4556 GaimRequestFields *fields; | |
| 4557 GaimRequestFieldGroup *g; | |
| 4558 GaimRequestField *f; | |
| 4559 | |
| 4560 char *msgA, *msgB; | |
| 4561 const char *val; | |
| 4562 /* gboolean prompt; */ | |
| 4563 | |
| 4564 gc = act->context; | |
| 4565 acct = gaim_connection_get_account(gc); | |
| 4566 | |
| 4567 fields = gaim_request_fields_new(); | |
| 4568 | |
| 4569 g = gaim_request_field_group_new(NULL); | |
| 4570 gaim_request_fields_add_group(fields, g); | |
| 4571 | |
| 4572 val = gaim_account_get_string(acct, MW_KEY_ACTIVE_MSG, | |
| 4573 MW_PLUGIN_DEFAULT_ACTIVE_MSG); | |
| 4574 f = gaim_request_field_string_new("active", "Active Message", val, FALSE); | |
| 4575 gaim_request_field_set_required(f, FALSE); | |
| 4576 gaim_request_field_group_add_field(g, f); | |
| 4577 | |
| 4578 val = gaim_account_get_string(acct, MW_KEY_AWAY_MSG, | |
| 4579 MW_PLUGIN_DEFAULT_AWAY_MSG); | |
| 4580 f = gaim_request_field_string_new("away", "Away Message", val, FALSE); | |
| 4581 gaim_request_field_set_required(f, FALSE); | |
| 4582 gaim_request_field_group_add_field(g, f); | |
| 4583 | |
| 4584 val = gaim_account_get_string(acct, MW_KEY_BUSY_MSG, | |
| 4585 MW_PLUGIN_DEFAULT_BUSY_MSG); | |
| 4586 f = gaim_request_field_string_new("busy", "Busy Message", val, FALSE); | |
| 4587 gaim_request_field_set_required(f, FALSE); | |
| 4588 gaim_request_field_group_add_field(g, f); | |
| 4589 | |
| 4590 #if 0 | |
| 4591 /** @todo not yet used. It should be possible to prompt the user for | |
| 4592 a message (ala the Sametime Connect client) when changing to one | |
| 4593 of the default states, and that preference is here */ | |
| 4594 prompt = gaim_account_get_bool(acct, MW_KEY_MSG_PROMPT, FALSE); | |
| 4595 f = gaim_request_field_bool_new("prompt", | |
| 4596 ("Prompt for message when changing" | |
| 4597 " to one of these states?"), FALSE); | |
| 4598 gaim_request_field_group_add_field(g, f); | |
| 4599 #endif | |
| 4600 | |
| 4601 msgA = ("Default status messages"); | |
| 4602 msgB = (""); | |
| 4603 | |
| 4604 gaim_request_fields(gc, "Edit Status Messages", | |
| 4605 msgA, msgB, fields, | |
| 4606 _("OK"), G_CALLBACK(status_msg_action_cb), | |
| 4607 _("Cancel"), NULL, | |
| 4608 gc); | |
| 4609 } | |
| 4610 | |
| 4611 | |
| 4612 static void st_import_action_cb(GaimConnection *gc, char *filename) { | |
| 4613 struct mwSametimeList *l; | |
| 4614 | |
| 4615 FILE *file; | |
| 4616 char buf[BUF_LEN]; | |
| 4617 size_t len; | |
| 4618 | |
| 4619 GString *str; | |
| 4620 | |
| 4621 file = fopen(filename, "r"); | |
| 4622 g_return_if_fail(file != NULL); | |
| 4623 | |
| 4624 str = g_string_new(NULL); | |
| 4625 while( (len = fread(buf, 1, BUF_LEN, file)) ) { | |
| 4626 g_string_append_len(str, buf, len); | |
| 4627 } | |
| 4628 | |
| 4629 fclose(file); | |
| 4630 | |
| 4631 l = mwSametimeList_load(str->str); | |
| 4632 g_string_free(str, TRUE); | |
| 4633 | |
| 4634 blist_import(gc, l); | |
| 4635 mwSametimeList_free(l); | |
| 4636 } | |
| 4637 | |
| 4638 | |
| 4639 /** prompts for a file to import blist from */ | |
| 4640 static void st_import_action(GaimPluginAction *act) { | |
| 4641 GaimConnection *gc; | |
| 4642 GaimAccount *account; | |
| 4643 char *title; | |
| 4644 | |
| 4645 gc = act->context; | |
| 4646 account = gaim_connection_get_account(gc); | |
| 4647 title = g_strdup_printf("Import Sametime List for Account %s", | |
| 4648 gaim_account_get_username(account)); | |
| 4649 | |
| 4650 gaim_request_file(gc, title, NULL, FALSE, | |
| 4651 G_CALLBACK(st_import_action_cb), NULL, | |
| 4652 gc); | |
| 4653 | |
| 4654 g_free(title); | |
| 4655 } | |
| 4656 | |
| 4657 | |
| 4658 static void st_export_action_cb(GaimConnection *gc, char *filename) { | |
| 4659 struct mwSametimeList *l; | |
| 4660 char *str; | |
| 4661 FILE *file; | |
| 4662 | |
| 4663 file = fopen(filename, "w"); | |
| 4664 g_return_if_fail(file != NULL); | |
| 4665 | |
| 4666 l = mwSametimeList_new(); | |
| 4667 blist_export(gc, l); | |
| 4668 str = mwSametimeList_store(l); | |
| 4669 mwSametimeList_free(l); | |
| 4670 | |
| 4671 fprintf(file, "%s", str); | |
| 4672 fclose(file); | |
| 4673 | |
| 4674 g_free(str); | |
| 4675 } | |
| 4676 | |
| 4677 | |
| 4678 /** prompts for a file to export blist to */ | |
| 4679 static void st_export_action(GaimPluginAction *act) { | |
| 4680 GaimConnection *gc; | |
| 4681 GaimAccount *account; | |
| 4682 char *title; | |
| 4683 | |
| 4684 gc = act->context; | |
| 4685 account = gaim_connection_get_account(gc); | |
| 4686 title = g_strdup_printf("Export Sametime List for Account %s", | |
| 4687 gaim_account_get_username(account)); | |
| 4688 | |
| 4689 gaim_request_file(gc, title, NULL, TRUE, | |
| 4690 G_CALLBACK(st_export_action_cb), NULL, | |
| 4691 gc); | |
| 4692 | |
| 4693 g_free(title); | |
| 4694 } | |
| 4695 | |
| 4696 | |
| 4697 static void remote_group_multi_cleanup(gpointer ignore, | |
| 4698 GaimRequestFields *fields) { | |
| 4699 | |
| 4700 GaimRequestField *f; | |
| 4701 const GList *l; | |
| 4702 | |
| 4703 f = gaim_request_fields_get_field(fields, "group"); | |
| 4704 l = gaim_request_field_list_get_items(f); | |
| 4705 | |
| 4706 for(; l; l = l->next) { | |
| 4707 const char *i = l->data; | |
| 4708 struct resolved_id *res; | |
| 4709 | |
| 4710 res = gaim_request_field_list_get_data(f, i); | |
| 4711 | |
| 4712 g_free(res->id); | |
| 4713 g_free(res->name); | |
| 4714 g_free(res); | |
| 4715 } | |
| 4716 } | |
| 4717 | |
| 4718 | |
| 4719 static void remote_group_done(struct mwGaimPluginData *pd, | |
| 4720 const char *id, const char *name) { | |
| 4721 GaimConnection *gc; | |
| 4722 GaimAccount *acct; | |
| 4723 GaimGroup *group; | |
| 4724 GaimBlistNode *gn; | |
| 4725 const char *owner; | |
| 4726 | |
| 4727 g_return_if_fail(pd != NULL); | |
| 4728 | |
| 4729 gc = pd->gc; | |
| 4730 acct = gaim_connection_get_account(gc); | |
| 4731 | |
| 4732 /* collision checking */ | |
| 4733 group = gaim_find_group(name); | |
| 4734 if(group) { | |
| 4735 char *msgA, *msgB; | |
| 4736 | |
| 4737 msgA = "Unable to add group: group exists"; | |
| 4738 msgB = "A group named '%s' already exists in your buddy list."; | |
| 4739 msgB = g_strdup_printf(msgB, name); | |
| 4740 | |
| 4741 gaim_notify_error(gc, "Unable to add group", msgA, msgB); | |
| 4742 | |
| 4743 g_free(msgB); | |
| 4744 return; | |
| 4745 } | |
| 4746 | |
| 4747 group = gaim_group_new(name); | |
| 4748 gn = (GaimBlistNode *) group; | |
| 4749 | |
| 4750 owner = gaim_account_get_username(acct); | |
| 4751 | |
| 4752 gaim_blist_node_set_string(gn, GROUP_KEY_NAME, id); | |
| 4753 gaim_blist_node_set_int(gn, GROUP_KEY_TYPE, mwSametimeGroup_DYNAMIC); | |
| 4754 gaim_blist_node_set_string(gn, GROUP_KEY_OWNER, owner); | |
| 4755 gaim_blist_add_group(group, NULL); | |
| 4756 | |
| 4757 group_add(pd, group); | |
| 4758 blist_schedule(pd); | |
| 4759 } | |
| 4760 | |
| 4761 | |
| 4762 static void remote_group_multi_cb(struct mwGaimPluginData *pd, | |
| 4763 GaimRequestFields *fields) { | |
| 4764 GaimRequestField *f; | |
| 4765 const GList *l; | |
| 4766 | |
| 4767 f = gaim_request_fields_get_field(fields, "group"); | |
| 4768 l = gaim_request_field_list_get_selected(f); | |
| 4769 | |
| 4770 if(l) { | |
| 4771 const char *i = l->data; | |
| 4772 struct resolved_id *res; | |
| 4773 | |
| 4774 res = gaim_request_field_list_get_data(f, i); | |
| 4775 remote_group_done(pd, res->id, res->name); | |
| 4776 } | |
| 4777 | |
| 4778 remote_group_multi_cleanup(NULL, fields); | |
| 4779 } | |
| 4780 | |
| 4781 | |
| 4782 static void remote_group_multi(struct mwResolveResult *result, | |
| 4783 struct mwGaimPluginData *pd) { | |
| 4784 | |
| 4785 GaimRequestFields *fields; | |
| 4786 GaimRequestFieldGroup *g; | |
| 4787 GaimRequestField *f; | |
| 4788 GList *l; | |
| 4789 char *msgA, *msgB; | |
| 4790 | |
| 4791 GaimConnection *gc = pd->gc; | |
| 4792 | |
| 4793 fields = gaim_request_fields_new(); | |
| 4794 | |
| 4795 g = gaim_request_field_group_new(NULL); | |
| 4796 gaim_request_fields_add_group(fields, g); | |
| 4797 | |
| 4798 f = gaim_request_field_list_new("group", "Possible Matches"); | |
| 4799 gaim_request_field_list_set_multi_select(f, FALSE); | |
| 4800 gaim_request_field_set_required(f, TRUE); | |
| 4801 | |
| 4802 for(l = result->matches; l; l = l->next) { | |
| 4803 struct mwResolveMatch *match = l->data; | |
| 4804 struct resolved_id *res = g_new0(struct resolved_id, 1); | |
| 4805 | |
| 4806 res->id = g_strdup(match->id); | |
| 4807 res->name = g_strdup(match->name); | |
| 4808 | |
| 4809 gaim_request_field_list_add(f, res->name, res); | |
| 4810 } | |
| 4811 | |
| 4812 gaim_request_field_group_add_field(g, f); | |
| 4813 | |
| 4814 msgA = ("Notes Address Book group results"); | |
| 4815 msgB = ("The identifier '%s' may possibly refer to any of the following" | |
| 4816 " Notes Address Book groups. Please select the correct group from" | |
| 4817 " the list below to add it to your buddy list."); | |
| 4818 msgB = g_strdup_printf(msgB, result->name); | |
| 4819 | |
| 4820 gaim_request_fields(gc, "Select Notes Address Book", | |
| 4821 msgA, msgB, fields, | |
| 4822 "Add Group", G_CALLBACK(remote_group_multi_cb), | |
| 4823 "Cancel", G_CALLBACK(remote_group_multi_cleanup), | |
| 4824 pd); | |
| 4825 | |
| 4826 g_free(msgB); | |
| 4827 } | |
| 4828 | |
| 4829 | |
| 4830 static void remote_group_resolved(struct mwServiceResolve *srvc, | |
| 4831 guint32 id, guint32 code, GList *results, | |
| 4832 gpointer b) { | |
|
11133
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
4833 |
|
1081735edc10
[gaim-migrate @ 13192]
Christopher O'Brien <siege@pidgin.im>
parents:
11132
diff
changeset
|
4834 struct mwResolveResult *res = NULL; |
| 10977 | 4835 struct mwSession *session; |
| 4836 struct mwGaimPluginData *pd; | |
| 4837 GaimConnection *gc; | |
| 4838 | |
| 4839 session = mwService_getSession(MW_SERVICE(srvc)); | |
| 4840 g_return_if_fail(session != NULL); | |
| 4841 | |
| 4842 pd = mwSession_getClientData(session); | |
| 4843 g_return_if_fail(pd != NULL); | |
| 4844 | |
| 4845 gc = pd->gc; | |
| 4846 g_return_if_fail(gc != NULL); | |
| 4847 | |
| 4848 if(!code && results) { | |
| 4849 res = results->data; | |
| 4850 | |
| 4851 if(res->matches) { | |
| 4852 remote_group_multi(res, pd); | |
| 4853 return; | |
| 4854 } | |
| 4855 } | |
| 4856 | |
| 4857 if(res && res->name) { | |
| 4858 char *msgA, *msgB; | |
| 4859 | |
| 4860 msgA = "Unable to add group: group not found"; | |
| 4861 | |
| 4862 msgB = ("The identifier '%s' did not match any Notes Address Book" | |
| 4863 " groups in your Sametime community."); | |
| 4864 msgB = g_strdup_printf(msgB, res->name); | |
| 4865 | |
| 4866 gaim_notify_error(gc, "Unable to add group", msgA, msgB); | |
| 4867 | |
| 4868 g_free(msgB); | |
| 4869 } | |
| 4870 } | |
| 4871 | |
| 4872 | |
| 4873 static void remote_group_action_cb(GaimConnection *gc, const char *name) { | |
| 4874 struct mwGaimPluginData *pd; | |
| 4875 struct mwServiceResolve *srvc; | |
| 4876 GList *query; | |
| 4877 enum mwResolveFlag flags; | |
| 4878 guint32 req; | |
| 4879 | |
| 4880 pd = gc->proto_data; | |
| 4881 srvc = pd->srvc_resolve; | |
| 4882 | |
| 4883 query = g_list_prepend(NULL, (char *) name); | |
| 4884 flags = mwResolveFlag_FIRST | mwResolveFlag_GROUPS; | |
| 4885 | |
| 4886 req = mwServiceResolve_resolve(srvc, query, flags, remote_group_resolved, | |
| 4887 NULL, NULL); | |
| 4888 g_list_free(query); | |
| 4889 | |
| 4890 if(req == SEARCH_ERROR) { | |
| 4891 /** @todo display error */ | |
| 4892 } | |
| 4893 } | |
| 4894 | |
| 4895 | |
| 4896 static void remote_group_action(GaimPluginAction *act) { | |
| 4897 GaimConnection *gc; | |
| 4898 const char *msgA, *msgB; | |
| 4899 | |
| 4900 gc = act->context; | |
| 4901 | |
| 4902 msgA = "Notes Address Book Group"; | |
| 4903 msgB = ("Enter the name of a Notes Address Book group in the field below" | |
| 4904 " to add the group and its members to your buddy list."); | |
| 4905 | |
| 4906 gaim_request_input(gc, "Add Group", msgA, msgB, NULL, | |
| 4907 FALSE, FALSE, NULL, | |
| 4908 "Add", G_CALLBACK(remote_group_action_cb), | |
| 4909 "Cancel", NULL, | |
| 4910 gc); | |
| 4911 } | |
| 4912 | |
| 4913 | |
| 4914 static GList *mw_plugin_actions(GaimPlugin *plugin, gpointer context) { | |
| 4915 GaimPluginAction *act; | |
| 4916 GList *l = NULL; | |
| 4917 | |
| 4918 act = gaim_plugin_action_new("Set Status Messages...", status_msg_action); | |
| 4919 l = g_list_append(l, act); | |
| 4920 | |
| 4921 act = gaim_plugin_action_new("Import Sametime List...", st_import_action); | |
| 4922 l = g_list_append(l, act); | |
| 4923 | |
| 4924 act = gaim_plugin_action_new("Export Sametime List...", st_export_action); | |
| 4925 l = g_list_append(l, act); | |
| 4926 | |
| 4927 act = gaim_plugin_action_new("Add Notes Address Book Group...", | |
| 4928 remote_group_action); | |
| 4929 l = g_list_append(l, act); | |
| 4930 | |
| 4931 return l; | |
| 4932 } | |
| 4933 | |
| 4934 | |
| 4935 static gboolean mw_plugin_load(GaimPlugin *plugin) { | |
| 4936 return TRUE; | |
| 4937 } | |
| 4938 | |
| 4939 | |
| 4940 static gboolean mw_plugin_unload(GaimPlugin *plugin) { | |
| 4941 return TRUE; | |
| 4942 } | |
| 4943 | |
| 4944 | |
| 4945 static void mw_plugin_destroy(GaimPlugin *plugin) { | |
| 4946 g_log_remove_handler(G_LOG_DOMAIN, log_handler[0]); | |
| 4947 g_log_remove_handler("meanwhile", log_handler[1]); | |
| 4948 } | |
| 4949 | |
| 4950 | |
| 4951 static GaimPluginInfo mw_plugin_info = { | |
| 4952 .magic = GAIM_PLUGIN_MAGIC, | |
| 4953 .major_version = GAIM_MAJOR_VERSION, | |
| 4954 .minor_version = GAIM_MINOR_VERSION, | |
| 4955 .type = GAIM_PLUGIN_PROTOCOL, | |
| 4956 .ui_requirement = NULL, | |
| 4957 .flags = 0, | |
| 4958 .dependencies = NULL, | |
| 4959 .priority = GAIM_PRIORITY_DEFAULT, | |
| 4960 .id = PLUGIN_ID, | |
| 4961 .name = PLUGIN_NAME, | |
| 4962 .version = VERSION, | |
| 4963 .summary = PLUGIN_SUMMARY, | |
| 4964 .description = PLUGIN_DESC, | |
| 4965 .author = PLUGIN_AUTHOR, | |
| 4966 .homepage = PLUGIN_HOMEPAGE, | |
| 4967 .load = mw_plugin_load, | |
| 4968 .unload = mw_plugin_unload, | |
| 4969 .destroy = mw_plugin_destroy, | |
| 4970 .ui_info = NULL, | |
| 4971 .extra_info = &mw_prpl_info, | |
| 4972 .prefs_info = &mw_plugin_ui_info, | |
| 4973 .actions = mw_plugin_actions, | |
| 4974 }; | |
| 4975 | |
| 4976 | |
| 4977 static void mw_log_handler(const gchar *domain, GLogLevelFlags flags, | |
| 4978 const gchar *msg, gpointer data) { | |
| 4979 char *nl; | |
| 4980 | |
| 4981 if(! msg) return; | |
| 4982 | |
| 4983 /* annoying! */ | |
| 4984 nl = g_strdup_printf("%s\n", msg); | |
| 4985 | |
| 4986 /* handle g_log requests via gaim's built-in debug logging */ | |
| 4987 if(flags & G_LOG_LEVEL_ERROR) { | |
| 4988 gaim_debug_error(domain, nl); | |
| 4989 | |
| 4990 } else if(flags & G_LOG_LEVEL_WARNING) { | |
| 4991 gaim_debug_warning(domain, nl); | |
| 4992 | |
| 4993 } else { | |
| 4994 gaim_debug_info(domain, nl); | |
| 4995 } | |
| 4996 | |
| 4997 g_free(nl); | |
| 4998 } | |
| 4999 | |
| 5000 | |
| 5001 static void mw_plugin_init(GaimPlugin *plugin) { | |
| 5002 GaimAccountOption *opt; | |
| 5003 GList *l = NULL; | |
| 5004 | |
| 5005 GLogLevelFlags logflags = | |
| 5006 G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION; | |
| 5007 | |
| 5008 /* host to connect to */ | |
| 5009 opt = gaim_account_option_string_new("Server", MW_KEY_HOST, | |
| 5010 MW_PLUGIN_DEFAULT_HOST); | |
| 5011 l = g_list_append(l, opt); | |
| 5012 | |
| 5013 /* port to connect to */ | |
| 5014 opt = gaim_account_option_int_new("Port", MW_KEY_PORT, | |
| 5015 MW_PLUGIN_DEFAULT_PORT); | |
| 5016 l = g_list_append(l, opt); | |
| 5017 | |
| 5018 mw_prpl_info.protocol_options = l; | |
| 5019 l = NULL; | |
| 5020 | |
| 5021 /* set up the prefs for blist options */ | |
| 5022 gaim_prefs_add_none(MW_PRPL_OPT_BASE); | |
| 5023 gaim_prefs_add_int(MW_PRPL_OPT_BLIST_ACTION, BLIST_CHOICE_DEFAULT); | |
| 5024 gaim_prefs_add_bool(MW_PRPL_OPT_PSYCHIC, FALSE); | |
| 5025 gaim_prefs_add_bool(MW_PRPL_OPT_FORCE_LOGIN, FALSE); | |
| 5026 gaim_prefs_add_bool(MW_PRPL_OPT_SAVE_DYNAMIC, TRUE); | |
| 5027 | |
| 5028 /* forward all our g_log messages to gaim. Generally all the logging | |
| 5029 calls are using gaim_log directly, but the g_return macros will | |
| 5030 get caught here */ | |
| 5031 log_handler[0] = g_log_set_handler(G_LOG_DOMAIN, logflags, | |
| 5032 mw_log_handler, NULL); | |
| 5033 | |
| 5034 /* redirect meanwhile's logging to gaim's */ | |
| 5035 log_handler[1] = g_log_set_handler("meanwhile", logflags, | |
| 5036 mw_log_handler, NULL); | |
| 5037 } | |
| 5038 | |
| 5039 | |
| 5040 GAIM_INIT_PLUGIN(meanwhile, mw_plugin_init, mw_plugin_info); | |
| 5041 /* The End. */ | |
| 5042 |
