对iksemel中的jabber进行了封装。
话不多说,直接上代码
头文件
// for fzjh
// auth: WenYF
// jxim is NOT safe thread, so this is a single thread task!!!!!!!!!!
#ifndef _JXIM_H_
#define _JXIM_H_
#include "common.h"
#include "iksemel.h"
#ifdef _WIN32
#include
#endif
#define TEST_MESSAGE
// for debug
#define ENABLE_DEBUG
#define ENABLE_IO_DEBUG
#define AUTO_SEND_PING
// connect timeout
#define CONNECT_TIMEOUT 30
#define PING_TIMEOUT 300
// de NOT modify this , unless you get it
#define ENABLE_SASL 1
// below is the connect state
#define CONNECT_STATE_OPEN 2001
#define CONNECT_STATE_SUSPEND 2002
#define CONNECT_STATE_CLOSE 2003
// below type is for j_connectErrorCallback
#define TYPE_HOST_ERROR 1001
#define TYPE_CONNECT_ERROR 1002
#define TYPE_IO_ERROR 1003
#define TYPE_AUTH_ERROR 1004
#define TYPE_CONNECT_TIOMEOUT 1005
#define TYPE_CONNECT_UNKNOW_ERROR 1006
// recv message callback
typedef void (j_messageRecvCallback)(char *from, char *to, char *id, char *data);
// connect successed callback
typedef void (j_connectSuccessedCallback)();
// connect faild callback
typedef void (j_connectErrorCallback)(int type);
// when recv a disconnect cmd from server, disconnect will callback
typedef void (j_disconnectCallback)();
// get current state
int j_getConnectState();
// must call first time , and then use j_initConnect to init
void j_constructConnect(j_messageRecvCallback *mcb, j_connectSuccessedCallback *cscb
, j_connectErrorCallback *cfcb, j_disconnectCallback *dcb);
// resource is the flag which describe teminate app
void j_initConnect(char *uid, char *password, char *domain, char *resource);
// see j_initConnect
void j_deInitConnect();
// you must call this function before check the state while is CONNECT_STATE_CLOSE
void j_connect();
void j_disconnect();
// ret 0 is OK, 2003 is CONNECT_STATE_CLOSE, other error
// you must call this function before check the state while is CONNECT_STATE_OPEN
// if state is CONNECT_STATE_SUSPEND, return ok
int j_suspendConnect();
// ret 0 is OK, 2003 is CONNECT_STATE_CLOSE, other error
// you must call this function before check the state while is CONNECT_STATE_SUSPEND
// if state is CONNECT_STATE_OPEN, return ok
int j_resumeConnect();
// send data, id is the msg unique flag, resource is the flag which describe teminal app
// ret 0 is OK, 2002 is CONNECT_STATE_SUSPEND, 2003 is CONNECT_STATE_CLOSE, other error
int j_send(char *to, char *resource, char *data, char *id);
#endif
源文件
/* auth: WenYF
** for fzjh
** use iksemel as a pipe, just route message to remote client
** NOT thread safe
*/
#include "jxim.h"
// TODO: to do level
#ifdef ENABLE_DEBUG
#define logD(...) {printf(__VA_ARGS__);}
#define logW(...) {printf(__VA_ARGS__);}
#define logE(...) {printf(__VA_ARGS__);}
#else
#define logD(...)
#define logW(...)
#define logE(...)
#endif
#ifdef TEST_MESSAGE
int test_counter = 0;
char *test_to;
struct timeval test_time;
#endif
j_messageRecvCallback *n_messageRecvCb;
j_connectSuccessedCallback *n_connectSuccessCb;
j_connectErrorCallback *n_connectErrorCb;
j_disconnectCallback *n_disconnectCb;
/* stuff we keep per session */
struct session {
// data parser
iksparser *prs;
// jid wrapper
iksid *acc;
// passwrod
char *pass;
// current features
int features;
// for login success
int authorized;
// for timeout
int counter;
// when send or recv a tag, must close this session
int state;
};
struct session *n_sess;
// out packet filter
iksfilter *n_filter;
char* getBareJid(char *uid, char *resource)
{
char *tmp;
logD( "getBareJid uid = %s\n" , uid);
tmp = iks_malloc (strlen (uid)
+ strlen (resource)
+ 3);
sprintf (tmp, "%s@%s/%s", uid, n_sess->acc->server, resource);
return tmp;
}
int on_pong (void *user_data, ikspak *pak )
{
iks *x;
char *from;
from = iks_find_attrib(pak->x , "to");
logD( "ping's to = %s\r\n" , from);
x = iks_make_iq(IKS_TYPE_RESULT , NULL);
iks_insert_attrib( x , "id" , pak->id );
iks_insert_attrib( x , "from" , from );
iks_insert_attrib( x , "to" , pak->from->server);
iks_send (n_sess->prs, x);
iks_delete (x);
return IKS_FILTER_EAT;
}
int on_auth_success (void *user_data, ikspak *pak)
{
// TODO: here to process iq result
logD("authorization success\n");
// send prensence to resume
j_resumeConnect();
if (n_connectSuccessCb) {
n_connectSuccessCb();
}
#ifdef TEST_MESSAGE
test_counter++;
char body[32];
sprintf(body, "test-%d", test_counter);
iks *sx = iks_make_msg(IKS_TYPE_NORMAL, test_to, body);
iks_send(n_sess->prs, sx);
iks_delete(sx);
gettimeofday(&test_time, NULL);
#endif
return IKS_FILTER_EAT;
}
int on_auth_faild (void *user_data, ikspak *pak)
{
logE ("authorization failed\n");
if (n_connectErrorCb) {
n_connectErrorCb(TYPE_AUTH_ERROR);
}
j_disconnect();
return IKS_FILTER_EAT;
}
void on_io (void *user_data, const char *data, size_t size, int is_incoming)
{
if (!data) {
return;
}
#ifdef AUTO_SEND_PING
if (n_sess->authorized && strlen(data) > 0) {
n_sess->counter = PING_TIMEOUT;
} else {
n_sess->counter = CONNECT_TIMEOUT;
}
#else
if (!n_sess->authorized && strlen(data) > 0) {
n_sess->counter = CONNECT_TIMEOUT;
}
#endif
#ifdef ENABLE_IO_DEBUG
if (is_incoming) {
printf ("RECV");
} else {
printf("SEND");
}
printf ("[%s]\n", data);
#endif
}
int on_message_normal (void *user_data, ikspak *pak)
{
// TODO: here to process message
logD("on_message_normal\n");
char *from;
char *to;
char *msg;
from = pak->from->user;
to = iks_find_attrib(pak->x , "to");
msg = iks_find_cdata(pak->x , "body");
if (n_messageRecvCb) {
n_messageRecvCb(from, to, pak->id, msg);
}
#ifdef TEST_MESSAGE
//char *fullFrom = iks_find_attrib(pak->x, "from");
char body[32];
iks *sx;
int pt = test_time.tv_usec;
gettimeofday(&test_time, NULL);
test_counter++;
sprintf(body, "test-%d", test_counter);
//logD("test_counter = %d\n", test_counter);
j_send(from, "test", body, body);
//sx = iks_make_msg(IKS_TYPE_NORMAL, fullFrom, body);
//iks_insert_attrib(sx, "from", n_sess->acc->full);
//iks_send(n_sess->prs, sx);
//iks_delete(sx);
logD("us: %d\n", test_time.tv_usec - pt);
#endif
return IKS_FILTER_EAT;
}
int on_presence (void *user_data, ikspak pak)
{
// TODO: here to process presence
logD("on_presence\n");
return IKS_FILTER_EAT;
}
int on_stream (void *user_data, int type, iks *node)
{
int ret = IKS_OK;
logD("on_stream\n");
logD("type = %d\n", type);
logD("node = %d\n", node);
switch (type) {
case IKS_NODE_START:
logD("session open");
break;
case IKS_NODE_NORMAL:
if (strcmp ("stream:features", iks_name (node)) == 0) {
n_sess->features = iks_stream_features (node);
if (ENABLE_SASL) {
if (n_sess->authorized) {
iks *t;
if (n_sess->features & IKS_STREAM_BIND) {
t = iks_make_resource_bind (n_sess->acc);
iks_send (n_sess->prs, t);
iks_delete (t);
}
if (n_sess->features & IKS_STREAM_SESSION) {
t = iks_make_session ();
iks_insert_attrib (t, "id", "auth");
iks_send (n_sess->prs, t);
iks_delete (t);
}
} else {
if (n_sess->features & IKS_STREAM_SASL_MD5)
iks_start_sasl (n_sess->prs, IKS_SASL_DIGEST_MD5,
n_sess->acc->user,
n_sess->pass);
else if (n_sess->features & IKS_STREAM_SASL_PLAIN)
iks_start_sasl (n_sess->prs, IKS_SASL_PLAIN,
n_sess->acc->user,
n_sess->pass);
}
}
} else if (strcmp ("failure", iks_name (node)) == 0) {
logE("sasl authentication failed\n");
// TODO: here login error, may be password wrong
// to get a new uid and password , then to call j_connect()
ret = TYPE_AUTH_ERROR;
} else if (strcmp ("success", iks_name (node)) == 0) {
n_sess->authorized = 1;
iks_send_header (n_sess->prs, n_sess->acc->server);
} else {
ikspak *pak;
pak = iks_packet (node);
// here parser packet data
iks_filter_packet (n_filter, pak);
//if (pak) {
// iks_stack_delete(pak);
//}
}
break;
case IKS_NODE_STOP:
logW ("server disconnected\n");
j_disconnect();
// notify disconnect by server
if (n_disconnectCb) {
n_disconnectCb();
}
break;
case IKS_NODE_ERROR:
logE ("stream error\n");
ret = TYPE_IO_ERROR;
break;
default:
logE ("stream error\n");
ret = TYPE_IO_ERROR;
break;
}
if (node) iks_delete (node);
return ret;
}
int j_getConnectState()
{
return n_sess->state;
}
void j_constructConnect(j_messageRecvCallback *mcb, j_connectSuccessedCallback *cscb
, j_connectErrorCallback *cfcb, j_disconnectCallback *dcb)
{
n_sess = (struct session *) iks_malloc(sizeof(struct session));
memset (n_sess, 0, sizeof(struct session));
n_messageRecvCb = mcb;
n_connectSuccessCb = cscb;
n_connectErrorCb = cfcb;
n_disconnectCb = dcb;
}
// resource : the desciber of this app
void j_initConnect(char *uid, char *password, char *domain, char *resource)
{
n_sess->prs = iks_stream_new (IKS_NS_CLIENT, n_sess, (iksStreamHook *) on_stream);
iks_set_log_hook (n_sess->prs, (iksLogHook *) on_io);
char *tmp;
tmp = iks_malloc (strlen (uid)
+ strlen (domain)
+ strlen (resource)
+ 3);
sprintf (tmp, "%s@%s/%s", uid, domain, resource);
n_sess->acc = iks_id_new (iks_parser_stack (n_sess->prs), tmp);
iks_free (tmp);
n_sess->authorized = 0;
n_sess->features = 0;
n_sess->counter = CONNECT_TIMEOUT;
n_sess->state = CONNECT_STATE_CLOSE;
n_sess->pass = password;
j_setup_filter ();
}
void j_deInitConnect()
{
iks_parser_delete(n_sess->prs);
iks_stack_delete(n_sess->acc);
iks_free(n_sess->pass);
n_sess->authorized = 0;
n_sess->features = 0;
n_sess->counter = 0;
n_sess->state = CONNECT_STATE_CLOSE;
}
void j_setup_filter ()
{
if (n_filter) iks_filter_delete (n_filter);
n_filter = iks_filter_new ();
iks_filter_add_rule (n_filter, (iksFilterHook *) on_auth_success, n_sess,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
IKS_RULE_ID, "auth",
IKS_RULE_DONE);
iks_filter_add_rule (n_filter, on_auth_faild, n_sess,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
IKS_RULE_ID, "auth",
IKS_RULE_DONE);
iks_filter_add_rule (n_filter, (iksFilterHook *) on_message_normal, n_sess,
IKS_RULE_TYPE, IKS_PAK_MESSAGE,
IKS_RULE_SUBTYPE, IKS_TYPE_NORMAL,
IKS_RULE_DONE);
iks_filter_add_rule (n_filter, (iksFilterHook *) on_presence, n_sess,
IKS_RULE_TYPE, IKS_PAK_PRESENCE,
IKS_RULE_DONE);
iks_filter_add_rule (n_filter , (iksFilterHook *) on_pong, n_sess,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_GET,
IKS_RULE_NS, IKS_NS_PING,
IKS_RULE_DONE);
}
void j_connect ()
{
int e;
logD("j_connect()\n");
if (n_sess && n_sess->state != CONNECT_STATE_CLOSE) {
return;
}
logD("n_sess->prs = %d\n", n_sess->prs);
logD("srever = %s\n", n_sess->acc->server);
e = iks_connect_tcp (n_sess->prs, n_sess->acc->server, IKS_JABBER_PORT);
switch (e) {
case IKS_OK:
break;
case IKS_NET_NODNS:
logE ("hostname lookup failed\n");
// process error
e = TYPE_HOST_ERROR;
break;
case IKS_NET_NOCONN:
logE ("connection failed\n");
// process error
e = TYPE_CONNECT_ERROR;
break;
default:
logE ("io error\n");
// process error
e = TYPE_IO_ERROR;
break;
}
if (e != IKS_OK) {
logE ("connect error : %d\n", e);
if (n_connectErrorCb) {
n_connectErrorCb(e);
}
return;
}
n_sess->state = CONNECT_STATE_OPEN;
n_sess->counter = CONNECT_TIMEOUT;
while (1) {
// if timeout is -1, wait until had data.
if (n_sess->authorized) {
#ifdef AUTO_SEND_PING
e = iks_recv (n_sess->prs, 1);
n_sess->counter--;
#else
e = iks_recv (n_sess->prs, -1);
#endif
} else {
e = iks_recv (n_sess->prs, 1);
n_sess->counter--;
}
if (e == IKS_OK) {
if (n_sess->state == CONNECT_STATE_CLOSE) {
break;
} else if (n_sess->counter == 0 && !n_sess->authorized) {
logE ("auth error\n");
if (n_connectErrorCb) {
n_connectErrorCb(TYPE_CONNECT_TIOMEOUT);
}
j_disconnect();
break;
}
#ifdef AUTO_SEND_PING
else if (n_sess->counter == 0 && n_sess->authorized) {
logE ("ping server\n");
iks *x = iks_make_iq(IKS_TYPE_GET, IKS_NS_PING);
iks_send(n_sess->prs, x);
iks_delete(x);
#if 0
else if (n_sess->state == CONNECT_STATE_OPEN) {
j_suspendConnect();
} else {
j_resumeConnect();
}
#endif
}
#endif
} else if (TYPE_IO_ERROR == e) {
// session close or error crash
logE ("io error\n");
if (n_connectErrorCb) {
n_connectErrorCb(e);
}
j_disconnect();
break;
} else if (TYPE_AUTH_ERROR == e) {
logE ("auth error\n");
if (n_connectErrorCb) {
n_connectErrorCb(e);
}
j_disconnect();
break;
} else {
logE ("unknow other error: ret = %d\n", e);
if (n_connectErrorCb) {
n_connectErrorCb(TYPE_CONNECT_UNKNOW_ERROR);
}
j_disconnect();
break;
}
}
}
// disconnect, send and close session
void j_disconnect()
{
logD("j_disconnect()\n");
if (n_sess && n_sess->state == CONNECT_STATE_CLOSE) {
return;
}
if (n_sess && n_sess->prs) {
iks_send_raw(n_sess->prs, "");
iks_disconnect(n_sess->prs);
n_sess->state = CONNECT_STATE_CLOSE;
n_sess->authorized = 0;
n_sess->features = 0;
}
}
// ret 0 is OK, 2003 is CONNECT_STATE_CLOSE, other error
int j_resumeConnect()
{
int ret = IKS_OK;
if (n_sess->state == CONNECT_STATE_CLOSE) {
ret = CONNECT_STATE_CLOSE;
} else {
iks *x = iks_make_pres(IKS_SHOW_AVAILABLE, "resume");
ret = iks_send(n_sess->prs, x);
iks_delete(x);
if (ret == IKS_OK) {
n_sess->state = CONNECT_STATE_OPEN;
}
}
logD("resume connect ret = %d\n", ret);
return ret;
}
// ret 0 is OK, 2003 is CONNECT_STATE_CLOSE, other error
int j_suspendConnect()
{
int ret = IKS_OK;
if (n_sess->state == CONNECT_STATE_CLOSE) {
ret = CONNECT_STATE_CLOSE;
} else {
iks *x = iks_make_pres(IKS_SHOW_UNAVAILABLE, "suspend");
ret = iks_send(n_sess->prs, x);
iks_delete(x);
if (ret == IKS_OK) {
n_sess->state = CONNECT_STATE_SUSPEND;
}
}
logD("suspend connect ret = %d\n", ret);
return ret;
}
int j_send(char *to, char *resource, char *data, char *id)
{
int ret = IKS_OK;
if (n_sess->state == CONNECT_STATE_CLOSE || n_sess->state == CONNECT_STATE_SUSPEND) {
ret = n_sess->state;
} else {
char *toJid = getBareJid(to, resource);
iks *x = iks_make_msg(IKS_TYPE_NORMAL, toJid, data);
iks_insert_attrib(x, "from", n_sess->acc->full);
iks_insert_attrib(x, "id", id);
ret = iks_send(n_sess->prs, x);
iks_free(toJid);
iks_delete(x);
}
return ret;
}
#ifdef TEST_MESSAGE
void msgcb(char *from, char *to, char *id, char *data)
{
logD("mesage callback\n");
}
void conscb()
{
logD("connect successed callback\n");
}
void confcb(int type)
{
logD("connect error callback\n");
}
void disccb()
{
logD("disconnect callback\n");
}
#endif
int main (int argc, char *argv[])
{
#ifdef TEST_MESSAGE
test_to = argv[2];
j_constructConnect((j_messageRecvCallback *)msgcb
, (j_connectSuccessedCallback *)conscb
, (j_connectErrorCallback *)confcb
, (j_disconnectCallback *)disccb);
j_initConnect(argv[1], argv[1], argv[3], "test");
j_connect();
#endif
return 0;
}
源代码链接:http://download.csdn.net/detail/juy19901128/9586387
编译步奏自己看着来,就不提供了。readme提供了在linux 下的工具编译。
如果用于嵌入式,注意socket是,其他的函数用宏来替换。