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