实现XMPP协议的跨平台C语言库,已经封装好

对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是,其他的函数用宏来替换。


你可能感兴趣的:(xmpp)