📄 sametime.c
字号:
/* Meanwhile Protocol Plugin for Purple Adds Lotus Sametime support to Purple using the Meanwhile library Copyright (C) 2004 Christopher (siege) O'Brien <siege@preoccupied.net> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*//* system includes */#include <stdlib.h>#include <time.h>/* glib includes */#include <glib.h>#include <glib/ghash.h>#include <glib/glist.h>/* purple includes */#include "internal.h"#include "config.h"#include "account.h"#include "accountopt.h"#include "circbuffer.h"#include "conversation.h"#include "debug.h"#include "ft.h"#include "imgstore.h"#include "mime.h"#include "notify.h"#include "plugin.h"#include "privacy.h"#include "prpl.h"#include "request.h"#include "util.h"#include "version.h"/* meanwhile includes */#include <mw_cipher.h>#include <mw_common.h>#include <mw_error.h>#include <mw_service.h>#include <mw_session.h>#include <mw_srvc_aware.h>#include <mw_srvc_conf.h>#include <mw_srvc_ft.h>#include <mw_srvc_im.h>#include <mw_srvc_place.h>#include <mw_srvc_resolve.h>#include <mw_srvc_store.h>#include <mw_st_list.h>/* plugin includes */#include "sametime.h"/* considering that there's no display of this information for prpls, I don't know why I even bother providing these. Oh valiant reader, I do it all for you. *//* scratch that, I just added it to the prpl options panel */#define PLUGIN_ID "prpl-meanwhile"#define PLUGIN_NAME "Sametime"#define PLUGIN_SUMMARY "Sametime Protocol Plugin"#define PLUGIN_DESC "Open implementation of a Lotus Sametime client"#define PLUGIN_AUTHOR "Christopher (siege) O'Brien <siege@preoccupied.net>"#define PLUGIN_HOMEPAGE "http://meanwhile.sourceforge.net/"/* plugin preference names */#define MW_PRPL_OPT_BASE "/plugins/prpl/meanwhile"#define MW_PRPL_OPT_BLIST_ACTION MW_PRPL_OPT_BASE "/blist_action"#define MW_PRPL_OPT_PSYCHIC MW_PRPL_OPT_BASE "/psychic"#define MW_PRPL_OPT_FORCE_LOGIN MW_PRPL_OPT_BASE "/force_login"#define MW_PRPL_OPT_SAVE_DYNAMIC MW_PRPL_OPT_BASE "/save_dynamic"/* stages of connecting-ness */#define MW_CONNECT_STEPS 11/* stages of conciousness */#define MW_STATE_OFFLINE "offline"#define MW_STATE_ACTIVE "active"#define MW_STATE_AWAY "away"#define MW_STATE_BUSY "dnd"#define MW_STATE_MESSAGE "message"#define MW_STATE_ENLIGHTENED "buddha"/* keys to get/set chat information */#define CHAT_KEY_CREATOR "chat.creator"#define CHAT_KEY_NAME "chat.name"#define CHAT_KEY_TOPIC "chat.topic"#define CHAT_KEY_INVITE "chat.invite"#define CHAT_KEY_IS_PLACE "chat.is_place"/* key for associating a mwLoginType with a buddy */#define BUDDY_KEY_CLIENT "meanwhile.client"/* store the remote alias so that we can re-create it easily */#define BUDDY_KEY_NAME "meanwhile.shortname"/* enum mwSametimeUserType */#define BUDDY_KEY_TYPE "meanwhile.type"/* key for the real group name for a meanwhile group */#define GROUP_KEY_NAME "meanwhile.group"/* enum mwSametimeGroupType */#define GROUP_KEY_TYPE "meanwhile.type"/* NAB group owning account */#define GROUP_KEY_OWNER "meanwhile.account"/* key gtk blist uses to indicate a collapsed group */#define GROUP_KEY_COLLAPSED "collapsed"/* verification replacement */#define mwSession_NO_SECRET "meanwhile.no_secret"/* keys to get/set purple plugin information */#define MW_KEY_HOST "server"#define MW_KEY_PORT "port"#define MW_KEY_FORCE "force_login"#define MW_KEY_FAKE_IT "fake_client_id"#define MW_KEY_CLIENT "client_id_val"#define MW_KEY_MAJOR "client_major"#define MW_KEY_MINOR "client_minor"/** number of seconds from the first blist change before a save to the storage service occurs. */#define BLIST_SAVE_SECONDS 15/** the possible buddy list storage settings */enum blist_choice { blist_choice_LOCAL = 1, /**< local only */ blist_choice_MERGE = 2, /**< merge from server */ blist_choice_STORE = 3, /**< merge from and save to server */ blist_choice_SYNCH = 4, /**< sync with server */};/** the default blist storage option */#define BLIST_CHOICE_DEFAULT blist_choice_SYNCH/* testing for the above */#define BLIST_PREF_IS(n) (purple_prefs_get_int(MW_PRPL_OPT_BLIST_ACTION)==(n))#define BLIST_PREF_IS_LOCAL() BLIST_PREF_IS(blist_choice_LOCAL)#define BLIST_PREF_IS_MERGE() BLIST_PREF_IS(blist_choice_MERGE)#define BLIST_PREF_IS_STORE() BLIST_PREF_IS(blist_choice_STORE)#define BLIST_PREF_IS_SYNCH() BLIST_PREF_IS(blist_choice_SYNCH)/* debugging output */#define DEBUG_ERROR(a...) purple_debug_error(G_LOG_DOMAIN, a)#define DEBUG_INFO(a...) purple_debug_info(G_LOG_DOMAIN, a)#define DEBUG_MISC(a...) purple_debug_misc(G_LOG_DOMAIN, a)#define DEBUG_WARN(a...) purple_debug_warning(G_LOG_DOMAIN, a)/** ensure non-null strings */#ifndef NSTR# define NSTR(str) ((str)? (str): "(null)")#endif/** calibrates distinct secure channel nomenclature */static const unsigned char no_secret[] = { 0x2d, 0x2d, 0x20, 0x73, 0x69, 0x65, 0x67, 0x65, 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x6a, 0x65, 0x6e, 0x6e, 0x69, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x7a, 0x6f, 0x65, 0x20, 0x2d, 0x2d, 0x00,};/** handler IDs from g_log_set_handler in mw_plugin_init */static guint log_handler[2] = { 0, 0 };/** the purple plugin data. available as gc->proto_data and mwSession_getClientData */struct mwPurplePluginData { struct mwSession *session; struct mwServiceAware *srvc_aware; struct mwServiceConference *srvc_conf; struct mwServiceFileTransfer *srvc_ft; struct mwServiceIm *srvc_im; struct mwServicePlace *srvc_place; struct mwServiceResolve *srvc_resolve; struct mwServiceStorage *srvc_store; /** map of PurpleGroup:mwAwareList and mwAwareList:PurpleGroup */ GHashTable *group_list_map; /** event id for the buddy list save callback */ guint save_event; /** socket fd */ int socket; gint outpa; /* like inpa, but the other way */ /** circular buffer for outgoing data */ PurpleCircBuffer *sock_buf; PurpleConnection *gc;};typedef struct { PurpleBuddy *buddy; PurpleGroup *group;} BuddyAddData;/* blist and aware functions */static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist);static void blist_store(struct mwPurplePluginData *pd);static void blist_schedule(struct mwPurplePluginData *pd);static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist);static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist);static gboolean buddy_is_external(PurpleBuddy *b);static void buddy_add(struct mwPurplePluginData *pd, PurpleBuddy *buddy);static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group, struct mwSametimeUser *stuser);static void group_add(struct mwPurplePluginData *pd, PurpleGroup *group);static PurpleGroup *group_ensure(PurpleConnection *gc, struct mwSametimeGroup *stgroup);static struct mwAwareList *list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group);/* session functions */static struct mwSession *gc_to_session(PurpleConnection *gc);static PurpleConnection *session_to_gc(struct mwSession *session);/* conference functions */static struct mwConference *conf_find_by_id(struct mwPurplePluginData *pd, int id);/* conversation functions */struct convo_msg { enum mwImSendType type; gpointer data; GDestroyNotify clear;};struct convo_data { struct mwConversation *conv; GList *queue; /**< outgoing message queue, list of convo_msg */};static void convo_data_new(struct mwConversation *conv);static void convo_data_free(struct convo_data *conv);static void convo_features(struct mwConversation *conv);static PurpleConversation *convo_get_gconv(struct mwConversation *conv);/* name and id */struct named_id { char *id; char *name;};/* connection functions */static void connect_cb(gpointer data, gint source, const gchar *error_message);/* ----- session ------ *//** resolves a mwSession from a PurpleConnection */static struct mwSession *gc_to_session(PurpleConnection *gc) { struct mwPurplePluginData *pd; g_return_val_if_fail(gc != NULL, NULL); pd = gc->proto_data; g_return_val_if_fail(pd != NULL, NULL); return pd->session;}/** resolves a PurpleConnection from a mwSession */static PurpleConnection *session_to_gc(struct mwSession *session) { struct mwPurplePluginData *pd; g_return_val_if_fail(session != NULL, NULL); pd = mwSession_getClientData(session); g_return_val_if_fail(pd != NULL, NULL); return pd->gc;}static void write_cb(gpointer data, gint source, PurpleInputCondition cond) { struct mwPurplePluginData *pd = data; PurpleCircBuffer *circ = pd->sock_buf; gsize avail; int ret; DEBUG_INFO("write_cb\n"); g_return_if_fail(circ != NULL); avail = purple_circ_buffer_get_max_read(circ); if(BUF_LONG < avail) avail = BUF_LONG; while(avail) { ret = write(pd->socket, circ->outptr, avail); if(ret <= 0) break; purple_circ_buffer_mark_read(circ, ret); avail = purple_circ_buffer_get_max_read(circ); if(BUF_LONG < avail) avail = BUF_LONG; } if(! avail) { purple_input_remove(pd->outpa); pd->outpa = 0; }}static int mw_session_io_write(struct mwSession *session, const guchar *buf, gsize len) { struct mwPurplePluginData *pd; int ret = 0; int err = 0; pd = mwSession_getClientData(session); /* socket was already closed. */ if(pd->socket == 0) return 1; if(pd->outpa) { DEBUG_INFO("already pending INPUT_WRITE, buffering\n"); purple_circ_buffer_append(pd->sock_buf, buf, len); return 0; } while(len) { ret = write(pd->socket, buf, (len > BUF_LEN)? BUF_LEN: len); if(ret <= 0) break; len -= ret; buf += ret; } if(ret <= 0) err = errno; if(err == EAGAIN) { /* append remainder to circular buffer */ DEBUG_INFO("EAGAIN\n"); purple_circ_buffer_append(pd->sock_buf, buf, len); pd->outpa = purple_input_add(pd->socket, PURPLE_INPUT_WRITE, write_cb, pd); } else if(len > 0) { DEBUG_ERROR("write returned %i, %i bytes left unwritten\n", ret, len); purple_connection_error(pd->gc, _("Connection closed (writing)"));#if 0 close(pd->socket); pd->socket = 0;#endif return -1; } return 0;}static void mw_session_io_close(struct mwSession *session) { struct mwPurplePluginData *pd; PurpleConnection *gc; pd = mwSession_getClientData(session); g_return_if_fail(pd != NULL); gc = pd->gc; if(pd->outpa) { purple_input_remove(pd->outpa); pd->outpa = 0; } if(pd->socket) { close(pd->socket); pd->socket = 0; } if(gc->inpa) { purple_input_remove(gc->inpa); gc->inpa = 0; }}static void mw_session_clear(struct mwSession *session) { ; /* nothing for now */}/* ----- aware list ----- */static void blist_resolve_alias_cb(struct mwServiceResolve *srvc, guint32 id, guint32 code, GList *results, gpointer data) { struct mwResolveResult *result; struct mwResolveMatch *match; g_return_if_fail(results != NULL); result = results->data; g_return_if_fail(result != NULL); g_return_if_fail(result->matches != NULL); match = result->matches->data; g_return_if_fail(match != NULL); purple_blist_server_alias_buddy(data, match->name); purple_blist_node_set_string(data, BUDDY_KEY_NAME, match->name);}static void mw_aware_list_on_aware(struct mwAwareList *list, struct mwAwareSnapshot *aware) { PurpleConnection *gc; PurpleAccount *acct; struct mwPurplePluginData *pd; guint32 idle; guint stat; const char *id; const char *status = MW_STATE_ACTIVE; gc = mwAwareList_getClientData(list); acct = purple_connection_get_account(gc);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -