存档

文章标签 ‘openfetion’

libofetion demo以及纯命令行飞信

十二月 20th, {2010 27 条评论 25,636 人阅读过  

之前一直有用户要求写一个libofetion的demo,再加上很多用户对于纯命令行版本飞信的强烈需求,于是我昨天简单地写了一个demo,把libofetion的API也做了一些修改,使它用起来更像是一个lib,不过对于第三方开发的话还是有很多很难理解的地方,因为最初并没有想把它当做一个lib来发布。到现在我对飞信的开发又要暂时先告一段落了,周末都在openfetion和娱乐中度过的,实验室项目和论文又要开始提上日程了,OK,先把code列出来,再做下简单地说明

/***************************************************************************
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
 
#include <openfetion.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define BUFLEN 1024
 
int   password_inputed = 0;
int   mobileno_inputed = 0;
int   tono_inputed = 0;
int   message_inputed = 0;
User *user;
pthread_t th;
 
static void usage(char *argv[]);
 
int fx_login(const char *mobileno, const char *password)
{
	Config           *config;
	FetionConnection *tcp;
	FetionSip        *sip;
	char             *res;
	char             *nonce;
	char             *key;
	char             *aeskey;
	char             *response;
	int               local_group_count;
	int               local_buddy_count;
	int               group_count;
	int               buddy_count;
	int               ret;
 
	/* construct a user object */
 	user = fetion_user_new(mobileno, password);
	/* construct a config object */
	config = fetion_config_new();
	/* attach config to user */
	fetion_user_set_config(user, config);
 
	/* start ssi authencation,result string needs to be freed after use */
	res = ssi_auth_action(user);
	/* parse the ssi authencation result,if success,user's sipuri and userid
	 * are stored in user object,orelse user->loginStatus was marked failed */
	parse_ssi_auth_response(res, user);
	free(res);
 
	/* whether needs to input a confirm code,or login failed
	 * for other reason like password error */
	if(USER_AUTH_NEED_CONFIRM(user) || USER_AUTH_ERROR(user)) {
		debug_error("authencation failed");
		return 1;
	}
 
	/* initialize configuration for current user */
	if(fetion_user_init_config(user) == -1) {
		debug_error("initialize configuration");
		return 1;
	}
 
	if(fetion_config_download_configuration(user) == -1) {
		debug_error("download configuration");
		return 1;
	}
 
	/* set user's login state to be hidden */
	fetion_user_set_st(user, P_HIDDEN);
 
	/* load user information and contact list information from local host */
	fetion_user_load(user);
	fetion_contact_load(user, &local_group_count, &local_buddy_count);
 
	/* construct a tcp object and connect to the sipc proxy server */
	tcp = tcp_connection_new();
	if((ret = tcp_connection_connect(tcp, config->sipcProxyIP, config->sipcProxyPort)) == -1) {
		debug_error("connect sipc server %s:%d\n", config->sipcProxyIP, config->sipcProxyPort);
		return 1;
	}
 
	/* construct a sip object with the tcp object and attach it to user object */
	sip = fetion_sip_new(tcp, user->sId);
	fetion_user_set_sip(user, sip);
 
	/* register to sipc server */
	if(!(res = sipc_reg_action(user))) {
		debug_error("register to sipc server");
		return 1;
	}
 
	parse_sipc_reg_response(res, &nonce, &key);
	free(res);
	aeskey = generate_aes_key();
 
	response = generate_response(nonce, user->userId, user->password, key, aeskey);
	free(nonce);
	free(key);
	free(aeskey);
 
	/* sipc authencation,you can printf res to see what you received */
	if(!(res = sipc_aut_action(user, response))) {
		debug_error("sipc authencation");
		return 1;
	}
 
	if(parse_sipc_auth_response(res, user, &group_count, &buddy_count) == -1) {
		debug_error("authencation failed");
		return 1;
	}
 
	free(res);
	free(response);
 
	if(USER_AUTH_ERROR(user) || USER_AUTH_NEED_CONFIRM(user)) {
		debug_error("login failed");
		return 1;
	}
 
	/* save the user information and contact list information back to the local database */
	fetion_user_save(user);
	fetion_contact_save(user);
 
	/* these... fuck the fetion protocol */
	struct timeval tv;
	tv.tv_sec = 1;
	tv.tv_usec = 0;
	char buf[1024];
	if(setsockopt(user->sip->tcp->socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
		debug_error("settimeout");
		return 1;
	}
	tcp_connection_recv(user->sip->tcp, buf, sizeof(buf));
 
	return 0;
}
 
int send_message(const char *mobileno, const char *receiveno, const char *message)
{
	Conversation *conv;
	Contact      *contact;
	Contact      *contact_cur;
	Contact      *target_contact = NULL;
	int           daycount;
	int           monthcount;
 
	/* send this message to yourself */
	if(*receiveno == '\0' || strcmp(receiveno, mobileno) == 0) {
		/* construct a conversation object with the sipuri to set NULL
		 * to send a message to yourself  */
		conv = fetion_conversation_new(user, NULL, NULL);
		if(fetion_conversation_send_sms_to_myself_with_reply(conv, message) == -1) {
			debug_error("send message \"%s\" to %s", message, user->mobileno);
			return 1;
		}
	}else{
		/* get the contact detail information by mobile number,
		 * note that the result doesn't contain sipuri */
		contact = fetion_contact_get_contact_info_by_no(user, receiveno, MOBILE_NO);
		if(!contact) {
			debug_error("get contact information of %s", receiveno);
			return 1;
		}
 
		/* find the sipuri of the target user */
		foreach_contactlist(user->contactList, contact_cur) {
			if(strcmp(contact_cur->userId, contact->userId) == 0) {
				target_contact = contact_cur;
				break;
			}
		}
 
		if(!target_contact) {
			debug_error("sorry,maybe %s isn't in your contact list");
			return 1;
		}
 
		/* do what the function name says */
		conv = fetion_conversation_new(user, target_contact->sipuri, NULL);
		if(fetion_conversation_send_sms_to_phone_with_reply(conv, message, &daycount, &monthcount) == -1) {
			debug_error("send sms to %s", receiveno);
			return 1;
		}else{
			debug_info("successfully send sms to %s\nyou have sent %d messages today, %d messages this monthcount",
					receiveno, daycount, monthcount);
			return 0;
		}
	}
	return 0;
}
 
int main(int argc, char *argv[])
{
	int ch;
	char mobileno[BUFLEN];
	char password[BUFLEN];
	char receiveno[BUFLEN];
	char message[BUFLEN];
 
	memset(mobileno, 0, sizeof(mobileno));
	memset(password, 0, sizeof(password));
	memset(receiveno, 0, sizeof(receiveno));
	memset(message, 0, sizeof(message));
 
	while((ch = getopt(argc, argv, "f:p:t:d:")) != -1) {
		switch(ch) {
			case 'f':
				mobileno_inputed = 1;
				strncpy(mobileno, optarg, sizeof(mobileno) - 1);	
				break;
			case 'p':
				password_inputed = 1;
				strncpy(password, optarg, sizeof(password) - 1);
				break;
			case 't':
				tono_inputed = 1;
				strncpy(receiveno, optarg, sizeof(receiveno) - 1);
				break;
			case 'd':
				message_inputed = 1;
				strncpy(message, optarg, sizeof(message) - 1);
				break;
			default:
				break;
		}
	}
 
	if(!mobileno_inputed || !password_inputed || !message_inputed) {
		usage(argv);
		return 1;
	}
 
	if(fx_login(mobileno, password))
		return 1;
 
	if(send_message(mobileno, receiveno, message))
		return 1;
 
	fetion_user_free(user);
	return 0;
 
}
 
static void usage(char *argv[])
{
	fprintf(stderr, "Usage:%s -f mobileno -p password -t receive_mobileno -d message\n", argv[0]);
}

首先需要libofetion的支持,因为用到了最新的API,所以需要从hg中clone最新版本编译安装后才可编译该程序:

hg clone https://ofetion.googlecode.com/hg/ ofetion

编译方法如下:

gcc -o cli cli.c `pkg-config --cflags --libs ofetion`

有一个地方需要说明,请大家找到/* these… fuck the fetion protocol */这个句注释,它下面的几句话的作用是这样的,飞信在用户完成身份验证之后订阅相关信息之前会推送过来一条信令:

BN 406472150 SIP-C/4.0
N: SyncUserInfoV4
I: 2
Q: 1 BN
L: 124
 
<events><event type="SyncUserInfo"><user-info>
<score value="1261" level="2" level-score="678"/>
</user-info></event></events>

用它来更新用户的积分等级之类的玩意,像这种浮云般的信令都被我直接忽略掉了,但在这种命令行模式纯粹为了发短信的情况下,不需要新建线程监听服务器推送过来的信息,但这条推送过来的信令就有可能影响其它信令的交互,而且更让人蛋疼的是并不是每次登录都会推送这条信令,有时候有,有时候没有,所以就加了在登录完成之后加了一个recv,设定了1s的超时,来处理这句信令,如果在网络情况不好的情况下,大家可以自己把超时时间设长一些,这样也就意味着在发送一条短信时在recv那里要停留几秒钟,否则就有可能导致信息发送失败。

下载地址:fetion-demo

分类: C/C++ 标签: ,

openfetion cli功能开发手记

十二月 19th, {2010 11 条评论 32,449 人阅读过  

之前一直有用户提出的命令行短信功能终于被我落实了下来,不过却让很多网友失望,因为所谓的命令行并非是纯命令行,而是需要先有openfetion GUI版本登录以作为server,cli程序通过IPC将数据交由server转发,自己并不进行与sipc server的直接数据通信,也就是必须有X图形界面的支持,当然如果要把现在的openfetion server做成不需要图形界面支持的deamon server还存在很多问题,如deamon在收到短信时该怎么处理等,另外deamon server的开发应该也需要花些时间,这个暂时未做考虑,而基于目前的飞信协议如果要发送短信则必须进行身份验证,也就是纯命令行不需要deamon server支持的飞信必须在每次发短信之前都需要登录一次,考虑到这个问题就迟迟没有开发纯命令行的版本,主要是觉得这种形式的短信发送方式存在的意义不大,不仅速度慢而且需要经过复杂的身份验证,但很多用户想用它管理server的话那也就只能这样来实现了,技术上其实问题不大,调用libofetion的api很简单就能实现,过几天抽点时间写一个看看,今天先把已经实现的CLI功能的开发过程总结一下。

首先,先看一下CLI的使用方法:

程序很简单,只加了三个个文件,分别是src/fx_server.c,src/fx_cli.c和include/fx_server.h

服务器端初始化init_server函数来初始化server,函数如下:

int init_server(FxMain *fxmain)
{
	int   fifo;
	User *user = fxmain->user;
 
	char server_fifo[128];
	snprintf(server_fifo, sizeof(server_fifo) - 1, OPENFETION_FIFO_FILE, user->mobileno);
 
	if(mkfifo(server_fifo, FIFO_FILE_MODE) == -1
			&& errno != EEXIST) {
		debug_error("create fifo %s:%s\n", server_fifo, strerror(errno));
		return -1;
	}
 
	if((fifo = open(server_fifo, O_RDONLY, 0)) == -1) {
		debug_error("open fifo %s:%s\n", server_fifo, strerror(errno));
		return -1;
	}
 
	if((idlefifo = open(server_fifo, O_WRONLY, 0)) == -1) {
		debug_error("open fifo %s:%s\n", server_fifo, strerror(errno));
		close(fifo);
		return -1;
	}
 
	return fifo;
}

这个函数对IPC进行了一些初始化,首先创建以“openfetion_fifo_登录手机号 ”命名的命名管道文件,分别打开两次,一次为只读,用于监听client发来的IPC请求,另一个为只写,这个描述符打开之后从来没有使用过,这也是UNP第二卷里面提到的小技巧,当client关闭时会关闭打开的命名管道描述符,这里server中的read函数便会返回0,从而标识client关闭,这时server便需要关闭描述符重新打开关监听,为了避免这样一种情况,server自己以只写的方式打开这个通用描述符而不写入任何数据,这样server在收不到数据时read函数便会一直阻塞。

FIFO也是基于流的通信方式,所以需要自定义消息,没有什么复杂的数据需要传输,我就简单定义了两种消息,请求消息和应答消息:

struct fifo_mesg {
	unsigned short type;
	unsigned short length;
	unsigned int  pid;
};
 
struct fifo_resp {
	unsigned short code;
	unsigned short length;
};

请求消息中的pid字段为client进程的pid,client在向server发起请求之后会打开openfetion_fifo_pid命名的FIFO等待server返回响应,而server在收到请求之后可以提取出请求消息中的pid,从来找到client用于监听的命名管道文件,将返回消息通过命名管道再反馈给client。
目前请求信令的类型只有两种:

/* 发送短信 */
#define CLI_SEND_MESSAGE    1
/* 获取用户信息 */
#define CLI_GET_INFORMATION 2

应答信令的应答码也只有两种:

/* 操作已成功 */
#define CLI_EXEC_OK   200
/* 操作失败 */
#define CLI_EXEC_FAIL 400

请求消息体中为XML格式,应答消息体中为纯文本提示消息。请求信令消息体如下:

<r><m no="15200000000" bd="hello world" p="1" /></r>

其中no表示要发送的好友手机号码,注意该好友必须在好友列表中,并且必须对你已设置公开手机号,bd为要发送的短信,p为是否用直接发送到用户手机。该消息发送到server后,server发现请求的号码与自己的号码相同时,会将消息发送至用户自己的手机中。

OK,过程就这么多吧,没有超过1K行代码,也复杂不到哪里去,接下来看看时间来不来得及写一个纯命令行的版本,有需要的同学请关注。

分类: C/C++ 标签: , , ,

抓取飞信协议数据包的bash脚本

十二月 11th, {2010 9 条评论 8,823 人阅读过  

昨晚想写个抓飞信协议包的脚本,结果刚写了一点就去看电影了,今天把它给完善了一下贴出来,也顺便可以帮助大家更好地理解飞信协议,也为了更多的人能够加入到openfetion的开发中来。

其实不是我不懂“工欲善其事必先利其器”这个道理,只是之前在写openfetion的时候我只能跑到windows下去抓包,windows里面的什么vbs之类的脚本我也不会写,就用最笨的办法用wireshark把数据包抓出来然后复制到txt中,现在在linux下可以抓openfetion的数据包,它的包格式与官方飞信相同(这句是废话,要不然也不可能实现互联互通)。

简单地说一下这个脚本,其实不用说它也很简单,就这么几行,飞信在登录的时候需要向一台sipc服务器注册,随后主要的数据包都是与这台服务器之前交换的,而这台服务器我想肯定也是分布式架构了,不同的飞信号会对应于自己的sipc服务器,而服务器的地址是通过配置文件传递给客户端的,客户端在注册之前先向nav.fetion.com.cn这个地址POST一个http请求,随后服务器会响应一个大的xml,之前版本的openfeion并没有对这些数据做本地缓存,所以在每次登录的时候都需要获取这个配置文件,所以经常会有用户反应卡在“正在下载配置文件”这一步进行不下去了,现在的方法是在本地把需要用到的字段做了缓存,再次登录的时候用缓存版本号查询服务器,如果版本号是最新则会返回一个很小的xml结构,速度当然也会比完全下载快得多。

接着说这个脚本,请求到配置文件之后便会得到飞信号对应的sipc服务器地址,之后就用tcpdump来抓取通往这个地址的包和来自这个地址的包,然后就简单做了一下解析。

#!/bin/sh
#written by @levin108
#This script is used to capture the packets of fetion client,and help to anlysis fetion protocol.
#You need to have openfetion installed in your computer,or maybe libfetion,I don't know,whatever...
 
if [ $UID -ne 0 ]; then
	echo "perminission denied,you must be root to run this script"
	exit 1
fi
 
if [ $# == 0 ]; then
	echo -n "please input the mobile number:"
	read mobileno
else
	mobileno=$1
fi
 
if [ ${#mobileno} -ne 11 ];then
	echo "wrong mobile number"
	exit 1
fi
 
protocol_version="4.0.2510"
config_uri="http://nav.fetion.com.cn/nav/getsystemconfig.aspx"
config_body="<config><user mobile-no=\"$mobileno\"/> \
			<client type=\"PC\" version=\"$protocol_version\" \
			platform=\"W5.1\"/><servers version=\"0\"/> \
			<parameters version=\"0\"/><hints version=\"0\"/></config>"
 
echo "Getting fetion sipc servr address..."
 
config_xml=`curl -d "$config_body" -A "IIC2.0/PC $protocol_version" $config_uri 2> /dev/null`
proxy_endpoint=`echo $config_xml | sed 's/.*<sipc-proxy>\([^<]*\).*/\1/'`
proxy_ip=`echo $proxy_endpoint | sed 's/:[0-9]*$//'`
proxy_port=`echo $proxy_endpoint | sed 's/.*://'`
 
echo "Sipc server address : $proxy_ip:$proxy_port"
echo "Start capturing,now start your fetion client and login."
echo
 
tcpdump -n -A -l -t -s 0 tcp and host $proxy_ip \
	and port $proxy_port or host nav.fetion.com.cn 2>/dev/null | awk '
/^IP/{
	if($NF == 0) {
		is_data_seg = 0;
	} else {
		printf("\n%s ----> %s\n", $2, $4)
		printf("-----------------------------------\n\n")
		# whether it is a data segment
		is_data_seg = 1;
		# eth/ip/tcp header length
		bytes_before_protocol = 52;
	}
}
 
!/^IP /{
	if(is_data_seg) {
		if(length($0) < bytes_before_protocol) {
			bytes_before_protocol -= length($0)
			next;
		}
		if(length($0) >= bytes_before_protocol && 
			bytes_before_protocol != 0) {
			$0 = substr($0, bytes_before_protocol);
			bytes_before_protocol = 0;
		}
		print $0
	}
}
'

分类: Linux 标签: , , ,

OpenFetion V2.0正式版发布

十月 24th, {2010 74 条评论 21,672 人阅读过  


经过近一个月的修改测试,Openfetion 2.0正式版今天release出来了,相比于之前的1.x版本相比,2.0有更多的优点和特性,最重要的是2.0稳定性较之于之前版本登录速度更快,稳定性更高。

这次版本主要集中在解决历史版本中所存在的登录崩溃,运行时崩溃的问题,以及用户所普遍反映的登录速度过慢的问题,引入了SQLITE3来实现了数据的本地化,使登录过程需要下载的数据量很小,从而大大提高了用户登录速度,也因为实现了数据的本地化,新版本也增加了离线和离线登录功能。

新版本另一个重要的特性是检测网络状态,可以实现断线检测并自动重连,失败时会显示用户离线,而不是之前离线时直接退出程序。检测网络状态需要NetworkManager的支持,此依赖库为可选,如果安装时disable,则不具有自动重连功能。

另外新版本增加了空闲时间自动离开功能,空闲时间目前固定为3分钟。此功能需要XScreenSaver的支持,此依赖库也为可选。

另外新版本再一次更换了图标,新图标由@riku设计,由@xhacker修改,感谢二位对OpenFetion做出的贡献。

下面是ChangeLog:

2010-10-24 levin

* 修复了登录和运行时的崩溃问题
* 修复了群发短信时的崩溃问题
* 修复了添加好友时的崩溃问题
* 添加了数据本地化,实现了离线登录功能
* 添加了断线自动重连功能
* 添加了空闲时间自动离开功能
* 添加了关闭上线提示的功能
* 优化了登录过程,登录速度更快
* 更换了新版图标,更美观

程序添加的依赖库为libsqlite3,NetworkManager,XScreenSaver,Ubuntu下的安装方法如下:

sudo apt-get install libxml2-dev
sudo apt-get install libgtk2.0-dev
sudo apt-get install libssl-dev
sudo apt-get install libnotify-dev
sudo apt-get install libgstreamer0.10-dev
sudo apt-get install intltool
sudo apt-get install libsqlite3-dev
sudo apt-get install libnm-glib-dev
sudo apt-get install libxss-dev

ubuntu用户推荐使用happyaron制作的PPA安装,该PPA于10.24日晚将会更新到最新版本,安装方法如下:

sudo apt-add-repository ppa:happyaron/ppa
sudo apt-get update
sudo apt-get install openfetion

Fedora用户依赖库安装方法如下:

pkcon install gcc gstreamer-devel gtk2-devel libxml2-devel openssl-devel intltool 
sqlite-devel NetworkManager-devel libXScrnSaver-devel libnotify-devel

期待各方打包人士制作相关安装包

源码下载地址:
http://code.google.com/p/ofetion/downloads/list

Follow Me @levin108

分类: Linux 标签: , , ,

Openfetion近期开发手记(相关功能实现技术)

十月 18th, {2010 29 条评论 8,282 人阅读过  

离上Openfetion上一个版本发布至今过了有将近一个月的时间了,上个版本放出的时候本以为已经解决了很多bug,但发布之后才发现用户遇到的问题还是很多,软件测试还是很重要的,当然有些协议上的问题只能交由用户去测试,我没有那么多飞信号,有些问题有很难遇到的,也很难复现的,所以就在这样不断地与用户沟通中解决问题。

其实做共享软件是件很开心的事情,不管你做得好不好,都会有人支持你,这个是很重要的,因为在最初开发openfetion的时候并没有想过要把它做为一个专业的飞信客户端放出来大家一起使用,可发布之后得到了广大linux用户的支持,即使那时候的bug比现在多得多,仍然有用户乐此不彼地帮我测试,反溃问题,提供建议,当然我也乐此不彼地修改程序,希望有朝一日它能让所有的用户都能稳定地运行,这也是自由软件的优势,如果当初这做为一个商业软件发布,我想毫无疑问,收到的会是一片骂声,然后这个项目也便会匆匆截止,而现在即便很忙也会拿出一些时间来加强它,因为总会有用户支持着它,也会有开源爱好者加入进来做一些贡献。

Openfetion下一个的版本号应该是2.0了,我想2.0应该和之前的1.x版本有所区别,功能上这个不是特别重要,因为之前的版本已经有了几乎所有的基本功能,其它的一些不常用的功能我也没考虑过,因为开发那些功能是一件性价比很低的事情,现在所能想到的2.0和1.x的区别应该是让2.0更加稳定,功能再丰富如果不能稳定运行这个软件就永远成不了优秀的软件,因为我在新版本里面做了大批量的代码修改,甚至包括以前一些很不专业的编码习惯,尽可能将所有潜在的问题都消灭掉,当然也加了一些用户一直以来要求的功能,下面简单地说一下。

首先,是在之前版本中,用户反映登录速度过慢,这个我承认,是因为在之前的版本中没有加入数据的本地缓存,每次用户登录的时候都会需要重新从服务器上请求自己相关的所有数据,包括用户列表和配置文件这样庞大到几个K甚至十几个K的数据,这不可避免地会导致登录过程过慢,甚至网络状况不好的时候,会导致在获取配置文件的时候卡在那里,这些问题都降低了用户体验,解决这些问题的方法毫无疑问是加入本地用户缓存,这也是网络软件所常用的方式,之前我把一些本地配置信息和聊天记录保存在本地所用的方法是直接使用二进制写入dat文件,那种方法灵活性非常差,而且效率也很低,所有就没有对其它的动态数据做缓存,现在在新版本中引用了sqlite3,这个轻量级的数据库无疑是实现这个功能的绝佳选择,使用起来很简单,而且灵活性也很高,基本的SQL语句几乎都支持,之前只是知道有这么个东西,但一直没用过,这次试了一下,发现使用起来也非常简单,于是毫不犹豫就把它给引入了,我想加入了这样一个依赖所带来的用户体验的提高是很大的,希望不会有用户抱怨引赖关系增多。

另外,在将数据进行本地缓存之后便为另一个功能的实现提供了基础,那就是离线登录功能,和IM的离线功能一样,就是在没有网络连接的情况下登录Openfetion,可以查看好友列表,当然这些好友信息都是存储在本地数据中的,通过sqlite3从数据库中提取出来的。

聊天记录改用sqlite3存储之后提取和写入也都方便了很多,而且还添加了删除聊天记录的功能,之前用二进制数据直接写入的方式保存聊天记录所带来的不便就是不能方便地删除聊天记录,如果要删除只能先把整个聊天记录都加载到内存中,然后从中删掉要删除的信息,之后再重新写入磁盘覆盖掉原来的文件,这样效率是非常低的,而用sqlite3直接可以用一条DELETE语句删掉想要删除的信息。

同样这次也加入了本地用户列表删除功能,用户登录完后记录在本地的用户名密码数据也同样都可以删除。

另一个很重要的功能是空闲时间自动离开功能,这个功能之前一直不知道该怎么实现,纠结于当焦点不在Openfetion中时,如何获取全局的鼠标键盘动作,而即便获取到了又如何检测是否空闲,这些问题都非常麻烦,后来查看了一下pidgin的源码,才发现其实IM软件所实现的空闲时间检测功能一般都是通过调用XScreenSaver来实现的,包括之前的evaqq也是之样实现的,过程很简单,下面的几行代码便是获取空闲时间的函数,通过周期性地检测空闲时间便可以判断出IM是否需要进行离开状态。

gint idle_timesec(void)
{
 
#ifdef USE_LIBXSS
	static XScreenSaverInfo *mit_info = NULL;
	static gint has_extension = -1;
	gint event_base, error_base;
 
	if (has_extension == -1)
		has_extension = XScreenSaverQueryExtension(
				GDK_DISPLAY(), &event_base, &error_base);
 
	if(has_extension){
		if (mit_info == NULL)
			mit_info = XScreenSaverAllocInfo();
 
		XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), mit_info);
		return (mit_info->idle)/1000;
	}
#endif
	return 0;
}

这需要XScreenSaver的支持才行,于是加入了条件编译,在ubuntu或者debian中可以通过下面的命令安装XScreenSaver开发包:

sudo apt-get install libxss-dev

另外一个很重要的功能是断线自动离开功能,这个用tcp的相关特性来检测链路状态灵敏性太低,参考了一下pidgin和empathy,它们用的方法都是调用NetworkManager的相关API来实现的,NetworkManager是基于dbus的,之前在slackware13.1上因为安装NetworkManager失败,而导致之前同学写的基于nm和dbus的程序在我这里不能跑,从而对这个一直留有某种恐惧感,当然现在也意识到要让程序在网络状态改变的时候即刻感知到,最佳方法还是使用libnm,下面的函数便是初始化libnm的函数,它为网络状态改变的事件注册了回调函数nm_state_change()

void fx_conn_init(FxMain *fxmain)
{
#ifdef USE_NETWORKMANAGER
	GError *error = NULL;
        DBusGConnection *nm_conn = NULL;
        DBusGProxy *nm_proxy = NULL;
 
	nm_conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
	if (!nm_conn) {
		debug_error("Error connecting to DBus System service: %s.\n", error->message);
	} else {
		nm_proxy = dbus_g_proxy_new_for_name(nm_conn,
		                                     NM_DBUS_SERVICE,
		                                     NM_DBUS_PATH,
		                                     NM_DBUS_INTERFACE);
		dbus_g_proxy_add_signal(nm_proxy, "StateChange", G_TYPE_UINT, G_TYPE_INVALID);
		dbus_g_proxy_connect_signal(nm_proxy, "StateChange",
		                        G_CALLBACK(nm_state_change), fxmain, NULL);
	}
#endif
}

下面的代码是回调函数的代码,在它里面能过检测各种网络状态而做出相应的动作:

static void
nm_state_change(DBusGProxy *proxy, NMState state, gpointer data)
{
	switch(state)
	{
		case NM_STATE_CONNECTED:
			debug_info("network is connected");
			break;
		case NM_STATE_ASLEEP:
			debug_info("network is sleeping...");
			break;
		case NM_STATE_CONNECTING:
			debug_info("network is connecting...");
			break;
		case NM_STATE_DISCONNECTED:
			debug_info("network is disconnected");
			break;
		case NM_STATE_UNKNOWN:
			debug_info("unknown network state");
		default:
			break;
	}
}

ubuntu或debian中NetworkManager开发包的安装方法如下:

sudo apt-get install libnm-glib-deb

要说的是,XScreenSaver和NetworkManager的使用都是可选项,为了避免不愿意引入这些库的用户抱怨,可以在configure的时候用–disable-screensaver和–disable-nm将其禁用。

另外也解决几个崩溃的bug,比如添加好友时崩溃,这个纯粹是我编码过程中出现的失误,还有群发短信时崩溃的问题,这个也是我编码的失误,都已经修改过来了,有时候收到的信息会显示发送失败,这个也修改好了,一个比较重要的bug是多人同时聊天在窗口切换的时候可能会崩溃的问题,这个问题现在也已经解决了。

有时候程序会出现 ”Program received signal SIGPIPE, Broken pipe.“这样的错误,这个信号一般是在服务器端主动关闭连接时客户端会收到的来自操作系统的信号,理论上服务器端主动关闭连接这个可能性不大,但它有时候确实会出现,之前没有对这个信号进行处理,这次把这个信号直接忽略了,然后在send和recv的时候就会返回-1,通过返回值就可以检测连接状态。

 struct sigaction sa;
 sa.sa_handler = SIG_IGN;
 sigaction( SIGPIPE, &sa, 0 );

还有用户提到在登录时登录按钮状态不变,这个之前没怎么在意,这次也修改过来了,不过没有加取消登录的功能,这个也考虑过,不过没想到实现的方法,因为登录线程用的是gthread库,而它没有像pthread一样提供取消线程的方法,一时间也不知道该怎么去实现,这个先暂时一放,希望有了解这个的朋友可以提供些帮助,现在的实现方法是像官方飞信一样在点击登录后改变登录界面,自己还用GIMP做了个登录正在进行的gif,就是让Openfetion的图标一直在转,哈哈,很有成就感,上两个登录界面的图吧。

image image

图片里面没有打码,这个飞信号是我测试用的小号,无所谓了,反正也不用。

这次新版本想多测试一段时间再发,肯定还存在问题,尽可能在发布之前能解决更多的问题,能让2.0正式版更稳定,能让用户更满意,同时也欢迎大家到svn上co最新的版本试用,并帮忙测试,如果你有问题,请向我反溃,这样我才能帮你解决问题。svn :

svn checkout http://ofetion.googlecode.com/svn/trunk/ ofetion-read-only

分类: C/C++ 标签: , , ,

写给OpenFetion用户(相关说明和常用问题汇总)

九月 11th, {2010 86 条评论 26,033 人阅读过  

OpenFetion从去年发布第一个版本到现在已经正式更新了将近10次,首先感谢大家对OpenFetion的支持,最初开发这个软件的时候是因为在Linux下找不到用得舒服的飞信客户端软件,而且也想找点事情打发一下无聊的研究生时光,突发奇想就写了这么个小东西,第一个版本刚发布的时候错误百出,当时没有专门做过测试,只是在自己机器上用自己的账号跑起来没有问题,用了几天就发布了,结果是网友报各种各样的bug,这也是自由软件的好处,总会有很多热心的网友帮助你测试软件,也正是有了广大网友的支持我一直以来才会不断地维护更新着它。

关于OpenFetion的版权问题

OpenFetion是遵循GPL协议的共享软件,但一直惭愧的是本人不善美工,在早期版本里面用了很多飞信官方的小图标,直到最近这个问题才开始慢慢地解决,也正是因为图标问题我到现在才迟迟没有正式发布新版本,如果大家在使用Openfetion的时候遇到登录问题或者想试用一下新版本里面的功能,可以到svn上下载最新开发版本使用:

$svn checkout http://ofetion.googlecode.com/svn/trunk/ ofetion-read-only

而且我们推荐使用SVN上的版本更新您的软件,因为这样可以在新版本发布之前最快地体验到新版本中的功能,更及时地解决旧版本中存在的BUG。

关于OpenFetion的安装包的问题

之前的版本都是由我一人维护,deb和rpm的包我都不会打,而且一直用slackware这些机制的包都不支持,所以也就没有在软件发布的时候及时推出这些相关的安装包,googlecode上经常会有热心的用户提供打好的优秀的安装包,所以大家可以到google code上去找用户提交的相关的安装包和安装脚本,另外ubuntu中文论坛的happyaron长期以来一直负责OpenFetion的PPA制作,为广告用户提供了方便,在这里向他表示感谢,另外这里再说一下happyaron的PPA的使用方法:

10.04:deb http://ppa.launchpad.net/happyaron/ppa/ubuntu lucid main

9.10: deb http://ppa.launchpad.net/happyaron/ppa/ubuntu karmic main

9.04: deb http://ppa.launchpad.net/happyaron/ppa/ubuntu jaunty main

8.10: deb http://ppa.launchpad.net/happyaron/ppa/ubuntu intrepid main

8.04: deb http://ppa.launchpad.net/happyaron/ppa/ubuntu hardy main

使用方法是选择您的发行版版本号对应的代码拷贝到源列表中,比如Ubuntu 10.04,则将”deb http://ppa.launchpad.net/happyaron/ppa/ubuntu lucid main”拷由到/etc/apt/sources.lst中,然后先用下面的代码获取密钥:

$sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys DDA4DB69

接下来在Console中输入:

$sudo apt-get update
$sudo apt-get install openfetion


如何卸载OpenFetion

需要注意的一个问题是,新版旧本的OpenFetion之间可能存在冲突,在安装新版本的时候如果和旧版本安装目录不一致则最好先把旧版本删除再安装,configure的默认安装prefix是/usr/local/,如果是使用PPA安装或者使用的安装脚本则很可能将prefix设成了/usr目录下,假设默认安装路径是/usr/local,那么需要删除二进制可执行文件和libofetion库文件,它们存放的目录是/usr/local/bin和/usr/local/lib目录下libofetion开头的库文件,当然我也提供了uninstall脚本。

下载地址:http://ofetion.googlecode.com/files/ofetion_uninstall.sh


关于OpenFetion的编译安装问题:

OpenFetion链接了几个共享库,分别是

libgtk2.0-dev(提供UI支持),
libssl-dev(提供加密安全支持),
libxml2-dev(提供xml创建解析支持),
libnotify-dev(提供gnome notification消息提示支持,可选),
libgstreamer0.10-dev(提供声音提示功能支持,可选)
intltool(提供国际化的支持)

注意:编译安装必须使用开发版本,后面不带dev的包是不行的(为了解决各种技术水平的用户的疑问,请允许我使用这种说法)。

这几个包在Ubuntu下我试着安装过,很简单,其它的发行版我手头实在找不到,大家可以自己想办法,肯定不难。
Ubuntu下的安装方法:

$apt-get install libxml2-dev
$sudo apt-get install libgtk2.0-dev
$sudo apt-get install libssl-dev
$sudo apt-get install libnotify-dev
$sudo apt-get install libgstreamer0.10-dev
$sudo apt-get install intltool

Fedora中的安装方法:

pkcon install gcc gstreamer-devel gtk2-devel libxml2-devel openssl-devel intltool

这些包都安装就绪之后就可以编译安装OpenFetion了,

$./configure --prefix=/usr
$make
$make install

或者

su -c "./configure=/usr && make && make install"

编译安装之前需要安装dev版本的共享库,其中libgtk2.0-dev可能会占几十M的空间,没有预安装这些库并且舍不得磁盘空间的用户就只有使用PPA安装或者到Google Code上找网友打好的安装包安装了。

关于如何调试OpenFetion

坦城的讲,OpenFetion现在仍然存在很多BUG,也仍然有用户报“段错误”,当然出现这些问题的时候我希望能得到您的帮助,如果能帮我高度一下程序给出断点的位置,无疑能让我更快地定位BUG,然后修复它们,其实调试的过程很简单,在终端中输入如下的命令:

levin@slack:~$ gdb openfetion
GNU gdb (GDB) 7.1
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-slackware-linux".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /usr/local/bin/openfetion...done.
(gdb) run
 
然后程序开始运行,当程序由于BUG运行停止后,再输入如下命令:
(gdb)back
然后把出现的断点信息发送给我就可以了,我会在最短的时间内修复。


关于OpenFetion其它的几点说明:

1.官方飞信支持同一个好友属于多个不同的分组。很惭愧,也很抱歉,我一直都不知道原来飞信支持这个功能,所以当时开发的时候就只按一个好友对应一个分组来开发的,这也导致了那些将一个好友同时放到不同的分组中的用户在登录之前出现段错误,这个BUG存在于当前1.8版本以及之前的所有版本中,在这里,对那些因为存在类似情况而不能使用OpenFetion的用户道歉,这是我协议分协过程中的失误,这个问题在最新的SVN版本中已经解决,happyaron的PPA中的最新版本的OpenFetion也解决了这个问题,但是我要强调的是OpenFeiton暂不支持同一个好友属于多个分组,为了解决这些用户的登录问题,我暂时将这些好友拿出来放到了未分组里面,很多网友最近过来报这个BUG,其实这还不算个BUG,只是我最近太忙,没时间再去找个windows分析飞信的分组相关的协议,大家可以先这样用一段时间,起码它已经能保证你能登录能正常使用了。

2.OpenFetion安装成功后却不能运行。这个问题是用户提问最多的问题,这个问题解决起来也很简单,表现是安装完成后出现下面的错误提示:

openfetion: error while loading shared libraries: libofetion.so.0: cannot open shared object file: No such file or directory

出现这种问题的原因我在之前的一篇日志里面简单地说了,参考:关于库文件的搜索路径问题

解决的方法也很简单:如果在安装的时候默认路径是/usr/local/或者其它自定义的非系统路径,那么lib就会被安装到相应的路径中去,这时候把你lib所在的路径添加到/etc/ld.so.conf中去,然后在终端中执行

$sudo ldconfig

3.近期移动推出web版飞信,OpenFetion将会何去何从。

其实我真挺希望移动官方可以推出一个用起来舒服方便的Linux版本客户端,而不是模仿腾讯做这种web的客户端,用起来确实很不方便,要是哪天移动推出了官方的Linux版本飞信,而且大家用起来也比OpenFetion这种第三方的客户端舒服,那OpenFetion就没有什么存在的必要了,我也就可以不用花时间去维护它了,最终目的还是使用方便,如果有好用的客户端干嘛还要花时间花精力去开发第三方的客户端,所以我从来没有想过把OpenFetion移植到windows上去,因为那里有官方飞信客户端,功能很完善,而且也比OpenFetion稳定的多。

我开发这个的初衷不是想拿它谋取利益,而且它确实给我带不来什么经济上的利益,虽然我blog上挂了几个谷歌的广告,但几乎没人点,等我从谷歌那里拿到那几百块钱的广告费的时候说不定我儿子都会开发软件了,有三位网友曾给予过我捐助,我的支付宝账户里面现在有40+RMB,钱虽然不多,但那是他们对我的鼓励和支持,想起来是很开心的,因为自己的成果得到了别人的认可,想公开对他们表示感谢,但因为在支付宝里面用的是真名,所以也不太方便公开网友的真实性名,就在这里谢过几位了。

其实开发OpenFetion最初的目的是为了自己给自己用,因为经常用飞信和女朋友聊天,用linux没有个好用的飞信怎么能行,另一个重要的原因也是为了杀时间,当然到了现在还在维护着它,是出于对用户的一种责任吧(哈,听起来很高尚的样子),因为已经有很多人在用它了,放着那些有能力解决的BUG不解决确实是对软件对用户的一种不负责任。

4.OpenFetion是否会支持文件传输功能。

目前来说暂时没有开发文件传输功能的打算,前一段时间分析过飞信文件传输相关的协议,发现实现起来不像我最初想象地那么简单,需要投入较多的时间,现在实验室项目赶得很紧,不像刚开始读研的时候时间总是那么充裕了,空余的时间只能够添加一些小功能,修改一些小bug,这个功能先暂时搁浅,还请大家见谅。


致谢(我觉得应该有这样一个环节)

这里是代表我和所有正在使用OpenFetion的用户向相关的人表示感谢。嗯,首先应该是我女朋友,刚到北京读研的时候她正在边工作边考研,自己一个人很辛苦,说起来也挺内疚的,那时候都不怎么给她发短信,打电话也说不了几分钟,因为那时候飞信刚刚推出现在最新版本的协议,那一个月我把所有的空余时间都拿出来重写OpenFetion了,她能理解我,我感到很欣慰也很感激,虽然一直以来她都对我的技术很不屑。

技术上要感谢的是:

Ubuntu中文论坛的happyaron,他不仅非常及时地更新PPA源,而且还在我的开发上给了很多的建议和意见,有时候也会给我反溃BUG。

wzssyqa 他完成了1.6和1.7版本的i18n,说实话在这之前我对i18n并不了解,有了他的模板我才让现在的版本支持了简体中文和英语两种语言。

liu.dongyuan 他解决了输入法不能输入英文的问题,并做了大量UI方面的改进特别是美工方面都做了很多工作。

xuhdev 他刚刚加入开发小组不久,改进了很多UI上英文的翻译错误(我为我的英语感到可耻)。

对OpenFetion做出贡献的人还有很多,不一一列举了,希望更多的人加到OpenFetion的开发行列上来,人多力量大,这样OpenFetion才能越做越好,为各位热衷开源的朋友提供方便。

加入开发小组或者技术交流

有意向加入开发小组的朋友可以在这里留言,或者邮件给我levin108(AT)gmail.com,或者加我GTalk,或者去推特上给我留言:@levin108,强烈欢迎各位热心网友的加入。

当然有什么问题或者技术交流都可以通过上面的方法联系我,但请不要索要我的QQ号,因为我一个月也就上个一两次,每次持续不了多长时间。

分类: Linux 标签:

近期飞信开发手记(群相关介绍)

八月 14th, {2010 22 条评论 11,437 人阅读过  

实验室项目中期检查结束,两天一夜的超负载工作换来了额外两天的休息时间,时间不敢随意浪费,飞信好久没有更新了,软件总不可能没有bug,用户的需求也不可能有止境,还是得赶紧把这么长时间以来网友提出的问题和要求给解决一下,用了近三天的时间完成了飞信i18n和飞信群功能,宅在宿舍里面写代码时间长了果然会觉得无聊,闲话少说,切入正题,写一下飞信群的开发过程。

另外,现在还不打算发布新版本,还想再测试两天看看,这里就截两个图先展示一下吧

image image

其实飞信群这个功能本来也没几个人在用,分析了一下协议,没有什么特别复杂的地方,很多东西都是套路,首先是在登录的时候获取一系统群相关的信息,如群列表,群成员列表,群详细信息,群个人信息,推荐群,群话题等等,一些没用的信息像推荐群,群话题这些在windows下都不会有人去点的信息就直接给忽略了,登录的过程中发送一系统的获取信息的信令,然后再统一根据 callid从服务器返回的信息中提取各自相关的信息,获取群列表信令如下:

S fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 3
Q: 1 S
N: PGGetGroupList
L: 27

<args><group-list /></args>

获取群详细信息信令如下:

S fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 5
Q: 1 S
N: PGGetGroupInfo
L: 150

<args><groups attributes=”all”><group uri=”sip:PG9777218@fetion.com.cn;p=12205″ /><group uri=”sip:PG31809932@fetion.com.cn;p=12207″ /></groups></args>

获取群成员列表信令如下:

S fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 12
Q: 1 S
N: PGGetGroupMembers
L: 229

<args><groups attributes=”member-uri;member-nickname;member-iicnickname;member-identity;member-t6svcid”><group uri=”sip:PG9777218@fetion.com.cn;p=12205″ member-list-major-version=”" member-list-minor-version=”" /></groups></args>

这些信息发送至服务器这后,服务器会将相关的请求信息推送过来,程序编写时在发送每一条信令都记住其相关的callid,再根据返回信息中的callid来判断其是什么信息,然后再将其解析。

这些信息获取之后就需要订阅每个群的Presence信息,信令如下:

SUB fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 2
Q: 2 SUB
N: PGPresence
L: 215

<args><subscription><groups><group uri=”sip:PG9777218@fetion.com.cn;p=12205″ /></groups><presence><basic attributes=”all” /><member attributes=”all” /><management attributes=”all” /></presence></subscription></args>

之后服务器会将群成员的Presence信息推送过来,信令格式大致如下:

BN 916098834 SIP-C/4.0
N: PresenceV4
I: 1
L: 159
Q: 55 BN
<events><event type=”PresenceChanged”><contacts><c id=”464933706″><pr di=”PCCL030524118392″ b=”400″ d=”" dt=”PC” dc=”137″></pr></c></contacts></event></events>

至此,群相关的信息都已经获取到了,已经可以在界面上展示飞信群了,但要发送群信息还不够,必须还要发起群会话邀请,也就是向sip服务器发送Invite信令,格式如下:

I fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 10
Q: 1 I
T: sip:PG31809932@fetion.com.cn;p=12207
K: text/html-fragment
K: multiparty
K: nudge
K: share-background
K: fetion-show
L: 21

s=session m=message

服务器返回100 trying,之后返回200 OK,收到OK之后再向服务器发送Ack信令:

A fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 9
Q: 1 A
T: sip:PG9777218@fetion.com.cn;p=12205

这样会话邀请就已经完成了,可以发送群信息了。但在这之后我发送群信息的时候却出了一点小问题,每次发送到服务器的Message信息格式完全正确,但服务器返回的是请求失败的信令,想了好久才想明白,其实发起会话邀请这一步并不是做给服务器看的,而是要通过它来注册一个callid,Invite信令的callid必须与Message信令的callid一致,消息才能发送成功,这表示发送的群消息是属于这个会话的,而标识正是在发起邀请时所用的callid,群消息发送的信令如下:

M fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 9
Q: 2 M
T: sip:PG31809932@fetion.com.cn;p=12207
C: text/html-fragment
K: SaveHistory
L: 60

<Font Face=” Color=’-16777216′ Size=’10′>hello</Font>

然后就是群短信:

M fetion.com.cn SIP-C/4.0
F: ×××××××××
I: 25
Q: 1 M
T: sip:PG31809932@fetion.com.cn;p=12207
N: PGSendCatSMS
L: 12

hello fetion

群短信这里的callid就无所谓啦,可以让它使用递增的全局变量,当然也只有群的超级管理员才可以发送群短信,连管理员也不能发,所以我觉得这个功能限制也太多啦。

其实关于飞信群做的事情也就这些,像其它的一些邀请好友加入群,群管理之类的功能我都没做,本来就没有几个人在用这个功能,而且linux下用的就更少了,而且我也确实拿不出再多的时间去整了,就先把基本功能都弄好了让过来提示需求的网友先凑合着吧,如果将来飞信群用的人多了我再想办法后时间来完善它。

另外,我弄这个群比飞信的官方软件里面的群帅的地方就是群成员可以显示头像,当然啦,这些也都是浮云。

HTTP代理原理及Base64编码

四月 14th, {2010 2 条评论 14,000 人阅读过  

有网友留言希望我在飞信的新版本中加上代理功能,这两天折腾了一下,把HTTP代理给加上了,SOCKS代理太麻烦了,暂时应该不会考虑加它了,而且用的人也不多,加不加意义不大了。

写一下HTTP代理的原理,其实原理很简单,就是通过HTTP协议与代理服务器建立连接,协议信令中包含要连接到的远程主机的IP和端口号,如果有需要身份验证的话还需要加上授权信息,服务器收到信令后首先进行身份验证,通过后便与远程主机建立连接,连接成功之后会返回给客户端200,表示验证通过,就这么简单,下面是具体的信令格式:

CONNECT 59.64.128.198:21 HTTP/1.1
Host: 59.64.128.198:21
Proxy-Authorization: Basic bGV2I1TU5OTIz
User-Agent: OpenFetion

其中Proxy-Authorization是身份验证信息,Basic后面的字符串是用户名和密码组合后进行base64编码的结果,也就是对username:password进行base64编码。

其实编码对安全性没什么意义,base64严格意义上都已经不能算是加密了,现在信息安全这么受重视的年代,不需要密钥的加密算法还是叫编码更贴切一些,抓到这种包之后瞬间就可以得到用户名和密码。

HTTP/1.0 200 Connection established

OK,客户端收到收面的信令后表示成功建立连接,接下来要发送给远程主机的数据就可以发送给代理服务器了,代理服务器建立连接后会在根据IP地址和端口号对应的连接放入缓存,收到信令后再根据IP地址和端口号从缓存中找到对应的连接,将数据通过该连接转发出去。

下面说一下base64这种编码方式,它还是很常用的,优点是使用起来简单,缺点是编码效率低劲(每3个字节会编码成4个字节),安全性差(这个刚才已经说过了)。

以前用.NET和java的时候形形色色的编码加密散列都见过也差不多都用过,不过高级语言会提供相应的类,一两句话就可以实现加密解密,当然在C里面也可以用OpenSSL来实现,同样简单,飞信里面也用到了base64编码,就是在2010版本里面要求输入图片验证码的时候用到的,服务器端会将生成的图片验证码图片的二进制数据流进行base64编码,以明文的方式放到xml中发送给客户端,客户端对其进行解码,然后就可以得到验证码图片(还是觉得这个过程有点多此一举)。

今天看了一下base64的编码规则,把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式,这样就形成了4个8位的字节,然后再将每个8位字节转换成10进制,再从编码表中找到对应的字符就可以了,编码的时候3个字节一组进行编码,当不足3个字节的时候,进行完上面的操作后,将剩余的位用0补齐(注意:并不是补到32位),于是2个字节编成3个字节,不足四个字节的用’='补齐,哈,感觉自己写得乱七八糟的,不过这种东西在网上一搜一大片,也没有必要细说了,另外附上刚才写的一个编码的小程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
char* base64_encode(const char *src);
 
int main(int argc , char *argv[])
{
	const char in[] = "levin";
	char *res = base64_encode(in);
	printf("%s\n" , res);
	free(res);
	return 0;
}
char* base64_encode(const char *src){
 
	char* dest;
	char in[4];
	long buf = 0 , tmp = 0;
	int i = 0 , j = 0 , count = 0;
	char table[] = {
	    'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G'
	  , 'H' , 'I' , 'J' , 'K' , 'L' , 'M' , 'N'
	  , 'O' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'U'
	  , 'V' , 'W' , 'X' , 'Y' , 'Z' , 'a' , 'b'
	  , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i'
	  , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p'
	  , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w'
	  , 'x' , 'y' , 'z' , '0' , '1' , '2' , '3'
	  , '4' , '5' , '6' , '7' , '8' , '9' , '+'
	  , '/' , '='
	};
 
	count = strlen(src) / 3 + (strlen(src) % 3 == 0 ? 0 : 1);
	dest = (char*)malloc(count * 4 + 1);
 
	for( ; j < count ; j++){
 
		bzero(in , sizeof(in));
		strncpy(in , src + j * 3 , 3 );
 
		buf = 0 , buf = 0 , i = 0; 
 
		for(; i < strlen(in) ; i++){
			tmp = (long)in[i];
			tmp <<= (16 - i * 8);
			buf |= tmp;
		}
 
		for(i = 0; i < 4 ; i ++){
			if(strlen(in) + 1 > i){
				tmp = buf >> (18 - i * 6);
				tmp &= 0x3F;
				dest[j * 4 + i] = table[tmp];
			}else{
				dest[j * 4 + i] = '=';
			}
		}
	}
	return dest;
}

上面的程序会输出bGV2aW4=,因为最后一组不足3字节,用=补了一个字节。

程序很简单,写它纯粹是为了强化一下对base64编码规则的记忆,解码的就不写了。

我程序里面是直接用OpenSSL来干的,少量数据进行编码的时候一个函数就够了,很方便:

1
EVP_EncodeBlock(out , (unsigned char*)in , strlen(in));

分类: C/C++ 标签: , , ,

Automake生成Makefile过程简介

二月 26th, {2010 1 条评论 10,836 人阅读过  

有校友在byr上看了我发的openfetion,发信息问我关于automake的东西,其实我了解的也并不多,只是自己查了些资料,然后借鉴了一下别人的项目中相关文件的写法,于是就想照着openfetion中的相关文件把自己对它的理解简单写一下,算是整理一下学过的知识,以后也好有个参考。
Automake支持三种目录层次:flat,shadow,deep
1.flat 所有的源文件及相关文件都放在顶层目录中。
2.shadow 主要的源文件存放在顶层目录中,其它的存放在各个子目录中。
3.deep 所有的源文件都分别存放在各个子目录中。

我的程序用了deep模式,所有的源文件都放在了src目录中。下面写一下automake生成Makefile的步骤:
1.运行autoscan命令,生成configure.scan。

configure.scan就是configure.in的模板,对它做一些修改,然后改名为configure.in或者configure.ac就可以了(新版本的automake好像是configure.ac)。
configure.in:

AC_INIT(src/openfetion.cpp)
AM_INIT_AUTOMAKE(openfetion,0.1)
AM_CONFIG_HEADER(config.h)
AM_PATH_GTK_2_0(,,AC_MSG_ERROR(openfetion 0.1 needs GTK+ 2.0))
AM_PATH_XML2(,,AC_MSG_ERROR(openfetion 0.1 needs LIBXML2))
AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL
AC_OUTPUT(src/Makefile)
AC_INIT宏以任何一个源文件作为参数,它只是检查这个源文件的存在,也说意味着着源文件所在的目录存在
AM_INIT_AUTOMAKE 增加了几个标准的检查,它以程序名称和版本号作为参数
AC_PROG_CC 指出源代码可能是用C写的,如果源代码是用C++写的我们就需要AC_PROP_CXX
AC_PROG_INSTALL 会生成一个install目录文件,这样用户就可以通过输入“make install”来安装这个软件
AC_OUTPUT 指出将会生成的Makefile文件的名字
AC_CONFIG_HEADER 表示将会使用config.h文件,autoconf需要一个config.h.in文件,用它来生成config.h,config.h.in可以通过autoheader工具生成
AM_PATH_GTK_2_0(,,AC_MSG_ERROR(openfetion 0.1 needs GTK+ 2.0))
AM_PATH_XML2(,,AC_MSG_ERROR(openfetion 0.1 needs LIBXML2)) 这两句话检查系统中是否安装了程序所需要共享库, GTK+2.0和libxml2,这两个库在安装的时候分别安装了AM_PATH_GTK_2_0和 AM_PATH_XML2这两个宏,所以可以用这种方法检测,如果系统中没有安装libxml2,configure脚本就会执行失败,并报错:openfetion 0.1 needs LIBXML2
关于library的检查可参见文章:Using C/C++ libraries with Automake and Autoconf

其它的就不详细说了,需要的时候可以谷歌。
2.在顶层目录中创建一个Makefile.am,在其它各级需要的子目录中也创建Makefile.am。

我的顶层目录Makefile.am中只有下面几句话:

1
2
3
4
5
6
7
8
9
10
#子目录变量,用于递归处理各级子目录
SUBDIRS=src
#安装路径,可使用./configure --prefix=/usr/local/openfetion修改
prefix=/usr/local
#数据文件的安装路径
datadir=$(prefix)/skin
#数据文件的具体内容,这里的意思是要将skin目录下的所有文件安装到/usr/local/skin中
data_DATA=skin/* 
#程序打包时要加入的其它文件,使用make dist生成tar.gz文件时会放进去的东西
EXTRA_DIST=skin

src目录下的Makefile.am文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#libxml2 , GTK+2.0和GThread-2.0的cflags和libs
#libxml2安装了xml2-config脚本,GTK+2.0和GThread-2.0安装了.pc pkg-config文件
AM_CPPFLAGS=`xml2-config --cflags` `pkg-config --cflags gtk+-2.0 gthread-2.0`
AM_LDFLAGS=`xml2-config --libs` `pkg-config --libs gtk+-2.0 gthread-2.0`
#预定义的目录,prefix已经说过了
prefix=/usr/local
#这个是可执行文件安装的目录
bindir=$(prefix)/bin
#这个是自定义的目录,用来安装包含协议实现部分的静态库的
slibdir=/usr/lib
#编译生成可执行文件名称
bin_PROGRAMS=openfetion
#这里必须以上一步定义的名称为前缀,编译openfetion所需要的源文件
openfetion_SOURCES=openfetion.cpp  fetion_ui.cpp  ... login_ui.cpp
#编译openfetion所需要的库文件,libfx.a是我事先编译好了的。
openfetion_LDADD=libfx.a
#这个变量前面也说了,要把下面这些文件打包放到tar.gz中
EXTRA_DIST= 	   fetion_ui.h  main_ui.h  .... libfx.a
#要安装的头文件,执行完make install后,这些文件将被安装到/usr/include里面
include_HEADERS=   fgroup.h   flogin.h ... common.h
#自定义目录的数据文件,执行完make install后,这个文件就会被安装到/usr/lib里面
slib_DATA=libfx.a
3.生成GNU风格的项目时需要在顶层目录中你创建NEWS、 README、 ChangeLog 、AUTHOR这几个文件。
touch NEWS README ChangeLog AUTHOR
如果不需要生成GNU风格的项目就不需要创建这几个文件,而是需要在Makefile.am中加入
AUTOMAKE_OPTIONS = foreign
这是执行automake命令时的选项
4.执行aclocal。
由configure.in生成aclocal.m4
5.执行autoconf
由configure.in和aclocal.m4生成configure脚本
6.执行automake
由Makefile.am和configure.in生成各级目录下的Makefile.in
./configure执行的时候会扫描各个目录下的Makefile.in生成不同的Makefile。然后就可以执行make和make install了

GNU Automake工具集的功能远不止这些,我了解地也不够深刻,继续学习。

分类: Linux 标签: , , ,