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