pidgin-openfetion-0.3/0000755000175000017500000000000011700221761013447 5ustar aronaronpidgin-openfetion-0.3/CMakeLists.txt0000644000175000017500000000431411700221613016205 0ustar aronaronPROJECT(libopenfetion) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) SET(TARNAME pidgin-ofetion) SET(ICON_FILE "res/openfetion.png") SET(RES_FILE "res/city.xml" "res/province.xml") SET(SRC_LIST fx_blist.c fx_buddy.c fx_chat.c fx_contact.c fx_login.c fx_sip.c fx_user.c fx_util.c openfetion.c ) IF(NOT PURPLE_MAJOR_VERSION) SET(PURPLE_MAJOR_VERSION "2") ENDIF(NOT PURPLE_MAJOR_VERSION) FIND_PACKAGE(PkgConfig REQUIRED) PKG_CHECK_MODULES(LIBXML2 REQUIRED libxml-2.0) PKG_CHECK_MODULES(OPENSSL REQUIRED openssl) PKG_CHECK_MODULES(PURPLE REQUIRED purple) option(NLS "Native language support" ON) if(NLS) find_package(Gettext) endif(NLS) ADD_DEFINITIONS(-Wall -Wextra -g) ADD_LIBRARY(libopenfetion MODULE ${SRC_LIST}) set_target_properties(libopenfetion PROPERTIES PREFIX "") INCLUDE_DIRECTORIES( ${LIBXML2_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${PURPLE_INCLUDE_DIRS} ) TARGET_LINK_LIBRARIES(libopenfetion ${LIBXML_LIBRARIES} ${OPENSSL_LIBRARIES} ${PURPLE_LIBRARIES} ) EXEC_PROGRAM("pkg-config --variable=datadir purple 2>/dev/null" OUTPUT_VARIABLE PURPLE_DATADIR) EXEC_PROGRAM("pkg-config --variable=libdir purple 2>/dev/null" OUTPUT_VARIABLE PURPLE_LIBDIR) SET(PIDGIN_PIX_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/pixmaps/pidgin/protocols/16/") SET(RES_INSTALL_DIR "${PURPLE_DATADIR}/purple/openfetion/") SET(LIB_INSTALL_DIR "${PURPLE_LIBDIR}/purple-${PURPLE_MAJOR_VERSION}") SET(LOCALE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/locale/") ADD_DEFINITIONS(-DRES_DIR="${RES_INSTALL_DIR}") if(NLS AND GETTEXT_FOUND) add_definitions(-DGETTEXT_PACKAGE="${TARNAME}") add_definitions(-DENABLE_NLS="1") message(STATUS "Native language support: YES" ) else(NLS AND GETTEXT_FOUND) message(STATUS "Native language support: NO" ) endif(NLS AND GETTEXT_FOUND) add_definitions(-DLOCALE_DIR="${LOCALE_INSTALL_DIR}") if(NLS AND GETTEXT_FOUND) file(GLOB POFILES "${CMAKE_CURRENT_SOURCE_DIR}/po/*.po") gettext_create_translations("${CMAKE_CURRENT_SOURCE_DIR}/po/pidgin-ofetion.pot" ALL ${POFILES}) endif(NLS AND GETTEXT_FOUND) INSTALL(TARGETS libopenfetion DESTINATION ${LIB_INSTALL_DIR}) INSTALL(FILES ${ICON_FILE} DESTINATION ${PIDGIN_PIX_INSTALL_DIR}) INSTALL(FILES ${RES_FILE} DESTINATION ${RES_INSTALL_DIR}) pidgin-openfetion-0.3/.pc/0000755000175000017500000000000011700221613014123 5ustar aronaronpidgin-openfetion-0.3/.pc/lp706076-bin-invalid-paths.patch/0000755000175000017500000000000011700221613021635 5ustar aronaronpidgin-openfetion-0.3/.pc/lp706076-bin-invalid-paths.patch/CMakeLists.txt0000644000175000017500000000440611700212607024404 0ustar aronaronPROJECT(libopenfetion) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) SET(TARNAME pidgin-ofetion) SET(ICON_FILE "res/openfetion.png") SET(RES_FILE "res/city.xml" "res/province.xml") SET(SRC_LIST fx_blist.c fx_buddy.c fx_chat.c fx_contact.c fx_login.c fx_sip.c fx_user.c fx_util.c openfetion.c ) IF(NOT PURPLE_MAJOR_VERSION) SET(PURPLE_MAJOR_VERSION "2") ENDIF(NOT PURPLE_MAJOR_VERSION) FIND_PACKAGE(PkgConfig REQUIRED) PKG_CHECK_MODULES(LIBXML2 REQUIRED libxml-2.0) PKG_CHECK_MODULES(OPENSSL REQUIRED openssl) PKG_CHECK_MODULES(PURPLE REQUIRED purple) option(NLS "Native language support" ON) if(NLS) find_package(Gettext) endif(NLS) ADD_DEFINITIONS(-Wall -Wextra -g) ADD_LIBRARY(libopenfetion MODULE ${SRC_LIST}) set_target_properties(libopenfetion PROPERTIES PREFIX "") INCLUDE_DIRECTORIES( ${LIBXML2_INCLUDE_DIRS} ${OPENSSL_INCLUDE_DIRS} ${PURPLE_INCLUDE_DIRS} ) TARGET_LINK_LIBRARIES(libopenfetion ${LIBXML_LIBRARIES} ${OPENSSL_LIBRARIES} ${PURPLE_LIBRARIES} ) EXEC_PROGRAM("whereis libpurple| sed -e 's/[^\\/]*//' -e 's/\\(.*\\)lib\\/libpurple\\.so.*/\\1/'" OUTPUT_VARIABLE PURPLE_PREFIX) EXEC_PROGRAM("whereis pidgin | sed -e 's/[^\\/]*//' -e 's/\\(.*\\)bin\\/pidgin.*/\\1/'" OUTPUT_VARIABLE PIDGIN_PREFIX) SET(PIDGIN_PIX_INSTALL_DIR "${PIDGIN_PREFIX}share/pixmaps/pidgin/protocols/16/") SET(RES_INSTALL_DIR "${PURPLE_PREFIX}share/purple/openfetion/") SET(LIB_INSTALL_DIR "${PURPLE_PREFIX}lib/purple-${PURPLE_MAJOR_VERSION}") SET(LOCALE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/locale/") ADD_DEFINITIONS(-DRES_DIR="${RES_INSTALL_DIR}") if(NLS AND GETTEXT_FOUND) add_definitions(-DGETTEXT_PACKAGE="${TARNAME}") add_definitions(-DENABLE_NLS="1") message(STATUS "Native language support: YES" ) else(NLS AND GETTEXT_FOUND) message(STATUS "Native language support: NO" ) endif(NLS AND GETTEXT_FOUND) add_definitions(-DLOCALE_DIR="${LOCALE_INSTALL_DIR}") if(NLS AND GETTEXT_FOUND) file(GLOB POFILES "${CMAKE_CURRENT_SOURCE_DIR}/po/*.po") gettext_create_translations("${CMAKE_CURRENT_SOURCE_DIR}/po/pidgin-ofetion.pot" ALL ${POFILES}) endif(NLS AND GETTEXT_FOUND) INSTALL(TARGETS libopenfetion DESTINATION ${LIB_INSTALL_DIR}) INSTALL(FILES ${ICON_FILE} DESTINATION ${PIDGIN_PIX_INSTALL_DIR}) INSTALL(FILES ${RES_FILE} DESTINATION ${RES_INSTALL_DIR}) pidgin-openfetion-0.3/.pc/applied-patches0000644000175000017500000000004111700221613017104 0ustar aronaronlp706076-bin-invalid-paths.patch pidgin-openfetion-0.3/.pc/.quilt_series0000644000175000017500000000000711700221613016631 0ustar aronaronseries pidgin-openfetion-0.3/.pc/.quilt_patches0000644000175000017500000000001711700221613016767 0ustar aronarondebian/patches pidgin-openfetion-0.3/.pc/.version0000644000175000017500000000000211700221613015601 0ustar aronaron2 pidgin-openfetion-0.3/fx_buddy.h0000644000175000017500000000624611700212607015433 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FX_BUDDY_H #define FX_BUDDY_H typedef struct { guint conn; gint source; gint size; gint sum; guchar *data; Contact *cnt; fetion_account *ac; } portrait_trans; typedef struct { Contact *cnt; fetion_account *ac; } portrait_data; portrait_trans *portrait_trans_new(); void portrait_trans_free(portrait_trans *trans); void fx_blist_init(fetion_account *ac); void process_presence(fetion_account *ac, const gchar *xml); gint get_info_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *trans); void fx_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); void process_sync_info(fetion_account *ac, const gchar *sipmsg); void process_add_buddy(fetion_account *ac, const gchar *sipmsg); gchar *get_city_name(const gchar *province, const gchar *city); gchar *get_province_name(const gchar *province); #endif pidgin-openfetion-0.3/openfetion.c0000644000175000017500000006742711700212607016000 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #define PURPLE_PLUGINS #include #include "fetion.h" #include "notify.h" #include "plugin.h" #include "version.h" #include "accountopt.h" #include "fx_sip.h" #include "fx_user.h" #include "fx_login.h" #include "fx_buddy.h" #include "fx_contact.h" #include "fx_chat.h" #include "fx_blist.h" #ifdef ENABLE_NLS # ifdef _WIN32 # include # endif # include # include #else # define _(a) a #endif PurplePlugin *openfetion_plugin = NULL; GSList *sessions; GSList *buddy_to_added; static void process_push_cb(fetion_account *ac, const gchar *sipmsg); static void process_notify_cb(fetion_account *ac, const gchar *sipmsg); static const char *fx_list_icon(PurpleAccount *UNUSED(a), PurpleBuddy *UNUSED(b)) { return "openfetion"; } static void fx_keep_alive(PurpleConnection *gc) { fetion_account *ses; GSList *list = sessions; fetion_account *ac = purple_connection_get_protocol_data(gc); fetion_user_keep_alive(ac); while(list) { ses = (fetion_account*)(list->data); if(ses->sk != 0) fetion_user_keep_alive(ses); list = list->next; } } static void fx_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean UNUSED(full)) { PurpleStatus *status; const gchar *impresa, *alias, *sid, *mobileno; g_return_if_fail(buddy != NULL); status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); impresa = purple_status_get_attr_string(status, "impresa"); sid = purple_status_get_attr_string(status, "fetionno"); mobileno = purple_status_get_attr_string(status, "mobileno"); alias = purple_buddy_get_alias(buddy); purple_notify_user_info_add_pair(user_info, _("FetionNo"), sid); purple_notify_user_info_add_pair(user_info, _("MobileNo"), mobileno); purple_notify_user_info_add_pair(user_info, _("Alias"), alias); purple_notify_user_info_add_pair(user_info, _("Signature"), impresa); } static void fx_alias_buddy(PurpleConnection *gc, const gchar *who, const gchar *alias) { fetion_account *ac = purple_connection_get_protocol_data(gc); fetion_contact_set_displayname(ac, who, alias); } static void fx_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *UNUSED(group)) { fetion_account *ac = purple_connection_get_protocol_data(gc); const gchar *userid = purple_buddy_get_name(buddy); fetion_contact_delete_buddy(ac, userid); } static void fx_rename_group(PurpleConnection *gc, const gchar *old_name, PurpleGroup *group, GList *UNUSED(moved_buddies)) { fetion_account *ac = purple_connection_get_protocol_data(gc); Group *blist = fetion_group_list_find_by_name(ac->user->groupList, old_name); const gchar *name = purple_group_get_name(group); fetion_buddylist_edit(ac, blist->groupid, name); } static void fx_group_buddy(PurpleConnection *gc, const gchar *who, const gchar *UNUSED(old_group), const gchar *new_group) { gchar buf[BUFLEN]; fetion_account *ac = purple_connection_get_protocol_data(gc); Contact *cnt = fetion_contact_list_find_by_userid(ac->user->contactList, who); Group *group = fetion_group_list_find_by_name(ac->user->groupList, new_group); if(!group) { snprintf(buf, sizeof(buf) - 1, _("'%s' is not a valid group of this account."), new_group); purple_notify_error(ac->gc, NULL, _("Failed"), buf); return; } fetion_contact_move_to_group(ac, cnt->userId, group->groupid); } static void fx_remove_group(PurpleConnection *gc, PurpleGroup *group) { const gchar *name = purple_group_get_name(group); fetion_account *ac = purple_connection_get_protocol_data(gc); Group *blist = fetion_group_list_find_by_name(ac->user->groupList, name); if(!blist) return; fetion_buddylist_delete(ac, blist->groupid); } static GList *fx_attention_types(PurpleAccount *UNUSED(account)) { PurpleAttentionType *attn; static GList *list = NULL; if (!list) { attn = g_new0(PurpleAttentionType, 1); attn->name = _("Nudge"); attn->incoming_description = _("%s has nudged you!"); attn->outgoing_description = _("Nudging %s..."); list = g_list_append(list, attn); } return list; } static gboolean fx_send_attention(PurpleConnection *gc, const gchar *who, guint UNUSED(type)) { PurpleBuddy *buddy; PurplePresence *presence; PurpleStatus *status; const gchar *status_id; fetion_account *sec; fetion_account *ac = purple_connection_get_protocol_data(gc); if(!(buddy = purple_find_buddy(ac->account, who))) return 0; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); status_id = purple_status_get_id(status); /* online,need invite */ if(strcmp(status_id, "Offline") != 0) { if(!(sec = session_find(who))) new_chat(ac, who, (gchar*)0); else fetion_send_nudge(sec, who); return TRUE; } return FALSE; } static int fx_im_send(PurpleConnection *gc, const gchar *who, const gchar *what, PurpleMessageFlags UNUSED(flags)) { PurpleBuddy *buddy; PurplePresence *presence; PurpleStatus *status; const gchar *status_id; fetion_account *sec; PurpleConversation *conv; fetion_account *ac = purple_connection_get_protocol_data(gc); Contact *cnt; gint shutdown = 0; if(!(buddy = purple_find_buddy(ac->account, who))) return 0; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); status_id = purple_status_get_id(status); cnt = fetion_contact_list_find_by_userid(ac->user->contactList, who); if(cnt->relationStatus == RELATION_STATUS_UNAUTHENTICATED) { if(!(conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, who, ac->account))) return -1; purple_conversation_write(conv, NULL, _("Failed to send message: Unverified Buddy!"), PURPLE_MESSAGE_ERROR, time(NULL)); return -1; } if(cnt->serviceStatus == BASIC_SERVICE_ABNORMAL){ if(cnt->carrierStatus == CARRIER_STATUS_CLOSED){ shutdown = 1; }else{ if((cnt->carrier[0] != '\0' && cnt->mobileno[0] == '\0') || cnt->carrier[0] == '\0') shutdown = 1; } }else if(cnt->carrierStatus == CARRIER_STATUS_DOWN) if(cnt->carrier[0] != '\0') shutdown = 1; if(shutdown) { if(!(conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_ANY, who, ac->account))) return -1; purple_conversation_write(conv, NULL, _("Fail to send message: Buddy has cancled Fetion service!"), PURPLE_MESSAGE_ERROR, time(NULL)); return -1; } /* online,need invite */ if(strcmp(status_id, "Offline") != 0) { if(!(sec = session_find(who))) new_chat(ac, who, what); else fetion_send_sms(sec, who, what); return 1; } fetion_send_sms(ac, who, what); return 1; } static void fx_get_info(PurpleConnection *gc, const char *who) { fetion_account *ses = purple_connection_get_protocol_data(gc); fetion_contact_get_contact_info(ses, who, (TransCallback)get_info_cb); } static void fx_set_status(PurpleAccount *account, PurpleStatus *status) { const gchar *status_id; gint state; PurpleConnection *gc = purple_account_get_connection(account); fetion_account *ses = purple_connection_get_protocol_data(gc); status_id = purple_status_get_id(status); if (!strcmp(status_id, "Online")) state = P_ONLINE; else if (!strcmp(status_id, "Away")) state = P_AWAY; else if (!strcmp(status_id, "Busy")) state = P_BUSY; else if (!strcmp(status_id, "Hidden")) state = P_HIDDEN; else if (!strcmp(status_id, "Offline")) state = P_OFFLINE; else state = 400; fetion_user_set_state(ses, state); } static gchar *fx_status_text(PurpleBuddy *buddy) { PurplePresence *presence; PurpleStatus *status; const gchar *msg; presence = purple_buddy_get_presence(buddy); status = purple_presence_get_active_status(presence); msg = purple_status_get_attr_string(status, "impresa"); if(msg && *msg) return g_markup_escape_text(msg, -1); return NULL; } static void send_sms_cb(PurpleBuddy *buddy, const gchar *text) { PurpleConnection *gc; fetion_account *ses; const gchar *userid = purple_buddy_get_name(buddy); gc = purple_account_get_connection(buddy->account); ses = purple_connection_get_protocol_data(gc); fetion_send_sms_to_phone(ses, userid, text); } static void fx_send_sms(PurpleBlistNode *node, gpointer UNUSED(data)) { PurpleBuddy *buddy; PurpleConnection *gc; g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); buddy = (PurpleBuddy *) node; gc = purple_account_get_connection(buddy->account); purple_request_input(gc, NULL, _("Send a mobile message."), NULL, NULL, TRUE, FALSE, NULL, _("Send"), G_CALLBACK(send_sms_cb), _("Cancel"), NULL, purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL, buddy); } static void send_sms_to_me_cb(PurpleConnection *gc, const gchar *text) { fetion_account *ses; g_return_if_fail(NULL != gc && NULL != gc->proto_data); ses = purple_connection_get_protocol_data(gc); fetion_sms_myself(ses, text); } static GList *fx_blist_node_menu(PurpleBlistNode * node) { GList *menu = NULL; PurpleMenuAction *act; PurpleBuddy *buddy; if (PURPLE_BLIST_NODE_IS_BUDDY(node)) { buddy = (PurpleBuddy*)node; g_return_val_if_fail(buddy != NULL, NULL); act = purple_menu_action_new(_("Send to Mobile"), PURPLE_CALLBACK(fx_send_sms), NULL, NULL); menu = g_list_append(menu, act); return menu; } else { return (GList*)0; } } gint push_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar sipmsg[BUFLEN * 10], *h, *pos, *msg; gint n, data_len; guint len; fetion_account *ses = (fetion_account*)data; if((n = recv(source, sipmsg, sizeof(sipmsg), 0)) == -1) return -1; sipmsg[n] = '\0'; data_len = ses->data ? strlen(ses->data) : 0; ses->data = (gchar*)realloc(ses->data, data_len + n + 1); memcpy(ses->data + data_len, sipmsg, n + 1); recheck: data_len = strlen(ses->data); if((pos = strstr(ses->data, "\r\n\r\n"))) { pos += 4; h = (gchar*)g_malloc0(data_len - strlen(pos) + 1); memcpy(h, ses->data, data_len - strlen(pos)); h[data_len - strlen(pos)] = '\0'; if(strstr(h, "L: ")) { len = fetion_sip_get_length(ses->data); if(len <= strlen(pos)) { msg = (gchar*)g_malloc0(strlen(h) + len + 1); memcpy(msg, ses->data, strlen(h) + len); msg[strlen(h) + len] = '\0'; process_push_cb(ses, msg); memmove(ses->data, ses->data + strlen(msg), data_len - strlen(msg)); ses->data = (gchar*)realloc(ses->data, data_len - strlen(msg) + 1); ses->data[data_len - strlen(msg)] = '\0'; g_free(msg); msg = (gchar*)0; g_free(h); h = (gchar*)0; goto recheck; } } else { process_push_cb(ses, h); memmove(ses->data, ses->data + strlen(h), data_len - strlen(h)); ses->data = (gchar*)realloc(ses->data, data_len - strlen(h) + 1); ses->data[data_len - strlen(h)] = '\0'; g_free(h); h = (gchar*)0; goto recheck; } g_free(h); } return 0; } static void process_sipc_cb(fetion_account *ses, const gchar *sipmsg) { gchar callid[16]; gint callid0; struct transaction *trans; GSList *trans_cur; fetion_sip_get_attr(sipmsg, "I", callid); callid0 = atoi(callid); trans_cur = ses->trans; while(trans_cur) { trans = (struct transaction*)(trans_cur->data); if(trans->callid == callid0) { if(trans->callback) (trans->callback)(ses, sipmsg, trans); transaction_remove(ses, trans); break; } trans_cur = g_slist_next(trans_cur); } } static void process_dereg_cb(fetion_account *ses, const gchar *UNUSED(sipmsg)) { PurpleConnection *pc; pc = purple_account_get_connection(ses->account); purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account has logined elsewhere. You are forced to quit.")); } static void process_info_cb(fetion_account *ses, const gchar *sipmsg) { InfoType type; gchar sipuri[48], callid[48], seq[48], buf[BUFLEN], *sid; fetion_sip_parse_info(sipmsg, &type); if(type == INFO_NUDGE) { memset(callid, 0, sizeof(callid)); memset(seq, 0, sizeof(seq)); memset(sipuri, 0, sizeof(sipuri)); snprintf(buf, sizeof(buf) -1, "SIP-C/4.0 200 OK\r\n" "F: %s\r\n" "I: %s \r\n" "Q: %s\r\n\r\n", sipuri , callid , seq); send(ses->sk, buf, strlen(buf), 0); sid = fetion_sip_get_sid_by_sipuri(sipuri); purple_prpl_got_attention(ses->gc, sid, FETION_NUDGE); } } static void process_push_cb(fetion_account *ses, const gchar *sipmsg) { gint type; type = fetion_sip_get_type(sipmsg); switch(type){ case SIP_NOTIFICATION : process_notify_cb(ses, sipmsg); break; case SIP_MESSAGE: process_message_cb(ses, sipmsg); break; case SIP_INVITATION: process_invite_cb(ses, sipmsg); break; case SIP_INCOMING : process_info_cb(ses, sipmsg); break; case SIP_SIPC_4_0: process_sipc_cb(ses, sipmsg); break; default: break; //printf("%s\n" , pos->message); } } static void process_notify_cb(fetion_account *ac, const gchar *sipmsg) { gint event; gint notification_type; gchar *xml; fetion_sip_parse_notification(sipmsg , ¬ification_type , &event , &xml); switch(notification_type) { case NOTIFICATION_TYPE_PRESENCE: if(event == NOTIFICATION_EVENT_PRESENCECHANGED) process_presence(ac , xml); break; case NOTIFICATION_TYPE_CONVERSATION : if(event == NOTIFICATION_EVENT_USERLEFT) { process_left_cb(ac, sipmsg); break; } else if(event == NOTIFICATION_EVENT_USERENTER) { process_enter_cb(ac, sipmsg); break; } break; case NOTIFICATION_TYPE_REGISTRATION : if(event == NOTIFICATION_EVENT_DEREGISTRATION) process_dereg_cb(ac, sipmsg); break; case NOTIFICATION_TYPE_SYNCUSERINFO : if(event == NOTIFICATION_EVENT_SYNCUSERINFO) process_sync_info(ac, sipmsg); break; case NOTIFICATION_TYPE_CONTACT : if(event == NOTIFICATION_EVENT_ADDBUDDYAPPLICATION) process_add_buddy(ac, sipmsg); break; #if 0 case NOTIFICATION_TYPE_PGGROUP : break; #endif default: break; } g_free(xml); } static void fx_login(PurpleAccount *account) { PurplePresence *presence; PurpleConnection *pc = purple_account_get_connection(account); const gchar *mobileno = purple_account_get_username(account); const gchar *password = purple_connection_get_password(pc); const gchar *status_id; fetion_account *ac = session_new(account); /* construct a user object */ ac->user = fetion_user_new(mobileno, password); ac->account = account; ac->gc = pc; ac->chan_ready = 1; purple_connection_set_protocol_data(pc, ac); presence = purple_account_get_presence(account); status_id = get_status_id(ac->user->state); if(ac->user->state == 0) status_id = "Hidden"; purple_presence_set_status_active(presence, status_id, TRUE); purple_connection_update_progress(pc, "Connecting", 1, 2); purple_ssl_connect(ac->account, SSI_SERVER, PURPLE_SSL_DEFAULT_PORT, (PurpleSslInputFunction)ssi_auth_action, (PurpleSslErrorFunction)0, ac); } static void fx_close(PurpleConnection *gc) { fetion_account *ses; fetion_account *ac = purple_connection_get_protocol_data(gc); purple_input_remove(ac->conn); close(ac->sk); g_free(ac->data); ac->data = (gchar*)0; while(sessions) { ses = (fetion_account*)(sessions->data); session_remove(ses); session_destroy(ses); } fetion_user_free(ac->user); ac->user = NULL; purple_connection_set_protocol_data(gc, NULL); } static GList *fx_status_types(PurpleAccount *UNUSED(account)) { PurpleStatusType *status; GList *types = NULL; status = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, "Online", _("Available"), FALSE, TRUE,FALSE, "impresa", "impresa", purple_value_new(PURPLE_TYPE_STRING), "fetionno", "fetionno", purple_value_new(PURPLE_TYPE_STRING), "mobileno", "mobileno", purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, "Away", _("Away"), FALSE, TRUE, FALSE, "impresa", "impresa", purple_value_new(PURPLE_TYPE_STRING), "fetionno", "fetionno", purple_value_new(PURPLE_TYPE_STRING), "mobileno", "mobileno", purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE, "Hidden", _("Invisible"), FALSE, TRUE, FALSE, "impresa", "impresa", purple_value_new(PURPLE_TYPE_STRING), "fetionno", "fetionno", purple_value_new(PURPLE_TYPE_STRING), "mobileno", "mobileno", purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, "Offline", _("Offline"), FALSE, TRUE, FALSE, "impresa", "impresa", purple_value_new(PURPLE_TYPE_STRING), "fetionno", "fetionno", purple_value_new(PURPLE_TYPE_STRING), "mobileno", "mobileno", purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); status = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, "Busy", _("Busy"), FALSE, TRUE, FALSE, "impresa", "impresa", purple_value_new(PURPLE_TYPE_STRING), "fetionno", "fetionno", purple_value_new(PURPLE_TYPE_STRING), "mobileno", "mobileno", purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, status); return types; } static void action_about_openfetion(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; GString *info; gchar *title; g_return_if_fail(NULL != gc); info = g_string_new(""); g_string_append(info, _("

Author:
\n")); g_string_append(info, "levin(@levin108)
\n"); g_string_append(info, "
\n"); g_string_append(info, _("pidgin-openfetion is a Fetion protocol plugin for libpurple,
" "implemented by the openfetion team.
" "It supports most features of China Mobile's Fetion V4 protocol.
" "It's lightweight and efficient.

" "Project homepage: http://code.google.com/p/ofetion/")); g_string_append(info, "

"); g_string_append(info, _("

Translators

:
\n")); /* Translators: HTML format, So do add a
to every translator, * such as Zhang
\nWang
*/ g_string_append(info, _("translator-credits")); g_string_append(info, "\n"); title = g_strdup_printf(_("About OpenFetion %s"), DISPLAY_VERSION); purple_notify_formatted(gc, title, title, NULL, info->str, NULL, NULL); g_free(title); g_string_free(info, TRUE); } static void action_show_account_info(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection *) action->context; GString *info; gchar *province, *city; fetion_account *ac = purple_connection_get_protocol_data(gc); User *user = ac->user; info = g_string_new(""); province = get_province_name(user->province); city = get_city_name(user->province, user->city); g_string_append_printf(info, _("Last Login Time: %s
\n"), user->lastLoginTime); g_string_append_printf(info, _("Last Login IP: %s
\n"), user->lastLoginIp); g_string_append_printf(info, _("Public IP: %s
\n"), user->publicIp); g_string_append(info, "
"); g_string_append_printf(info, _("Fetion Number: %s
\n"), user->sId); g_string_append_printf(info, _("Mobile Number: %s
\n"), user->mobileno); g_string_append_printf(info, _("Login Time: %s
\n"), user->lastLoginTime); g_string_append_printf(info, _("NickName: %s
\n"), user->nickname); g_string_append_printf(info, _("Signature: %s
\n"), user->impression); g_string_append_printf(info, _("Province: %s
\n"), province); g_string_append_printf(info, _("City: %s
\n"), city); g_string_append(info, "
"); g_string_append_printf(info, _("SMS Sent Today: %d
\n"), user->smsDayCount); g_string_append_printf(info, _("SMS Sent This Month: %d
\n"), user->smsMonthCount); g_string_append_printf(info, _("SMS Limit Today: %d
\n"), user->smsDayLimit); g_string_append_printf(info, _("SMS Limit This Month: %d
\n"), user->smsMonthLimit); g_string_append(info, ""); purple_notify_formatted(gc, NULL, _("Account Information"), NULL, info->str, NULL, NULL); g_string_free(info, TRUE); g_free(province); g_free(city); } static void modify_nickname_cb(PurpleConnection *gc, const gchar *text) { fetion_account *ac; g_return_if_fail(NULL != gc && NULL != gc->proto_data); ac = purple_connection_get_protocol_data(gc); fetion_modify_info(ac, MODIFY_INFO_NICKNAME, text); } static void modify_impresa_cb(PurpleConnection *gc, const gchar *text) { fetion_account *ac; g_return_if_fail(NULL != gc && NULL != gc->proto_data); ac = purple_connection_get_protocol_data(gc); fetion_modify_info(ac, MODIFY_INFO_IMPRESA, text); } static void action_modify_nickname(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection*)action->context; g_return_if_fail(NULL != gc && NULL != gc->proto_data); purple_request_input(gc, NULL, _("Change Nickname"), NULL, NULL, FALSE, FALSE, NULL, _("OK"), G_CALLBACK(modify_nickname_cb), _("Cancel"), NULL, purple_connection_get_account(gc), NULL, NULL, gc); } static void action_modify_impresa(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection*)action->context; g_return_if_fail(NULL != gc && NULL != gc->proto_data); purple_request_input(gc, NULL, _("Change Signature"), NULL, NULL, FALSE, FALSE, NULL, _("OK"), G_CALLBACK(modify_impresa_cb), _("Cancel"), NULL, purple_connection_get_account(gc), NULL, NULL, gc); } static void action_sms_myself(PurplePluginAction *action) { PurpleConnection *gc = (PurpleConnection*)action->context; g_return_if_fail(NULL != gc && NULL != gc->proto_data); purple_request_input(gc, NULL, _("Send SMS to Your Phone"), NULL, NULL, TRUE, FALSE, NULL, _("Send"), G_CALLBACK(send_sms_to_me_cb), _("Close"), NULL, purple_connection_get_account(gc), NULL, NULL, gc); } static GList *plugin_actions(PurplePlugin *UNUSED(plugin), gpointer UNUSED(context)) { GList *m; PurplePluginAction *act; m = NULL; act = purple_plugin_action_new(_("Send SMS To Phone"), action_sms_myself); m = g_list_append(m, act); m = g_list_append(m, NULL); act = purple_plugin_action_new(_("View Information"), action_show_account_info); m = g_list_append(m, act); act = purple_plugin_action_new(_("Change Nickname"), action_modify_nickname); m = g_list_append(m, act); act = purple_plugin_action_new(_("Change Signature"), action_modify_impresa); m = g_list_append(m, act); m = g_list_append(m, NULL); act = purple_plugin_action_new(_("About OpenFetion"), action_about_openfetion); m = g_list_append(m, act); return m; } static PurplePluginProtocolInfo protocol_info = { 0, NULL, /* user_splits */ NULL, /* protocol_options */ {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ fx_list_icon, /* list_icon */ NULL, /* list_emblems */ fx_status_text, /* status_text */ fx_tooltip_text, /* tooltip_text */ fx_status_types, /* away_states */ fx_blist_node_menu, /* blist_node_menu */ NULL, /* chat_info */ NULL, /* chat_info_defaults */ fx_login, /* login */ fx_close, /* close */ fx_im_send, /* send_im */ NULL, /* set_info */ NULL, // fetion_typing, /* send_typing */ fx_get_info, /* get_info */ fx_set_status, /* set_status */ NULL, /* set_idle */ NULL, /* change_passwd */ fx_add_buddy, /* add_buddy */ NULL, /* add_buddies */ fx_remove_buddy, /* remove_buddy */ NULL, /* remove_buddies */ NULL, /* add_permit */ NULL, /* add_deny */ NULL, /* rem_permit */ NULL, /* rem_deny */ NULL, /* set_permit_deny */ NULL, /* join_chat */ NULL, /* reject_chat */ NULL, /* get_chat_name */ NULL, /* chat_invite */ NULL, /* chat_leave */ NULL, /* chat_whisper */ NULL, /* chat_send */ fx_keep_alive, /* keepalive */ NULL, /* register_user */ NULL, /* get_cb_info */ NULL, /* get_cb_away */ fx_alias_buddy, /* alias_buddy */ fx_group_buddy, /* group_buddy */ fx_rename_group, /* rename_group */ NULL, /* buddy_free */ NULL, /* convo_closed */ NULL, /* normalize */ NULL, /* set_buddy_icon */ fx_remove_group, /* remove_group */ NULL, /* get_cb_real_name */ NULL, /* set_chat_topic */ NULL, /* find_blist_chat */ NULL, /* roomlist_get_list */ NULL, /* roomlist_cancel */ NULL, /* roomlist_expand_category */ NULL, /* can_receive_file */ NULL, /* send_file */ NULL, /* new_xfer */ NULL, /* offline_message */ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ NULL, fx_send_attention, fx_attention_types, sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, NULL }; static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_PROTOCOL, NULL, 0, NULL, PURPLE_PRIORITY_DEFAULT, "Openfetion", "Openfetion", "1.1", N_("Fetion Plugin"), N_("libpurple plugin implementing Fetion Protocol version 4"), "Wenpeng Li ", "http://code.google.com/p/ofetion/", NULL, NULL, NULL, NULL, &protocol_info, NULL, plugin_actions, /* this tells libpurple the address of the function to call to get the list of plugin actions. */ NULL, NULL, NULL, NULL }; static void init_plugin(PurplePlugin *UNUSED(plugin)) { #ifdef ENABLE_NLS setlocale(LC_ALL, ""); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); bindtextdomain(GETTEXT_PACKAGE , LOCALE_DIR); textdomain(GETTEXT_PACKAGE); #endif buddy_to_added = NULL; } PURPLE_INIT_PLUGIN(openfetion, init_plugin, info) pidgin-openfetion-0.3/res/0000755000175000017500000000000011700212607014237 5ustar aronaronpidgin-openfetion-0.3/res/province.xml0000644000175000017500000000303211700212607016604 0ustar aronaron 北京市 上海市 天津市 重庆市 安徽省 福建省 广东省 甘肃省 广西自治区 贵州省 河北省 湖北省 黑龙江省 海南省 河南省 湖南省 吉林省 江苏省 江西省 辽宁省 内蒙古自治区 宁夏自治区 青海省 四川省 山东省 山西省 陕西省 新疆自治区 西藏自治区 云南省 浙江省 台湾 香港特别行政区 澳门特别行政区 香港特别行政区 pidgin-openfetion-0.3/res/city.xml0000644000175000017500000003126711700212607015742 0ustar aronaron 北京 其他 天津 其他 上海 其他 香港 其他 重庆 其他 其他 石家庄 保定 沧州 承德 邯郸 衡水 廊坊 秦皇岛 唐山 邢台 张家口 其他 太原 长治 大同 晋城 离石 临汾 朔州 忻州 阳泉 榆次 运城 其他 呼和浩特 阿拉善左旗 包头 赤峰 东胜 海拉尔 集宁 临河 乌兰浩特 通辽 乌海 锡林浩特 其他 沈阳 鞍山 本溪 朝阳 大连 丹东 抚顺 阜新 葫芦岛 锦州 辽阳 盘锦 铁岭 营口 其他 长春 白城 白山 吉林 辽源 梅河口 四平 松原 通化 延吉 其他 哈尔滨 大庆 大兴安岭 鹤岗 黑河 鸡西 佳木斯 牡丹江 七台河 齐齐哈尔 双鸭山 绥化 伊春 其他 南昌 抚州 赣州 吉安 景德镇 九江 萍乡 上饶 新余 宜春 鹰潭 其他 杭州 湖州 嘉兴 金华 丽水 宁波 衢州 绍兴 台州 温州 舟山 其他 南京 常州 淮安 连云港 南通 苏州 宿迁 泰州 无锡 徐州 盐城 扬州 张家港 镇江 其他 合肥 安庆 蚌埠 巢湖 池州 滁州 阜阳 淮北 黄山 六安 马鞍山 宿州 铜陵 芜湖 宣城 准南 其他 福州 龙岩 南平 宁德 莆田 泉州 三明 厦门 漳州 其他 济南 滨州 德州 东营 菏泽 济宁 莱芜 聊城 临沂 青岛 日照 泰安 威海 潍坊 烟台 枣庄 淄博 其他 郑州 安阳 鹤壁 潢川 焦作 开封 洛阳 漯河 南阳 平顶山 濮阳 三门峡 商丘 新乡 信阳 许昌 周口 驻马店 其他 武汉 鄂州 恩施 洪湖 黄冈 黄石 荆门 荆州 十堰 随州 咸宁 襄樊 孝感 宜昌 其他 长沙 常德 郴州 衡阳 怀化 吉首 娄底 邵阳 湘潭 益阳 永州 岳阳 张家界 株洲 其他 广州 潮阳 潮州 东莞 佛山 河源 惠州 江门 揭阳 茂名 梅州 清远 汕头 汕尾 韶关 深圳 顺德 阳江 云浮 湛江 肇庆 中山 珠海 其他 南宁 百色 北海 防城港 桂林 河池 柳州 钦州 梧州 玉林 其他 海口 儋州 三亚 其他 成都 巴中 达川 德阳 广安 广元 康定 乐山 泸州 马尔康 绵阳 内江 南充 攀枝花 遂宁 西昌 雅安 宜宾 自贡 其他 贵阳 安顺 毕节 都匀 凯里 六盘水 铜仁 兴义 遵义 其他 昆明 保山 楚雄 大理 德宏 迪庆 东川 个旧 丽江 临沧 六库 曲靖 思茅 文山 西双版纳 玉溪 昭通 其他 拉萨 阿里 昌都 林芝 那曲 日喀则 山南 其他 西安 安康 宝鸡 汉中 商州 铜川 渭南 咸阳 延安 榆林 其他 兰州 白银 定西 甘南州 酒泉 临夏 平凉 天水 武都 武威 西峰 张掖 其他 银川 固原 石嘴山 吴忠 其他 西宁 德令哈 格尔木 共和 海东 海晏 玛沁 门源 同仁 玉树 其他 乌鲁木齐 阿克苏 阿勒泰 阿图什 博乐 昌吉 哈密 和田 喀会 喀什 克拉玛依 库尔勒 奎屯 石河子 塔城 吐鲁番 伊宁 其他 其他 其他 pidgin-openfetion-0.3/res/openfetion.png0000644000175000017500000000113411700212607017112 0ustar aronaronPNG  IHDR&/sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT(nRA5{o؀)1; M/1k05fs(/8XѺ$O?~f%s 7Wv.*Ϟrn˺am6{T2Wr k[ ۈB9P;*S^:DƺFeAzÀ?:cCI-@aB,TUW'Ňzea@hBfz?x-O&FBQh,O),W@}h@_~6^/(EV s=Lcd0qv cQEy S;Lԣ3Ip2I+q(lh5Sh!"}cD%`4D+ 4nj0ŹaR\D)ݚ(X ajHȠFh&blpy~z/*r}18IENDB`pidgin-openfetion-0.3/fx_blist.h0000644000175000017500000000501411700212607015431 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_BUDDYLIST_H #define FETION_BUDDYLIST_H gint fetion_buddylist_create(User *user, const gchar *name); gint fetion_buddylist_delete(fetion_account *ac, gint id); gint fetion_buddylist_edit(fetion_account *ac, gint id, const gchar *name); #endif pidgin-openfetion-0.3/fx_contact.h0000644000175000017500000001032511700212607015750 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_CONTACT_H #define FETION_CONTACT_H #include "fx_types.h" typedef enum { FETION_NO = 1, MOBILE_NO } NumberType; typedef enum { BUDDY_OK = 200 , BUDDY_SAME_USER_DAILY_LIMIT = 486 , BUDDY_USER_EXIST = 521 , BUDDY_BAD_REQUEST = 400 } AddBuddyType; #define foreach_contactlist(head , cl) \ for(cl = head ; (cl = cl->next) != head ;) Contact* fetion_contact_new(); void fetion_contact_list_append(Contact* cl , Contact* contact); Contact *fetion_contact_list_find_by_userid(Contact* contactlist , const char* userid); Contact *fetion_contact_list_find_by_mobileno(Contact *contactlist, const char *mobileno); Contact* fetion_contact_list_find_by_sid(Contact *contactlist , const gchar *sid); void fetion_contact_list_remove_by_userid(Contact* contactlist , const char* userid); void fetion_contact_list_remove(Contact *contact); void fetion_contact_list_free(Contact* contactlist); gint fetion_contact_has_ungrouped(Contact *contactlist); gint fetion_contact_has_strangers(Contact *contactlist); gint fetion_contact_subscribe_only(gint sk, User* user); gint fetion_contact_get_contact_info(fetion_account *ac, const gchar *userid, TransCallback callback); Contact* fetion_contact_get_contact_info_by_no(User* user , const char* no , NumberType nt); int fetion_contact_set_mobileno_permission(User* user , const char* userid , int show); gint fetion_contact_set_displayname(fetion_account *ac, const gchar *userid, const gchar *name); gint fetion_contact_move_to_group(fetion_account *ac, const gchar *userid, gint buddylist); gint fetion_contact_delete_buddy(fetion_account *ac, const gchar *userid); Contact* fetion_contact_add_buddy(User* user , const char* no , NumberType notype , int buddylist , const char* localname , const char* desc , int phraseid , int* statuscode); Contact* fetion_contact_handle_contact_request(User* user, const char* sipuri , const char* userid , const char* localname , int buddylist , int result); #endif pidgin-openfetion-0.3/fx_chat.c0000644000175000017500000004152711700212607015237 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "notify.h" #include "debug.h" #include "pounce.h" #include "fx_contact.h" #include "fx_sip.h" #include "fx_chat.h" static gchar *generate_invite_friend_body(const gchar *sipuri); static gchar *generate_send_nudge_body(); static void parse_send_sms_to_phone(const gchar *xml, gint *daycount, gint *mountcount); void process_message_cb(fetion_account *ac, const gchar *sipmsg) { gchar len[16], callid[16], sequence[16]; gchar sendtime[32], from[64], rep[256], *msg, *sid; PurpleConnection *pc; Contact *cnt; fetion_sip_get_attr(sipmsg , "F" , from); fetion_sip_get_attr(sipmsg , "L" , len); fetion_sip_get_attr(sipmsg , "I" , callid); fetion_sip_get_attr(sipmsg , "Q" , sequence); fetion_sip_get_attr(sipmsg , "D" , sendtime); msg = strstr(sipmsg, "\r\n\r\n") + 4; snprintf(rep, sizeof(rep) - 1 , "SIP-C/4.0 200 OK\r\n" "I: %s\r\n" "Q: %s\r\n" "F: %s\r\n\r\n", callid, sequence, from); pc = purple_account_get_connection(ac->account); sid = fetion_sip_get_sid_by_sipuri(from); cnt = fetion_contact_list_find_by_sid(ac->user->contactList, sid); serv_got_im(pc, cnt->userId, msg, 0, time(NULL)); g_free(sid); send(ac->sk, rep, strlen(rep), 0); } static gint process_invite_conn_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar *sipres; SipHeader *aheader; SipHeader *theader; SipHeader *mheader; SipHeader *nheader; process_invite_data *conn_data = (process_invite_data*)data; fetion_account *ses = conn_data->ac; fetion_sip *sip = ses->user->sip; ses->sk = source; /* start listen subthread */ if((ses->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)push_cb, ses)) == 0) return -1; fetion_sip_set_type(sip, SIP_REGISTER); aheader = fetion_sip_credential_header_new(conn_data->credential); theader = fetion_sip_header_new("K" , "text/html-fragment"); mheader = fetion_sip_header_new("K" , "multiparty"); nheader = fetion_sip_header_new("K" , "nudge"); fetion_sip_add_header(sip , aheader); fetion_sip_add_header(sip , theader); fetion_sip_add_header(sip , mheader); fetion_sip_add_header(sip , nheader); sipres = fetion_sip_to_string(sip , NULL); if(send(ses->sk, sipres, strlen(sipres), 0) == -1) { g_free(sipres); return -1; } g_free(sipres); return 0; } gint process_invite_cb(fetion_account *ac, const gchar *sipmsg) { gchar from[128], auth[128], *ipaddress; gchar buf[1024]; gint port; gchar *credential, *sid; Contact *cnt; fetion_account *ses; process_invite_data *data = g_malloc0(sizeof(process_invite_data)); fetion_sip_get_attr(sipmsg , "F" , from); fetion_sip_get_attr(sipmsg , "A" , auth); fetion_sip_get_auth_attr(auth , &ipaddress , &port , &credential); snprintf(buf, sizeof(buf) - 1, "SIP-C/4.0 200 OK\r\n" "F: %s\r\n" "I: 61\r\n" "Q: 200002 I\r\n\r\n", from); if(send(ac->sk, buf, strlen(buf), 0) == -1) { g_free(data); return -1; } sid = fetion_sip_get_sid_by_sipuri(from); cnt = fetion_contact_list_find_by_sid(ac->user->contactList, sid); ses = session_clone(ac); session_set_userid(ses, cnt->userId); session_add(ses); data->ac = ses; strncpy(data->credential, credential, sizeof(data->credential) - 1); if(!(ses->conn_data = purple_proxy_connect(NULL, ac->account, ipaddress, port, (PurpleProxyConnectFunction)process_invite_conn_cb, data))) { if(!(ses->conn_data = purple_proxy_connect(NULL, ac->account, ipaddress, 443, (PurpleProxyConnectFunction)process_invite_conn_cb, data))) { g_free(ipaddress); g_free(credential); g_free(sid); session_remove(ses); return -1; } } g_free(ipaddress); g_free(credential); g_free(sid); return 0; } void process_enter_cb(fetion_account *ses, const gchar *sipmsg) { GSList *cur = ses->trans_wait; struct transaction *trans; ses->chan_ready = 1; while(cur) { trans = (struct transaction*)(cur->data); fetion_send_sms(ses, trans->userId, trans->msg); transaction_wakeup(ses, trans); cur = ses->trans_wait; } purple_debug_info("fetion", "%s\n" , sipmsg); } void process_left_cb(fetion_account *ses, const gchar *sipmsg) { gchar *sipuri; fetion_sip_parse_userleft(sipmsg, &sipuri); session_remove(ses); session_destroy(ses); purple_debug_info("fetion", "%s\n", sipmsg); g_free(sipuri); } static gint sms_response_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *trans) { gint code = fetion_sip_get_code(sipmsg); PurpleConversation *conv; purple_timeout_remove(trans->timer); if(code == 200 || code == 280) return 0; if(!(conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, trans->userId, ac->account))) return -1; purple_conversation_write(conv, NULL, _("Message sent failed:"), PURPLE_MESSAGE_ERROR, time(NULL)); purple_conversation_write(conv, NULL, trans->msg, PURPLE_MESSAGE_ERROR, time(NULL)); purple_conversation_write(conv, NULL, sipmsg, PURPLE_MESSAGE_RAW, time(NULL)); return 0; } static void sms_timeout_cb(gpointer data) { sms_timeout_data *st_data = (sms_timeout_data*)data; PurpleConversation *conv; struct transaction *trans = st_data->trans; if(!(conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, trans->userId, st_data->ac->account))) return; purple_conversation_write(conv, NULL, _("Message sent timeout:"), PURPLE_MESSAGE_ERROR, time(NULL)); purple_conversation_write(conv, NULL, trans->msg, PURPLE_MESSAGE_ERROR, time(NULL)); purple_timeout_remove(trans->timer); transaction_remove(st_data->ac, trans); g_free(st_data); } gint fetion_send_sms(fetion_account *ses, const gchar* who, const gchar *msg) { fetion_sip *sip = ses->user->sip; SipHeader *toheader, *cheader, *kheader, *nheader; Contact *cnt; gchar *res; sms_timeout_data *st_data; if(!(cnt = fetion_contact_list_find_by_userid(ses->user->contactList, who))) return -1; struct transaction *trans = transaction_new(); transaction_set_userid(trans, who); transaction_set_msg(trans, msg); if(ses->chan_ready) { fetion_sip_set_type(sip , SIP_MESSAGE); nheader = fetion_sip_event_header_new(SIP_EVENT_CATMESSAGE); toheader = fetion_sip_header_new("T" , cnt->sipuri); cheader = fetion_sip_header_new("C" , "text/plain"); kheader = fetion_sip_header_new("K" , "SaveHistory"); fetion_sip_add_header(sip , toheader); fetion_sip_add_header(sip , cheader); fetion_sip_add_header(sip , kheader); fetion_sip_add_header(sip , nheader); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, sms_response_cb); st_data = g_malloc0(sizeof(sms_timeout_data)); st_data->ac = ses; st_data->trans = trans; transaction_set_timeout(trans, (GSourceFunc)sms_timeout_cb, st_data); transaction_add(ses, trans); res = fetion_sip_to_string(sip , msg); if(send(ses->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); } else { transaction_wait(ses, trans); } return 0; } static gint send_sms_to_phone_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gint code, daycount, monthcount; PurpleConnection *pc; gchar *pos; if((code = fetion_sip_get_code(sipmsg)) != 280) { pc = purple_account_get_connection(ac->account); purple_notify_error(pc, NULL, "message", "send message to mobile failed"); return -1; } else{ if(!(pos = strstr(sipmsg, "\r\n\r\n"))) return -1; parse_send_sms_to_phone(pos + 4, &daycount, &monthcount); purple_debug_info("message", "send message to mobile success, " "%d messages sent today, %d messages sent this monty", daycount, monthcount); return 0; } return 0; } gint fetion_send_sms_to_phone(fetion_account *ac, const gchar *userid, const gchar *msg) { SipHeader *toheader; SipHeader *eheader; SipHeader *aheader; gchar *res; User *user = ac->user; fetion_sip *sip = user->sip; gchar astr[1024]; Contact *cnt; struct transaction *trans; if(!(cnt = fetion_contact_list_find_by_userid(user->contactList, userid))) return -1; fetion_sip_set_type(sip , SIP_MESSAGE); toheader = fetion_sip_header_new("T" , cnt->sipuri); eheader = fetion_sip_event_header_new(SIP_EVENT_SENDCATMESSAGE); fetion_sip_add_header(sip , toheader); if(user->verification != NULL){ snprintf(astr, sizeof(astr) - 1, "Verify algorithm=\"picc\",chid=\"%s\",response=\"%s\"" , user->verification->guid , user->verification->code); aheader = fetion_sip_header_new("A" , astr); fetion_sip_add_header(sip , aheader); } trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, send_sms_to_phone_cb); transaction_add(ac, trans); fetion_sip_add_header(sip , eheader); res = fetion_sip_to_string(sip , msg); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } //debug_info("Sent a message to (%s)`s mobile phone" , sid); g_free(res); return 0; } gint fetion_send_nudge(fetion_account *ses, const gchar *who) { SipHeader *toheader; gchar *res; gchar *body; fetion_sip *sip = ses->user->sip; Contact *cnt; cnt = fetion_contact_list_find_by_userid(ses->user->contactList, who); fetion_sip_set_type(sip , SIP_INCOMING); toheader = fetion_sip_header_new("T" , cnt->sipuri); fetion_sip_add_header(sip , toheader); body = generate_send_nudge_body(); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ses->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } static gint invite_buddy_cb(fetion_account *ses, const gchar *UNUSED(sipmsg), struct transaction *trans) { if(trans->msg[0] == '\0') fetion_send_nudge(ses, trans->userId); else fetion_send_sms(ses, trans->userId, trans->msg); return 0; } static gint chat_reg_cb(fetion_account *ses, const gchar *UNUSED(sipmsg), struct transaction *trans) { fetion_sip *sip = ses->user->sip; SipHeader *eheader; gchar *body, *res; struct transaction *trans0; Contact *cnt; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_INVITEBUDDY); fetion_sip_add_header(sip , eheader); cnt = fetion_contact_list_find_by_userid(ses->user->contactList, trans->userId); body = generate_invite_friend_body(cnt->sipuri); trans0 = transaction_new(); transaction_set_userid(trans0, trans->userId); transaction_set_msg(trans0, trans->msg); transaction_set_callid(trans0, sip->callid); transaction_set_callback(trans0, invite_buddy_cb); transaction_add(ses, trans0); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ses->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } return 0; } static gint invite_connect_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { SipHeader *theader, *mheader, *nheader, *aheader; invite_data *d = (invite_data*)data; gchar *res; fetion_account *ses; fetion_sip *sip; if(!(ses = session_find(d->trans->userId))) { g_free(d); return -1; } ses->sk = source; sip = fetion_sip_clone(ses->user->sip); /* listen for this thread */ if((ses->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)push_cb, ses)) == 0) { g_free(d); return -1; } fetion_sip_set_type(sip , SIP_REGISTER); aheader = fetion_sip_credential_header_new(d->credential); theader = fetion_sip_header_new("K" , "text/html-fragment"); mheader = fetion_sip_header_new("K" , "multiparty"); nheader = fetion_sip_header_new("K" , "nudge"); transaction_set_callid(d->trans, sip->callid); transaction_set_callback(d->trans, chat_reg_cb); transaction_add(ses, d->trans); fetion_sip_add_header(sip , aheader); fetion_sip_add_header(sip , theader); fetion_sip_add_header(sip , mheader); fetion_sip_add_header(sip , nheader); res = fetion_sip_to_string(sip , NULL); if(send(source, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); g_free(d->credential); g_free(d); return 0; } static gint new_chat_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *trans) { gchar auth[256], *credential, *ip; gint port; fetion_account *se; invite_data *data = g_malloc0(sizeof(invite_data)); fetion_sip_get_attr(sipmsg , "A" , auth); fetion_sip_get_auth_attr(auth , &ip , &port , &credential); data->trans = transaction_new(); memcpy(data->trans, trans, sizeof(struct transaction)); data->credential = credential; se = session_clone(ac); session_set_userid(se, trans->userId); session_add(se); if(!(se->conn_data = purple_proxy_connect(NULL, ac->account, ip, port, (PurpleProxyConnectFunction)invite_connect_cb, data))) { if(!(se->conn_data = purple_proxy_connect(NULL, ac->account, ip, 443, (PurpleProxyConnectFunction)invite_connect_cb, data))) { session_remove(se); g_free(se); return -1; } } g_free(ip); return 0; } gint new_chat(fetion_account *ac, const gchar *userid, const gchar *what) { fetion_sip *sip = ac->user->sip; gchar *res; SipHeader *eheader; struct transaction *trans; /*start chat*/ fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_STARTCHAT); fetion_sip_add_header(sip , eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_userid(trans, userid); transaction_set_msg(trans, what); transaction_set_callback(trans, new_chat_cb); transaction_add(ac, trans); res = fetion_sip_to_string(sip , (gchar*)0); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } static gchar *generate_invite_friend_body(const gchar *sipuri) { xmlChar *buf; xmlDocPtr doc; xmlNodePtr node; gchar body[] = ""; doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "contact" , NULL); xmlNewProp(node , BAD_CAST "uri" , BAD_CAST sipuri); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static gchar *generate_send_nudge_body() { xmlChar *buf; xmlDocPtr doc; xmlNodePtr node; gchar body[] = ""; doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "state" , NULL); xmlNodeSetContent(node , BAD_CAST "nudge"); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static void parse_send_sms_to_phone(const gchar *xml, gint *daycount, gint *mountcount) { xmlDocPtr doc; xmlNodePtr node; xmlChar* res; doc = xmlParseMemory(xml , strlen(xml)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "frequency"); res = xmlGetProp(node , BAD_CAST "day-count"); *daycount = atoi((gchar*)res); xmlFree(res); res = xmlGetProp(node , BAD_CAST "month-count"); *mountcount = atoi((gchar*)res); xmlFree(res); xmlFreeDoc(doc); } pidgin-openfetion-0.3/fx_contact.c0000644000175000017500000003001211700212607015736 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_contact.h" #include "fx_sip.h" #include "fx_user.h" #include "fx_types.h" /*private */ static gchar *generate_subscribe_body(const gchar *version); static gchar *generate_contact_info_body(const gchar *userid); static gchar *generate_set_displayname_body(const gchar *userid , const gchar *name); static gchar *generate_move_to_group_body(const gchar *userid, gint buddylist); static gchar *generate_delete_buddy_body(const gchar *userid); Contact* fetion_contact_new() { Contact* list = (Contact*)g_malloc0(sizeof(Contact)); memset(list , 0 , sizeof(Contact)); list->state = P_HIDDEN; list->pre = list; list->next = list; return list; } void fetion_contact_list_append(Contact* cl , Contact* contact) { cl->next->pre = contact; contact->next = cl->next; contact->pre = cl; cl->next = contact; } Contact* fetion_contact_list_find_by_userid(Contact* contactlist , const char* userid) { Contact* cl_cur; foreach_contactlist(contactlist , cl_cur){ if(strcmp(cl_cur->userId , userid) == 0) return cl_cur; } return NULL; } Contact* fetion_contact_list_find_by_sid(Contact *contactlist , const gchar *sid) { Contact *cl_cur; gchar *sid1; foreach_contactlist(contactlist , cl_cur){ sid1 = fetion_sip_get_sid_by_sipuri(cl_cur->sipuri); if(strcmp(sid , sid1) == 0){ free(sid1); return cl_cur; } free(sid1); } return (Contact*)0; } Contact *fetion_contact_list_find_by_mobileno(Contact *contactlist, const char *mobileno) { Contact *cl_cur; foreach_contactlist(contactlist, cl_cur) { if(strcmp(cl_cur->mobileno, mobileno) == 0) return cl_cur; } return NULL; } void fetion_contact_list_remove_by_userid(Contact* contactlist , const char* userid) { Contact *cl_cur; foreach_contactlist(contactlist , cl_cur){ if(strcmp(cl_cur->userId , userid) == 0){ cl_cur->pre->next = cl_cur->next; cl_cur->next->pre = cl_cur->pre; free(cl_cur); break; } } } void fetion_contact_list_remove(Contact *contact) { contact->next->pre = contact->pre; contact->pre->next = contact->next; } void fetion_contact_list_free(Contact* contact) { Contact *cl_cur , *del_cur; for(cl_cur = contact->next ; cl_cur != contact ;){ cl_cur->pre->next = cl_cur->next; cl_cur->next->pre = cl_cur->pre; del_cur = cl_cur; cl_cur = cl_cur->next; free(del_cur); } free(contact); } gint fetion_contact_subscribe_only(gint sk, User* user) { gchar *res, *body; fetion_sip* sip; SipHeader* eheader; sip = user->sip; fetion_sip_set_type(sip , SIP_SUBSCRIPTION); eheader = fetion_sip_event_header_new(SIP_EVENT_PRESENCE); if(!eheader) return -1; fetion_sip_add_header(sip , eheader); body = generate_subscribe_body("0"); if(!body) { free(eheader);return -1; } res = fetion_sip_to_string(sip , body); if(!res) { free(eheader);free(body);return -1; } g_free(body); if(send(sk , res , strlen(res), 0) == -1) { g_free(res); return -1; } return 0; } gint fetion_contact_get_contact_info(fetion_account *ac, const gchar *userid, TransCallback callback) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; Contact *contact; gchar *res , *body; struct transaction *trans; contact = fetion_contact_list_find_by_userid(ac->user->contactList, userid); body = generate_contact_info_body(contact->userId); if(!body) return -1; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_GETCONTACTINFO); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, callback); transaction_add(ac, trans); fetion_sip_add_header(sip , eheader); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) return -1; g_free(res); return 0; } gint fetion_contact_has_ungrouped(Contact *contactlist) { Contact *cur; foreach_contactlist(contactlist , cur){ if(cur->groupid == BUDDY_LIST_NOT_GROUPED) return 1; } return 0; } gint fetion_contact_has_strangers(Contact *contactlist) { Contact *cur; foreach_contactlist(contactlist , cur){ if(cur->groupid == BUDDY_LIST_STRANGER) return 1; } return 0; } gint fetion_contact_set_displayname(fetion_account *ac, const gchar *userid, const gchar *name) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res, *body; Contact *cnt; cnt = fetion_contact_list_find_by_userid(ac->user->contactList, userid); fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_SETCONTACTINFO); fetion_sip_add_header(sip , eheader); body = generate_set_displayname_body(cnt->userId , name); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } gint fetion_contact_move_to_group(fetion_account *ac, const gchar *userid, gint buddylist) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res, *body; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_SETCONTACTINFO); fetion_sip_add_header(sip , eheader); body = generate_move_to_group_body(userid , buddylist); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } gint fetion_contact_delete_buddy(fetion_account *ac, const gchar *userid) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res, *body; Contact *cnt; if(!(cnt = fetion_contact_list_find_by_userid(ac->user->contactList, userid))) return -1; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_DELETEBUDDY); fetion_sip_add_header(sip , eheader); body = generate_delete_buddy_body(cnt->userId); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } #if 0 Contact* fetion_contact_handle_contact_request(User* user , const char* sipuri , const char* userid , const char* localname , int buddylist , int result) { fetion_sip* sip = user->sip; SipHeader* eheader; char *res , *body; int ret; Contact* contact; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_HANDLECONTACTREQUEST); if(eheader == NULL){ return NULL; } fetion_sip_add_header(sip , eheader); body = generate_handle_contact_request_body(sipuri , userid , localname , buddylist , result); if(body == NULL){ return NULL; } res = fetion_sip_to_string(sip , body); free(body); if(res == NULL){ return NULL; } tcp_connection_send(sip->tcp , res , strlen(res)); free(res); res = fetion_sip_get_response(sip); if(res == NULL){ return NULL; } ret = fetion_sip_get_code(res); switch(ret) { case 200 : contact = parse_handle_contact_request_response(res); free(res); if(contact == NULL){ debug_info("handle contact request from (%s) failed" , userid); return NULL; } fetion_contact_list_append(user->contactList , contact); debug_info("handle contact request from (%s) success" , userid); return contact; default: free(res); debug_info("handle contact request from (%s) failed" , userid); return NULL; } return NULL; } #endif static gchar *generate_subscribe_body(const gchar *version) { xmlChar *buf; xmlDocPtr doc; xmlNodePtr node; gchar body[] = ""; doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "subscription" , NULL); xmlNewProp(node , BAD_CAST "self" , BAD_CAST "v4default;mail-count"); xmlNewProp(node , BAD_CAST "buddy" , BAD_CAST "v4default"); xmlNewProp(node , BAD_CAST "version" , BAD_CAST version); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static gchar *generate_contact_info_body(const gchar *userid) { xmlChar *buf; xmlDocPtr doc; xmlNodePtr node; gchar body[] = ""; doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contact" , NULL); xmlNewProp(node , BAD_CAST "user-id" , BAD_CAST userid); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static gchar *generate_set_displayname_body(const gchar *userid , const gchar *name) { gchar args[] = ""; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "contact" , NULL); xmlNewProp(node , BAD_CAST "user-id" , BAD_CAST userid); xmlNewProp(node , BAD_CAST "local-name" , BAD_CAST name); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } gchar *generate_move_to_group_body(const gchar *userid, gint buddylist) { gchar args[] = ""; gchar bl[5]; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "contact" , NULL); xmlNewProp(node , BAD_CAST "user-id" , BAD_CAST userid); sprintf(bl , "%d" , buddylist); xmlNewProp(node , BAD_CAST "buddy-lists" , BAD_CAST bl); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static gchar *generate_delete_buddy_body(const gchar *userid) { gchar args[] = ""; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddies" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy" , NULL); xmlNewProp(node , BAD_CAST "user-id" , BAD_CAST userid); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } pidgin-openfetion-0.3/fx_types.h0000644000175000017500000002261211700212607015463 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_TYPES_H #define FETION_TYPES_H #include "fetion.h" /* inline function to trace program track */ /** * some other buddylists */ typedef enum { BUDDY_LIST_NOT_GROUPED = 0 , BUDDY_LIST_STRANGER = -1 , BUDDY_LIST_PGGROUP = -2 } BuddyListType; /** * Presence states */ typedef enum { P_ONLINE = 400 , P_RIGHTBACK = 300 , P_AWAY = 100 , P_BUSY = 600 , P_OUTFORLUNCH = 500 , P_ONTHEPHONE = 150 , P_MEETING = 850 , P_DONOTDISTURB = 800 , P_HIDDEN = 0 , P_OFFLINE = -1 } StateType; /** * Type to indicate user`s service status */ typedef enum { STATUS_NORMAL = 1 , /* normal status */ STATUS_OFFLINE , /* user offline , deleted you from his list or out of service*/ STATUS_NOT_AUTHENTICATED , /* user has not accept your add buddy request */ STATUS_SMS_ONLINE , /* user has not start fetion service */ STATUS_REJECTED , /* user rejected your add buddy request,wait for deleting */ STATUS_SERVICE_CLOSED , /* user has closed his fetion service */ STATUS_NOT_BOUND /* user doesn`t bound fetion number to a mobile number */ } StatusType; /** * Sip header that in form of "name: value" such as "AK: ak-value" */ typedef struct sipheader { char name[8]; /* sip header namne*/ char *value; /* sip header value*/ struct sipheader *next; /* next sip header */ } SipHeader; /** * Sip type include some common attributes */ typedef struct { int type; /* sip message type */ char from[20]; /* sender`s fetion no ,in sip it`s "F: " */ int callid; int sequence; /* sequence number , in sip it`s "Q: " */ int threadCount; /* listening threads count using this sip */ char sipuri[48]; /* outer sipuri used when listening */ SipHeader* header; /* some othre header list */ } fetion_sip; /** * Contact lists information (Two-way linked list) */ typedef struct contact { char userId[16]; /* userid used since v4 protocal */ char sId[16]; /* fetion no */ char sipuri[48]; /* sipuri like 'sip:100@fetion.com.cn' */ char localname[256]; /* name set by yourself */ char nickname[256]; /* user`s nickname */ char impression[2048]; /* user`s mood phrase */ char mobileno[12]; /* mobile phone number */ char devicetype[10]; /* user`s client type , like PC and J2ME,etc */ char portraitCrc[12]; /* a number generated by crc algorithm */ char birthday[16]; /* user`s bitrhday */ char country[6]; /* user`s country`s simplified form,like CN */ char province[6]; /* user`s province`s simplified form,like bj */ char city[6]; /* user`s city`s code ,like 10 for beijing */ int identity; /* whethere to show mobileno to this user */ int scoreLevel; /* user`s score level,unused now */ int serviceStatus; /* basic service status */ int carrierStatus; int relationStatus; char carrier[16]; StateType state; /* state type like online,busy,etc */ int groupid; /* buddylist id */ int gender; /* gender 1 for male 2 for female,0 for private */ int imageChanged; /* whether user`s portrait has changed */ int dirty; /* whether the contact just read from the server is newer than that int the local disk */ struct contact* next; struct contact* pre; } Contact; /** * Buddy lists information (Two-way linked list) */ typedef struct group { char groupname[32]; /* current buddy list name */ int groupid; /* current buddy list Id */ int dirty; struct group *next; struct group *pre; } Group; /** * Verification information used for picture code confirm */ typedef struct { char *algorithm; char *type; char *text; char *tips; char *code; char *guid; } Verification; /** * User`s personal information and some related structs */ typedef struct { char sId[16]; /* fetion number */ char userId[16]; /* user id */ char mobileno[16]; /* mobile phone number */ char password[48]; /* raw password not hashed */ char sipuri[48]; /* sipuri like 'sip:100@fetion.com.cn' */ char publicIp[32]; /* public ip of current session */ char lastLoginIp[32]; /* public ip of last login */ char lastLoginTime[48]; /* last login time , got after sipc authentication */ char sipcProxyIP[32]; int sipcProxyPort; char portraitServerName[48]; /* portrait server`s hostname ,read from configuration.xml*/ char portraitServerPath[32]; /* portrait server`s path , such as /HD_POOL8 */ char configServersVersion[16]; /* the version of some related servers such as sipc server */ char configParametersVersion[16]; char configHintsVersion[16]; /* the version of hints */ char personalVersion[16]; /* the version of personal information */ char contactVersion[16]; /* the version of contact information */ char customConfigVersion[16]; /* the version of custom config string,unused now */ char nickname[48]; /* nickname of yourself */ char impression[256]; /* mood phrase of yourself */ char portraitCrc[16]; /* a number generated by crc algorithm */ char country[6]; /* the country which your number belongs to */ char province[6]; /* the province which your number belongs to */ char city[6]; /* the city which your number belongs to */ int gender; /* the gender of your self */ char smsOnLineStatus[32]; int smsDayLimit; int smsDayCount; int smsMonthLimit; int smsMonthCount; int pgGroupCallId; /* callid for get group list request */ int groupInfoCallId; /* callid for get group info request */ int state; /* presence state */ int loginType; /* using sid or mobileno */ int loginStatus; /* login status code */ int carrierStatus; int boundToMobile; /* whether this number is bound to a mobile number */ long loginTimes; int contactCount; int groupCount; char* ssic; /* cookie string read from reply message after ssi login */ char* customConfig; /* custom config string used to set personal information */ Verification* verification; /* a struct used to generate picture code */ Contact* contactList; /* friend list of current user */ Group* groupList; /* buddylist list of current user */ fetion_sip* sip; /* sip object used to handle sip event */ } User; typedef struct { gint sk; guint conn; gint left_len; User *user; gchar *data; gchar userId[48]; gint chan_ready; /* only used in chat session */ GSList *trans; GSList *trans_wait; /* only used in chat session */ PurpleConnection *gc; PurpleProxyConnectData *conn_data; PurpleAccount *account; } fetion_account; struct transaction; typedef gint(*TransCallback) (fetion_account *, const gchar *, struct transaction *); struct transaction { gint callid; gchar userId[32]; gchar msg[1024]; guint timer; TransCallback callback; }; #endif pidgin-openfetion-0.3/fx_chat.h0000644000175000017500000000616111700212607015237 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FX_CHAT_H #define fX_CHAT_H typedef struct { fetion_account *ac; struct transaction *trans; } sms_timeout_data; typedef struct { struct transaction *trans; gchar *credential; } invite_data; typedef struct { fetion_account *ac; gchar credential[1024]; } process_invite_data; void process_message_cb(fetion_account *ac, const gchar *sipmsg); gint process_invite_cb(fetion_account *ses, const gchar *sipmsg); void process_left_cb(fetion_account *sec, const gchar *sipmsg); void process_enter_cb(fetion_account *ses, const gchar *sipmsg); gint fetion_send_nudge(fetion_account *ses, const gchar *who); gint fetion_send_sms(fetion_account *ac, const gchar* who, const gchar *msg); gint fetion_send_sms_to_phone(fetion_account *ac, const gchar *userid, const gchar *msg); gint new_chat(fetion_account *ac, const gchar *sid, const gchar *what); #endif pidgin-openfetion-0.3/fx_login.c0000644000175000017500000012016611700212607015425 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_sip.h" #include "fx_user.h" #include "fx_contact.h" #include "fx_login.h" #include "fx_buddy.h" #include #include #include #define VERIFY_TYPE_SSI 1 #define VERIFY_TYPE_SIP 2 struct verify_data { gint type; /* ssi verify || sipc verify */ fetion_account *ac; /* common data */ PurpleSslConnection *ssl; /* used by ssi verify */ gint sipc_conn; /* used by sipc verify */ gchar response[BUFLEN]; /* used by sipc verify */ gchar *data; } verify_data; /*private method*/ static gchar *generate_auth_body(User* user); static gchar *generate_configuration_body(User* user); static void parse_personal_info(xmlNodePtr node , User* user); static void parse_contact_list(xmlNodePtr node , User* user, int *group_count, int *buddy_count); static void parse_stranger_list(xmlNodePtr node , User* user); static void parse_ssi_auth_success(xmlNodePtr node , User* user); static void parse_ssi_auth_failed(xmlNodePtr node , User* user); static guchar *strtohex(const gchar *in, gint *len); static gchar *hextostr(const guchar *in, gint len); static gchar *hash_password_v1(const guchar *b0, gint b0len, const guchar *password, gint psdlen); static gchar *hash_password_v2(const gchar *userid , const gchar *passwordhex); static gchar *hash_password_v4(const gchar *userid , const gchar *password); static gchar *generate_cnouce() ; static gint download_cfg(gpointer data, gint source, const gchar * error_message); static void ssi_auth_cb(gpointer data, PurpleSslConnection *source, PurpleInputCondition *cond); static int sipc_reg_action(gpointer data, gint source, const gchar *error_message); static gint parse_configuration_xml(User *user, const char *xml); static gchar *decode_base64(const gchar *in, gint *len); static gint parse_sipc_verification(User *user, const gchar *str); static void parse_sipc_reg_response(const gchar *reg_response, gchar **nouce, gchar **key); static gchar *generate_aes_key(); static gint parse_sipc_auth_response(const gchar *auth_response, User *user, gint *group_count, gint *buddy_count); char* generate_response(const char* nouce , const char* userid , const char* password , const char* publickey , const char* key) { char* psdhex = hash_password_v4(userid , password); char modulus[257]; char exponent[7]; int ret, flen; BIGNUM *bnn, *bne; unsigned char *out; unsigned char *nonce , *aeskey , *psd , *res; int nonce_len , aeskey_len , psd_len; RSA *r = RSA_new(); key = NULL; memset(modulus, 0, sizeof(modulus)); memset(exponent, 0, sizeof(exponent)); memcpy(modulus , publickey , 256); memcpy(exponent , publickey + 256 , 6); nonce = (guchar*)g_malloc0(strlen(nouce) + 1); memcpy(nonce , (guchar*)nouce , strlen(nouce)); nonce_len = strlen(nouce); psd = strtohex(psdhex , &psd_len); aeskey = strtohex(generate_aes_key() , &aeskey_len); res = (guchar*)g_malloc0(nonce_len + aeskey_len + psd_len + 1); memcpy(res , nonce , nonce_len); memcpy(res + nonce_len , psd , psd_len ); memcpy(res + nonce_len + psd_len , aeskey , aeskey_len); bnn = BN_new(); bne = BN_new(); BN_hex2bn(&bnn, modulus); BN_hex2bn(&bne, exponent); r->n = bnn; r->e = bne; r->d = NULL; // RSA_print_fp(stdout, r, 5); flen = RSA_size(r); out = (guchar*)g_malloc0(flen); purple_debug_info("fetion", "start encrypting response"); ret = RSA_public_encrypt(nonce_len + aeskey_len + psd_len, res , out, r, RSA_PKCS1_PADDING); if (ret < 0){ purple_debug_info("fetion", "encrypt response failed!"); g_free(res); g_free(aeskey); g_free(psd); g_free(nonce); return NULL; } RSA_free(r); purple_debug_info("fetion", "encrypting reponse success"); g_free(res); g_free(aeskey); g_free(psd); g_free(nonce); return hextostr(out , ret); } static void pic_ok_cb(fetion_account *ac, PurpleRequestFields *fields) { const gchar *code; code = purple_request_fields_get_string(fields, "code_entry"); fetion_user_set_verification_code(ac->user, code); if(verify_data.type == VERIFY_TYPE_SSI) { purple_ssl_connect(ac->account, SSI_SERVER, PURPLE_SSL_DEFAULT_PORT, (PurpleSslInputFunction)ssi_auth_action, (PurpleSslErrorFunction)0, ac); } else if(verify_data.type == VERIFY_TYPE_SIP) { sipc_aut_action(verify_data.sipc_conn, ac, verify_data.response); } } static void pic_cancel_cb(fetion_account *ac, PurpleRequestFields *UNUSED(fields)) { fetion_verification_free(ac->user->verification); ac->user->verification = NULL; purple_connection_error_reason(ac->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Login Failed.")); } static void fetion_code_request(fetion_account *ac, const gchar *code_data, gint code_size) { PurpleRequestFieldGroup *field_group; PurpleRequestField *code_entry; PurpleRequestField *code_pic; PurpleRequestFields *fields; fields = purple_request_fields_new(); field_group = purple_request_field_group_new((gchar*)0); purple_request_fields_add_group(fields, field_group); code_pic = purple_request_field_image_new("code_pic", _("Confirmation code"), code_data, code_size); purple_request_field_group_add_field(field_group, code_pic); code_entry = purple_request_field_string_new("code_entry", _("Please input the code"), "", FALSE); purple_request_field_group_add_field(field_group, code_entry); purple_request_fields(ac->account, NULL, ac->user->verification->tips, (gchar*)0, fields, _("OK"), G_CALLBACK(pic_ok_cb), _("Cancel"), G_CALLBACK(pic_cancel_cb), ac->account, NULL, NULL, ac); } static gint pic_read_cb(gpointer data, gint source, const gchar *UNUSED(message)) { gint n, len; gchar sipmsg[BUFLEN]; xmlDocPtr doc; xmlNodePtr node; gchar *code, *pos; gchar *pic; gint piclen; fetion_account *ac = (fetion_account*)data; len = ac->data ? strlen(ac->data) : 0; if((n = recv(source, sipmsg, strlen(sipmsg), 0)) == -1) return -1; sipmsg[n] = 0; if(n == 0) { purple_input_remove(ac->conn); close(source); if(! ac->data) return 0; if(!(pos = strstr(ac->data, "\r\n\r\n"))) { g_free(ac->data); ac->data = (gchar*)0; return -1; } doc = xmlParseMemory(pos + 4, strlen(pos + 4)); node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; ac->user->verification->guid = (gchar*)xmlGetProp(node , BAD_CAST "id"); code = (gchar*)xmlGetProp(node , BAD_CAST "pic"); xmlFreeDoc(doc); purple_debug_info("fetion", "Generating verification code picture"); pic = decode_base64(code , &piclen); g_free(code); fetion_code_request(ac, pic, piclen); g_free(pic); g_free(ac->data); ac->data = (gchar*)0; return 0; } ac->data = (gchar*)realloc(ac->data, len + n + 1); memcpy(ac->data + len, sipmsg, n + 1); return 0; } static gint pic_code_cb(gpointer data, gint source, const gchar *UNUSED(message)) { gchar cookie[BUFLEN]; gchar http[BUFLEN]; fetion_account *ac = (fetion_account*)data; User *user = ac->user; if(user->ssic) snprintf(cookie, sizeof(cookie) - 1, "Cookie: ssic=%s\r\n", user->ssic); snprintf(http, sizeof(http) - 1, "GET /nav/GetPicCodeV4.aspx?algorithm=%s HTTP/1.1\r\n" "%sHost: %s\r\n" "User-Agent: IIC2.0/PC "PROTO_VERSION"\r\n" "Connection: close\r\n\r\n", user->verification->algorithm == NULL ? "" : user->verification->algorithm, user->ssic == NULL ? "" : cookie, NAV_SERVER); if(send(source, http, strlen(http), 0) == -1) return -1; ac->data = (gchar*)0; ac->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)pic_read_cb, data); return 0; } gboolean ssi_auth_action(gpointer data, PurpleSslConnection * gsc, gint UNUSED(con)) { gchar sslbuf[BUFLEN]; gchar noUri[256]; gchar verifyUri[256]; gchar *password; gint passwordType; fetion_account *ac = (fetion_account*)data; User *user = ac->user; purple_debug_info("fetion", "initialize ssi authentication action"); password = hash_password_v4(user->userId , user->password); memset(noUri, 0, sizeof(noUri)); if(user->loginType == LOGIN_TYPE_MOBILENO) snprintf(noUri, sizeof(noUri) - 1, "mobileno=%s" , user->mobileno); else snprintf(noUri, sizeof(noUri) - 1, "sid=%s" , user->sId); memset(verifyUri, 0, sizeof(verifyUri)); if(user->verification != NULL && user->verification->code != NULL) { snprintf(verifyUri, sizeof(verifyUri) - 1, "&pid=%s&pic=%s&algorithm=%s", user->verification->guid, user->verification->code, user->verification->algorithm); } passwordType = (strlen(user->userId) == 0 ? 1 : 2); snprintf(sslbuf, sizeof(sslbuf) - 1, "GET /ssiportal/SSIAppSignInV4.aspx?%s" "&domains=fetion.com.cn%s&v4digest-type=%d&v4digest=%s\r\n" "User-Agent: IIC2.0/pc "PROTO_VERSION"\r\n" "Host: %s\r\n" "Cache-Control: private\r\n" "Connection: Keep-Alive\r\n\r\n", noUri, verifyUri, passwordType, password, SSI_SERVER); purple_ssl_write(gsc, sslbuf, strlen(sslbuf)); purple_ssl_input_add(gsc, (PurpleSslInputFunction)ssi_auth_cb, ac); return TRUE; } static gint sipc_reg_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar sipmsg[BUFLEN]; gint n; gchar *nonce, *key, *aeskey, *response; fetion_account *ac = (fetion_account*)data; if((n = recv(source , sipmsg , sizeof(sipmsg), 0)) < 0) return -1; sipmsg[n] = '\0'; parse_sipc_reg_response(sipmsg, &nonce, &key); aeskey = generate_aes_key(); response = generate_response(nonce, ac->user->userId, ac->user->password, key, aeskey); /* fill verify_data for pic confirm */ strncpy(verify_data.response, response, sizeof(verify_data.response)); g_free(key); g_free(aeskey); g_free(nonce); if(sipc_aut_action(source, ac, response) == -1) { g_free(response); return -1; } g_free(response); return 0; } static gint sipc_aut_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar sipmsg[BUFLEN * 20], *pos; gint n; guint length = 0; gint group_count, buddy_count; PurpleConnection *pc; fetion_account *ac = (fetion_account*)data; if((n = recv(source, sipmsg, sizeof(sipmsg) - 1, 0)) == -1) return -1; sipmsg[n] = '\0'; length = ac->data ? strlen(ac->data) : 0; ac->data = (gchar*)realloc(ac->data, length + strlen(sipmsg) + 1); memcpy(ac->data + length, sipmsg, strlen(sipmsg) + 1); if(strstr(ac->data, "\r\n\r\n")) { length = fetion_sip_get_length(ac->data); pos = strstr(ac->data, "\r\n\r\n") + 4; if(length < strlen(pos)) { n = strlen(ac->data) - (strlen(pos) - length); ac->data = (gchar*)realloc(ac->data, n + 1); ac->data[n] = '\0'; goto aut_fin; } else if (length == strlen(pos)) { goto aut_fin; } } return 0; aut_fin: if(!purple_input_remove(ac->conn)) return -1; parse_sipc_auth_response(ac->data, ac->user, &group_count, &buddy_count); if(fetion_contact_has_ungrouped(ac->user->contactList)) { Group *grp = fetion_group_new(); strncpy(grp->groupname, "未分组", sizeof(grp->groupname)); grp->groupid = 0; fetion_group_list_append(ac->user->groupList, grp); } g_free(ac->data); ac->data = (gchar*)0; ac->sk = source; pc = purple_account_get_connection(ac->account); if(USER_AUTH_NEED_CONFIRM(ac->user)) { verify_data.type = VERIFY_TYPE_SIP; verify_data.sipc_conn = source; if(ac->conn_data) { purple_proxy_connect_cancel(ac->conn_data); ac->conn_data = NULL; } ac->conn_data = purple_proxy_connect(NULL, ac->account, NAV_SERVER, 80, (PurpleProxyConnectFunction)pic_code_cb, ac); return -1; } else if(USER_AUTH_ERROR(ac->user) ) { purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); return -1; } purple_connection_set_state(pc, PURPLE_CONNECTED); fx_blist_init(ac); if(fetion_contact_subscribe_only(source, ac->user) == -1) return -1; purple_connection_set_display_name(pc, ac->user->nickname); ac->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)push_cb, ac); return 0; } static gint sipc_reg_action(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar *sipmsg; gchar *cnouce = generate_cnouce(); gint ret; fetion_account *ac = (fetion_account*)data; fetion_sip* sip = fetion_sip_new0(ac->user); purple_debug_info("fetion", "initialize sipc registeration action"); fetion_sip_set_type(sip , SIP_REGISTER); SipHeader* cheader = fetion_sip_header_new("CN" , cnouce); SipHeader* client = fetion_sip_header_new("CL" , "type=\"pc\" ,version=\""PROTO_VERSION"\""); fetion_sip_add_header(sip , cheader); fetion_sip_add_header(sip , client); g_free(cnouce); sipmsg = fetion_sip_to_string(sip , NULL); purple_debug_info("fetion", "start registering to sip server(%s:%d)" , ac->user->sipcProxyIP , ac->user->sipcProxyPort); if((ret = write(source , sipmsg , strlen(sipmsg))) == -1) return -1; g_free(sipmsg); ac->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)sipc_reg_cb, ac); return 0; } gint sipc_aut_action(gint sk, fetion_account *ac, const gchar *response) { gchar* sipmsg; gchar* xml; User *user = ac->user; SipHeader* aheader = NULL; SipHeader* akheader = NULL; SipHeader* ackheader = NULL; fetion_sip* sip = ac->user->sip; purple_debug_info("fetion", "Initialize sipc authencation action"); xml = generate_auth_body(user); fetion_sip_set_type(sip , SIP_REGISTER); aheader = fetion_sip_authentication_header_new(response); akheader = fetion_sip_header_new("AK" , "ak-value"); fetion_sip_add_header(sip , aheader); fetion_sip_add_header(sip , akheader); if(user->verification != NULL && user->verification->algorithm != NULL) { ackheader = fetion_sip_ack_header_new(user->verification->code , user->verification->algorithm , user->verification->type , user->verification->guid); fetion_sip_add_header(sip , ackheader); } sipmsg = fetion_sip_to_string(sip , xml); fetion_verification_free(user->verification); user->verification = NULL; purple_debug_info("fetion", "Start sipc authentication , with ak-value"); if(send(sk, sipmsg, strlen(sipmsg), 0) == -1) { g_free(sipmsg); return -1; } g_free(sipmsg); if(!purple_input_remove(ac->conn)) return -1; ac->data = (gchar*)0; ac->left_len = 0; ac->conn = purple_input_add(sk, PURPLE_INPUT_READ, (PurpleInputFunction)sipc_aut_cb, ac); return 0; } static void ssi_auth_cb(gpointer data, PurpleSslConnection *source, PurpleInputCondition *UNUSED(cond)) { xmlDocPtr doc; xmlNodePtr node; fetion_account *ac = (fetion_account*)data; PurpleConnection *pc; User *user = ac->user; const gchar *uri = "nav.fetion.com.cn"; gchar ssi_response[BUFLEN]; gint n; gchar *pos; gchar *xml; n = purple_ssl_read(source, ssi_response, sizeof(ssi_response)); ssi_response[n] = '\0'; purple_ssl_close(source); if(strstr(ssi_response , "ssic=")){ pos = strstr(ssi_response , "ssic=") + 5; n = strlen(pos) - strlen(strstr(pos , ";")); user->ssic = (gchar*)g_malloc0(n + 1); strncpy(user->ssic , pos , n); } if(!(xml = strstr(ssi_response, "\r\n\r\n"))) { pc = purple_account_get_connection(ac->account); purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Login Failed.")); return; } doc = xmlParseMemory(xml + 4 , strlen(xml + 4)); node = xmlDocGetRootElement(doc); pos = (gchar*)xmlGetProp(node , BAD_CAST "status-code"); user->loginStatus = atoi(pos); node = node->xmlChildrenNode; if(atoi(pos) == 200) { fetion_verification_free(user->verification); user->verification = NULL; purple_debug_info("fetion", "ssi login success"); parse_ssi_auth_success(node , user); } else { purple_debug_info("fetion", "ssi login failed , status-code :%s" , pos); parse_ssi_auth_failed(node , user); g_free(pos); xmlFreeDoc(doc); if(USER_AUTH_NEED_CONFIRM(user)) { verify_data.type = VERIFY_TYPE_SSI; verify_data.ssl = source; ac->conn_data = purple_proxy_connect(NULL, ac->account, NAV_SERVER, 80, (PurpleProxyConnectFunction)pic_code_cb, ac); } else { pc = purple_account_get_connection(ac->account); purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password.")); } return; } g_free(pos); xmlFreeDoc(doc); if(ac->conn_data) { purple_proxy_connect_cancel(ac->conn_data); ac->conn_data = NULL; } /* download configuration routine */ ac->conn_data = purple_proxy_connect(NULL, ac->account, uri, 80, (PurpleProxyConnectFunction)download_cfg, ac); if(!ac->conn_data) { purple_debug_error("fetion", "connect to cfg server failed\n"); pc = purple_account_get_connection(ac->account); purple_connection_error_reason(pc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Login Failed.")); } return; } static void parse_sipc_reg_response(const gchar *reg_response, gchar **nouce, gchar **key) { gchar digest[2048] = { 0 }; gchar *pos; gint n; fetion_sip_get_attr(reg_response , "W" , digest); pos = strstr(digest , "nonce") + 7; n = strlen(pos) - strlen(strstr(pos , "\",")); *nouce = (gchar*)g_malloc0(n + 1); strncpy(*nouce , pos , n); (*nouce)[n] = '\0'; pos = strstr(pos , "key") + 5; n = strlen(pos) - strlen(strstr(pos , "\",")); *key = (gchar*)g_malloc0(n + 1); strncpy(*key , pos , n); (*key)[n] = '\0'; purple_debug_info("fetion", "register to sip server success"); purple_debug_info("fetion", "nonce:%s" , *nouce); } static void parse_sms_frequency(xmlNodePtr node , User *user) { xmlChar *res; node = node->xmlChildrenNode; if(xmlHasProp(node , BAD_CAST "day-limit")){ res = xmlGetProp(node , BAD_CAST "day-limit"); user->smsDayLimit = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "day-count")){ res = xmlGetProp(node , BAD_CAST "day-count"); user->smsDayCount = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "month-limit")){ res = xmlGetProp(node , BAD_CAST "month-limit"); user->smsMonthLimit = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "month-count")){ res = xmlGetProp(node , BAD_CAST "month-count"); user->smsMonthCount = atoi((char*)res); xmlFree(res); } } static gint cfg_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gint n, length = 0; gchar msg[BUFLEN * 10], *pos; fetion_account *ac = (fetion_account*)data; if((n = recv(source, msg, sizeof(msg), 0)) == -1) return -1; msg[n] = '\0'; if(n == 0) { /* get cfg failed */ if(!strstr(ac->data, "HTTP/1.1 200")) { g_free(ac->data); ac->data = (gchar*)0; close(source); purple_input_remove(ac->conn); purple_connection_error_reason(ac->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Download configuration file error.")); return -1; } if(!(pos = strstr(ac->data, "\r\n\r\n"))) return -1; pos += 4; if(parse_configuration_xml(ac->user, pos) == -1) { g_free(ac->data); ac->data = (gchar*)0; close(source); purple_input_remove(ac->conn); return -1; } g_free(ac->data); ac->data = (gchar*)0; close(source); purple_input_remove(ac->conn); ac->conn_data = purple_proxy_connect(NULL, ac->account, ac->user->sipcProxyIP, ac->user->sipcProxyPort, (PurpleProxyConnectFunction)sipc_reg_action, ac); if(!ac->conn_data) { purple_debug_error("fetion", "connect to ssi server failed"); purple_connection_error_reason(ac->gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Login Failed.")); return -1; } return 0; } length = ac->data ? strlen(ac->data) : 0; ac->data = (gchar*)realloc(ac->data, length + n + 1); memcpy(ac->data + length, msg, n + 1); return 0; } static gint download_cfg(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar http[BUFLEN] , *body; fetion_account *ac = (fetion_account*)data; const gchar *uri = "nav.fetion.com.cn"; body = generate_configuration_body(ac->user); snprintf(http, sizeof(http), "POST /nav/getsystemconfig.aspx HTTP/1.1\r\n" "User-Agent: IIC2.0/PC "PROTO_VERSION"\r\n" "Host: %s\r\n" "Connection: Close\r\n" "Content-Length: %d\r\n\r\n%s", uri , strlen(body) , body); g_free(body); if(send(source, http, strlen(http), 0) == -1) return -1; ac->data = (gchar*)0; if((ac->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)cfg_cb, ac)) == 0) return -1; return 0; } static gint parse_sipc_auth_response(const gchar *auth_response, User *user, gint *group_count, gint *buddy_count) { gchar *pos; xmlChar *buf; xmlDocPtr doc; xmlNodePtr rootnode; xmlNodePtr node; xmlNodePtr node1; gint code; code = fetion_sip_get_code(auth_response); user->loginStatus = code; if(code == 200){ fetion_verification_free(user->verification); user->verification = NULL; purple_debug_info("fetion", "sipc authentication success"); }else if(code == 421 || code == 420){ parse_sipc_verification(user , auth_response); return 2; }else{ fetion_verification_free(user->verification); user->verification = NULL; purple_debug_error("fetion", "Sipc authentication failed\n"); printf("%s\n", auth_response); return -1; } if(!strstr(auth_response, "\r\n\r\n")) return -1; pos = strstr(auth_response , "\r\n\r\n") + 4; if(!pos) return -1; doc = xmlParseMemory(pos , strlen(pos)); if(!doc) return -1; rootnode = xmlDocGetRootElement(doc); node = rootnode->xmlChildrenNode; buf = xmlGetProp(node, BAD_CAST "public-ip"); strcpy(user->publicIp, (gchar*)buf); xmlFree(buf); buf = xmlGetProp(node, BAD_CAST "last-login-ip"); strcpy(user->lastLoginIp, (gchar*)buf); xmlFree(buf); buf = xmlGetProp(node, BAD_CAST "last-login-time"); strcpy(user->lastLoginTime, (gchar*)buf); xmlFree(buf); node = node->next; node1 = node->xmlChildrenNode; parse_personal_info(node1, user); node1 = xml_goto_node(node, "custom-config"); buf = xmlGetProp(node1, BAD_CAST "version"); strcpy(user->customConfigVersion, (gchar*)buf); xmlFree(buf); buf = xmlNodeGetContent(node1); if(xmlStrlen(buf) > 0){ user->customConfig = g_malloc0(strlen((gchar*)buf) + 1); strcpy(user->customConfig, (gchar*)buf); } xmlFree(buf); node1 = xml_goto_node(node , "contact-list"); parse_contact_list(node1 , user, group_count, buddy_count); node1 = xml_goto_node(node , "chat-friends"); if(node1) parse_stranger_list(node1 , user); node1 = xml_goto_node(node , "quota-frequency"); if(node1) parse_sms_frequency(node1 , user); xmlFreeDoc(doc); return 0; } static gchar *generate_aes_key() { gint ret; gchar *key = (gchar*)g_malloc0(65); FILE *rand_fd = fopen("/dev/urandom", "r"); if(rand_fd == NULL){ g_free(key); return NULL; } ret = fread(key, 64, 1, rand_fd); if(ret != 1){ g_free(key); fclose(rand_fd); return NULL; } fclose(rand_fd); return key; } static gchar* generate_auth_body(User* user) { gchar basexml[] = ""; gchar state[5]; xmlChar* buf = NULL; xmlDocPtr doc = NULL; xmlNodePtr rootnode = NULL; xmlNodePtr node = NULL; xmlNodePtr node1 = NULL; doc = xmlParseMemory( basexml , strlen(basexml)); rootnode = xmlDocGetRootElement(doc); node = xmlNewChild(rootnode , NULL , BAD_CAST "device" , NULL); xmlNewProp(node , BAD_CAST "machine-code" , BAD_CAST "001676C0E351"); node = xmlNewChild(rootnode , NULL , BAD_CAST "caps" , NULL); xmlNewProp(node , BAD_CAST "value" , BAD_CAST "1ff"); node = xmlNewChild(rootnode , NULL , BAD_CAST "events" , NULL); xmlNewProp(node , BAD_CAST "value" , BAD_CAST "7f"); node = xmlNewChild(rootnode , NULL , BAD_CAST "user-info" , NULL); xmlNewProp(node , BAD_CAST "mobile-no" , BAD_CAST user->mobileno); xmlNewProp(node , BAD_CAST "user-id" , BAD_CAST user->userId); node1 = xmlNewChild(node , NULL , BAD_CAST "personal" , NULL); xmlNewProp(node1 , BAD_CAST "version" , BAD_CAST "0"/*user->personalVersion*/); xmlNewProp(node1 , BAD_CAST "attributes" , BAD_CAST "v4default"); node1 = xmlNewChild(node , NULL , BAD_CAST "custom-config" , NULL); xmlNewProp(node1 , BAD_CAST "version" , BAD_CAST "0"/*user->customConfigVersion*/); node1 = xmlNewChild(node , NULL , BAD_CAST "contact-list" , NULL); xmlNewProp(node1 , BAD_CAST "version" , BAD_CAST "0"/*user->contactVersion*/); xmlNewProp(node1 , BAD_CAST "buddy-attributes" , BAD_CAST "v4default"); node = xmlNewChild(rootnode , NULL , BAD_CAST "credentials" , NULL); xmlNewProp(node , BAD_CAST "domains" , BAD_CAST "fetion.com.cn"); node = xmlNewChild(rootnode , NULL , BAD_CAST "presence" , NULL); node1 = xmlNewChild(node , NULL , BAD_CAST "basic" , NULL); snprintf(state, sizeof(state) - 1, "%d" , user->state); xmlNewProp(node1 , BAD_CAST "value" , BAD_CAST state); xmlNewProp(node1 , BAD_CAST "desc" , BAD_CAST ""); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static void parse_personal_info(xmlNodePtr node , User* user) { xmlChar *buf; char *pos; buf = xmlGetProp(node , BAD_CAST "version"); strcpy(user->personalVersion , (char*)buf); xmlFree(buf); if(xmlHasProp(node , BAD_CAST "sid")) { buf = xmlGetProp(node , BAD_CAST "sid"); strcpy(user->sId , (char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "mobile-no")) { buf = xmlGetProp(node , BAD_CAST "mobile-no"); if(xmlStrlen(buf)){ user->boundToMobile = BOUND_MOBILE_ENABLE; }else{ user->boundToMobile = BOUND_MOBILE_DISABLE; } strcpy(user->mobileno , (char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "carrier-status")) { buf = xmlGetProp(node , BAD_CAST "carrier-status"); user->carrierStatus = atoi((char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "nickname")) { buf = xmlGetProp(node , BAD_CAST "nickname"); strcpy(user->nickname , (char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "gender")) { buf = xmlGetProp(node , BAD_CAST "gender"); user->gender = atoi((char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "sms-online-status")) { buf = xmlGetProp(node , BAD_CAST "sms-online-status"); strcpy(user->smsOnLineStatus , (char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "impresa")) { buf = xmlGetProp(node , BAD_CAST "impresa"); strcpy(user->impression , (char*)buf); xmlFree(buf); } if(xmlHasProp(node , BAD_CAST "carrier-region")) { int n; buf = xmlGetProp(node , BAD_CAST "carrier-region"); pos = (char*)buf; n = strlen(pos) - strlen(strstr(pos , ".")); strncpy(user->country , pos , n); pos = strstr(pos , ".") + 1; n = strlen(pos) - strlen(strstr(pos , ".")); strncpy(user->province , pos , n); pos = strstr(pos , ".") + 1; n = strlen(pos) - strlen(strstr(pos , ".")); strncpy(user->city , pos , n); xmlFree(buf); } } static void parse_contact_list(xmlNodePtr node, User* user, int *group_count, int *buddy_count) { xmlChar* buf = NULL; xmlNodePtr node1 , node2; Group* group = NULL; Contact* contact = NULL; int hasGroup = 1 , hasBuddy = 1; int nr = 0; *group_count = 0; *buddy_count = 0; buf = xmlGetProp(node , BAD_CAST "version"); purple_debug_info("fetion", "start reading contact list "); if(strcmp(user->contactVersion , (char*) buf) == 0) return; strcpy(user->contactVersion , (char*)buf); xmlFree(buf); node1 = xml_goto_node(node , "buddy-lists"); node2 = node1->xmlChildrenNode; user->groupCount = 0; while(node2 != NULL){ hasGroup = 1; buf = xmlGetProp(node2 , BAD_CAST "id"); group = fetion_group_list_find_by_id(user->groupList , atoi((char*)buf)); if(group == NULL){ hasGroup = 0; group = fetion_group_new(); } group->groupid = atoi((char*)buf); xmlFree(buf); buf = xmlGetProp(node2 , BAD_CAST "name"); strcpy(group->groupname , (char*)buf); xmlFree(buf); nr ++; group->dirty = 1; user->groupCount ++; if(hasGroup == 0){ fetion_group_list_append(user->groupList , group); hasGroup = 1; } node2 = node2->next; } *group_count = nr; nr = 0; node1 = xml_goto_node(node , "buddies"); node1 = node1->xmlChildrenNode; user->contactCount = 0; while(node1 != NULL){ hasBuddy = 1; if(! xmlHasProp(node1 , BAD_CAST "i")){ node1 = node1->next; continue; } buf = xmlGetProp(node1 , BAD_CAST "i"); contact = fetion_contact_list_find_by_userid(user->contactList , (char*)buf); if(contact == NULL){ hasBuddy = 0; contact = fetion_contact_new(); } strcpy(contact->userId , (char*)buf); xmlFree(buf); /* maybe a buddy belongs to two groups */ if(contact->dirty == 1){ node = node->next; continue; } user->contactCount ++; /* set the dirty flags */ contact->dirty = 1; nr ++; if(xmlHasProp(node1 , BAD_CAST "n")){ buf = xmlGetProp(node1 , BAD_CAST "n"); strcpy(contact->localname , (char*)buf); xmlFree(buf); } if(xmlHasProp(node1 , BAD_CAST "l")){ buf = xmlGetProp(node1 , BAD_CAST "l"); contact->groupid = atoi((char*)buf); if(xmlStrstr(buf , BAD_CAST ";") != NULL || contact->groupid < 0) contact->groupid = 0; xmlFree(buf); } if(xmlHasProp(node1 , BAD_CAST "p")){ buf = xmlGetProp(node1 , BAD_CAST "p"); if(strstr((char*)buf , "identity=1") != NULL) contact->identity = 1; else contact->identity = 0; xmlFree(buf); } if(xmlHasProp(node1 , BAD_CAST "r")){ buf = xmlGetProp(node1 , BAD_CAST "r"); contact->relationStatus = atoi((char*)buf); xmlFree(buf); } if(xmlHasProp(node1 , BAD_CAST "u")){ buf = xmlGetProp(node1 , BAD_CAST "u"); strcpy(contact->sipuri , (char*)buf); //if(strstr((char*)buf , "tel") != NULL) // contact->serviceStatus = STATUS_SMS_ONLINE; xmlFree(buf); } strcpy(contact->portraitCrc , "0"); if(hasBuddy == 0){ fetion_contact_list_append(user->contactList , contact); hasBuddy = 1; } node1 = node1->next; } *buddy_count = nr; purple_debug_info("fetion", "read contact list complete"); } static void parse_stranger_list(xmlNodePtr node, User *user) { xmlNodePtr node1 = node->xmlChildrenNode; xmlChar *buf = NULL; Contact *contact = NULL; int hasBuddy; while(node1 != NULL) { hasBuddy = 1; user->contactCount ++; buf = xmlGetProp(node1 , BAD_CAST "i"); contact = fetion_contact_list_find_by_userid(user->contactList , (char*)buf); if(contact == NULL){ hasBuddy = 0; contact = fetion_contact_new(); } strcpy(contact->userId , (char*)buf); xmlFree(buf); buf = xmlGetProp(node1 , BAD_CAST "u"); strcpy(contact->sipuri , (char*)buf); contact->groupid = BUDDY_LIST_STRANGER; contact->dirty = 1; if(hasBuddy == 0) fetion_contact_list_append(user->contactList , contact); node1 = node1->next; } } static void parse_ssi_auth_success(xmlNodePtr node , User* user) { gchar *pos; pos = (gchar*)xmlGetProp(node , BAD_CAST "uri"); strcpy(user->sipuri , pos); g_free(pos); pos = fetion_sip_get_sid_by_sipuri(user->sipuri); strcpy(user->sId , pos); g_free(pos); pos = (gchar*)xmlGetProp(node , BAD_CAST "mobile-no"); strcpy(user->mobileno , pos); g_free(pos); pos = (gchar*)xmlGetProp(node , BAD_CAST "user-id"); strcpy(user->userId , pos); g_free(pos); } static void parse_ssi_auth_failed(xmlNodePtr node , User* user) { Verification *ver = fetion_verification_new(); ver->algorithm = (gchar*)xmlGetProp(node, BAD_CAST "algorithm"); ver->type = (gchar*)xmlGetProp(node, BAD_CAST "type"); ver->text = (gchar*)xmlGetProp(node, BAD_CAST "text"); ver->tips = (gchar*)xmlGetProp(node, BAD_CAST "tips"); user->verification = ver; } static guchar *strtohex(const gchar *in, gint *len) { guchar* out = (guchar*)g_malloc0(strlen(in)/2 ); gint i = 0 , j = 0 , k = 0 ,length = 0; gchar tmp[3] = { 0 }; gint inlength; inlength=(gint)strlen(in); while(i < inlength) { tmp[k++] = in[i++]; tmp[k] = '\0'; if(k == 2) { out[j++] = (guchar)strtol(tmp , (gchar**)NULL , 16); k = 0; length ++; } } if(len != NULL ) *len = length; return out; } static gchar *hextostr(const guchar *in, gint len) { gchar *res = (gchar*)g_malloc0(len * 2 + 1); gint reslength; gint i = 0; while(i < len) { sprintf(res + i * 2 , "%02x" , in[i]); i ++; } i = 0; reslength=(gint)strlen(res); while(i < reslength) { res[i] = toupper(res[i]); i ++; }; return res; } static gchar *hash_password_v1(const guchar *b0, gint b0len, const guchar *password, gint psdlen) { guchar *dst = (guchar*)g_malloc0(b0len + psdlen + 1); guchar tmp[20]; gchar *res; memset(tmp , 0 , sizeof(tmp)); memcpy(dst , b0 , b0len); memcpy(dst + b0len , password , psdlen); SHA_CTX ctx; SHA1_Init(&ctx); SHA1_Update(&ctx , dst , b0len + psdlen ); SHA1_Final(tmp , &ctx); g_free(dst); res = hextostr(tmp , 20); return res; } static gchar *hash_password_v2(const gchar *userid , const gchar *passwordhex) { gint id = atoi(userid); gchar *res; guchar *bid = (guchar*)(&id); guchar ubid[4]; gint bpsd_len; guchar* bpsd = strtohex(passwordhex , &bpsd_len); memcpy(ubid , bid , 4); res = hash_password_v1(ubid , sizeof(id) , bpsd , bpsd_len); g_free(bpsd); return res; } static gchar *hash_password_v4(const gchar *userid , const gchar *password) { const gchar *domain = "fetion.com.cn:"; gchar *res, *dst; guchar *udomain = (guchar*)g_malloc0(strlen(domain)); guchar *upassword = (guchar*)g_malloc0(strlen(password)); memcpy(udomain, (guchar*)domain, strlen(domain)); memcpy(upassword, (guchar*)password, strlen(password)); res = hash_password_v1(udomain, strlen(domain), upassword, strlen(password)); g_free(udomain); g_free(upassword); if(userid == NULL || *userid == '\0') return res; dst = hash_password_v2(userid , res); g_free(res); return dst; } static gchar *generate_cnouce() { gchar *cnouce = (gchar*)g_malloc0(33); sprintf(cnouce , "%04X%04X%04X%04X%04X%04X%04X%04X" , rand() & 0xFFFF , rand() & 0xFFFF , rand() & 0xFFFF , rand() & 0xFFFF , rand() & 0xFFFF , rand() & 0xFFFF, rand() & 0xFFFF , rand() & 0xFFFF ); return cnouce; } static gchar *decode_base64(const char* in , int* len) { unsigned int n , t = 0 , c = 0; gchar *res; unsigned char out[3]; unsigned char inp[4]; n = strlen(in); if(n % 4 != 0) { purple_debug_error("fetion", "Try to decode a string which is not a base64 string(decode_base64)"); return NULL; } n = n / 4 * 3; if(len != NULL) *len = n; res = (gchar*)malloc(n); memset(res , 0 , n); while(1) { memset(inp , 0 , 4); memset(out , 0 , 3); memcpy(inp , in + c , 4); c += 4; n = EVP_DecodeBlock(out , inp , 4 ); memcpy(res + t , out , n); t += n; if(c >= strlen(in)) break; } return res; } static gchar *generate_configuration_body(User *user) { xmlChar* buf; xmlDocPtr doc; xmlNodePtr node , cnode; gchar body[] = ""; doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); cnode = xmlNewChild(node , NULL , BAD_CAST "user" , NULL); if(user->loginType == LOGIN_TYPE_FETIONNO) xmlNewProp(cnode , BAD_CAST "sid" , BAD_CAST user->sId); else xmlNewProp(cnode , BAD_CAST "mobile-no" , BAD_CAST user->mobileno); cnode = xmlNewChild(node , NULL , BAD_CAST "client" , NULL); xmlNewProp(cnode , BAD_CAST "type" , BAD_CAST "PC"); xmlNewProp(cnode , BAD_CAST "version" , BAD_CAST PROTO_VERSION); xmlNewProp(cnode , BAD_CAST "platform" , BAD_CAST "W5.1"); cnode = xmlNewChild(node , NULL , BAD_CAST "servers" , NULL); xmlNewProp(cnode , BAD_CAST "version", BAD_CAST "0");//user->configServersVersion); cnode = xmlNewChild(node , NULL , BAD_CAST "parameters" , NULL); xmlNewProp(cnode , BAD_CAST "version", BAD_CAST "0");//user->configParametersVersion); cnode = xmlNewChild(node , NULL , BAD_CAST "hints" , NULL); xmlNewProp(cnode , BAD_CAST "version", BAD_CAST "0");//user->configHintsVersion); xmlDocDumpMemory(doc , &buf , NULL); xmlFreeDoc(doc); return xml_convert(buf); } static gint parse_configuration_xml(User *user, const char *xml) { gchar sipcIP[20] , sipcPort[6]; gchar* pos; gint n; xmlChar* res; xmlDocPtr doc; xmlNodePtr node; xmlNodePtr cnode; memset(sipcIP, 0, sizeof(sipcIP)); memset(sipcPort, 0, sizeof(sipcPort)); doc = xmlParseMemory(xml, strlen(xml)); if(!doc) return -1; node = xmlDocGetRootElement(doc); cnode = xml_goto_node(node, "servers"); if(cnode && xmlHasProp(cnode, BAD_CAST "version")){ res = xmlGetProp(cnode, BAD_CAST "version"); strcpy(user->configServersVersion, (gchar*)res); xmlFree(res); } cnode = xml_goto_node(node, "parameters"); if(cnode && xmlHasProp(cnode, BAD_CAST "version")){ res = xmlGetProp(cnode, BAD_CAST "version"); strncpy(user->configParametersVersion, (gchar*)res, sizeof(user->configParametersVersion)); xmlFree(res); } cnode = xml_goto_node(node, "hints"); if(cnode && xmlHasProp(cnode, BAD_CAST "version")){ res = xmlGetProp(cnode, BAD_CAST "version"); strncpy(user->configHintsVersion, (gchar*)res, sizeof(user->configHintsVersion)); xmlFree(res); } cnode = xml_goto_node(node, "sipc-proxy"); if(cnode){ res = xmlNodeGetContent(cnode); n = strlen((gchar*)res) - strlen(strstr((gchar*)res , ":")); strncpy(user->sipcProxyIP , (gchar*)res , n); pos = strstr((gchar*)res , ":") + 1; user->sipcProxyPort = atoi(pos); xmlFree(res); } cnode = xml_goto_node(node , "get-uri"); if(cnode){ res = xmlNodeGetContent(cnode); pos = strstr((gchar*)res , "//") + 2; n = strlen(pos) - strlen(strstr(pos , "/")); strncpy(user->portraitServerName , pos , n); pos = strstr(pos , "/") + 1; n = strlen(pos) - strlen(strstr(pos , "/")); strncpy(user->portraitServerPath , pos , n); xmlFree(res); } return 0; } static gint parse_sipc_verification(User *user, const gchar *str) { gchar *xml; gchar w[128]; gint n = 0; xmlDocPtr doc; xmlNodePtr node; xmlChar *res; Verification *ver; ver = fetion_verification_new(); fetion_sip_get_attr(str , "W" , w); xml = strstr(w , "algorithm=") + 11; n = strlen(xml) - strlen(strstr(xml , "\"")); ver->algorithm = (gchar*)g_malloc0(n + 1); strncpy(ver->algorithm , xml , n); xml = strstr(w , "type=") + 6; n = strlen(xml) - strlen(strstr(xml , "\"")); ver->type = (gchar*)g_malloc0(n + 1); strncpy(ver->type , xml , n); xml = strstr(str , "\r\n\r\n"); doc = xmlParseMemory(xml , strlen(xml)); node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; res = xmlGetProp(node , BAD_CAST "text"); n = xmlStrlen(res) + 1; ver->text = (gchar*)g_malloc0(n); strncpy(ver->text , (char*)res , n - 1); xmlFree(res); res = xmlGetProp(node , BAD_CAST "tips"); n = xmlStrlen(res) + 1; ver->tips = (gchar*)g_malloc0(n); strncpy(ver->tips , (char*)res , n - 1); xmlFree(res); user->verification = ver; return 0; } pidgin-openfetion-0.3/fx_sip.h0000644000175000017500000001255411700212607015116 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_SIP_H #define FETION_SIP_H #define SIP_BUFFER_SIZE 2048 #include "fx_types.h" typedef enum { SIP_REGISTER = 1 , SIP_SERVICE , SIP_SUBSCRIPTION , SIP_NOTIFICATION , SIP_INVITATION , SIP_INCOMING , SIP_OPTION , SIP_MESSAGE , SIP_SIPC_4_0 , SIP_ACKNOWLEDGE , SIP_UNKNOWN } SipType; typedef enum { NOTIFICATION_TYPE_PRESENCE , NOTIFICATION_TYPE_CONTACT , NOTIFICATION_TYPE_CONVERSATION , NOTIFICATION_TYPE_REGISTRATION , NOTIFICATION_TYPE_SYNCUSERINFO , NOTIFICATION_TYPE_PGGROUP , NOTIFICATION_TYPE_UNKNOWN } NotificationType; typedef enum { NOTIFICATION_EVENT_PRESENCECHANGED , NOTIFICATION_EVENT_ADDBUDDYAPPLICATION , NOTIFICATION_EVENT_USERENTER , NOTIFICATION_EVENT_USERLEFT , NOTIFICATION_EVENT_DEREGISTRATION , NOTIFICATION_EVENT_SYNCUSERINFO , NOTIFICATION_EVENT_PGGETGROUPINFO , NOTIFICATION_EVENT_UNKNOWN } NotificationEvent; typedef enum { SIP_EVENT_PRESENCE = 0, SIP_EVENT_SETPRESENCE , SIP_EVENT_CONTACT , SIP_EVENT_CONVERSATION , SIP_EVENT_CATMESSAGE , SIP_EVENT_SENDCATMESSAGE , SIP_EVENT_STARTCHAT , SIP_EVENT_INVITEBUDDY , SIP_EVENT_GETCONTACTINFO , SIP_EVENT_CREATEBUDDYLIST , SIP_EVENT_DELETEBUDDYLIST , SIP_EVENT_SETCONTACTINFO , SIP_EVENT_SETUSERINFO , SIP_EVENT_SETBUDDYLISTINFO , SIP_EVENT_DELETEBUDDY , SIP_EVENT_ADDBUDDY , SIP_EVENT_KEEPALIVE , SIP_EVENT_DIRECTSMS , SIP_EVENT_SENDDIRECTCATSMS , SIP_EVENT_HANDLECONTACTREQUEST , SIP_EVENT_PGGETGROUPLIST , SIP_EVENT_PGGETGROUPINFO , SIP_EVENT_PGGETGROUPMEMBERS , SIP_EVENT_PGSENDCATSMS , SIP_EVENT_PGPRESENCE } SipEvent; typedef enum { INFO_NUDGE, INFO_UNKNOWN } InfoType; typedef enum { INFO_ACTION_ACCEPT, INFO_ACTION_CANCEL, INFO_ACTION_UNKNOWN } InfoActionType; fetion_sip* fetion_sip_new0(User *user); fetion_sip* fetion_sip_clone(fetion_sip* sip); SipHeader* fetion_sip_header_new(const gchar *name , const gchar *value); void fetion_sip_set_type(fetion_sip *sip, SipType type); void fetion_sip_set_callid(fetion_sip *sip, gint callid); SipHeader* fetion_sip_authentication_header_new(const gchar *response); SipHeader* fetion_sip_ack_header_new(const gchar *code, const gchar *algorithm, const gchar *type, const gchar *guid); SipHeader *fetion_sip_event_header_new(gint eventType); SipHeader* fetion_sip_credential_header_new(const gchar *credential); void fetion_sip_add_header(fetion_sip *sip, SipHeader *header); gchar *fetion_sip_to_string(fetion_sip *sip, const gchar *body); void fetion_sip_free(fetion_sip *sip); gchar *fetion_sip_get_sid_by_sipuri(const gchar *sipuri); gint fetion_sip_get_attr(const gchar *sip, const gchar *name, gchar *result); gint fetion_sip_get_length(const gchar* sipmsg); gint fetion_sip_get_code(const gchar *sip); gint fetion_sip_get_type(const gchar *sip); void fetion_sip_get_auth_attr(const gchar *auth, gchar **ipaddress, gint *port, gchar **credential); void fetion_sip_parse_notification(const gchar *sip, gint *type, gint *event, gchar **xml); gint fetion_sip_parse_info(const gchar *sipmsg, InfoType *type); void fetion_sip_parse_userleft(const gchar *sipmsg, gchar **sipuri); gint fetion_sip_parse_sipc(const gchar *sipmsg, gint *callid, gchar **xml); #endif pidgin-openfetion-0.3/fx_user.h0000644000175000017500000001000111700212607015262 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_USER_H #define FETION_USER_H #include "fx_types.h" #define MODIFY_INFO_NICKNAME 0 #define MODIFY_INFO_IMPRESA 1 #define USER_AUTH_NEED_CONFIRM(u) ((u)->loginStatus == 421 || (u)->loginStatus == 420) #define USER_AUTH_ERROR(u) ((u)->loginStatus == 401 || (u)->loginStatus == 400 || (u)->loginStatus == 404) User *fetion_user_new(const gchar *no , const gchar *password); void fetion_user_set_userid(User* user , const char* userid); void fetion_user_set_sid(User* user , const char* sId); void fetion_user_set_mobileno(User* user , const char* mobileno); void fetion_user_set_verification_code(User* user , const char* code); void fetion_user_free(User* user); gint fetion_user_set_state(fetion_account *ac, gint state); gint fetion_user_keep_alive(fetion_account *ac); gint fetion_sms_myself(fetion_account *ac, const gchar *msg); gint fetion_modify_info(fetion_account *ac, gint info_type, const gchar *value); #define foreach_grouplist(head , gl) for(gl = head ; (gl = gl->next) != head ;) Group* fetion_group_new(); void fetion_group_list_append(Group* head , Group* group); void fetion_group_list_prepend(Group* head , Group* group); void fetion_group_list_remove(Group *group); void fetion_group_remove(Group* head , int groupid); Group *fetion_group_list_find_by_id(Group *head, gint id); Group *fetion_group_list_find_by_name(Group *head, const gchar *name); Verification* fetion_verification_new(); void fetion_verification_free(Verification* ver); Contact *fetion_user_parse_presence_body(const gchar *body , User* user); Contact *fetion_user_parse_syncuserinfo_body(const gchar *body , User* user); gint fetion_user_set_sms_status(User *user , int days); static inline void fetion_user_set_st(User *user, int state){ user->state = state; } #endif pidgin-openfetion-0.3/fetion.h0000644000175000017500000001226611700212607015112 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_H #define FETION_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "glib/gi18n.h" #include "blist.h" #include "debug.h" #include "account.h" #include "request.h" #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #else # define UNUSED(x) x #endif #define PROTO_VERSION "4.0.2510" #define LOGIN_TYPE_FETIONNO 1 #define LOGIN_TYPE_MOBILENO 0 #define BOUND_MOBILE_ENABLE 1 #define BOUND_MOBILE_DISABLE 0 #define BASIC_SERVICE_NORMAL 1 #define BASIC_SERVICE_ABNORMAL 0 #define CARRIER_STATUS_OFFLINE -1 #define CARRIER_STATUS_NORMAL 0 #define CARRIER_STATUS_DOWN 1 #define CARRIER_STATUS_CLOSED 2 #define RELATION_STATUS_AUTHENTICATED 1 #define RELATION_STATUS_UNAUTHENTICATED 0 #define NAV_SERVER "nav.fetion.com.cn" #define SSI_SERVER "uid.fetion.com.cn" #define DISPLAY_VERSION "1.1" #define FETION_NUDGE 0 #define BUFLEN 4096 #include "fx_types.h" gint push_cb(gpointer data, gint source, const gchar *error_message); xmlNodePtr xml_goto_node(xmlNodePtr node , const gchar *xml); gchar *xml_convert(xmlChar* in); struct transaction *transaction_new(); const gchar *get_status_id(gint state); void transaction_free(struct transaction *trans); void transaction_set_callid(struct transaction *trans, gint callid); void transaction_set_userid(struct transaction *trans, const gchar *userid); void transaction_set_callback(struct transaction *trans, TransCallback callback); void transaction_set_timeout(struct transaction *trans, GSourceFunc timeout, gpointer data); void transaction_set_msg(struct transaction *trans, const gchar *msg); void transaction_wait(fetion_account *ses, struct transaction *trans); void transaction_wakeup(fetion_account *ses, struct transaction *trans); void transaction_add(fetion_account *ses, struct transaction *trans); void transaction_remove(fetion_account *ses, struct transaction *trans); fetion_account *session_new(PurpleAccount *account); fetion_account *session_clone(fetion_account *ac); void session_set_userid(fetion_account *ses, const gchar *userid); void session_add(fetion_account *ac); fetion_account *session_find(const gchar *key); fetion_account *session_find_by_sk(gint sk); void session_remove(fetion_account *ses); void session_destroy(fetion_account *ses); #ifdef __cplusplus } #endif #endif pidgin-openfetion-0.3/fx_login.h0000644000175000017500000000472611700212607015435 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #ifndef FETION_LOGIN_H #define FETION_LOGIN_H gboolean ssi_auth_action(gpointer data, PurpleSslConnection * gsc, gint con); gint sipc_aut_action(gint sk, fetion_account *ac, const gchar *response); #endif pidgin-openfetion-0.3/fx_sip.c0000644000175000017500000003412411700212607015106 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #define _XOPEN_SOURCE #include "fetion.h" #include "fx_sip.h" #include "fx_types.h" gint callid = 1; fetion_sip* fetion_sip_new0(User *user) { fetion_sip* sip = g_malloc0(sizeof(fetion_sip)); strcpy(sip->from , user->sId); sip->sequence = 2; sip->header = NULL; user->sip = sip; return sip; } fetion_sip* fetion_sip_clone(fetion_sip* sip) { fetion_sip* res = g_malloc0(sizeof(fetion_sip)); memcpy(res, sip, sizeof(fetion_sip)); sip->header = NULL; return res; } SipHeader* fetion_sip_header_new(const gchar *name , const gchar *value) { SipHeader* header = g_malloc0(sizeof(SipHeader)); strcpy(header->name , name); header->value = (gchar*)g_malloc0(strlen(value) + 1); strcpy(header->value , value); header->next = NULL; return header; } void fetion_sip_set_type(fetion_sip *sip, SipType type) { sip->type = type; sip->callid = callid; } void fetion_sip_set_callid(fetion_sip *sip, gint callid) { sip->callid = callid; } SipHeader *fetion_sip_authentication_header_new(const gchar *response) { gint len; gchar *res; gchar start[] = "Digest response=\""; gchar end[] = "\",algorithm=\"SHA1-sess-v4\""; SipHeader* header; len = strlen(start) + strlen(end) + strlen(response) + 1; res = (gchar*)g_malloc0(len); sprintf(res, "%s%s%s" , start , response , end); header = (SipHeader*)malloc(sizeof(SipHeader)); memset(header , 0 , sizeof(SipHeader)); strcpy(header->name , "A"); header->value = res; return header; } SipHeader* fetion_sip_ack_header_new(const gchar *code, const gchar *algorithm, const gchar *type, const gchar *guid) { gchar ack[512]; sprintf(ack , "Verify response=\"%s\",algorithm=\"%s\",type=\"%s\",chid=\"%s\"" , code , algorithm , type , guid); return fetion_sip_header_new("A" , ack); } SipHeader *fetion_sip_event_header_new(gint eventType) { gchar event[48]; memset(event, 0, sizeof(event)); switch(eventType) { case SIP_EVENT_PRESENCE : strcpy(event , "PresenceV4"); break; case SIP_EVENT_SETPRESENCE : strcpy(event , "SetPresenceV4"); break; case SIP_EVENT_CATMESSAGE : strcpy(event , "CatMsg"); break; case SIP_EVENT_SENDCATMESSAGE : strcpy(event , "SendCatSMS"); break; case SIP_EVENT_STARTCHAT : strcpy(event , "StartChat"); break; case SIP_EVENT_GETCONTACTINFO : strcpy(event , "GetContactInfoV4"); break; case SIP_EVENT_CONVERSATION : strcpy(event , "Conversation"); break; case SIP_EVENT_INVITEBUDDY : strcpy(event , "InviteBuddy"); break; case SIP_EVENT_CREATEBUDDYLIST : strcpy(event , "CreateBuddyList"); break; case SIP_EVENT_DELETEBUDDYLIST : strcpy(event , "DeleteBuddyList"); break; case SIP_EVENT_SETCONTACTINFO : strcpy(event , "SetContactInfoV4"); break; case SIP_EVENT_SETUSERINFO : strcpy(event , "SetUserInfoV4"); break; case SIP_EVENT_SETBUDDYLISTINFO : strcpy(event , "SetBuddyListInfo"); break; case SIP_EVENT_DELETEBUDDY : strcpy(event , "DeleteBuddyV4"); break; case SIP_EVENT_ADDBUDDY : strcpy(event , "AddBuddyV4"); break; case SIP_EVENT_KEEPALIVE : strcpy(event , "KeepAlive"); break; case SIP_EVENT_DIRECTSMS : strcpy(event , "DirectSMS"); break; case SIP_EVENT_HANDLECONTACTREQUEST : strcpy(event , "HandleContactRequestV4"); break; case SIP_EVENT_SENDDIRECTCATSMS : strcpy(event , "SendDirectCatSMS"); break; case SIP_EVENT_PGGETGROUPLIST: strcpy(event , "PGGetGroupList"); break; case SIP_EVENT_PGGETGROUPINFO: strcpy(event , "PGGetGroupInfo"); break; case SIP_EVENT_PGPRESENCE: strcpy(event , "PGPresence"); break; case SIP_EVENT_PGGETGROUPMEMBERS: strcpy(event , "PGGetGroupMembers"); break; case SIP_EVENT_PGSENDCATSMS: strcpy(event , "PGSendCatSMS"); break; default: break; } return fetion_sip_header_new("N" , event); } SipHeader* fetion_sip_credential_header_new(const gchar *credential) { gchar value[64]; memset(value , 0, sizeof(value)); sprintf(value , "TICKS auth=\"%s\"" , credential); return fetion_sip_header_new("A" , value); } void fetion_sip_add_header(fetion_sip *sip, SipHeader *header) { SipHeader* pos = sip->header; if(!pos) { sip->header = header; return; } while(pos) { if(!pos->next) { pos->next = header; break; } pos = pos->next; } } char* fetion_sip_to_string(fetion_sip* sip , const char* body) { char *res , *head , buf[1024] , type[128]; SipHeader *pos , *tmp; int len = 0; pos = sip->header; while(pos){ len += (strlen(pos->value) + strlen(pos->name) + 5); pos = pos->next; } len += (body == NULL ? 100 : strlen(body) + 100 ); res = (gchar*)g_malloc0(len + 1); memset(type, 0 , sizeof(type)); switch(sip->type){ case SIP_REGISTER : strcpy(type , "R"); break; case SIP_SUBSCRIPTION : strcpy(type , "SUB"); break; case SIP_SERVICE : strcpy(type , "S"); break; case SIP_MESSAGE : strcpy(type , "M"); break; case SIP_INCOMING : strcpy(type , "IN"); break; case SIP_OPTION : strcpy(type , "O"); break; case SIP_INVITATION : strcpy(type , "I"); break; case SIP_ACKNOWLEDGE : strcpy(type , "A"); break; default: break; }; if(*type == '\0'){ g_free(res); return NULL; } sprintf(buf, "%s fetion.com.cn SIP-C/4.0\r\n" "F: %s\r\n" "I: %d\r\n" "Q: 2 %s\r\n", type, sip->from, sip->callid, type); strcat(res , buf); pos = sip->header; while(pos){ len = strlen(pos->value) + strlen(pos->name) + 5; head = (gchar*)g_malloc0(len); sprintf(head, "%s: %s\r\n", pos->name, pos->value); strcat(res , head); tmp = pos; pos = pos->next; g_free(head); g_free(tmp->value); g_free(tmp); } if(body){ sprintf(buf, "L: %d\r\n\r\n", strlen(body)); strcat(res, buf); strcat(res, body); }else{ strcat(res, "\r\n"); } callid ++; sip->header = NULL; return res; } void fetion_sip_free(fetion_sip *sip) { g_free(sip); } gchar *fetion_sip_get_sid_by_sipuri(const gchar *sipuri) { gchar *res, *pos; gint n; pos = strstr(sipuri, ":") + 1; n = strlen(pos) - (strstr(pos , "@") == 0 ? 0 : strlen(strstr(pos , "@"))) ; res = (gchar*)g_malloc0(n + 1); strncpy(res, pos, n); return res; } gint fetion_sip_get_attr(const gchar *sip, const gchar *name, gchar *result) { gchar m_name[16]; gchar *pos; gint n; sprintf(m_name, "%s: ", name); if(!strstr(sip, m_name)) return -1; pos = strstr(sip , m_name) + strlen(m_name); if(!strstr(pos , "\r\n")) n = strlen(pos); else n = strlen(pos) - strlen(strstr(pos, "\r\n")); strncpy(result , pos , n); result[n] = '\0'; return 0; } gint fetion_sip_get_length(const gchar *sip) { gchar res[6]; gchar name[] = "L"; if(fetion_sip_get_attr(sip , name , res) == -1) return 0; return atoi(res); } gint fetion_sip_get_code(const gchar *sip) { gchar *pos , res[32]; gint n; memset(res, 0, sizeof(res)); if(strstr(sip , "4.0 ") == NULL) return 400; pos = strstr(sip , "4.0 ") + 4; if(strstr(pos , " ") == NULL) return 400; n = strlen(pos) - strlen(strstr(pos , " ")); strncpy(res , pos , n); return atoi(res); } gint fetion_sip_get_type(const gchar *sip) { gchar res[128]; gint n; if(!strstr(sip, " ")) return SIP_UNKNOWN; n = strlen(sip) - strlen(strstr(sip , " ")); memset(res, 0, sizeof(res)); strncpy(res , sip , n); if(strcmp(res , "I") == 0 ) return SIP_INVITATION; if(strcmp(res , "M") == 0 ) return SIP_MESSAGE; if(strcmp(res , "BN") == 0) return SIP_NOTIFICATION; if(strcmp(res , "SIP-C/4.0") == 0 || strcmp(res , "SIP-C/2.0") == 0) return SIP_SIPC_4_0; if(strcmp(res , "IN") == 0) return SIP_INCOMING; if(strcmp(res , "O") == 0 ) return SIP_OPTION; return SIP_UNKNOWN; } void fetion_sip_parse_notification(const gchar *sip, gint *type, gint *event, gchar **xml) { gchar type1[16] , *pos; xmlChar *event1; xmlDocPtr doc; xmlNodePtr node; fetion_sip_get_attr(sip , "N" , type1); if(strcmp(type1 , "PresenceV4") == 0) *type = NOTIFICATION_TYPE_PRESENCE; else if(strcmp(type1 , "Conversation") == 0) *type = NOTIFICATION_TYPE_CONVERSATION; else if(strcmp(type1 , "contact") == 0) *type = NOTIFICATION_TYPE_CONTACT; else if(strcmp(type1 , "registration") == 0) *type = NOTIFICATION_TYPE_REGISTRATION; else if(strcmp(type1 , "SyncUserInfoV4") == 0) *type = NOTIFICATION_TYPE_SYNCUSERINFO; else if(strcmp(type1 , "PGGroup") == 0) *type = NOTIFICATION_TYPE_PGGROUP; else *type = NOTIFICATION_TYPE_UNKNOWN; if(!(pos = strstr(sip , "\r\n\r\n"))) { *event = NOTIFICATION_TYPE_UNKNOWN; return; } *xml = (gchar*)g_malloc0(strlen(pos) + 1); strcpy(*xml , pos + 4); doc = xmlParseMemory(*xml , strlen(*xml)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "event"); event1 = xmlGetProp(node , BAD_CAST "type"); if(xmlStrcmp(event1, BAD_CAST "Support") == 0) { xmlFree(event1); node = node->next; event1 = xmlGetProp(node, BAD_CAST "type"); if(xmlStrcmp(event1, BAD_CAST "UserEntered") == 0) { *event = NOTIFICATION_EVENT_USERENTER; xmlFree(event1); xmlFreeDoc(doc); return; } *event = NOTIFICATION_EVENT_UNKNOWN; xmlFree(event1); xmlFreeDoc(doc); return; } if(xmlStrcmp(event1 , BAD_CAST "PresenceChanged") == 0) *event = NOTIFICATION_EVENT_PRESENCECHANGED; else if(xmlStrcmp(event1 , BAD_CAST "UserEntered") == 0) *event = NOTIFICATION_EVENT_USERENTER; else if(xmlStrcmp(event1 , BAD_CAST "UserLeft") == 0) *event = NOTIFICATION_EVENT_USERLEFT; else if(xmlStrcmp(event1 , BAD_CAST "deregistered") == 0) *event = NOTIFICATION_EVENT_DEREGISTRATION; else if(xmlStrcmp(event1 , BAD_CAST "SyncUserInfo") == 0) *event = NOTIFICATION_EVENT_SYNCUSERINFO; else if(xmlStrcmp(event1 , BAD_CAST "AddBuddyApplication") == 0) *event = NOTIFICATION_EVENT_ADDBUDDYAPPLICATION; else if(xmlStrcmp(event1 , BAD_CAST "PGGetGroupInfo") == 0) *event = NOTIFICATION_EVENT_PGGETGROUPINFO; else *event = NOTIFICATION_EVENT_UNKNOWN; xmlFree(event1); xmlFreeDoc(doc); } gint fetion_sip_parse_info(const gchar *sipmsg, InfoType *type) { gchar *pos = NULL; xmlDocPtr doc = NULL; xmlNodePtr node = NULL; xmlChar *res = NULL; *type = INFO_UNKNOWN; if(!(pos = strstr(sipmsg, "\r\n\r\n"))) return -1; doc = xmlParseMemory(pos + 4 , strlen(pos + 4)); node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; res = xmlNodeGetContent(node); if(xmlStrcmp(res , BAD_CAST "nudge") == 0) *type = INFO_NUDGE; xmlFree(res); xmlFreeDoc(doc); return 0; } void fetion_sip_parse_userleft(const gchar *sipmsg, gchar **sipuri) { gchar *pos = NULL; xmlDocPtr doc = NULL; xmlNodePtr node = NULL; xmlChar *res; printf("%s\n", sipmsg); pos = strstr(sipmsg , "\r\n\r\n") + 4; doc = xmlParseMemory(pos , strlen(pos)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "member"); res = xmlGetProp(node , BAD_CAST "uri"); *sipuri = (gchar*)malloc(xmlStrlen(res) + 1); memset(*sipuri, 0, xmlStrlen(res) + 1); strcpy(*sipuri , (gchar*)res); xmlFreeDoc(doc); } gint fetion_sip_parse_sipc(const gchar *sipmsg, gint *callid, gchar **xml) { gchar callid_str[16]; gchar *pos; gint n; gchar code[16]; pos = strstr(sipmsg , " ") + 1; n = strlen(pos) - strlen(strstr(pos , " ")); strncpy(code , pos , n); fetion_sip_get_attr(sipmsg , "I" , callid_str); *callid = atoi(callid_str); if(!(pos = strstr(sipmsg , "\r\n\r\n"))) { *xml = (gchar*)0; return -1;} *xml = (gchar*)malloc(strlen(pos + 4) + 1); memset(*xml , 0 , strlen(pos + 4) + 1); strcpy(*xml , pos); return atoi(code); } void fetion_sip_get_auth_attr(const gchar *auth, gchar **ipaddress, gint *port, gchar **credential) { gchar *pos = strstr(auth , "address=\"") + 9; gint n = strlen(pos) - strlen(strstr(pos , ":")); gchar port_str[6] = { 0 }; *credential = (gchar*)malloc(256); memset(*credential , 0 , 256); *ipaddress = (gchar*)malloc(256); memset(*ipaddress , 0 , 256); strncpy(*ipaddress , pos , n); pos = strstr(pos , ":") + 1; n = strlen(pos) - strlen(strstr(pos , ";")); strncpy(port_str , pos , n); *port = atoi(port_str); pos = strstr(pos , "credential=\"") + 12; strncpy(*credential , pos , strlen(pos) - 1); } pidgin-openfetion-0.3/fx_user.c0000644000175000017500000003446311700212607015277 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_sip.h" #include "fx_user.h" #include "fx_contact.h" #include struct unacked_list *unackedlist; static gchar *generate_set_state_body(StateType state); static gchar *generate_keep_alive_body(); static gchar *generate_modify_info(gint info_type, const gchar *value, const gchar *customConfig); User *fetion_user_new(const gchar *no , const gchar *password) { User *user = (User*)g_malloc0(sizeof(User)); struct sigaction sa; sa.sa_handler = SIG_IGN; sigaction(SIGPIPE, &sa, 0 ); memset(user, 0, sizeof(User)); if(strlen(no) == 11){ strcpy(user->mobileno , no); user->loginType = LOGIN_TYPE_MOBILENO; }else{ strcpy(user->sId , no); user->loginType = LOGIN_TYPE_FETIONNO; } strcpy(user->password , password); user->contactList = fetion_contact_new(); user->groupList = fetion_group_new(); user->sip = NULL; user->verification = NULL; user->customConfig = NULL; user->ssic = NULL; return user; } void fetion_user_set_userid(User *user, const gchar *userid1) { strcpy(user->userId, userid1); } void fetion_user_set_sid(User *user, const gchar *sId1) { strcpy(user->sId, sId1); } void fetion_user_set_mobileno(User *user, const gchar *mobileno1) { strcpy(user->mobileno, mobileno1); } void fetion_user_set_verification_code(User *user, const gchar *code) { g_return_if_fail(user != NULL); g_return_if_fail(code != NULL); user->verification->code = (gchar*)g_malloc0(strlen(code) + 1); strcpy(user->verification->code, code); } void fetion_user_free(User* user) { g_return_if_fail(user != NULL); g_free(user->ssic); g_free(user->customConfig); fetion_verification_free(user->verification); fetion_sip_free(user->sip); g_free(user); } static gint set_state_cb(fetion_account *UNUSED(ac), const gchar *sipmsg, struct transaction *UNUSED(trans)) { purple_debug_info("fetion", "%s", sipmsg); return 0; } gint fetion_user_set_state(fetion_account *ac, gint state) { SipHeader *eheader; fetion_sip *sip = ac->user->sip; gchar *body; gchar *res; struct transaction *trans; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_SETPRESENCE); fetion_sip_add_header(sip , eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, set_state_cb); transaction_add(ac, trans); body = generate_set_state_body(state); res = fetion_sip_to_string(sip , body); if(send(ac->sk, res, strlen(res), 0) == -1) return -1; ac->user->state = state; g_free(body); g_free(res); purple_debug_info("user","user state changed to %d" , state); return 0; } static gint modify_info_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gint code = fetion_sip_get_code(sipmsg); if(code != 200) { purple_notify_error(ac->gc, NULL, _("Failed"), _("Modify account information failed")); return -1; } return 0; } gint fetion_modify_info(fetion_account *ac, gint info_type, const gchar *value) { fetion_sip *sip = ac->user->sip; gchar *body; gchar *sipmsg; SipHeader *eheader; struct transaction *trans; fetion_sip_set_type(sip, SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_SETUSERINFO); fetion_sip_add_header(sip , eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, modify_info_cb); transaction_add(ac, trans); body = generate_modify_info(info_type, value, ac->user->customConfig); sipmsg = fetion_sip_to_string(sip, body); g_free(body); if(send(ac->sk, sipmsg, strlen(sipmsg), 0) == -1) { g_free(sipmsg); return -1; } g_free(sipmsg); return 0; } static gint sms_myself_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gint code = fetion_sip_get_code(sipmsg); if(code != 200 && code != 280) { purple_notify_error(ac->gc, NULL, _("Failed"), _("send sms to phone failed,unknown reason.")); return -1; } return 0; } gint fetion_sms_myself(fetion_account *ac, const gchar *msg) { SipHeader *toheader; SipHeader *eheader; gchar *sipmsg; fetion_sip *sip = ac->user->sip; struct transaction *trans; fetion_sip_set_type(sip, SIP_MESSAGE); toheader = fetion_sip_header_new("T", ac->user->sipuri); eheader = fetion_sip_event_header_new(SIP_EVENT_SENDCATMESSAGE); fetion_sip_add_header(sip, toheader); fetion_sip_add_header(sip, eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, sms_myself_cb); transaction_add(ac, trans); sipmsg = fetion_sip_to_string(sip, msg); purple_debug_info("fetion", "sent a message to myself"); if(send(ac->sk, sipmsg, strlen(sipmsg), 0) == -1) { g_free(sipmsg); return -1; } g_free(sipmsg); return 0; } static gint keep_alive_cb(fetion_account *ses, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gint code; if((code = fetion_sip_get_code(sipmsg)) == 200) { purple_debug_info("util", "success keep alive %d\n", ses->sk); } return 0; } gint fetion_user_keep_alive(fetion_account *ac) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res, *body; struct transaction *trans; fetion_sip_set_type(sip , SIP_REGISTER); eheader = fetion_sip_event_header_new(SIP_EVENT_KEEPALIVE); fetion_sip_add_header(sip , eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, keep_alive_cb); transaction_add(ac, trans); body = generate_keep_alive_body(); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(free); return -1; } g_free(res); return 0; } Group *fetion_group_new() { Group* list = (Group*)g_malloc0(sizeof(Group)); list->pre = list; list->next = list; return list; } void fetion_group_list_append(Group *head, Group *group) { head->next->pre = group; group->next = head->next; group->pre = head; head->next = group; } void fetion_group_list_prepend(Group *head, Group *group) { head->pre->next = group; group->next = head; group->pre = head->pre; head->pre = group; } void fetion_group_list_remove(Group *group) { group->next->pre = group->pre; group->pre->next = group->next; } void fetion_group_remove(Group *head, gint groupid) { Group *gl_cur; foreach_grouplist(head , gl_cur){ if(gl_cur->groupid == groupid){ gl_cur->pre->next = gl_cur->next; gl_cur->next->pre = gl_cur->pre; free(gl_cur); break; } } } Group *fetion_group_list_find_by_id(Group *head, gint id) { Group *gl_cur; foreach_grouplist(head , gl_cur) if(gl_cur->groupid == id) return gl_cur; return (Group*)0; } Group *fetion_group_list_find_by_name(Group *head, const gchar *name) { Group *gl_cur; foreach_grouplist(head , gl_cur) if(strcmp(gl_cur->groupname, name) == 0) return gl_cur; return (Group*)0; } Verification *fetion_verification_new() { Verification* ver = g_malloc0(sizeof(Verification)); ver->algorithm = NULL; ver->guid = NULL; ver->type = NULL; ver->text = NULL; ver->tips = NULL; ver->code = NULL; return ver; } void fetion_verification_free(Verification *ver) { g_return_if_fail(ver != NULL); g_free(ver->algorithm); g_free(ver->type); g_free(ver->text); g_free(ver->tips); g_free(ver->guid); g_free(ver->code); g_free(ver); } Contact *fetion_user_parse_presence_body(const gchar *body, User *user) { xmlDocPtr doc; xmlNodePtr node , cnode; xmlChar* pos; Contact* contact; Contact* contactres; Contact* contactlist = user->contactList; Contact* currentContact; contactres = fetion_contact_new(); doc = xmlParseMemory(body , strlen(body)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "c"); while(node != NULL) { pos = xmlGetProp(node , BAD_CAST "id"); currentContact = fetion_contact_list_find_by_userid(contactlist , (char*)pos); if(currentContact == NULL) { /*not a valid information*/ /*debug_error("User %s is not a valid user" , (char*)pos);*/ node = node->next; continue; } cnode = node->xmlChildrenNode; if(xmlHasProp(cnode , BAD_CAST "sid")) { pos = xmlGetProp(cnode , BAD_CAST "sid"); strcpy(currentContact->sId , (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "m")) { pos = xmlGetProp(cnode , BAD_CAST "m"); strcpy(currentContact->mobileno , (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "l")) { pos = xmlGetProp(cnode , BAD_CAST "l"); currentContact->scoreLevel = atoi((char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "n")) { pos = xmlGetProp(cnode , BAD_CAST "n"); strcpy(currentContact->nickname , (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "i")) { pos = xmlGetProp(cnode , BAD_CAST "i"); strcpy(currentContact->impression , (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "p")) { pos = xmlGetProp(cnode , BAD_CAST "p"); if(strcmp(currentContact->portraitCrc, (char*)pos) == 0 || strcmp((char*)pos, "0") == 0) currentContact->imageChanged = 0; else currentContact->imageChanged = 1; strcpy(currentContact->portraitCrc , (char*)pos); xmlFree(pos); } else { currentContact->imageChanged = 0; } if(xmlHasProp(cnode , BAD_CAST "c")) { pos = xmlGetProp(cnode , BAD_CAST "c"); strcpy(currentContact->carrier , (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "cs")) { pos = xmlGetProp(cnode , BAD_CAST "cs"); currentContact->carrierStatus = atoi((char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "s")) { pos = xmlGetProp(cnode , BAD_CAST "s"); currentContact->serviceStatus = atoi((char*)pos); xmlFree(pos); } #if 0 if(xmlHasProp(cnode , BAD_CAST "sms")){ pos = xmlGetProp(cnode , BAD_CAST "sms"); xmlFree(pos); } #endif cnode = xml_goto_node(node , "pr"); if(xmlHasProp(cnode , BAD_CAST "dt")) { pos = xmlGetProp(cnode , BAD_CAST "dt"); strcpy(currentContact->devicetype , *((char*)pos) == '\0' ? "PC" : (char*)pos); xmlFree(pos); } if(xmlHasProp(cnode , BAD_CAST "b")) { pos = xmlGetProp(cnode , BAD_CAST "b"); currentContact->state = atoi((char*)pos); xmlFree(pos); } contact = fetion_contact_new(); memset(contact , 0 , sizeof(contact)); memcpy(contact , currentContact , sizeof(Contact)); fetion_contact_list_append(contactres , contact); node = node->next; } xmlFreeDoc(doc); return contactres; } static gchar *generate_set_state_body(StateType state) { gchar s[16]; gchar data[] = ""; xmlChar* res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(data , strlen(data)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "presence" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "basic" , NULL); snprintf(s, sizeof(s) - 1 , "%d" , state); xmlNewProp(node , BAD_CAST "value" , BAD_CAST s); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static gchar *generate_keep_alive_body() { gchar args[] = ""; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "credentials" , NULL); xmlNewProp(node , BAD_CAST "domains" , BAD_CAST "fetion.com.cn"); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static gchar *generate_modify_info(gint info_type, const gchar *value, const gchar *customConfig) { gchar args[] = ""; xmlChar *res; xmlDocPtr doc; xmlNodePtr node , cnode; doc = xmlParseMemory(args, strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node, NULL, BAD_CAST "userinfo", NULL); cnode = xmlNewChild(node, NULL, BAD_CAST "personal", NULL); switch(info_type) { case MODIFY_INFO_NICKNAME : xmlNewProp(cnode, BAD_CAST "nickname", BAD_CAST value); break; case MODIFY_INFO_IMPRESA : xmlNewProp(cnode, BAD_CAST "impresa", BAD_CAST value); break; default: break; } xmlNewProp(cnode, BAD_CAST "version", BAD_CAST "0"); cnode = xmlNewChild(node, NULL, BAD_CAST "custom-config", BAD_CAST customConfig); xmlNewProp(cnode, BAD_CAST "type", BAD_CAST "PC"); xmlNewProp(cnode, BAD_CAST "version", BAD_CAST "0"); xmlDocDumpMemory(doc, &res, NULL); xmlFreeDoc(doc); return xml_convert(res); } pidgin-openfetion-0.3/Message.sh0000755000175000017500000000064611700212607015377 0ustar aronaron#!/bin/sh # This script use find and xgettext command to generate the translation template file for openfetion find . -type f -iname "*.c" | xgettext --files-from=- --output=po/pidgin-ofetion.pot --from-code=utf-8 --sort-by-file --language=C --keyword=_ --keyword=gettext --keyword=i18n --keyword=N_ --add-comments --add-location --package-name=OpenFetion --msgid-bugs-address=http://code.google.com/p/ofetion/issues/list pidgin-openfetion-0.3/fx_blist.c0000644000175000017500000001342511700212607015431 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_sip.h" #include "fx_user.h" #include "fx_types.h" #include "fx_blist.h" static gchar *generate_create_buddylist_body(const gchar *name); static gchar *generate_delete_buddylist_body(gint id); static gchar *generate_edit_buddylist_body(gint id , const gchar *name); gint fetion_buddylist_create(User *user, const gchar *name) { fetion_sip* sip = user->sip; SipHeader* eheader; gchar *res , *body; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_CREATEBUDDYLIST); fetion_sip_add_header(sip , eheader); body = generate_create_buddylist_body(name); res = fetion_sip_to_string(sip , body); g_free(body); g_free(res); return 0; } gint fetion_buddylist_delete(fetion_account *ac, gint id) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res , *body; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_DELETEBUDDYLIST); fetion_sip_add_header(sip , eheader); body = generate_delete_buddylist_body(id); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } gint fetion_buddylist_edit(fetion_account *ac, gint id, const gchar *name) { fetion_sip *sip = ac->user->sip; SipHeader *eheader; gchar *res, *body; fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_SETBUDDYLISTINFO); fetion_sip_add_header(sip , eheader); body = generate_edit_buddylist_body(id , name); res = fetion_sip_to_string(sip , body); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return -1; } g_free(res); return 0; } static gchar *generate_create_buddylist_body(const gchar *name) { gchar args[] = ""; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-lists" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-list" , NULL); xmlNewProp(node , BAD_CAST "name" , BAD_CAST name); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static gchar *generate_edit_buddylist_body(gint id , const gchar *name) { gchar args[] = ""; gchar ids[128]; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-lists" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-list" , NULL); xmlNewProp(node , BAD_CAST "name" , BAD_CAST name); memset(ids, 0, sizeof(ids)); snprintf(ids, sizeof(ids) - 1 , "%d" , id); xmlNewProp(node , BAD_CAST "id" , BAD_CAST ids); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static gchar *generate_delete_buddylist_body(gint id) { gchar args[] = ""; gchar ida[4]; memset(ida, 0, sizeof(ida)); sprintf(ida , "%d" , id); xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-lists" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy-list" , NULL); xmlNewProp(node , BAD_CAST "id" , BAD_CAST ida); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } pidgin-openfetion-0.3/fx_util.c0000644000175000017500000001455211700212607015273 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_types.h" extern GSList *sessions; xmlNodePtr xml_goto_node(xmlNodePtr node, const gchar *name) { xmlNodePtr pos = node; xmlNodePtr tmp = NULL; while(pos != NULL) { if(strcmp(name , (gchar*)pos->name) == 0) return pos; tmp = pos->xmlChildrenNode; if(tmp != NULL && xmlStrcmp(tmp->name , BAD_CAST "text") != 0 &&tmp->type == XML_ELEMENT_NODE && (tmp = xml_goto_node(tmp , name)) != NULL ) return tmp; pos = pos->next; }; return NULL; } gchar* xml_convert(xmlChar *in) { gchar *res, *pos ; pos = strstr((gchar*)in, "?>") + 2; res = (gchar*)g_malloc0(strlen(pos) + 1); memcpy(res, pos, strlen(pos)); xmlFree(in); return res; } const gchar *get_status_id(gint state) { switch(state) { case P_ONLINE: return "Online"; case P_MEETING: return "Meeting"; case P_RIGHTBACK: return "Right back"; case P_OUTFORLUNCH: return "Out for lunch"; case P_AWAY: return "Away"; case P_ONTHEPHONE: return "On the phone"; case P_BUSY: return "Busy"; case P_DONOTDISTURB:return "Don't disturb"; case P_HIDDEN: return "Offline"; case P_OFFLINE: return "Offline"; default: return "Online"; } return "Online"; } struct transaction *transaction_new() { struct transaction *trans; trans = g_malloc0(sizeof(struct transaction)); memset(trans, 0, sizeof(struct transaction)); return trans; } void transaction_free(struct transaction *trans) { g_free(trans); } void transaction_set_callid(struct transaction *trans, gint callid) { trans->callid = callid; } void transaction_set_userid(struct transaction *trans, const gchar *userId) { snprintf(trans->userId, sizeof(trans->userId) - 1, "%s", userId); } void transaction_set_msg(struct transaction *trans, const gchar *msg) { memset(trans->msg, 0, sizeof(trans->msg)); if(msg) snprintf(trans->msg, sizeof(trans->msg) - 1, "%s", msg); } void transaction_set_callback(struct transaction *trans, TransCallback callback) { trans->callback = callback; } void transaction_set_timeout(struct transaction *trans, GSourceFunc timeout, gpointer data) { trans->timer = purple_timeout_add_seconds(20, timeout, data); } void transaction_add(fetion_account *ses, struct transaction *trans) { ses->trans = g_slist_append(ses->trans, trans); } void transaction_wait(fetion_account *ses, struct transaction *trans) { ses->trans_wait = g_slist_append(ses->trans_wait, trans); } void transaction_wakeup(fetion_account *ses, struct transaction *trans) { ses->trans_wait = g_slist_remove(ses->trans_wait, trans); } void transaction_remove(fetion_account *ses, struct transaction *trans) { ses->trans = g_slist_remove(ses->trans, trans); } fetion_account *session_new(PurpleAccount *account) { fetion_account *ses = g_malloc0(sizeof(fetion_account)); ses->account = account; ses->trans = (GSList*)0; ses->trans_wait = (GSList*)0; ses->data = (gchar*)0; return ses; } fetion_account *session_clone(fetion_account *ac) { fetion_account *ses = g_malloc0(sizeof(fetion_account)); ses->account = ac->account; ses->trans = (GSList*)0; ses->trans_wait = (GSList*)0; ses->data = (gchar*)0; ses->user = ac->user; ses->gc = ac->gc; ses->chan_ready = 0; return ses; } void session_set_userid(fetion_account *ses, const gchar *userId) { snprintf(ses->userId, sizeof(ses->userId) - 1, "%s", userId); } void session_add(fetion_account *ac) { sessions = g_slist_append(sessions, ac); } fetion_account *session_find(const gchar *key) { fetion_account *ac; GSList *list = sessions; while(list) { ac = (fetion_account*)(list->data); if(strcmp(ac->userId, key) == 0) return ac; list = list->next; } return (fetion_account*)0; } fetion_account *session_find_by_sk(gint sk) { fetion_account *ses; GSList *list = sessions; while(list) { ses = (fetion_account *)(list->data); if(ses->sk == sk) return ses; list = list->next; } return (fetion_account*)0; } void session_remove(fetion_account *ses) { sessions = g_slist_remove(sessions, ses); } void session_destroy(fetion_account *ses) { g_return_if_fail(ses != NULL); purple_input_remove(ses->conn); g_free(ses->data); close(ses->sk); g_free(ses); } pidgin-openfetion-0.3/po/0000755000175000017500000000000011700221754014067 5ustar aronaronpidgin-openfetion-0.3/po/pidgin-ofetion.pot0000644000175000017500000001475711700221754017544 0ustar aronaron# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: OpenFetion\n" "Report-Msgid-Bugs-To: http://code.google.com/p/ofetion/issues/list\n" "POT-Creation-Date: 2011-01-12 17:45+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: fx_buddy.c:126 msgid "[Unverified]" msgstr "" #: fx_buddy.c:129 fx_buddy.c:136 fx_buddy.c:139 msgid "[Has shut fetion service]" msgstr "" #: fx_buddy.c:133 msgid "[Online with SMS]" msgstr "" #: fx_buddy.c:145 msgid "[Out of service]" msgstr "" #: fx_buddy.c:155 msgid "Unexposed" msgstr "" #: fx_buddy.c:173 #, c-format msgid "'%s' has accepted your add-buddy request" msgstr "" #: fx_buddy.c:174 msgid "Success" msgstr "" #: fx_buddy.c:185 #, c-format msgid "'%s' has declined your add-buddy request" msgstr "" #: fx_buddy.c:186 fx_user.c:140 fx_user.c:175 openfetion.c:140 msgid "Failed" msgstr "" #: fx_buddy.c:237 msgid "Nickname" msgstr "" #: fx_buddy.c:238 msgid "Gender" msgstr "" #: fx_buddy.c:239 msgid "Female" msgstr "" #: fx_buddy.c:239 msgid "Male" msgstr "" #: fx_buddy.c:239 msgid "Secrecy" msgstr "" #: fx_buddy.c:240 msgid "Mobile" msgstr "" #: fx_buddy.c:243 msgid "Fetion" msgstr "" #: fx_buddy.c:244 openfetion.c:103 msgid "Signature" msgstr "" #: fx_buddy.c:247 msgid "Province" msgstr "" #: fx_buddy.c:248 msgid "City" msgstr "" #: fx_buddy.c:249 msgid "Service Provider" msgstr "" #: fx_buddy.c:272 fx_buddy.c:283 fx_buddy.c:289 fx_buddy.c:302 fx_buddy.c:305 msgid "Add buddy error.Unknown reason" msgstr "" #: fx_buddy.c:272 fx_buddy.c:278 fx_buddy.c:283 fx_buddy.c:289 fx_buddy.c:302 #: fx_buddy.c:305 fx_buddy.c:343 fx_buddy.c:359 fx_buddy.c:378 msgid "Error" msgstr "" #: fx_buddy.c:276 #, c-format msgid "Add buddy error.%s." msgstr "" #: fx_buddy.c:343 msgid "Not a valid group" msgstr "" #: fx_buddy.c:359 msgid "Network Error!" msgstr "" #: fx_buddy.c:377 #, c-format msgid "'%s' is not a valid group\n" msgstr "" #: fx_chat.c:175 msgid "Message sent failed:" msgstr "" #: fx_chat.c:194 msgid "Message sent timeout:" msgstr "" #: fx_login.c:159 fx_login.c:510 fx_login.c:561 fx_login.c:660 msgid "Login Failed." msgstr "" #: fx_login.c:173 msgid "Confirmation code" msgstr "" #: fx_login.c:176 msgid "Please input the code" msgstr "" #: fx_login.c:181 openfetion.c:708 openfetion.c:722 msgid "OK" msgstr "" #: fx_login.c:182 openfetion.c:318 openfetion.c:709 openfetion.c:723 msgid "Cancel" msgstr "" #: fx_login.c:392 fx_login.c:540 msgid "Incorrect password." msgstr "" #: fx_login.c:633 msgid "Download configuration file error." msgstr "" #: fx_user.c:140 msgid "Modify account information failed" msgstr "" #: fx_user.c:175 msgid "send sms to phone failed,unknown reason." msgstr "" #: openfetion.c:100 msgid "FetionNo" msgstr "" #: openfetion.c:101 msgid "MobileNo" msgstr "" #: openfetion.c:102 msgid "Alias" msgstr "" #: openfetion.c:139 #, c-format msgid "'%s' is not a valid group of this account." msgstr "" #: openfetion.c:162 msgid "Nudge" msgstr "" #: openfetion.c:163 #, c-format msgid "%s has nudged you!" msgstr "" #: openfetion.c:164 #, c-format msgid "Nudging %s..." msgstr "" #: openfetion.c:220 msgid "Failed to send message: Unverified Buddy!" msgstr "" #: openfetion.c:239 msgid "Fail to send message: Buddy has cancled Fetion service!" msgstr "" #: openfetion.c:315 msgid "Send a mobile message." msgstr "" #: openfetion.c:317 openfetion.c:736 msgid "Send" msgstr "" #: openfetion.c:344 msgid "Send to Mobile" msgstr "" #: openfetion.c:437 msgid "Your account has logined elsewhere. You are forced to quit." msgstr "" #: openfetion.c:566 msgid "Available" msgstr "" #: openfetion.c:574 msgid "Away" msgstr "" #: openfetion.c:582 msgid "Invisible" msgstr "" #: openfetion.c:590 msgid "Offline" msgstr "" #: openfetion.c:598 msgid "Busy" msgstr "" #: openfetion.c:618 msgid "

Author:
\n" msgstr "" #: openfetion.c:622 msgid "" "pidgin-openfetion is a Fetion protocol plugin for libpurple,
implemented by the openfetion team.
It supports most features of China " "Mobile's Fetion V4 protocol.
It's lightweight and efficient.

Project homepage: http://code.google.com/p/ofetion/" msgstr "" #: openfetion.c:628 msgid "

Translators

:
\n" msgstr "" #. Translators: HTML format, So do add a
to every translator, #. * such as Zhang
\nWang
#. #: openfetion.c:632 msgid "translator-credits" msgstr "" #: openfetion.c:635 #, c-format msgid "About OpenFetion %s" msgstr "" #: openfetion.c:656 #, c-format msgid "Last Login Time: %s
\n" msgstr "" #: openfetion.c:657 #, c-format msgid "Last Login IP: %s
\n" msgstr "" #: openfetion.c:658 #, c-format msgid "Public IP: %s
\n" msgstr "" #: openfetion.c:660 #, c-format msgid "Fetion Number: %s
\n" msgstr "" #: openfetion.c:661 #, c-format msgid "Mobile Number: %s
\n" msgstr "" #: openfetion.c:662 #, c-format msgid "Login Time: %s
\n" msgstr "" #: openfetion.c:663 #, c-format msgid "NickName: %s
\n" msgstr "" #: openfetion.c:664 #, c-format msgid "Signature: %s
\n" msgstr "" #: openfetion.c:665 #, c-format msgid "Province: %s
\n" msgstr "" #: openfetion.c:666 #, c-format msgid "City: %s
\n" msgstr "" #: openfetion.c:668 #, c-format msgid "SMS Sent Today: %d
\n" msgstr "" #: openfetion.c:669 #, c-format msgid "SMS Sent This Month: %d
\n" msgstr "" #: openfetion.c:670 #, c-format msgid "SMS Limit Today: %d
\n" msgstr "" #: openfetion.c:671 #, c-format msgid "SMS Limit This Month: %d
\n" msgstr "" #: openfetion.c:676 msgid "Account Information" msgstr "" #: openfetion.c:706 openfetion.c:757 msgid "Change Nickname" msgstr "" #: openfetion.c:720 openfetion.c:760 msgid "Change Signature" msgstr "" #: openfetion.c:734 msgid "Send SMS to Your Phone" msgstr "" #: openfetion.c:737 msgid "Close" msgstr "" #: openfetion.c:749 msgid "Send SMS To Phone" msgstr "" #: openfetion.c:754 msgid "View Information" msgstr "" #: openfetion.c:765 msgid "About OpenFetion" msgstr "" #: openfetion.c:858 msgid "Fetion Plugin" msgstr "" #: openfetion.c:859 msgid "libpurple plugin implementing Fetion Protocol version 4" msgstr "" pidgin-openfetion-0.3/po/zh_CN.po0000644000175000017500000002146311700212607015433 0ustar aronaron# Chinese translations for OpenFetion package # OpenFetion 软件包的简体中文翻译. # Copyright (C) 2011 THE OpenFetion'S COPYRIGHT HOLDER # This file is distributed under the same license as the OpenFetion package. # Aron Xu , 2011. # YunQiang Su , 2011. # msgid "" msgstr "" "Project-Id-Version: OpenFetion\n" "Report-Msgid-Bugs-To: http://code.google.com/p/ofetion/issues/list\n" "POT-Creation-Date: 2011-01-12 17:45+0800\n" "PO-Revision-Date: 2011-01-12 17:45+0800\n" "Last-Translator: YunQiang Su \n" "Language-Team: Chinese (simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" #: openfetion.c:163 #, c-format msgid "%s has nudged you!" msgstr "%s 给您发送了一个窗口抖动!" #: fx_buddy.c:173 #, c-format msgid "'%s' has accepted your add-buddy request" msgstr "%s 接受了您的添加好友请求" #: fx_buddy.c:185 #, c-format msgid "'%s' has declined your add-buddy request" msgstr "%s 拒绝了您的添加好友请求" #: fx_buddy.c:377 #, c-format msgid "'%s' is not a valid group\n" msgstr "%s 不是一个合法的分组\n" #: openfetion.c:139 #, c-format msgid "'%s' is not a valid group of this account." msgstr "%s 不是该帐号合法的分组。" #: openfetion.c:666 #, c-format msgid "City: %s
\n" msgstr "城市:%s
\n" #: openfetion.c:660 #, c-format msgid "Fetion Number: %s
\n" msgstr "飞信号:%s
\n" #: openfetion.c:657 #, c-format msgid "Last Login IP: %s
\n" msgstr "上次登录 IP:%s
\n" #: openfetion.c:656 #, c-format msgid "Last Login Time: %s
\n" msgstr "上次登录时间:%s
\n" #: openfetion.c:662 #, c-format msgid "Login Time: %s
\n" msgstr "登录时间:%s
\n" #: openfetion.c:661 #, c-format msgid "Mobile Number: %s
\n" msgstr "手机号:%s
\n" #: openfetion.c:663 #, c-format msgid "NickName: %s
\n" msgstr "昵称:%s
\n" #: openfetion.c:665 #, c-format msgid "Province: %s
\n" msgstr "省份:%s
\n" #: openfetion.c:658 #, c-format msgid "Public IP: %s
\n" msgstr "登录 IP:%s
\n" #: openfetion.c:671 #, c-format msgid "SMS Limit This Month: %d
\n" msgstr "本月发送短信限制:%d
\n" #: openfetion.c:670 #, c-format msgid "SMS Limit Today: %d
\n" msgstr "今天发送短信限制:%d
\n" #: openfetion.c:669 #, c-format msgid "SMS Sent This Month: %d
\n" msgstr "本月发送短信数量:%d
\n" #: openfetion.c:668 #, c-format msgid "SMS Sent Today: %d
\n" msgstr "今天发送短信数量:%d
\n" #: openfetion.c:664 #, c-format msgid "Signature: %s
\n" msgstr "签名:%s
\n" #: openfetion.c:618 msgid "

Author:
\n" msgstr "

作者
\n" #: openfetion.c:628 msgid "

Translators

:
\n" msgstr "

翻译者


\n" #: openfetion.c:765 msgid "About OpenFetion" msgstr "关于 OpenFetion" #: openfetion.c:635 #, c-format msgid "About OpenFetion %s" msgstr "关于 OpenFetion %s" #: openfetion.c:676 msgid "Account Information" msgstr "帐号信息" #: fx_buddy.c:276 #, c-format msgid "Add buddy error.%s." msgstr "添加好友失败.%s." #: fx_buddy.c:272 fx_buddy.c:283 fx_buddy.c:289 fx_buddy.c:302 fx_buddy.c:305 msgid "Add buddy error.Unknown reason" msgstr "添加好友失败,原因未知" #: openfetion.c:102 msgid "Alias" msgstr "别名" #: openfetion.c:566 msgid "Available" msgstr "在线" #: openfetion.c:574 msgid "Away" msgstr "离开" #: openfetion.c:598 msgid "Busy" msgstr "忙碌" #: fx_login.c:182 openfetion.c:318 openfetion.c:709 openfetion.c:723 msgid "Cancel" msgstr "取消" #: openfetion.c:706 openfetion.c:757 msgid "Change Nickname" msgstr "更改昵称" #: openfetion.c:720 openfetion.c:760 msgid "Change Signature" msgstr "更改签名" #: fx_buddy.c:248 msgid "City" msgstr "城市" #: openfetion.c:737 msgid "Close" msgstr "关闭" #: fx_login.c:173 msgid "Confirmation code" msgstr "验证码" #: fx_login.c:633 msgid "Download configuration file error." msgstr "下载配置文件出错。" #: fx_buddy.c:272 fx_buddy.c:278 fx_buddy.c:283 fx_buddy.c:289 fx_buddy.c:302 #: fx_buddy.c:305 fx_buddy.c:343 fx_buddy.c:359 fx_buddy.c:378 msgid "Error" msgstr "错误" #: openfetion.c:239 msgid "Fail to send message: Buddy has cancled Fetion service!" msgstr "发送失败:好友已关闭飞信服务!" #: fx_buddy.c:186 fx_user.c:140 fx_user.c:175 openfetion.c:140 msgid "Failed" msgstr "失败" #: openfetion.c:220 msgid "Failed to send message: Unverified Buddy!" msgstr "发送失败:好友尚未通过验证!" #: fx_buddy.c:239 msgid "Female" msgstr "女" #: fx_buddy.c:243 msgid "Fetion" msgstr "飞信号" #: openfetion.c:858 msgid "Fetion Plugin" msgstr "飞信插件" #: openfetion.c:100 msgid "FetionNo" msgstr "飞信号" #: fx_buddy.c:238 msgid "Gender" msgstr "姓别" #: fx_login.c:392 fx_login.c:540 msgid "Incorrect password." msgstr "密码错误" #: openfetion.c:582 msgid "Invisible" msgstr "隐身" #: fx_login.c:159 fx_login.c:510 fx_login.c:561 fx_login.c:660 msgid "Login Failed." msgstr "登录失败" #: fx_buddy.c:239 msgid "Male" msgstr "男" #: fx_chat.c:175 msgid "Message sent failed:" msgstr "消息发送失败:" #: fx_chat.c:194 msgid "Message sent timeout:" msgstr "消息发送超时:" #: fx_buddy.c:240 msgid "Mobile" msgstr "手机号" #: openfetion.c:101 msgid "MobileNo" msgstr "手机号" #: fx_user.c:140 msgid "Modify account information failed" msgstr "修改帐号信息失败" #: fx_buddy.c:359 msgid "Network Error!" msgstr "网络错误" #: fx_buddy.c:237 msgid "Nickname" msgstr "昵称" #: fx_buddy.c:343 msgid "Not a valid group" msgstr "无效用户分组" #: openfetion.c:162 msgid "Nudge" msgstr "窗口抖动" #: openfetion.c:164 #, c-format msgid "Nudging %s..." msgstr "给 %s 发送了一个窗口抖动。" #: fx_login.c:181 openfetion.c:708 openfetion.c:722 msgid "OK" msgstr "确定" #: openfetion.c:590 msgid "Offline" msgstr "离线" #: fx_login.c:176 msgid "Please input the code" msgstr "请输入验证码" #: fx_buddy.c:247 msgid "Province" msgstr "省份" #: fx_buddy.c:239 msgid "Secrecy" msgstr "保密" #: openfetion.c:317 openfetion.c:736 msgid "Send" msgstr "发送" #: openfetion.c:749 msgid "Send SMS To Phone" msgstr "给自己发短信" #: openfetion.c:734 msgid "Send SMS to Your Phone" msgstr "给自己发短信" #: openfetion.c:315 msgid "Send a mobile message." msgstr "发送到对方手机。" #: openfetion.c:344 msgid "Send to Mobile" msgstr "发送到手机" #: fx_buddy.c:249 msgid "Service Provider" msgstr "服务提供商" #: fx_buddy.c:244 openfetion.c:103 msgid "Signature" msgstr "签名" #: fx_buddy.c:174 msgid "Success" msgstr "成功" #: fx_buddy.c:155 msgid "Unexposed" msgstr "未公开" #: openfetion.c:754 msgid "View Information" msgstr "查看信息" #: openfetion.c:437 msgid "Your account has logined elsewhere. You are forced to quit." msgstr "您的飞信号已在别处登录,您被强制退出。" #: fx_buddy.c:129 fx_buddy.c:136 fx_buddy.c:139 msgid "[Has shut fetion service]" msgstr "[已关闭飞信服务]" #: fx_buddy.c:133 msgid "[Online with SMS]" msgstr "[短信在线]" #: fx_buddy.c:145 msgid "[Out of service]" msgstr "[停机]" #: fx_buddy.c:126 msgid "[Unverified]" msgstr "[未验证]" #: openfetion.c:859 msgid "libpurple plugin implementing Fetion Protocol version 4" msgstr "libpurple 的飞信协议(第四版)插件" #: openfetion.c:622 msgid "" "pidgin-openfetion is a Fetion protocol plugin for libpurple,
implemented by the openfetion team.
It supports most features of China " "Mobile's Fetion V4 protocol.
It's lightweight and efficient.

Project homepage: http://code.google.com/p/ofetion/" msgstr "" "pidgin-openfetion 是 OpenFetion 团队开发的 libpurple 的飞信协议插件。

程序简洁轻快,支持飞信的大多数功能。

项目主页:http://code." "google.com/p/ofetion/" #: fx_user.c:175 msgid "send sms to phone failed,unknown reason." msgstr "发送短信到手机失败,原因未知。" #. Translators: HTML format, So do add a
to every translator, #. * such as Zhang
\nWang
#. #: openfetion.c:632 msgid "translator-credits" msgstr "" "Aron Xu , 2011
\n" "levin , 2011
\n" "YunQiang Su , 2011" #~ msgid "Click here getting new verification code" #~ msgstr "点击重新获取验证码" #~ msgid "Please entry your verification code" #~ msgstr "请输入验证码" #~ msgid "Write confirm code Failed." #~ msgstr "获取验证码失败" pidgin-openfetion-0.3/fx_buddy.c0000644000175000017500000006712211700212607015426 0ustar aronaron/*************************************************************************** * Copyright (C) 2010 by lwp * * levin108@gmail.com * * * * 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., * * 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. * * * * OpenSSL linking exception * * -------------------------- * * If you modify this Program, or any covered work, by linking or * * combining it with the OpenSSL project's "OpenSSL" library (or a * * modified version of that library), containing parts covered by * * the terms of OpenSSL/SSLeay license, the licensors of this * * Program grant you additional permission to convey the resulting * * work. Corresponding Source for a non-source form of such a * * combination shall include the source code for the parts of the * * OpenSSL library used as well as that of the covered work. * ***************************************************************************/ #include "fetion.h" #include "fx_user.h" #include "fx_sip.h" #include "fx_buddy.h" #include "fx_contact.h" static gchar* http_connection_encode_url(const gchar* url); static gint http_connection_get_body_length(const gchar *http); static void update_portrait(fetion_account *ac, Contact *contact_cur); static gint download_portrait_cb(gpointer data, gint source, const gchar *error_message); static gint get_portrait_cb(gpointer data, gint source, const gchar *error_message); static gchar *generate_add_buddy_body(const gchar *no, gint notype, gint buddylist, const gchar *localname , const gchar *desc); static Contact *parse_add_buddy_response(const gchar *sipmsg, gint *status_code, gchar **errMsg); static Contact *parse_syncinfo(Contact *clist, const gchar *sipmsg); static Contact *parse_addbuddyapplication(const gchar *sipmsg); static gchar *generate_handle_contact_request_body(const gchar *sipuri , const gchar *userid, const gchar *localname , gint buddylist, gint result ); static Group *fx_group_find_by_id(Group *group_list, gint id) { Group *group_cur; foreach_contactlist(group_list, group_cur) if(group_cur->groupid == id) return group_cur; return (Group*)0; } void fx_blist_init(fetion_account *ac) { Contact *contact_cur; Group *group_cur; User *user = ac->user; PurpleAccount *account = ac->account; PurpleGroup *group = NULL; PurpleBuddy *buddy = NULL; foreach_contactlist(user->groupList, group_cur) { if(!(group = purple_find_group(group_cur->groupname))) group = purple_group_new(group_cur->groupname); } foreach_contactlist(user->contactList, contact_cur) { if(!(buddy = purple_find_buddy(account, contact_cur->userId))) buddy = purple_buddy_new(account, contact_cur->userId, contact_cur->nickname); group_cur = fx_group_find_by_id(user->groupList, contact_cur->groupid); if(!(group = purple_find_group(group_cur->groupname))) group = purple_group_new(group_cur->groupname); purple_blist_add_buddy(buddy, NULL, group, NULL); if(contact_cur->localname[0] != '\0') purple_blist_alias_buddy(buddy, contact_cur->localname); purple_blist_alias_buddy(buddy, contact_cur->localname); purple_prpl_got_user_status(account, contact_cur->userId, "Offline", NULL); } } void process_presence(fetion_account *ac, const gchar *xml) { Contact *contact_list; Contact *cnt; User *user = ac->user; Group *group_cur; const gchar *state = NULL; const gchar *name; PurpleBuddy *buddy; PurpleGroup *group; PurpleAccount *account = ac->account; gchar alias[BUFLEN], status[BUFLEN]; gchar *sid; contact_list = fetion_user_parse_presence_body(xml , user); foreach_contactlist(contact_list, cnt) { if(!(buddy = purple_find_buddy(account, cnt->userId))) { buddy = purple_buddy_new(account, cnt->userId, cnt->localname); group_cur = fx_group_find_by_id(user->groupList, cnt->groupid); group = purple_find_group(group_cur->groupname); purple_blist_add_buddy(buddy, NULL, group, NULL); } if(cnt->localname[0] == '\0') purple_blist_alias_buddy(buddy, cnt->nickname); state = get_status_id(cnt->state); snprintf(alias, sizeof(alias) - 1, "%s", cnt->localname[0] == '\0' ? cnt->nickname : cnt->localname); purple_blist_server_alias_buddy(buddy, alias); name = cnt->localname[0] == '\0'? cnt->nickname : cnt->localname; *status = '\0'; if(cnt->relationStatus == RELATION_STATUS_UNAUTHENTICATED){ snprintf(status, sizeof(status) - 1, "%s", _("[Unverified]")); }else if(cnt->serviceStatus == BASIC_SERVICE_ABNORMAL){ if(cnt->carrierStatus == CARRIER_STATUS_CLOSED){ snprintf(status, sizeof(status) - 1, "%s", _("[Has shut fetion service]")); }else{ if(cnt->carrier[0] != '\0'){ snprintf(status , sizeof(status) - 1, "%s", _("[Online with SMS]")); if(cnt->mobileno[0] == '\0') snprintf(status , sizeof(status) - 1, "%s", _("[Has shut fetion service]")); }else snprintf(status, sizeof(status) - 1, "%s", _("[Has shut fetion service]")); } }else if(cnt->carrierStatus == CARRIER_STATUS_DOWN){ if(cnt->carrier[0] != '\0'){ snprintf(status, sizeof(status) - 1, "%s", _("[Out of service]")); } } sid = fetion_sip_get_sid_by_sipuri(cnt->sipuri); snprintf(alias, sizeof(alias) - 1, "%s%s", name, status); purple_blist_alias_buddy(buddy, *alias == '\0' ? sid : alias); purple_prpl_got_user_status(account, cnt->userId, state, "impresa", cnt->impression, "fetionno", sid, "mobileno", cnt->mobileno[0] == '\0' ? _("Unexposed") : cnt->mobileno, NULL); g_free(sid); sid = (gchar*)0; update_portrait(ac, cnt); } } void process_sync_info(fetion_account *ac, const gchar *sipmsg) { Contact *cnt; gchar buf[BUFLEN]; PurpleBuddy *buddy, *new_buddy; PurpleGroup *group; Group *grp; if(!(cnt = parse_syncinfo(ac->user->contactList, sipmsg))) return; if(cnt->relationStatus == 1) { snprintf(buf, sizeof(buf) - 1, _("'%s' has accepted your add-buddy request"), cnt->localname); purple_notify_info(ac->gc, NULL, _("Success"), buf); if(!(buddy = purple_find_buddy(ac->account, cnt->userId))) { grp = fetion_group_list_find_by_id(ac->user->groupList, cnt->groupid); if(!(group = purple_find_group(grp->groupname))) return; new_buddy = purple_buddy_new(ac->account, cnt->userId, cnt->localname); purple_blist_add_buddy(new_buddy, NULL, group, NULL); /*TODO alias buddy */ } } else { if((buddy = purple_find_buddy(ac->account, cnt->userId))) purple_blist_remove_buddy(buddy); snprintf(buf, sizeof(buf) - 1, _("'%s' has declined your add-buddy request"), cnt->localname); purple_notify_error(ac->gc, NULL, _("Failed"), buf); } } void process_add_buddy(fetion_account *ac, const gchar *sipmsg) { Contact *cnt; extern GSList *buddy_to_added; cnt = parse_addbuddyapplication(sipmsg); //purple_account_request_add(ac->account, sid, (gchar*)0, desc, _("\nI want to add you as a friend")); buddy_to_added = g_slist_append(buddy_to_added, cnt); purple_blist_request_add_buddy(ac->account, cnt->userId, ac->user->groupList->next->groupname, cnt->localname); } gint get_info_cb(fetion_account *ac, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gchar *pos, *cur; xmlDocPtr doc; xmlNodePtr node; xmlChar *cs; Contact *cnt; PurpleNotifyUserInfo *info; PurpleConnection *pc; gchar *province, *city, *sid; pos = strstr(sipmsg , "\r\n\r\n") + 4; doc = xmlParseMemory(pos , strlen(pos)); if(!doc) return -1; node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; if(!xmlHasProp(node, BAD_CAST "user-id")) return -1; cs = xmlGetProp(node, BAD_CAST "user-id"); if(!(cnt = fetion_contact_list_find_by_userid(ac->user->contactList, (gchar*)cs))) return -1; if(xmlHasProp(node , BAD_CAST "carrier-region")){ cs = xmlGetProp(node , BAD_CAST "carrier-region"); pos = (char*)cs; for(cur = cnt->country;*pos && *pos != '.';*cur ++ = *pos ++); *cur = '\0'; pos ++; for(cur = cnt->province;*pos && *pos != '.';*cur ++ = *pos ++); *cur = '\0'; pos ++; for(cur = cnt->city;*pos && *pos != '.';*cur ++ = *pos ++); *cur = '\0'; xmlFree(cs); } info = purple_notify_user_info_new(); purple_notify_user_info_add_pair(info, _("Nickname"), cnt->nickname); purple_notify_user_info_add_pair(info, _("Gender"), cnt->gender == 1 ? _("Male") : ( cnt->gender == 2 ? _("Female") : _("Secrecy"))); purple_notify_user_info_add_pair(info, _("Mobile"), cnt->mobileno); purple_notify_user_info_add_section_break(info); sid = fetion_sip_get_sid_by_sipuri(cnt->sipuri); purple_notify_user_info_add_pair(info, _("Fetion"), sid); purple_notify_user_info_add_pair(info, _("Signature"), cnt->impression); province = get_province_name(cnt->province); city = get_city_name(cnt->province, cnt->city); purple_notify_user_info_add_pair(info, _("Province"), province); purple_notify_user_info_add_pair(info, _("City"), city); purple_notify_user_info_add_pair(info, _("Service Provider"), cnt->carrier); pc = purple_account_get_connection(ac->account); purple_notify_userinfo(pc, cnt->userId, info, NULL, NULL); purple_notify_user_info_destroy(info); g_free(province); g_free(city); g_free(sid); return 0; } static gint add_buddy_cb(fetion_account *ses, const gchar *sipmsg, struct transaction *UNUSED(trans)) { gint code, status_code; gchar *errMsg, errBuf[BUFLEN], nameBuf[BUFLEN]; Contact *cnt; Group *blist; PurpleBuddy *buddy; PurpleGroup *grp; code = fetion_sip_get_code(sipmsg); if(code == 200) { if(!(cnt = parse_add_buddy_response(sipmsg, &status_code, &errMsg))) { purple_notify_error(ses->gc, NULL, _("Error"), _("Add buddy error.Unknown reason")); return -1; } if(status_code != 200) { snprintf(errBuf, sizeof(errBuf) - 1, _("Add buddy error.%s."), errMsg ? errMsg : "Unknown reason"); if(errMsg) g_free(errMsg); purple_notify_error(ses->gc, NULL, _("Error"), errBuf); g_free(cnt); return -1; } if(!(blist = fetion_group_list_find_by_id(ses->user->groupList, cnt->groupid))) { purple_notify_error(ses->gc, NULL, _("Error"), _("Add buddy error.Unknown reason")); g_free(cnt); return -1; } if(!(grp = purple_find_group(blist->groupname))) { purple_notify_error(ses->gc, NULL, _("Error"), _("Add buddy error.Unknown reason")); g_free(cnt); return -1; } fetion_contact_list_append(ses->user->contactList, cnt); buddy = purple_buddy_new(ses->account, cnt->userId, (gchar*)0); purple_buddy_set_protocol_data(buddy, NULL); purple_blist_add_buddy(buddy, NULL, grp, NULL); snprintf(nameBuf, sizeof(nameBuf) - 1, "%s[Unverified]", cnt->localname); purple_blist_alias_buddy(buddy, nameBuf); purple_blist_server_alias_buddy(buddy, nameBuf); purple_prpl_got_user_status(ses->account, cnt->userId, "Offline", NULL); } else if(code == 421 || code == 420) { purple_notify_error(ses->gc, NULL, _("Error"), _("Add buddy error.Unknown reason")); return -1; } else { purple_notify_error(ses->gc, NULL, _("Error"), _("Add buddy error.Unknown reason")); return -1; } return 0; } static gint handle_contact_cb(fetion_account *UNUSED(ac), const gchar *sipmsg, struct transaction *UNUSED(trans)) { purple_debug_info("fetion", "%s", sipmsg); return 0; } void fx_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) { fetion_account *ac = purple_connection_get_protocol_data(gc); const gchar *buddy_name; const gchar *group_name; const gchar *alias; gchar errMsg[BUFLEN]; Group *grp; User *user = ac->user; fetion_sip *sip = user->sip; SipHeader *eheader, *ackheader; gchar *res, *body; extern GSList *buddy_to_added; struct transaction *trans; alias = purple_buddy_get_alias(buddy); buddy_name = purple_buddy_get_name(buddy); group_name = purple_group_get_name(group); /* process add buddy request */ Contact *cnt; GSList *cur = buddy_to_added; while(cur) { cnt = (Contact*)(cur->data); if(strcmp(cnt->userId, buddy_name) == 0) { if(!(grp = fetion_group_list_find_by_name(ac->user->groupList, group_name))) { purple_notify_error(gc, NULL, _("Error"), _("Not a valid group")); purple_blist_remove_buddy(buddy); return; } fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_HANDLECONTACTREQUEST); fetion_sip_add_header(sip , eheader); trans = transaction_new(); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, handle_contact_cb); transaction_add(ac, trans); body = generate_handle_contact_request_body(cnt->sipuri, cnt->userId, cnt->localname, grp->groupid, 1); res = fetion_sip_to_string(sip , body); if(send(ac->sk, res, strlen(res), 0 ) == -1) { purple_notify_error(gc, NULL, _("Error"), _("Network Error!")); purple_blist_remove_buddy(buddy); return; } g_free(body); purple_prpl_got_user_status(ac->account, cnt->userId, "Offline", NULL); buddy_to_added = g_slist_remove(buddy_to_added, cnt); return; } } /* remove the buddy just added */ purple_blist_remove_buddy(buddy); /* add a new buddy */ if(strlen(buddy_name) > 11) return; if(!(grp = fetion_group_list_find_by_name(ac->user->groupList, group_name))) { snprintf(errMsg, sizeof(errMsg) - 1, _("'%s' is not a valid group\n"), group_name); purple_notify_error(gc, NULL, _("Error"), errMsg); return; } fetion_sip_set_type(sip , SIP_SERVICE); eheader = fetion_sip_event_header_new(SIP_EVENT_ADDBUDDY); trans = transaction_new(); transaction_set_userid(trans, buddy_name); transaction_set_callid(trans, sip->callid); transaction_set_callback(trans, add_buddy_cb); transaction_add(ac, trans); fetion_sip_add_header(sip , eheader); if(user->verification != NULL && user->verification->algorithm != NULL) { ackheader = fetion_sip_ack_header_new(user->verification->code , user->verification->algorithm , user->verification->type , user->verification->guid); fetion_sip_add_header(sip , ackheader); } body = generate_add_buddy_body(buddy_name, strlen(buddy_name) == 11 ? MOBILE_NO : FETION_NO, grp->groupid, g_strdup(alias), g_strdup(user->nickname)); purple_prpl_got_user_status(ac->account, buddy_name, "Offline", NULL); res = fetion_sip_to_string(sip , body); printf("%s\n", res); g_free(body); if(send(ac->sk, res, strlen(res), 0) == -1) { g_free(res); return; } g_free(res); } static void update_portrait(fetion_account *ac, Contact *contact_cur) { const gchar *crc; PurpleAccount *account = ac->account; PurpleBuddy *buddy; portrait_data *data; g_return_if_fail(ac != NULL && ac->user != NULL); if(!(buddy = purple_find_buddy(account, contact_cur->userId))) return; crc = purple_buddy_icons_get_checksum_for_user(buddy); if((!crc && contact_cur->portraitCrc[0] == '\0') || (crc && strcmp(crc, contact_cur->portraitCrc) == 0)) return; data = g_malloc0(sizeof(portrait_data)); data->cnt = contact_cur; data->ac = ac; purple_proxy_connect(NULL, ac->account, ac->user->portraitServerName, 80, (PurpleProxyConnectFunction)download_portrait_cb, data); } static gint download_portrait_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar *encodedSipuri, *encodedSsic; gchar http[BUFLEN], uri[BUFLEN]; portrait_data *udata; fetion_account *ac; portrait_trans *trans; udata = (portrait_data*)data; ac = udata->ac; g_return_val_if_fail(ac->user != NULL, -1); snprintf(uri, sizeof(uri) - 1 , "/%s/getportrait.aspx" , ac->user->portraitServerPath); encodedSipuri = http_connection_encode_url(udata->cnt->sipuri); encodedSsic = http_connection_encode_url(ac->user->ssic); snprintf(http, sizeof(http) - 1, "GET %s?Uri=%s" "&Size=120&c=%s HTTP/1.1\r\n" "User-Agent: IIC2.0/PC "PROTO_VERSION"\r\n" "Accept: image/pjpeg;image/jpeg;image/bmp;" "image/x-windows-bmp;image/png;image/gif\r\n" "Host: %s\r\nConnection: Keep-Alive\r\n\r\n", uri, encodedSipuri, encodedSsic, ac->user->portraitServerName); if(send(source, http, strlen(http), 0) == -1) goto pcb_fin; trans = portrait_trans_new(); trans->source = source; trans->cnt = udata->cnt; trans->ac = ac; trans->data = (guchar*)0; trans->size = 0; trans->conn = purple_input_add(source, PURPLE_INPUT_READ, (PurpleInputFunction)get_portrait_cb, trans); g_free(udata); g_free(encodedSipuri); g_free(encodedSsic); return 0; pcb_fin: g_free(udata); g_free(encodedSipuri); g_free(encodedSsic); g_free(udata); close(source); return -1; } static gint get_portrait_cb(gpointer data, gint source, const gchar *UNUSED(error_message)) { gchar buf[BUFLEN], *pos; gint n, hl; portrait_trans *trans; fetion_account *ac; trans = (portrait_trans*)data; ac = trans->ac; if((n = recv(source, buf, sizeof(buf), 0)) == -1) goto pt_fin; if(trans->size == 0) { if((pos = strstr(buf, "HTTP/1.1 404"))) goto pt_fin; if((pos = strstr(buf, "HTTP/1.1 200"))) goto pt_fnd; if((pos = strstr(buf, "HTTP/1.1 302"))) { printf("302 portrait\n"); goto pt_fin; } goto pt_fin; } else { memcpy(trans->data + trans->size, buf, n); trans->size += n; if(trans->size == trans->sum) goto pt_upd; } return 0; pt_fnd: if(!(pos = strstr(buf, "\r\n\r\n"))) goto pt_fin; if((trans->sum = http_connection_get_body_length(buf)) == 0) goto pt_fin; hl = pos - buf + 4; trans->data = (guchar*)g_malloc0(trans->sum); trans->size = n - hl; memcpy(trans->data, buf + hl, trans->size); if(trans->size != trans->sum) return 0; pt_upd: purple_buddy_icons_set_for_user(trans->ac->account, trans->cnt->userId, trans->data, trans->size, trans->cnt->portraitCrc); pt_fin: purple_input_remove(trans->conn); g_free(trans); return 0; } static gchar* http_connection_encode_url(const gchar* url) { gchar pos, *res; gchar tmp[2]; gint i = 1; res = (gchar*)g_malloc0(2048); if(!res) return (gchar*)0; pos = url[0]; memset(res , 0 , 2048); while(pos != '\0') { if(pos == '/') strcat(res , "%2f"); else if(pos == '@') strcat(res , "%40"); else if(pos == '=') strcat(res , "%3d"); else if(pos == ':') strcat(res , "%3a"); else if(pos == ';') strcat(res , "%3b"); else if(pos == '+'){ strcat(res , "%2b"); }else{ memset(tmp, 0, sizeof(tmp)); sprintf(tmp, "%c", pos); strcat(res, tmp); } pos = (url + (i ++))[0]; } return res; } static gint http_connection_get_body_length(const gchar *http) { gchar *pos , length[16]; gint len; pos = strstr(http , "Content-Length: "); if(!pos) return 0; pos += 16; len = strlen(pos) - strlen(strstr(pos , "\r\n")); memset(length, 0, sizeof(length)); strncpy(length , pos , (len<9)?len:9); return atoi(length); } portrait_trans *portrait_trans_new() { return (portrait_trans*)g_malloc0(sizeof(portrait_trans)); } void portrait_trans_free(portrait_trans *trans) { g_free(trans); } gchar *get_city_name(const gchar *province, const gchar *city) { gchar path[] = RES_DIR"city.xml"; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseFile(path); if(!doc) return (gchar*)0; node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; while(node) { if(node->type != XML_ELEMENT_NODE) { node = node->next; continue; } res = xmlGetProp(node , BAD_CAST "id"); if(xmlStrcmp(res , BAD_CAST province) == 0) { node = node->xmlChildrenNode; while(node) { if(node->type != XML_ELEMENT_NODE) { node = node->next; continue; } xmlFree(res); res = xmlGetProp(node , BAD_CAST "id"); if(xmlStrcmp(res , BAD_CAST city) == 0) { xmlFree(res); return (gchar*)xmlNodeGetContent(node); break; } node = node->next; } break; } xmlFree(res); node = node->next; } return (gchar*)0; } gchar *get_province_name(const gchar *province) { gchar path[] = RES_DIR"province.xml"; xmlChar* res; xmlDocPtr doc; xmlNodePtr node; doc = xmlReadFile(path, "UTF-8", XML_PARSE_RECOVER); if(!doc) return (gchar*)0; node = xmlDocGetRootElement(doc); node = node->xmlChildrenNode; while(node) { res = xmlGetProp(node , BAD_CAST "id"); if(xmlStrcmp(res , BAD_CAST province) == 0) { return (gchar*)xmlNodeGetContent(node); xmlFree(res); break; } xmlFree(res); node = node->next; } xmlFreeDoc(doc); return (gchar*)0; } static gchar *generate_add_buddy_body(const gchar *no, gint notype, gint buddylist, const gchar *localname , const gchar *desc) { const gchar args[] = ""; gchar uri[48]; gchar groupid[16]; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node , NULL , BAD_CAST "contacts" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddies" , NULL); node = xmlNewChild(node , NULL , BAD_CAST "buddy" , NULL); if(notype == FETION_NO) snprintf(uri, sizeof(uri) - 1 , "sip:%s" , no); else snprintf(uri, sizeof(uri) - 1 , "tel:%s" , no); snprintf(groupid, sizeof(groupid) - 1 , "%d" , buddylist); xmlNewProp(node, BAD_CAST "uri" , BAD_CAST uri); xmlNewProp(node, BAD_CAST "local-name" , BAD_CAST localname); xmlNewProp(node, BAD_CAST "buddy-lists" , BAD_CAST groupid); xmlNewProp(node, BAD_CAST "desc" , BAD_CAST desc); xmlNewProp(node, BAD_CAST "expose-mobile-no" , BAD_CAST "1"); xmlNewProp(node, BAD_CAST "expose-name" , BAD_CAST "1"); xmlNewProp(node, BAD_CAST "addbuddy-phrase-id" , BAD_CAST "0"); xmlDocDumpMemory(doc , &res , NULL); xmlFreeDoc(doc); return xml_convert(res); } static Contact *parse_add_buddy_response(const gchar *sipmsg, gint *status_code, gchar **errMsg) { char *pos; Contact* contact; xmlChar* res; xmlDocPtr doc; xmlNodePtr node; contact = fetion_contact_new(); pos = strstr(sipmsg , "\r\n\r\n") + 4; doc = xmlParseMemory(pos , strlen(pos)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "buddy"); if(!node) { g_free(contact); xmlFreeDoc(doc); return (Contact*)0; } if(xmlHasProp(node , BAD_CAST "uri")) { res = xmlGetProp(node , BAD_CAST "uri"); strcpy(contact->sipuri , (char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "user-id")) { res = xmlGetProp(node , BAD_CAST "user-id"); strcpy(contact->userId , (char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "mobile-no")) { res = xmlGetProp(node , BAD_CAST "mobile-no"); strcpy(contact->mobileno , (char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "local-name")) { res = xmlGetProp(node , BAD_CAST "local-name"); strcpy(contact->localname , (char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "buddy-lists")) { res = xmlGetProp(node , BAD_CAST "buddy-lists"); contact->groupid = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "status-code")) { res = xmlGetProp(node , BAD_CAST "status-code"); *status_code = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node , BAD_CAST "basic-service-status")) { res = xmlGetProp(node , BAD_CAST "basic-service-status"); contact->serviceStatus = atoi((char*)res); xmlFree(res); } *errMsg = (gchar*)0; if(xmlHasProp(node, BAD_CAST "error-reason")) *errMsg = (gchar*)xmlGetProp(node, BAD_CAST "error-reason"); contact->relationStatus = STATUS_NOT_AUTHENTICATED; xmlFreeDoc(doc); return contact; } static Contact *parse_syncinfo(Contact *clist, const gchar *sipmsg) { gchar *pos; Contact *contact; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; if(!(pos = strstr(sipmsg , "\r\n\r\n"))) return (Contact*)0; doc = xmlParseMemory(pos + 4 , strlen(pos + 4)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "buddies"); if(!node) { xmlFreeDoc(doc); return (Contact*)0; } node = node->xmlChildrenNode; while(node) { if(!xmlHasProp(node, BAD_CAST "action")) { node = node->next; continue; } res = xmlGetProp(node, BAD_CAST "action"); if(strcmp((gchar*)res, "add") != 0) { xmlFree(res); node = node->next; continue; } xmlFree(res); if(! xmlHasProp(node , BAD_CAST "user-id")) return (Contact*)0; res = xmlGetProp(node , BAD_CAST "user-id"); if(!(contact = fetion_contact_list_find_by_userid(clist, (gchar*)res))) { contact = fetion_contact_new(); strcpy(contact->userId , (gchar*)res); } xmlFree(res); if(xmlHasProp(node, BAD_CAST "uri")) { res = xmlGetProp(node, BAD_CAST "uri"); strcpy(contact->sipuri, (gchar*)res); xmlFree(res); } if(xmlHasProp(node, BAD_CAST "local-name")) { res = xmlGetProp(node, BAD_CAST "local-name"); strcpy(contact->localname, (gchar*)res); xmlFree(res); } if(xmlHasProp(node, BAD_CAST "buddy-lists")) { res = xmlGetProp(node, BAD_CAST "buddy-lists"); contact->groupid = atoi((char*)res); xmlFree(res); } if(xmlHasProp(node, BAD_CAST "relation-status")) { res = xmlGetProp(node, BAD_CAST "relation-status"); contact->relationStatus = atoi((char*)res); xmlFree(res); }else contact->relationStatus = 0; node = node->next; } xmlFreeDoc(doc); return contact; } static Contact *parse_addbuddyapplication(const gchar *sipmsg) { gchar *pos = NULL; xmlDocPtr doc; xmlNodePtr node; xmlChar *res = NULL; Contact *cnt; pos = strstr(sipmsg , "\r\n\r\n") + 4; doc = xmlParseMemory(pos , strlen(pos)); node = xmlDocGetRootElement(doc); node = xml_goto_node(node , "application"); cnt = fetion_contact_new(); res = xmlGetProp(node, BAD_CAST "uri"); strcpy(cnt->sipuri, (gchar*)res); xmlFree(res); res = xmlGetProp(node, BAD_CAST "user-id"); strcpy(cnt->userId, (gchar*)res); xmlFree(res); res = xmlGetProp(node, BAD_CAST "desc"); strcpy(cnt->localname, (gchar*)res); xmlFree(res); xmlFreeDoc(doc); return cnt; } static gchar *generate_handle_contact_request_body(const gchar *sipuri , const gchar *userid, const gchar *localname , gint buddylist, gint result ) { gchar args[] = ""; gchar result_s[4]; gchar buddylist_s[4]; xmlChar *res; xmlDocPtr doc; xmlNodePtr node; doc = xmlParseMemory(args , strlen(args)); node = xmlDocGetRootElement(doc); node = xmlNewChild(node, NULL, BAD_CAST "contacts", NULL); node = xmlNewChild(node, NULL, BAD_CAST "buddies", NULL); node = xmlNewChild(node, NULL, BAD_CAST "buddy", NULL); xmlNewProp(node, BAD_CAST "user-id", BAD_CAST userid); xmlNewProp(node, BAD_CAST "uri", BAD_CAST sipuri); snprintf(result_s, sizeof(result_s) - 1, "%d", result); snprintf(buddylist_s, sizeof(buddylist_s) - 1, "%d", buddylist); xmlNewProp(node, BAD_CAST "result", BAD_CAST result_s); xmlNewProp(node, BAD_CAST "buddy-lists", BAD_CAST buddylist_s); xmlNewProp(node, BAD_CAST "expose-mobile-no", BAD_CAST "1"); xmlNewProp(node, BAD_CAST "expose-name", BAD_CAST "1"); xmlNewProp(node, BAD_CAST "local-name", BAD_CAST localname); xmlDocDumpMemory(doc, &res, NULL); xmlFreeDoc(doc); return xml_convert(res); }