📄 sametime.c
字号:
pd = gc->proto_data; idle = aware->status.time; stat = aware->status.status; id = aware->id.user; if(idle) { guint32 idle_len; /*< how long a client has been idle */ guint32 ugly_idle_len; /*< how long a broken client has been idle */ DEBUG_INFO("%s has idle value 0x%x\n", NSTR(id), idle); idle_len = time(NULL) - idle; ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000; /* what's the deal here? Well, good clients are smart enough to publish their idle time by using an attribute to indicate that they went idle at some time UTC, in seconds since epoch. Bad clients use milliseconds since epoch. So we're going to compute the idle time for either method, then figure out the lower of the two and use that. Blame the ST 7.5 development team for this. */ DEBUG_INFO("idle time: %u, ugly idle time: %u\n", idle_len, ugly_idle_len);#if 1 if(idle_len <= ugly_idle_len) { ; /* DEBUG_INFO("sane idle value, let's use it\n"); */ } else { idle = time(NULL) - ugly_idle_len; }#else if(idle < 0 || idle > time(NULL)) { DEBUG_INFO("hiding a messy idle value 0x%x\n", NSTR(id), idle); idle = -1; }#endif } switch(stat) { case mwStatus_ACTIVE: status = MW_STATE_ACTIVE; idle = 0; break; case mwStatus_IDLE: if(! idle) idle = -1; break; case mwStatus_AWAY: status = MW_STATE_AWAY; break; case mwStatus_BUSY: status = MW_STATE_BUSY; break; } /* NAB group members */ if(aware->group) { PurpleGroup *group; PurpleBuddy *buddy; PurpleBlistNode *bnode; group = g_hash_table_lookup(pd->group_list_map, list); buddy = purple_find_buddy_in_group(acct, id, group); bnode = (PurpleBlistNode *) buddy; if(! buddy) { struct mwServiceResolve *srvc; GList *query; buddy = purple_buddy_new(acct, id, NULL); purple_blist_add_buddy(buddy, NULL, group, NULL); bnode = (PurpleBlistNode *) buddy; srvc = pd->srvc_resolve; query = g_list_append(NULL, (char *) id); mwServiceResolve_resolve(srvc, query, mwResolveFlag_USERS, blist_resolve_alias_cb, buddy, NULL); g_list_free(query); } purple_blist_node_set_int(bnode, BUDDY_KEY_TYPE, mwSametimeUser_NORMAL); } if(aware->online) { purple_prpl_got_user_status(acct, id, status, NULL); purple_prpl_got_user_idle(acct, id, !!idle, (time_t) idle); } else { purple_prpl_got_user_status(acct, id, MW_STATE_OFFLINE, NULL); }}static void mw_aware_list_on_attrib(struct mwAwareList *list, struct mwAwareIdBlock *id, struct mwAwareAttribute *attrib) { ; /* nothing. We'll get attribute data as we need it */}static void mw_aware_list_clear(struct mwAwareList *list) { ; /* nothing for now */}static struct mwAwareListHandler mw_aware_list_handler = { mw_aware_list_on_aware, mw_aware_list_on_attrib, mw_aware_list_clear,};/** Ensures that an Aware List is associated with the given group, and returns that list. */static struct mwAwareList *list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group) { struct mwAwareList *list; g_return_val_if_fail(pd != NULL, NULL); g_return_val_if_fail(group != NULL, NULL); list = g_hash_table_lookup(pd->group_list_map, group); if(! list) { list = mwAwareList_new(pd->srvc_aware, &mw_aware_list_handler); mwAwareList_setClientData(list, pd->gc, NULL); mwAwareList_watchAttributes(list, mwAttribute_AV_PREFS_SET, mwAttribute_MICROPHONE, mwAttribute_SPEAKERS, mwAttribute_VIDEO_CAMERA, mwAttribute_FILE_TRANSFER, NULL); g_hash_table_replace(pd->group_list_map, group, list); g_hash_table_insert(pd->group_list_map, list, group); } return list;}static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) { /* - find the account for this connection - iterate through the buddy list - add each buddy matching this account to the stlist */ PurpleAccount *acct; PurpleBuddyList *blist; PurpleBlistNode *gn, *cn, *bn; PurpleGroup *grp; PurpleBuddy *bdy; struct mwSametimeGroup *stg = NULL; struct mwIdBlock idb = { NULL, NULL }; acct = purple_connection_get_account(gc); g_return_if_fail(acct != NULL); blist = purple_get_blist(); g_return_if_fail(blist != NULL); for(gn = blist->root; gn; gn = gn->next) { const char *owner; const char *gname; enum mwSametimeGroupType gtype; gboolean gopen; if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue; grp = (PurpleGroup *) gn; /* the group's type (normal or dynamic) */ gtype = purple_blist_node_get_int(gn, GROUP_KEY_TYPE); if(! gtype) gtype = mwSametimeGroup_NORMAL; /* if it's a normal group with none of our people in it, skip it */ if(gtype == mwSametimeGroup_NORMAL && !purple_group_on_account(grp, acct)) continue; /* if the group has an owner and we're not it, skip it */ owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); if(owner && strcmp(owner, purple_account_get_username(acct))) continue; /* the group's actual name may be different from the purple group's name. Find whichever is there */ gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME); if(! gname) gname = grp->name; /* we save this, but never actually honor it */ gopen = ! purple_blist_node_get_bool(gn, GROUP_KEY_COLLAPSED); stg = mwSametimeGroup_new(stlist, gtype, gname); mwSametimeGroup_setAlias(stg, grp->name); mwSametimeGroup_setOpen(stg, gopen); /* don't attempt to put buddies in a dynamic group, it breaks other clients */ if(gtype == mwSametimeGroup_DYNAMIC) continue; for(cn = gn->child; cn; cn = cn->next) { if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue; for(bn = cn->child; bn; bn = bn->next) { if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue; if(! PURPLE_BLIST_NODE_SHOULD_SAVE(bn)) continue; bdy = (PurpleBuddy *) bn; if(bdy->account == acct) { struct mwSametimeUser *stu; enum mwSametimeUserType utype; idb.user = bdy->name; utype = purple_blist_node_get_int(bn, BUDDY_KEY_TYPE); if(! utype) utype = mwSametimeUser_NORMAL; stu = mwSametimeUser_new(stg, utype, &idb); mwSametimeUser_setShortName(stu, bdy->server_alias); mwSametimeUser_setAlias(stu, bdy->alias); } } } } }static void blist_store(struct mwPurplePluginData *pd) { struct mwSametimeList *stlist; struct mwServiceStorage *srvc; struct mwStorageUnit *unit; PurpleConnection *gc; struct mwPutBuffer *b; struct mwOpaque *o; g_return_if_fail(pd != NULL); srvc = pd->srvc_store; g_return_if_fail(srvc != NULL); gc = pd->gc; if(BLIST_PREF_IS_LOCAL() || BLIST_PREF_IS_MERGE()) { DEBUG_INFO("preferences indicate not to save remote blist\n"); return; } else if(MW_SERVICE_IS_DEAD(srvc)) { DEBUG_INFO("aborting save of blist: storage service is not alive\n"); return; } else if(BLIST_PREF_IS_STORE() || BLIST_PREF_IS_SYNCH()) { DEBUG_INFO("saving remote blist\n"); } else { g_return_if_reached(); } /* create and export to a list object */ stlist = mwSametimeList_new(); blist_export(gc, stlist); /* write it to a buffer */ b = mwPutBuffer_new(); mwSametimeList_put(b, stlist); mwSametimeList_free(stlist); /* put the buffer contents into a storage unit */ unit = mwStorageUnit_new(mwStore_AWARE_LIST); o = mwStorageUnit_asOpaque(unit); mwPutBuffer_finalize(o, b); /* save the storage unit to the service */ mwServiceStorage_save(srvc, unit, NULL, NULL, NULL);}static gboolean blist_save_cb(gpointer data) { struct mwPurplePluginData *pd = data; blist_store(pd); pd->save_event = 0; return FALSE;}/** schedules the buddy list to be saved to the server */static void blist_schedule(struct mwPurplePluginData *pd) { if(pd->save_event) return; pd->save_event = purple_timeout_add(BLIST_SAVE_SECONDS * 1000, blist_save_cb, pd);}static gboolean buddy_is_external(PurpleBuddy *b) { g_return_val_if_fail(b != NULL, FALSE); return purple_str_has_prefix(b->name, "@E ");}/** Actually add a buddy to the aware service, and schedule the buddy list to be saved to the server */static void buddy_add(struct mwPurplePluginData *pd, PurpleBuddy *buddy) { struct mwAwareIdBlock idb = { mwAware_USER, (char *) buddy->name, NULL }; struct mwAwareList *list; PurpleGroup *group; GList *add; add = g_list_prepend(NULL, &idb); group = purple_buddy_get_group(buddy); list = list_ensure(pd, group); if(mwAwareList_addAware(list, add)) { purple_blist_remove_buddy(buddy); } blist_schedule(pd); g_list_free(add); }/** ensure that a PurpleBuddy exists in the group with data appropriately matching the st user entry from the st list */static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group, struct mwSametimeUser *stuser) { struct mwPurplePluginData *pd = gc->proto_data; PurpleBuddy *buddy; PurpleAccount *acct = purple_connection_get_account(gc); const char *id = mwSametimeUser_getUser(stuser); const char *name = mwSametimeUser_getShortName(stuser); const char *alias = mwSametimeUser_getAlias(stuser); enum mwSametimeUserType type = mwSametimeUser_getType(stuser); g_return_val_if_fail(id != NULL, NULL); g_return_val_if_fail(strlen(id) > 0, NULL); buddy = purple_find_buddy_in_group(acct, id, group); if(! buddy) { buddy = purple_buddy_new(acct, id, alias); purple_blist_add_buddy(buddy, NULL, group, NULL); buddy_add(pd, buddy); } purple_blist_alias_buddy(buddy, alias); purple_blist_server_alias_buddy(buddy, name); purple_blist_node_set_string((PurpleBlistNode *) buddy, BUDDY_KEY_NAME, name); purple_blist_node_set_int((PurpleBlistNode *) buddy, BUDDY_KEY_TYPE, type); return buddy;}/** add aware watch for a dynamic group */static void group_add(struct mwPurplePluginData *pd, PurpleGroup *group) { struct mwAwareIdBlock idb = { mwAware_GROUP, NULL, NULL }; struct mwAwareList *list; const char *n; GList *add; n = purple_blist_node_get_string((PurpleBlistNode *) group, GROUP_KEY_NAME); if(! n) n = group->name; idb.user = (char *) n; add = g_list_prepend(NULL, &idb); list = list_ensure(pd, group); mwAwareList_addAware(list, add); g_list_free(add);}/** ensure that a PurpleGroup exists in the blist with data appropriately matching the st group entry from the st list */static PurpleGroup *group_ensure(PurpleConnection *gc, struct mwSametimeGroup *stgroup) { PurpleAccount *acct; PurpleGroup *group = NULL; PurpleBuddyList *blist; PurpleBlistNode *gn; const char *name, *alias, *owner; enum mwSametimeGroupType type; acct = purple_connection_get_account(gc); owner = purple_account_get_username(acct); blist = purple_get_blist(); g_return_val_if_fail(blist != NULL, NULL); name = mwSametimeGroup_getName(stgroup); alias = mwSametimeGroup_getAlias(stgroup); type = mwSametimeGroup_getType(stgroup); DEBUG_INFO("attempting to ensure group %s, called %s\n", NSTR(name), NSTR(alias)); /* first attempt at finding the group, by the name key */ for(gn = blist->root; gn; gn = gn->next) { const char *n, *o; if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue; n = purple_blist_node_get_string(gn, GROUP_KEY_NAME); o = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); DEBUG_INFO("found group named %s, owned by %s\n", NSTR(n), NSTR(o)); if(n && !strcmp(n, name)) { if(!o || !strcmp(o, owner)) { DEBUG_INFO("that'll work\n"); group = (PurpleGroup *) gn; break; } } } /* try again, by alias */ if(! group) { DEBUG_INFO("searching for group by alias %s\n", NSTR(alias)); group = purple_find_group(alias); } /* oh well, no such group. Let's create it! */ if(! group) { DEBUG_INFO("creating group\n"); group = purple_group_new(alias); purple_blist_add_group(group, NULL); } gn = (PurpleBlistNode *) group; purple_blist_node_set_string(gn, GROUP_KEY_NAME, name); purple_blist_node_set_int(gn, GROUP_KEY_TYPE, type); if(type == mwSametimeGroup_DYNAMIC) { purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner); group_add(gc->proto_data, group); } return group;}/** merge the entries from a st list into the purple blist */static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist) { struct mwSametimeGroup *stgroup; struct mwSametimeUser *stuser; PurpleGroup *group; PurpleBuddy *buddy; GList *gl, *gtl, *ul, *utl; gl = gtl = mwSametimeList_getGroups(stlist); for(; gl; gl = gl->next) { stgroup = (struct mwSametimeGroup *) gl->data; group = group_ensure(gc, stgroup); ul = utl = mwSametimeGroup_getUsers(stgroup); for(; ul; ul = ul->next) { stuser = (struct mwSametimeUser *) ul->data; buddy = buddy_ensure(gc, group, stuser); } g_list_free(utl); } g_list_free(gtl);}/** remove all buddies on account from group. If del is TRUE and group is left empty, remove group as well */static void group_clear(PurpleGroup *group, PurpleAccount *acct, gboolean del) { PurpleConnection *gc; GList *prune = NULL; PurpleBlistNode *gn, *cn, *bn;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -