freeswitch/libs/libdingaling/src/libdingaling.c

3297 lines
84 KiB
C
Raw Normal View History

/*
* libDingaLing XMPP Jingle Library
2014-02-05 21:02:28 +00:00
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is libDingaLing XMPP Jingle Library
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
*
* libdingaling.c -- Main Library Code
*
* QMOD: XMPP Video Signaling + Presentation (video-v1 & camera-v1)
*
*/
#ifndef _MSC_VER
#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <iksemel.h>
#include <apr.h>
#include <apr_network_io.h>
#include <apr_errno.h>
#include <apr_general.h>
#include <apr_thread_proc.h>
#include <apr_thread_mutex.h>
#include <apr_thread_cond.h>
#include <apr_thread_rwlock.h>
#include <apr_file_io.h>
#include <apr_poll.h>
#include <apr_dso.h>
#include <apr_hash.h>
#include <apr_strings.h>
#include <apr_network_io.h>
#include <apr_poll.h>
#include <apr_queue.h>
#include <apr_uuid.h>
#include <apr_strmatch.h>
#define APR_WANT_STDIO
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_env.h>
#include "ldl_compat.h"
#include "libdingaling.h"
#include "sha1.h"
#ifdef _MSC_VER
#include <io.h>
#pragma warning(disable:4127 4706)
#endif
#define microsleep(x) apr_sleep(x * 1000)
#define LDL_CAPS_VER "1.0.0.1"
static int opt_timeout = 30;
static void sha1_hash(char *out, unsigned char *in, unsigned int len);
static int b64encode(unsigned char *in, size_t ilen, unsigned char *out, size_t olen);
static void ldl_random_string(char *buf, uint16_t len, char *set);
static struct {
unsigned int flags;
FILE *log_stream;
int debug;
apr_pool_t *memory_pool;
unsigned int id;
ldl_logger_t logger;
apr_hash_t *avatar_hash;
apr_thread_mutex_t *flag_mutex;
} globals;
struct packet_node {
char id[80];
iks *xml;
unsigned int retries;
apr_time_t next;
};
struct ldl_buffer {
char *buf;
unsigned int len;
int hit;
};
typedef enum {
CS_NEW,
CS_START,
CS_CONNECTED
} ldl_handle_state_t;
struct ldl_handle {
iksparser *parser;
iksid *acc;
iksfilter *filter;
char *login;
char *password;
char *server;
char *status_msg;
char *priority;
uint16_t port;
int features;
int counter;
int job_done;
unsigned int flags;
apr_queue_t *queue;
apr_queue_t *retry_queue;
apr_hash_t *sessions;
apr_hash_t *retry_hash;
apr_hash_t *probe_hash;
apr_hash_t *sub_hash;
apr_thread_mutex_t *lock;
apr_thread_mutex_t *flag_mutex;
ldl_loop_callback_t loop_callback;
ldl_session_callback_t session_callback;
ldl_response_callback_t response_callback;
apr_pool_t *pool;
void *private_info;
FILE *log_stream;
ldl_handle_state_t state;
int fail_count;
};
struct ldl_session {
ldl_state_t state;
ldl_handle_t *handle;
char *id;
char *initiator;
char *them;
char *ip;
char *login;
ldl_payload_t payloads[LDL_MAX_PAYLOADS];
unsigned int payload_len;
/*! \brief Transport candidates, organized per type */
ldl_candidate_t candidates[LDL_TPORT_MAX][LDL_MAX_CANDIDATES];
/*! \brief Length of the candidate list, per transport type */
unsigned int candidate_len[LDL_TPORT_MAX];
apr_pool_t *pool;
apr_hash_t *variables;
apr_time_t created;
void *private_data;
ldl_user_flag_t flags;
};
static int on_disco_default(void *user_data, ikspak *pak);
static int on_vcard(void *user_data, ikspak *pak);
typedef int (*iks_filter_callback_t)(void *user_data, ikspak *pak);
struct ldl_feature {
const char *name;
iks_filter_callback_t callback;
};
typedef struct ldl_feature ldl_feature_t;
#define FEATURE_DISCO "http://jabber.org/protocol/disco"
#define FEATURE_DISCO_INFO "http://jabber.org/protocol/disco#info"
#define FEATURE_VERSION "jabber:iq:version"
#define FEATURE_VCARD "vcard-temp"
#define FEATURE_VOICE "http://www.google.com/xmpp/protocol/voice/v1"
#define FEATURE_VIDEO "http://www.google.com/xmpp/protocol/video/v1"
#define FEATURE_CAMERA "http://www.google.com/xmpp/protocol/camera/v1"
#define FEATURE_LAST "jabber:iq:last"
static ldl_feature_t FEATURES[] = {
{ FEATURE_DISCO, on_disco_default },
{ FEATURE_DISCO_INFO, on_disco_default },
{ FEATURE_VERSION, on_disco_default },
{ FEATURE_VCARD, on_vcard},
{ FEATURE_VOICE, on_disco_default },
{ FEATURE_VIDEO, on_disco_default },
{ FEATURE_CAMERA, on_disco_default },
{ FEATURE_LAST, on_disco_default },
{ NULL, NULL}
};
struct ldl_avatar {
char *path;
char *base64;
char hash[256];
};
typedef struct ldl_avatar ldl_avatar_t;
static void lowercase(char *str)
{
size_t x = 0;
if (str) {
for (x = 0; x < strlen(str); x++) {
str[x] = (char)tolower((int)str[x]);
}
}
}
static char *cut_path(char *in)
{
char *p, *ret = in;
char delims[] = "/\\";
char *i;
for (i = delims; *i; i++) {
p = in;
while ((p = strchr(p, *i)) != 0) {
ret = ++p;
}
}
return ret;
}
static void default_logger(char *file, const char *func, int line, int level, char *fmt, ...)
{
char *fp;
char data[1024];
va_list ap;
fp = cut_path(file);
va_start(ap, fmt);
vsnprintf(data, sizeof(data), fmt, ap);
2011-05-13 21:17:17 +00:00
fprintf(globals.log_stream, "%s:%d %s() %s", fp, line, func, data);
va_end(ap);
}
static unsigned int next_id(void)
{
return globals.id++;
}
static char *iks_name_nons(iks *x)
{
char *r = iks_name(x);
char *p;
if (r && (p = strchr(r, ':'))) {
r = p + 1;
}
return r;
}
char *ldl_session_get_value(ldl_session_t *session, char *key)
{
return apr_hash_get(session->variables, key, APR_HASH_KEY_STRING);
}
void ldl_session_set_value(ldl_session_t *session, const char *key, const char *val)
{
apr_hash_set(session->variables, apr_pstrdup(session->pool, key), APR_HASH_KEY_STRING, apr_pstrdup(session->pool, val));
}
char *ldl_session_get_id(ldl_session_t *session)
{
return session->id;
}
void ldl_session_send_msg(ldl_session_t *session, char *subject, char *body)
{
ldl_handle_send_msg(session->handle, session->login, session->them, subject, body);
}
ldl_status ldl_session_destroy(ldl_session_t **session_p)
{
ldl_session_t *session = *session_p;
if (session) {
apr_pool_t *pool = session->pool;
apr_hash_t *hash = session->handle->sessions;
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Destroyed Session %s\n", session->id);
}
if (session->id) {
apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
}
if (session->them) {
apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
}
apr_pool_destroy(pool);
pool = NULL;
*session_p = NULL;
return LDL_STATUS_SUCCESS;
}
return LDL_STATUS_FALSE;
}
ldl_status ldl_session_create(ldl_session_t **session_p, ldl_handle_t *handle, char *id, char *them, char *me, ldl_user_flag_t flags)
{
ldl_session_t *session = NULL;
if (!(session = apr_palloc(handle->pool, sizeof(ldl_session_t)))) {
globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
*session_p = NULL;
return LDL_STATUS_MEMERR;
}
memset(session, 0, sizeof(ldl_session_t));
apr_pool_create(&session->pool, globals.memory_pool);
session->id = apr_pstrdup(session->pool, id);
session->them = apr_pstrdup(session->pool, them);
if (flags & LDL_FLAG_OUTBOUND) {
session->initiator = apr_pstrdup(session->pool, me);
}
if (ldl_test_flag(handle, LDL_FLAG_COMPONENT)) {
session->login = apr_pstrdup(session->pool, me);
} else {
session->login = apr_pstrdup(session->pool, handle->login);
}
apr_hash_set(handle->sessions, session->id, APR_HASH_KEY_STRING, session);
apr_hash_set(handle->sessions, session->them, APR_HASH_KEY_STRING, session);
session->handle = handle;
session->created = apr_time_now();
session->state = LDL_STATE_NEW;
session->variables = apr_hash_make(session->pool);
session->flags = flags;
*session_p = session;
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Created Session %s\n", id);
}
return LDL_STATUS_SUCCESS;
}
static ldl_status parse_session_code(ldl_handle_t *handle, char *id, char *from, char *to, iks *xml, char *xtype)
{
ldl_session_t *session = NULL;
ldl_signal_t dl_signal = LDL_SIGNAL_NONE;
char *initiator = iks_find_attrib(xml, "initiator");
char *msg = NULL;
if (!(session = apr_hash_get(handle->sessions, id, APR_HASH_KEY_STRING))) {
ldl_session_create(&session, handle, id, from, to, LDL_FLAG_NONE);
if (!session) {
return LDL_STATUS_MEMERR;
}
}
if (!session) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Non-Existent Session %s!\n", id);
}
return LDL_STATUS_MEMERR;
}
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Message for Session %s\n", id);
}
while(xml) {
2011-03-10 02:26:17 +00:00
char *type = NULL;
iks *tag;
2011-03-10 02:26:17 +00:00
if (iks_type(xml) != IKS_CDATA) {
2011-03-10 02:26:17 +00:00
type = xtype ? xtype : iks_find_attrib(xml, "type");
}
2011-03-10 02:26:17 +00:00
if (type) {
if (!strcasecmp(type, "redirect")) {
apr_hash_t *hash = session->handle->sessions;
char *p = to;
if ((p = strchr(to, ':'))) {
p++;
} else {
p = to;
}
apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
session->them = apr_pstrdup(session->pool, p);
apr_hash_set(handle->sessions, session->them, APR_HASH_KEY_STRING, session);
apr_hash_set(handle->sessions, session->id, APR_HASH_KEY_STRING, session);
dl_signal = LDL_SIGNAL_REDIRECT;
} else if (!strcasecmp(type, "initiate") || !strcasecmp(type, "accept")) {
dl_signal = LDL_SIGNAL_INITIATE;
if (!strcasecmp(type, "accept")) {
msg = "accept";
}
if (!session->initiator && initiator) {
session->initiator = apr_pstrdup(session->pool, initiator);
}
tag = iks_child (xml);
while(tag) {
if (!strcasecmp(iks_name_nons(tag), "description")) {
iks * itag = iks_child (tag);
while(itag) {
if (!strcasecmp(iks_name_nons(itag), "payload-type") && session->payload_len < LDL_MAX_PAYLOADS) {
char *name = iks_find_attrib(itag, "name");
char *id = iks_find_attrib(itag, "id");
char *rate = iks_find_attrib(itag, "clockrate");
char *ptime = iks_find_attrib(itag, "ptime");
if (name && id) {
session->payloads[session->payload_len].name = apr_pstrdup(session->pool, name);
session->payloads[session->payload_len].id = atoi(id);
if (ptime) {
session->payloads[session->payload_len].ptime = atoi(ptime);
} else {
session->payloads[session->payload_len].ptime = 20;
}
if (rate) {
session->payloads[session->payload_len].rate = atoi(rate);
} else {
if (!strncasecmp(iks_name(itag), "vid", 3)) {
session->payloads[session->payload_len].rate = 90000;
} else {
session->payloads[session->payload_len].rate = 8000;
}
}
session->payload_len++;
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Add Payload [%s] id='%s'\n", name, id);
}
}
}
itag = iks_next_tag(itag);
}
}
tag = iks_next_tag(tag);
}
} else if (!strcasecmp(type, "transport-accept")) {
dl_signal = LDL_SIGNAL_TRANSPORT_ACCEPT;
} else if (!strcasecmp(type, "reject")) {
dl_signal = LDL_SIGNAL_REJECT;
} else if (!strcasecmp(type, "transport-info") || !strcasecmp(type, "candidates")) {
char *tid = iks_find_attrib(xml, "id");
dl_signal = LDL_SIGNAL_CANDIDATES;
tag = iks_child (xml);
id = type;
if (tag && !strcasecmp(iks_name_nons(tag), "transport")) {
tag = iks_child(tag);
}
for (;tag;tag = iks_next_tag(tag)) {
if (!strcasecmp(iks_name_nons(tag), "info_element")) {
char *name = iks_find_attrib(tag, "name");
char *value = iks_find_attrib(tag, "value");
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Info Element [%s]=[%s]\n", name, value);
}
ldl_session_set_value(session, name, value);
} else if (!strcasecmp(iks_name_nons(tag), "candidate") /*&& session->candidate_len < LDL_MAX_CANDIDATES*/) {
char *key;
double pref = 0.0;
int index = -1;
ldl_transport_type_t tport;
unsigned int *candidate_len = NULL;
ldl_candidate_t (*candidates)[LDL_MAX_CANDIDATES] = NULL;
if ((key = iks_find_attrib(tag, "preference"))) {
unsigned int x;
pref = strtod(key, NULL);
/* Check what kind of candidate this is */
if ((key = iks_find_attrib(tag, "name")) && ((tport = ldl_transport_type_parse(key)) != LDL_TPORT_MAX)) {
candidates = &(session->candidates[tport]);
candidate_len = &(session->candidate_len[tport]);
} else {
globals.logger(DL_LOG_WARNING, "No such transport type: %s\n", key);
continue;
}
if (*candidate_len >= LDL_MAX_CANDIDATES) {
globals.logger(DL_LOG_WARNING, "Too many %s candidates\n", key);
continue;
}
for (x = 0; x < *candidate_len; x++) {
if ((*candidates)[x].pref == pref) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Duplicate Pref! Updating...\n");
}
index = x;
break;
}
}
}
if (index < 0) {
index = (*candidate_len)++;
}
(*candidates)[index].pref = pref;
if (tid) {
(*candidates)[index].tid = apr_pstrdup(session->pool, tid);
}
if ((key = iks_find_attrib(tag, "name"))) {
(*candidates)[index].name = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "type"))) {
(*candidates)[index].type = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "protocol"))) {
(*candidates)[index].protocol = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "username"))) {
(*candidates)[index].username = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "password"))) {
(*candidates)[index].password = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "address"))) {
(*candidates)[index].address = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(tag, "port"))) {
(*candidates)[index].port = (uint16_t)atoi(key);
}
if (!(*candidates)[index].type) {
(*candidates)[index].type = apr_pstrdup(session->pool, "stun");
}
if (globals.debug) {
globals.logger(DL_LOG_CRIT,
"New Candidate %d\n"
"name=%s\n"
"type=%s\n"
"protocol=%s\n"
"username=%s\n"
"password=%s\n"
"address=%s\n"
"port=%d\n"
"pref=%0.2f\n",
*candidate_len,
(*candidates)[index].name,
(*candidates)[index].type,
(*candidates)[index].protocol,
(*candidates)[index].username,
(*candidates)[index].password,
(*candidates)[index].address,
(*candidates)[index].port,
(*candidates)[index].pref
);
}
}
}
} else if (!strcasecmp(type, "terminate")) {
dl_signal = LDL_SIGNAL_TERMINATE;
} else if (!strcasecmp(type, "error")) {
dl_signal = LDL_SIGNAL_ERROR;
}
}
xml = iks_child(xml);
}
if (handle->session_callback && dl_signal) {
handle->session_callback(handle, session, dl_signal, to, from, id, msg);
}
return LDL_STATUS_SUCCESS;
}
static ldl_status parse_jingle_code(ldl_handle_t *handle, iks *xml, char *to, char *from, char *type)
{
ldl_session_t *session = NULL;
ldl_signal_t dl_signal = LDL_SIGNAL_NONE;
char *initiator = iks_find_attrib(xml, "initiator");
char *msg = NULL;
char *id = iks_find_attrib(xml, "sid");
char *action = iks_find_attrib(xml, "action");
iks *tag;
if (!strcasecmp(type, "error")) {
action = type;
}
2012-10-31 15:46:17 +00:00
if (!(id && action && from)) {
globals.logger(DL_LOG_CRIT, "missing required params\n");
return LDL_STATUS_FALSE;
}
if (!(session = apr_hash_get(handle->sessions, id, APR_HASH_KEY_STRING))) {
ldl_session_create(&session, handle, id, from, to, LDL_FLAG_NONE);
if (!session) {
return LDL_STATUS_MEMERR;
}
}
if (!session) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Non-Existent Session %s!\n", id);
}
return LDL_STATUS_MEMERR;
}
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Message for Session %s\n", id);
}
if (action) {
if (!strcasecmp(action, "redirect")) {
apr_hash_t *hash = session->handle->sessions;
char *p = to;
if ((p = strchr(to, ':'))) {
p++;
} else {
p = to;
}
apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
session->them = apr_pstrdup(session->pool, p);
apr_hash_set(handle->sessions, session->them, APR_HASH_KEY_STRING, session);
apr_hash_set(handle->sessions, session->id, APR_HASH_KEY_STRING, session);
dl_signal = LDL_SIGNAL_REDIRECT;
} else if (!strcasecmp(action, "session-initiate") || !strcasecmp(action, "session-accept")) {
dl_signal = LDL_SIGNAL_INITIATE;
if (!strcasecmp(action, "session-accept")) {
msg = "accept";
}
if (!session->initiator && initiator) {
session->initiator = apr_pstrdup(session->pool, initiator);
}
tag = iks_child (xml);
while(tag) {
if (!strcasecmp(iks_name_nons(tag), "content")) {
iks *dtag = iks_child (tag);
char key[512];
if (!strcasecmp(iks_name_nons(dtag), "description")) {
iks *itag = iks_child (dtag);
char *name = iks_find_attrib(tag, "name");
char *media = iks_find_attrib(dtag, "media");
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Found description of type '%s' media type '%s'\n", name, media);
}
while(itag) {
if (!strcasecmp(iks_name_nons(itag), "rtcp-mux")) {
snprintf(key, sizeof(key), "%s:rtcp-mux", media);
ldl_session_set_value(session, key, "true");
}
if (!strcasecmp(iks_name_nons(itag), "encryption")) {
iks *etag = iks_child (itag);
while(etag) {
char *suite = iks_find_attrib(etag, "crypto-suite");
char *params = iks_find_attrib(etag, "key-params");
char *tag = iks_find_attrib(etag, "tag");
char val[512];
if (suite && params && tag) {
snprintf(key, sizeof(key), "%s:crypto:%s", media, tag);
snprintf(val, sizeof(val), "%s %s %s", tag, suite, params);
ldl_session_set_value(session, key, val);
}
etag = iks_next_tag(etag);
}
}
if (!strcasecmp(iks_name_nons(itag), "payload-type") && session->payload_len < LDL_MAX_PAYLOADS) {
char *name = iks_find_attrib(itag, "name");
char *id = iks_find_attrib(itag, "id");
char *rate = iks_find_attrib(itag, "clockrate");
if (name && id) {
session->payloads[session->payload_len].name = apr_pstrdup(session->pool, name);
session->payloads[session->payload_len].id = atoi(id);
if (rate) {
session->payloads[session->payload_len].rate = atoi(rate);
} else {
if (!strcasecmp(media, "video")) {
session->payloads[session->payload_len].rate = 90000;
} else {
session->payloads[session->payload_len].rate = 8000;
}
}
session->payload_len++;
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Add Payload [%s] id='%s'\n", name, id);
}
}
}
itag = iks_next_tag(itag);
}
}
}
tag = iks_next_tag(tag);
}
} else if (!strcasecmp(action, "transport-accept")) {
dl_signal = LDL_SIGNAL_TRANSPORT_ACCEPT;
} else if (!strcasecmp(action, "reject")) {
dl_signal = LDL_SIGNAL_REJECT;
} else if (!strcasecmp(action, "transport-info")) {
tag = iks_child (xml);
while(tag) {
if (!strcasecmp(iks_name_nons(tag), "content")) {
iks *ttag = iks_child (tag);
dl_signal = LDL_SIGNAL_CANDIDATES;
id = action;
if (ttag && !strcasecmp(iks_name_nons(ttag), "transport")) {
ttag = iks_child(ttag);
}
for (;ttag;ttag = iks_next_tag(ttag)) {
if (!strcasecmp(iks_name_nons(ttag), "info_element")) {
char *name = iks_find_attrib(ttag, "name");
char *value = iks_find_attrib(ttag, "value");
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Info Element [%s]=[%s]\n", name, value);
}
ldl_session_set_value(session, name, value);
} else if (!strcasecmp(iks_name_nons(ttag), "candidate")) {
char *key;
double pref = 0.0;
int index = -1;
ldl_transport_type_t tport;
unsigned int *candidate_len = NULL;
ldl_candidate_t (*candidates)[LDL_MAX_CANDIDATES] = NULL;
if ((key = iks_find_attrib(ttag, "preference"))) {
unsigned int x;
pref = strtod(key, NULL);
/* Check what kind of candidate this is */
if ((key = iks_find_attrib(ttag, "name")) && ((tport = ldl_transport_type_parse(key)) != LDL_TPORT_MAX)) {
candidates = &(session->candidates[tport]);
candidate_len = &(session->candidate_len[tport]);
} else {
globals.logger(DL_LOG_WARNING, "No such transport type: %s\n", key);
continue;
}
if (*candidate_len >= LDL_MAX_CANDIDATES) {
globals.logger(DL_LOG_WARNING, "Too many %s candidates\n", key);
continue;
}
for (x = 0; x < *candidate_len; x++) {
if ((*candidates)[x].pref == pref) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Duplicate Pref!\n");
}
index = x;
break;
}
}
2012-10-31 15:45:09 +00:00
} else {
globals.logger(DL_LOG_WARNING, "No preference specified");
continue;
}
if (index < 0) {
index = (*candidate_len)++;
}
(*candidates)[index].pref = pref;
if ((key = iks_find_attrib(ttag, "name"))) {
(*candidates)[index].name = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "type"))) {
(*candidates)[index].type = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "protocol"))) {
(*candidates)[index].protocol = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "username"))) {
(*candidates)[index].username = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "password"))) {
(*candidates)[index].password = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "address"))) {
(*candidates)[index].address = apr_pstrdup(session->pool, key);
}
if ((key = iks_find_attrib(ttag, "port"))) {
(*candidates)[index].port = (uint16_t)atoi(key);
}
if (!(*candidates)[index].type) {
(*candidates)[index].type = apr_pstrdup(session->pool, "stun");
}
if (globals.debug) {
globals.logger(DL_LOG_CRIT,
"New Candidate %d\n"
"name=%s\n"
"type=%s\n"
"protocol=%s\n"
"username=%s\n"
"password=%s\n"
"address=%s\n"
"port=%d\n"
"pref=%0.2f\n",
*candidate_len,
(*candidates)[index].name,
(*candidates)[index].type,
(*candidates)[index].protocol,
(*candidates)[index].username,
(*candidates)[index].password,
(*candidates)[index].address,
(*candidates)[index].port,
(*candidates)[index].pref
);
}
}
}
}
tag = iks_next_tag(tag);
}
} else if (!strcasecmp(action, "session-terminate")) {
dl_signal = LDL_SIGNAL_TERMINATE;
} else if (!strcasecmp(action, "error")) {
dl_signal = LDL_SIGNAL_ERROR;
}
}
if (handle->session_callback && dl_signal) {
handle->session_callback(handle, session, dl_signal, to, from, id, msg);
}
return LDL_STATUS_SUCCESS;
}
const char *marker = "TRUE";
static int on_vcard(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
char *from = iks_find_attrib(pak->x, "from");
char *to = iks_find_attrib(pak->x, "to");
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_VCARD, to, from, pak->id, NULL);
}
return IKS_FILTER_EAT;
}
static int on_disco_default(void *user_data, ikspak *pak)
{
char *node = NULL;
char *ns = NULL;
ldl_handle_t *handle = user_data;
iks *iq, *query, *tag;
uint8_t send = 0;
int x;
if (pak && pak->query) {
ns = iks_find_attrib(pak->query, "xmlns");
node = iks_find_attrib(pak->query, "node");
}
if (pak->subtype == IKS_TYPE_RESULT) {
globals.logger(DL_LOG_CRIT, "FixME!!! node=[%s]\n", node?node:"");
} else if (pak->subtype == IKS_TYPE_GET) {
if ((iq = iks_new("iq"))) {
int all = 0;
iks_insert_attrib(iq, "from", handle->login);
if (pak->from) {
iks_insert_attrib(iq, "to", pak->from->full);
}
iks_insert_attrib(iq, "id", pak->id);
iks_insert_attrib(iq, "type", "result");
if (!(query = iks_insert (iq, "query"))) {
goto fail;
}
iks_insert_attrib(query, "xmlns", ns);
if (!strcasecmp(ns, FEATURE_LAST)) {
iks_insert_attrib(query, "seconds", "1");
}
if (!(tag = iks_insert (query, "identity"))) {
goto fail;
}
iks_insert_attrib(tag, "category", "gateway");
//iks_insert_attrib(tag, "type", "voice");
iks_insert_attrib(tag, "name", "LibDingaLing");
if (!strcasecmp(ns, FEATURE_DISCO_INFO)) {
if (!node) {
all++;
} else {
char *p;
if ((p = strstr(node, "caps#"))) {
char *what = p + 5;
if (!strcasecmp(what, "voice-v1")) {
if (!(tag = iks_insert (query, "feature"))) {
goto fail;
}
iks_insert_attrib(tag, "var", FEATURE_VOICE);
goto done;
}
}
}
}
for (x = 0; FEATURES[x].name; x++) {
if (all || !ns || !strcasecmp(ns, FEATURES[x].name)) {
if (!(tag = iks_insert (query, "feature"))) {
goto fail;
}
iks_insert_attrib(tag, "var", FEATURES[x].name);
}
}
done:
apr_queue_push(handle->queue, iq);
iq = NULL;
send = 1;
}
fail:
if (iq) {
iks_delete(iq);
}
if (!send) {
globals.logger(DL_LOG_CRIT, "Memory Error!\n");
}
}
return IKS_FILTER_EAT;
}
static int on_presence(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
char *from = iks_find_attrib(pak->x, "from");
char *to = iks_find_attrib(pak->x, "to");
char *type = iks_find_attrib(pak->x, "type");
char *show = iks_find_cdata(pak->x, "show");
char *status = iks_find_cdata(pak->x, "status");
char id[1024];
char *resource;
struct ldl_buffer *buffer;
ldl_signal_t dl_signal = LDL_SIGNAL_PRESENCE_IN;
int done = 0;
if (type && *type) {
if (!strcasecmp(type, "unavailable")) {
dl_signal = LDL_SIGNAL_PRESENCE_OUT;
} else if (!strcasecmp(type, "probe")) {
dl_signal = LDL_SIGNAL_PRESENCE_PROBE;
}
if (!status) {
status = type;
}
} else {
if (!status) {
status = "Available";
}
}
apr_cpystrn(id, from, sizeof(id));
lowercase(id);
if ((resource = strchr(id, '/'))) {
*resource++ = '\0';
}
if (!apr_hash_get(handle->sub_hash, from, APR_HASH_KEY_STRING)) {
iks *msg;
apr_hash_set(handle->sub_hash, apr_pstrdup(handle->pool, from), APR_HASH_KEY_STRING, &marker);
if ((msg = iks_make_s10n (IKS_TYPE_SUBSCRIBED, id, "Ding A Ling...."))) {
apr_queue_push(handle->queue, msg);
msg = NULL;
}
}
if (resource && (strstr(resource, "talk") || strstr(resource, "telepathy")) && (buffer = apr_hash_get(handle->probe_hash, id, APR_HASH_KEY_STRING))) {
apr_cpystrn(buffer->buf, from, buffer->len);
buffer->hit = 1;
done = 1;
}
if (!done) {
iks *xml = iks_find(pak->x, "c");
if (!xml) {
xml = iks_find(pak->x, "caps:c");
}
if (xml) {
char *ext = iks_find_attrib(xml, "ext");;
if (ext && strstr(ext, "voice-v1") && (buffer = apr_hash_get(handle->probe_hash, id, APR_HASH_KEY_STRING))) {
apr_cpystrn(buffer->buf, from, buffer->len);
buffer->hit = 1;
done = 1;
}
}
}
if (handle->session_callback) {
handle->session_callback(handle, NULL, dl_signal, to, id, status ? status : "n/a", show ? show : "n/a");
}
return IKS_FILTER_EAT;
}
static char *ldl_handle_strdup(ldl_handle_t *handle, char *str)
{
char *dup;
apr_size_t len;
len = strlen(str) + 1;
dup = apr_palloc(handle->pool, len);
assert(dup != NULL);
strncpy(dup, str, len);
return dup;
}
static void ldl_strip_resource(char *in)
{
char *p;
if ((p = strchr(in, '/'))) {
*p = '\0';
}
}
static ldl_avatar_t *ldl_get_avatar(ldl_handle_t *handle, char *path, char *from)
{
ldl_avatar_t *ap;
uint8_t image[8192];
unsigned char base64[9216] = "";
int fd = -1;
size_t bytes;
char *key;
//char hash[128] = "";
if (from && (ap = (ldl_avatar_t *) apr_hash_get(globals.avatar_hash, from, APR_HASH_KEY_STRING))) {
return ap;
}
if (path && from) {
if ((ap = (ldl_avatar_t *) apr_hash_get(globals.avatar_hash, path, APR_HASH_KEY_STRING))) {
key = ldl_handle_strdup(handle, from);
ldl_strip_resource(key);
apr_hash_set(globals.avatar_hash, key, APR_HASH_KEY_STRING, ap);
return ap;
}
}
if (!(path && from)) {
return NULL;
}
if ((fd = open(path, O_RDONLY, 0)) < 0) {
globals.logger(DL_LOG_ERR, "File %s does not exist!\n", path);
return NULL;
}
bytes = read(fd, image, sizeof(image));
close(fd);
fd = -1;
ap = malloc(sizeof(*ap));
assert(ap != NULL);
memset(ap, 0, sizeof(*ap));
sha1_hash(ap->hash, (unsigned char *) image, (unsigned int)bytes);
ap->path = strdup(path);
key = ldl_handle_strdup(handle, from);
ldl_strip_resource(key);
b64encode((unsigned char *)image, bytes, base64, sizeof(base64));
ap->base64 = strdup((const char *)base64);
apr_hash_set(globals.avatar_hash, ap->path, APR_HASH_KEY_STRING, ap);
apr_hash_set(globals.avatar_hash, key, APR_HASH_KEY_STRING, ap);
return ap;
}
static void do_presence(ldl_handle_t *handle, char *from, char *to, char *type, char *rpid, char *message, char *avatar)
{
iks *pres;
char buf[512];
iks *tag;
if (from && !strchr(from, '/')) {
snprintf(buf, sizeof(buf), "%s/talk", from);
from = buf;
}
if (ldl_test_flag(handle, LDL_FLAG_COMPONENT) && from && to && ldl_jid_domcmp(from, to)) {
globals.logger(DL_LOG_ERR, "Refusal to send presence from and to the same domain in component mode [%s][%s]\n", from, to);
return;
}
if ((pres = iks_new("presence"))) {
iks_insert_attrib(pres, "xmlns", "jabber:client");
if (from) {
iks_insert_attrib(pres, "from", from);
}
if (to) {
iks_insert_attrib(pres, "to", to);
}
if (type) {
iks_insert_attrib(pres, "type", type);
}
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
if (rpid) {
if ((tag = iks_insert (pres, "show"))) {
iks_insert_cdata(tag, rpid, 0);
}
}
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
if (message) {
if ((tag = iks_insert (pres, "status"))) {
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
iks_insert_cdata(tag, message, 0);
}
}
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
if (message || rpid) {
ldl_avatar_t *ap;
if (avatar) {
if ((ap = ldl_get_avatar(handle, avatar, from))) {
if ((tag = iks_insert(pres, "x"))) {
iks *hash;
iks_insert_attrib(tag, "xmlns", "vcard-temp:x:update");
if ((hash = iks_insert(tag, "photo"))) {
iks_insert_cdata(hash, ap->hash, 0);
}
}
}
}
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
if ((tag = iks_insert(pres, "c"))) {
iks_insert_attrib(tag, "node", "http://www.freeswitch.org/xmpp/client/caps");
iks_insert_attrib(tag, "ver", LDL_CAPS_VER);
iks_insert_attrib(tag, "ext", "sidebar voice-v1 video-v1 camera-v1");
Presence and Chat Gateway Code This is some brand new stuff to gateway chat/presence/audio from one protocol to another So far it only works between google/jingle and SIP All I had to test the SIP end was X-Lite and Eyebeam and GoogleTalk on the jingle end. With this setup registered X-Lite's can chat with each other and call each other as well as X-Lite to GoogleTalk and GoogleTalk to X-Lite audio calls. Chat May also be done between X-Lite and jabber You'll also need a jabber server configured for component login so you can interface. We have only tested with jabberd2 so far. Configure DNS so srv records for jabber for your subdomain (fs.mydomain.com in the example) so the jabber records are pointed at your jabber server. RELEVANT CONFIGS <!-- Brian has no jingle support so send calls to him over to his iax url --> <extension name="bkw"> <condition field="destination_number" expression="^jingle\+brian@agents.cylynx.com$"> <action application="bridge" data="iax/guest@brianwest.homeunix.org/9184290404"/> </condition> </extension> <!-- Assumption is made here that both sip and jingle have the same profile/domain name as documented below --> <extension name="jingle2sip"> <condition field="source" expression="mod_dingaling"/> <condition field="destination_number" expression="^sip\+([^\@]+)\@(.*)$"> <action application="bridge" data="sofia/$2/$1%$2"/> </condition> </extension> <extension name="sip2jingle"> <condition field="source" expression="mod_sofia"/> <condition field="destination_number" expression="^jingle\+([^\@]+)\@(.*)$"> <action application="bridge" data="dingaling/sip+${sip_fromuser}@${sip_fromhost}/$1@$2"/> </condition> </extension> <configuration name="sofia.conf" description="sofia Endpoint"> <global_settings> <param name="log-level" value="0"/> </global_settings> <profiles> <profile name="fs.mydomain.com"> <registrations/> <settings> <param name="debug" value="1"/> <param name="rfc2833-pt" value="101"/> <param name="sip-port" value="5060"/> <param name="dialplan" value="XML"/> <param name="dtmf-duration" value="100"/> <param name="codec-prefs" value="PCMU"/> <param name="codec-ms" value="20"/> <param name="accept-blind-reg" value="true"/> <param name="manage-presence" value="true"/> <!--<param name="full-id-in-dialplan" value="true"/>--> <!--<param name="auth-calls" value="true"/>--> <!--<param name="auth-all-packets" value="true"/>--> <param name="use-rtp-timer" value="true"/> <param name="rtp-timer-name" value="soft"/> <param name="rtp-ip" value="100.200.100.200"/> <param name="sip-ip" value="fs.mydomain.com"/> </settings> </profile> </profiles> </configuration> <configuration name="dingaling.conf" description="XMPP Jingle Endpoint"> <settings> <param name="debug" value="0"/> <param name="codec-prefs" value="PCMU"/> </settings> <profile type="component"> <param name="name" value="fs.mydomain.com"/> <param name="password" value="secret"/> <param name="dialplan" value="XML"/> <param name="rtp-ip" value="208.64.200.42"/> <param name="server" value="jabber.freeswitch.org:5347"/> <!-- disable to trade async for more calls --> <param name="use-rtp-timer" value="true"/> <param name="exten" value="_auto_"/> <!--<param name="vad" value="both"/>--> </profile> </configuration> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3115 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-10-20 06:17:00 +00:00
iks_insert_attrib(tag, "client", "libdingaling");
iks_insert_attrib(tag, "xmlns", "http://jabber.org/protocol/caps");
}
}
apr_queue_push(handle->queue, pres);
pres = NULL;
}
}
static void do_roster(ldl_handle_t *handle)
{
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_ROSTER, NULL, handle->login, NULL, NULL);
}
}
static int on_unsubscribe(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
char *from = iks_find_attrib(pak->x, "from");
char *to = iks_find_attrib(pak->x, "to");
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_UNSUBSCRIBE, to, from, NULL, NULL);
}
return IKS_FILTER_EAT;
}
static int on_subscribe(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
char *from = iks_find_attrib(pak->x, "from");
char *to = iks_find_attrib(pak->x, "to");
iks *msg = NULL;
char *id = strdup(from);
char *r;
if (!id) {
return -1;
}
if ((r = strchr(from, '/'))) {
*r++ = '\0';
}
if ((msg = iks_make_s10n (IKS_TYPE_SUBSCRIBED, id, "Ding A Ling...."))) {
if (to && ldl_test_flag(handle, LDL_FLAG_COMPONENT)) {
iks_insert_attrib(msg, "from", to);
}
apr_queue_push(handle->queue, msg);
msg = NULL;
}
if ((msg = iks_make_s10n (IKS_TYPE_SUBSCRIBE, id, "Ding A Ling...."))) {
if (to && ldl_test_flag(handle, LDL_FLAG_COMPONENT)) {
iks_insert_attrib(msg, "from", to);
}
apr_queue_push(handle->queue, msg);
msg = NULL;
}
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_SUBSCRIBE, to, from, NULL, NULL);
}
return IKS_FILTER_EAT;
}
static void cancel_retry(ldl_handle_t *handle, char *id)
{
struct packet_node *packet_node;
apr_thread_mutex_lock(handle->lock);
if ((packet_node = apr_hash_get(handle->retry_hash, id, APR_HASH_KEY_STRING))) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Cancel packet %s\n", packet_node->id);
}
packet_node->retries = 0;
}
apr_thread_mutex_unlock(handle->lock);
}
static iks* working_find(iks *tag, const char *name)
{
while(tag) {
if (!strcasecmp(iks_name(tag), name)) {
return tag;
}
tag = iks_next_tag(tag);
}
return NULL;
}
static iks* working_find_nons(iks *tag, const char *name)
{
while(tag) {
char *a = iks_name(tag);
char *b = (char *)name;
char *p;
if ((p = strchr(a, ':'))) {
a = p+1;
}
if ((p = strchr(b, ':'))) {
b = p+1;
}
if (!strcasecmp(a,b)) {
return tag;
}
tag = iks_next_tag(tag);
}
return NULL;
}
static int on_commands(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
char *from = iks_find_attrib(pak->x, "from");
char *to = iks_find_attrib(pak->x, "to");
char *iqid = iks_find_attrib(pak->x, "id");
char *type = iks_find_attrib(pak->x, "type");
uint8_t is_result = strcasecmp(type, "result") ? 0 : 1;
uint8_t is_error = strcasecmp(type, "error") ? 0 : 1;
iks *xml, *xsession, *xerror = NULL, *xredir = NULL;
iks *xjingle;
2011-05-13 21:17:17 +00:00
xml = iks_child (pak->x);
2011-05-13 21:17:17 +00:00
if (is_error) {
if ((xerror = working_find(xml, "error"))) {
char *code = iks_find_attrib(xerror, "code");
if (code && !strcmp(code, "302") &&
((xredir = iks_find(xerror, "ses:redirect")) || (xredir = iks_find(xerror, "redirect")))) {
is_result = 0;
is_error = 0;
cancel_retry(handle, iqid);
}
}
}
if (is_result) {
iks *tag = iks_child (pak->x);
while(tag) {
if (!strcasecmp(iks_name_nons(tag), "bind")) {
char *jid = iks_find_cdata(tag, "jid");
char *resource = strchr(jid, '/');
if (resource) {
resource++;
handle->acc->resource = apr_pstrdup(handle->pool, resource);
}
handle->login = apr_pstrdup(handle->pool, jid);
#if 0
if ((iq = iks_new("iq"))) {
iks_insert_attrib(iq, "type", "get");
iks_insert_attrib(iq, "id", "roster");
x = iks_insert(iq, "query");
iks_insert_attrib(x, "xmlns", "jabber:iq:roster");
iks_insert_attrib(x, "xmlns:gr", "google:roster");
iks_insert_attrib(x, "gr:ext", "2");
iks_insert_attrib(x, "gr:include", "all");
apr_queue_push(handle->queue, iq);
iq = NULL;
break;
}
#endif
}
tag = iks_next_tag(tag);
}
}
if ((is_result || is_error) && iqid && from) {
cancel_retry(handle, iqid);
if (is_result) {
if (handle->response_callback) {
handle->response_callback(handle, iqid);
}
return IKS_FILTER_EAT;
} else if (is_error) {
return IKS_FILTER_EAT;
}
}
if ((handle->flags & LDL_FLAG_JINGLE) && (xjingle = working_find_nons(xml, "jin:jingle"))) {
if (parse_jingle_code(handle, xjingle, to, from, type) == LDL_STATUS_SUCCESS) {
iks *reply;
if ((reply = iks_make_iq(IKS_TYPE_RESULT, NULL))) {
iks_insert_attrib(reply, "to", from);
iks_insert_attrib(reply, "from", to);
iks_insert_attrib(reply, "id", iqid);
apr_queue_push(handle->queue, reply);
reply = NULL;
}
}
} else if ((xsession = working_find_nons(xml, "ses:session"))) {
char *id;
id = iks_find_attrib(xsession, "id");
if (xredir) {
to = iks_cdata(iks_child(xredir));
type = "redirect";
}
if (strcasecmp(type, "error") && strcasecmp(type, "redirect")) {
type = NULL;
}
if (parse_session_code(handle, id, from, to, xsession, type) == LDL_STATUS_SUCCESS) {
iks *reply;
if ((reply = iks_make_iq(IKS_TYPE_RESULT, NULL))) {
iks_insert_attrib(reply, "to", from);
iks_insert_attrib(reply, "from", to);
iks_insert_attrib(reply, "id", iqid);
apr_queue_push(handle->queue, reply);
reply = NULL;
}
}
}
return IKS_FILTER_EAT;
}
static int on_result(void *user_data, ikspak *pak)
{
ldl_handle_t *handle = user_data;
iks *msg, *ctag, *tag;
if ((msg = iks_make_pres (IKS_SHOW_AVAILABLE, handle->status_msg))) {
ctag = iks_insert(msg, "c");
iks_insert_attrib(ctag, "node", "http://www.freeswitch.org/xmpp/client/caps");
iks_insert_attrib(ctag, "ver", "1.0.0.1");
iks_insert_attrib(ctag, "ext", "sidebar voice-v1 video-v1");
iks_insert_attrib(ctag, "client", "libdingaling");
iks_insert_attrib(ctag, "xmlns", "http://jabber.org/protocol/caps");
if (handle->priority && strlen(handle->priority)) {
tag = iks_insert (msg, "priority");
iks_insert_cdata(tag, handle->priority, 0);
}
apr_queue_push(handle->queue, msg);
msg = NULL;
}
return IKS_FILTER_EAT;
}
static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#define B64BUFFLEN 1024
static int b64encode(unsigned char *in, size_t ilen, unsigned char *out, size_t olen)
{
int y=0,bytes=0;
size_t x=0;
unsigned int b=0,l=0;
for(x=0;x<ilen;x++) {
b = (b<<8) + in[x];
l += 8;
while (l >= 6) {
out[bytes++] = c64[(b>>(l-=6))%64];
if(++y!=72) {
continue;
}
//out[bytes++] = '\n';
y=0;
}
}
if (l > 0) {
out[bytes++] = c64[((b%16)<<(6-l))%64];
}
if (l != 0) while (l < 6) {
out[bytes++] = '=', l += 2;
}
return 0;
}
static void sha1_hash(char *out, unsigned char *in, unsigned int len)
{
SHA1Context sha;
char *p;
int x;
unsigned char digest[SHA1_HASH_SIZE] = "";
SHA1Init(&sha);
SHA1Update(&sha, in, len);
SHA1Final(&sha, digest);
p = out;
for (x = 0; x < SHA1_HASH_SIZE; x++) {
p += sprintf(p, "%2.2x", digest[x]);
}
}
static int on_stream_component(ldl_handle_t *handle, int type, iks *node)
{
ikspak *pak = NULL;
if (node) {
pak = iks_packet(node);
}
switch (type) {
case IKS_NODE_START:
if (pak && handle->state == CS_NEW) {
char secret[256] = "";
char hash[256] = "";
char handshake[512] = "";
snprintf(secret, sizeof(secret), "%s%s", pak->id, handle->password);
sha1_hash(hash, (unsigned char *) secret, (unsigned int)strlen(secret));
snprintf(handshake, sizeof(handshake), "<handshake>%s</handshake>", hash);
iks_send_raw(handle->parser, handshake);
handle->state = CS_START;
if (iks_recv(handle->parser, 1) == 2) {
handle->state = CS_CONNECTED;
if (!ldl_test_flag(handle, LDL_FLAG_AUTHORIZED)) {
do_roster(handle);
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_LOGIN_SUCCESS, "user", "core", "Login Success", handle->login);
}
globals.logger(DL_LOG_DEBUG, "XMPP authenticated\n");
ldl_set_flag_locked(handle, LDL_FLAG_AUTHORIZED);
ldl_set_flag_locked(handle, LDL_FLAG_CONNECTED);
handle->fail_count = 0;
}
} else {
globals.logger(DL_LOG_ERR, "LOGIN ERROR!\n");
handle->state = CS_NEW;
}
break;
}
break;
case IKS_NODE_NORMAL:
break;
case IKS_NODE_ERROR:
globals.logger(DL_LOG_ERR, "NODE ERROR!\n");
return IKS_HOOK;
case IKS_NODE_STOP:
globals.logger(DL_LOG_ERR, "DISCONNECTED!\n");
return IKS_HOOK;
}
iks_filter_packet(handle->filter, pak);
if (handle->job_done == 1) {
return IKS_HOOK;
}
if (node) {
iks_delete(node);
}
return IKS_OK;
}
static int on_stream(ldl_handle_t *handle, int type, iks *node)
{
handle->counter = opt_timeout;
switch (type) {
case IKS_NODE_START:
if (ldl_test_flag(handle, LDL_FLAG_TLS) && !iks_is_secure(handle->parser)) {
if (iks_has_tls()) {
iks_start_tls(handle->parser);
} else {
globals.logger(DL_LOG_WARNING, "TLS NOT SUPPORTED IN THIS BUILD!\n");
}
}
break;
case IKS_NODE_NORMAL:
if (node && strcmp("stream:features", iks_name(node)) == 0) {
handle->features = iks_stream_features(node);
if (ldl_test_flag(handle, LDL_FLAG_TLS) && !iks_is_secure(handle->parser))
break;
if (ldl_test_flag(handle, LDL_FLAG_CONNECTED)) {
iks *t;
if (handle->features & IKS_STREAM_BIND) {
if ((t = iks_make_resource_bind(handle->acc))) {
apr_queue_push(handle->queue, t);
t = NULL;
}
}
if (handle->features & IKS_STREAM_SESSION) {
if ((t = iks_make_session())) {
iks_insert_attrib(t, "id", "auth");
apr_queue_push(handle->queue, t);
t = NULL;
}
}
} else {
if (handle->features & IKS_STREAM_SASL_MD5) {
iks_start_sasl(handle->parser, IKS_SASL_DIGEST_MD5, handle->acc->user, handle->password);
} else if (handle->features & IKS_STREAM_SASL_PLAIN) {
iks *x = NULL;
if ((x = iks_new("auth"))) {
char s[512] = "";
char base64[1024] = "";
uint32_t slen;
iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
iks_insert_attrib(x, "mechanism", "PLAIN");
iks_insert_attrib(x, "encoding", "UTF-8");
snprintf(s, sizeof(s), "%c%s%c%s", 0, handle->acc->user, 0, handle->password);
slen = (uint32_t)(strlen(handle->acc->user) + strlen(handle->password) + 2);
b64encode((unsigned char *)s, slen, (unsigned char *) base64, sizeof(base64));
iks_insert_cdata(x, base64, 0);
apr_queue_push(handle->queue, x);
x = NULL;
} else {
globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
break;
}
}
}
} else if (node && strcmp("failure", iks_name_nons(node)) == 0) {
globals.logger(DL_LOG_CRIT, "sasl authentication failed\n");
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_LOGIN_FAILURE, "user", "core", "Login Failure", handle->login);
}
} else if (node && strcmp("success", iks_name_nons(node)) == 0) {
globals.logger(DL_LOG_NOTICE, "XMPP server connected\n");
iks_send_header(handle->parser, handle->acc->server);
ldl_set_flag_locked(handle, LDL_FLAG_CONNECTED);
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_CONNECTED, "user", "core", "Server Connected", handle->login);
}
} else {
ikspak *pak;
if (!ldl_test_flag(handle, LDL_FLAG_AUTHORIZED)) {
if (handle->session_callback) {
handle->session_callback(handle, NULL, LDL_SIGNAL_LOGIN_SUCCESS, "user", "core", "Login Success", handle->login);
}
globals.logger(DL_LOG_NOTICE, "XMPP authenticated\n");
ldl_set_flag_locked(handle, LDL_FLAG_AUTHORIZED);
}
if (node) {
pak = iks_packet(node);
iks_filter_packet(handle->filter, pak);
}
if (handle->job_done == 1) {
return IKS_HOOK;
}
}
break;
#if 0
case IKS_NODE_STOP:
globals.logger(DL_LOG_DEBUG, "server disconnected\n");
break;
case IKS_NODE_ERROR:
globals.logger(DL_LOG_DEBUG, "stream error\n");
break;
#endif
}
if (node) {
iks_delete(node);
}
return IKS_OK;
}
static int on_msg(void *user_data, ikspak *pak)
{
char *cmd = iks_find_cdata(pak->x, "body");
char *to = iks_find_attrib(pak->x, "to");
char *from = iks_find_attrib(pak->x, "from");
char *subject = iks_find_attrib(pak->x, "subject");
ldl_handle_t *handle = user_data;
ldl_session_t *session = NULL;
if (from) {
session = apr_hash_get(handle->sessions, from, APR_HASH_KEY_STRING);
}
if (handle->session_callback) {
handle->session_callback(handle, session, LDL_SIGNAL_MSG, to, from, subject ? subject : "N/A", cmd);
}
return 0;
}
static int on_error(void *user_data, ikspak * pak)
{
globals.logger(DL_LOG_ERR, "authorization failed\n");
return IKS_FILTER_EAT;
}
static void on_log(ldl_handle_t *handle, const char *data, size_t size, int is_incoming)
{
if (globals.debug) {
if (is_incoming) {
globals.logger(DL_LOG_INFO, "+xml:%s%s:%s", iks_is_secure(handle->parser) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
} else {
globals.logger(DL_LOG_NOTICE, "+xml:%s%s:%s", iks_is_secure(handle->parser) ? "Sec" : "", is_incoming ? "RECV" : "SEND", data);
}
}
}
static void j_setup_filter(ldl_handle_t *handle)
{
int x = 0;
if (handle->filter) {
iks_filter_delete(handle->filter);
}
handle->filter = iks_filter_new();
iks_filter_add_rule(handle->filter, on_msg, handle, IKS_RULE_TYPE, IKS_PAK_MESSAGE, IKS_RULE_SUBTYPE, IKS_TYPE_CHAT, IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_result, handle,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_presence, handle,
IKS_RULE_TYPE, IKS_PAK_PRESENCE,
//IKS_RULE_SUBTYPE, IKS_TYPE_SET,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_commands, handle,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_SET,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_commands, handle,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_commands, handle,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_subscribe, handle,
IKS_RULE_TYPE, IKS_PAK_S10N,
IKS_RULE_SUBTYPE, IKS_TYPE_SUBSCRIBE,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_unsubscribe, handle,
IKS_RULE_TYPE, IKS_PAK_S10N,
IKS_RULE_SUBTYPE, IKS_TYPE_UNSUBSCRIBE,
IKS_RULE_DONE);
iks_filter_add_rule(handle->filter, on_error, handle,
IKS_RULE_TYPE, IKS_PAK_IQ,
IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_ID, "auth", IKS_RULE_DONE);
for (x = 0; FEATURES[x].name; x++) {
iks_filter_add_rule(handle->filter, FEATURES[x].callback, handle,
IKS_RULE_NS, FEATURES[x].name, IKS_RULE_DONE);
}
}
2011-10-17 13:56:04 +00:00
static ldl_queue_t ldl_flush_queue(ldl_handle_t *handle, int done)
{
iks *msg;
void *pop = NULL;
unsigned int len = 0, x = 0;
2011-10-17 13:56:04 +00:00
ldl_queue_t sent_data = LDL_QUEUE_NONE;
apr_thread_mutex_lock(handle->lock);
while(apr_queue_trypop(handle->queue, &pop) == APR_SUCCESS) {
if (pop) {
msg = (iks *) pop;
if (!done) {
if (iks_send(handle->parser, msg) != IKS_OK) {
globals.logger(DL_LOG_DEBUG, "Failed sending data!\n");
};
};
iks_delete(msg);
pop = NULL;
2011-10-17 13:56:04 +00:00
sent_data = LDL_QUEUE_SENT;
} else {
break;
}
}
len = apr_queue_size(handle->retry_queue);
if (globals.debug && len) {
globals.logger(DL_LOG_CRIT, "Processing %u packets in retry queue\n", len);
}
pop = NULL;
while(x < len && apr_queue_trypop(handle->retry_queue, &pop) == APR_SUCCESS) {
if (!pop) {
break;
} else {
struct packet_node *packet_node = (struct packet_node *) pop;
apr_time_t now = apr_time_now();
x++;
if (packet_node->next <= now) {
if (packet_node->retries > 0) {
packet_node->retries--;
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Sending packet %s (%d left)\n", packet_node->id, packet_node->retries);
}
if (iks_send(handle->parser, packet_node->xml) != IKS_OK) {
globals.logger(DL_LOG_DEBUG, "Failed trying re-sending data!\n");
};
packet_node->next = now + 5000000;
2011-10-17 13:56:04 +00:00
sent_data = LDL_QUEUE_SENT;
}
}
if (packet_node->retries == 0 || done) {
if (globals.debug) {
globals.logger(DL_LOG_CRIT, "Discarding packet %s\n", packet_node->id);
}
apr_hash_set(handle->retry_hash, packet_node->id, APR_HASH_KEY_STRING, NULL);
iks_delete(packet_node->xml);
free(packet_node);
} else {
apr_queue_push(handle->retry_queue, packet_node);
packet_node = NULL;
}
pop = NULL;
}
}
apr_thread_mutex_unlock(handle->lock);
2011-10-17 13:56:04 +00:00
return sent_data;
}
static void xmpp_connect(ldl_handle_t *handle, char *jabber_id, char *pass)
{
int count_ka = LDL_KEEPALIVE_TIMEOUT;
time_t tstart, tnow;
while (ldl_test_flag((&globals), LDL_FLAG_READY) && ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
int e;
char tmp[512], *sl;
int fd;
handle->parser = iks_stream_new(ldl_test_flag(handle, LDL_FLAG_COMPONENT) ? IKS_NS_COMPONENT : IKS_NS_CLIENT,
handle,
(iksStreamHook *) (ldl_test_flag(handle, LDL_FLAG_COMPONENT) ? on_stream_component : on_stream));
iks_set_log_hook(handle->parser, (iksLogHook *) on_log);
strncpy(tmp, jabber_id, sizeof(tmp)-1);
sl = strchr(tmp, '/');
if (!sl && !ldl_test_flag(handle, LDL_FLAG_COMPONENT)) {
/* user gave no resource name, use the default */
snprintf(tmp + strlen(tmp), sizeof(tmp) - strlen(tmp), "/%s", "talk");
} else if (sl && ldl_test_flag(handle, LDL_FLAG_COMPONENT)) {
*sl = '\0';
}
handle->acc = iks_id_new(iks_parser_stack(handle->parser), tmp);
handle->password = pass;
j_setup_filter(handle);
globals.logger(DL_LOG_DEBUG, "xmpp connecting\n");
e = iks_connect_via(handle->parser,
handle->server ? handle->server : handle->acc->server,
handle->port ? handle->port : IKS_JABBER_PORT,
handle->acc->server);
switch (e) {
case IKS_OK:
break;
case IKS_NET_NODNS:
globals.logger(DL_LOG_CRIT, "hostname lookup failed\n");
microsleep(1000);
goto fail;
case IKS_NET_NOCONN:
globals.logger(DL_LOG_CRIT, "connection failed\n");
microsleep(1000);
goto fail;
default:
globals.logger(DL_LOG_CRIT, "io error 1 %d\n", e);
microsleep(1000);
goto fail;
}
handle->counter = opt_timeout;
if ((tstart = time(NULL)) == -1) {
globals.logger(DL_LOG_DEBUG, "error determining connection time");
}
while (ldl_test_flag((&globals), LDL_FLAG_READY) && ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
e = iks_recv(handle->parser, 1);
if (handle->loop_callback) {
if (handle->loop_callback(handle) != LDL_STATUS_SUCCESS) {
ldl_clear_flag_locked(handle, LDL_FLAG_RUNNING);
break;
}
}
if (handle->job_done) {
break;
}
if (IKS_HOOK == e) {
break;
}
if (IKS_OK != e || ldl_test_flag(handle, LDL_FLAG_BREAK)) {
globals.logger(DL_LOG_DEBUG, "io error 2 %d retry in %d second(s)", e, ++handle->fail_count);
if ((tnow = time(NULL)) == -1) {
globals.logger(DL_LOG_DEBUG, "error deterniming io error time");
}
if (difftime(tnow, tstart) > 30) {
/* this is a new error situation: reset counter */
globals.logger(DL_LOG_DEBUG, "resetting fail count");
handle->fail_count = 1;
}
microsleep(1000 * handle->fail_count);
goto fail;
}
if (ldl_test_flag(handle, LDL_FLAG_RUNNING)) {
if (ldl_flush_queue(handle, 0) == LDL_QUEUE_SENT) {
count_ka = LDL_KEEPALIVE_TIMEOUT;
}
}
if (!ldl_test_flag(handle, LDL_FLAG_CONNECTED)) {
handle->counter--;
if (IKS_NET_TLSFAIL == e) {
globals.logger(DL_LOG_CRIT, "tls handshake failed\n");
microsleep(500);
break;
}
if (handle->counter == 0) {
globals.logger(DL_LOG_CRIT, "network timeout\n");
2012-01-06 03:27:03 +00:00
microsleep(500);
break;
}
}
if (count_ka-- <= 0) {
if( iks_send_raw(handle->parser, " ") == IKS_OK) {
globals.logger(DL_LOG_DEBUG, "Sent keep alive signal");
count_ka = LDL_KEEPALIVE_TIMEOUT;
} else {
globals.logger(DL_LOG_DEBUG, "Failed sending keep alive signal");
microsleep(500);
break;
}
}
}
fail:
ldl_clear_flag_locked(handle, LDL_FLAG_CONNECTED);
ldl_clear_flag_locked(handle, LDL_FLAG_AUTHORIZED);
ldl_clear_flag_locked(handle, LDL_FLAG_BREAK);
handle->state = CS_NEW;
if ((fd = iks_fd(handle->parser)) > -1) {
shutdown(fd, 0x02);
}
iks_disconnect(handle->parser);
iks_parser_delete(handle->parser);
}
ldl_clear_flag_locked(handle, LDL_FLAG_RUNNING);
ldl_flush_queue(handle, 1);
ldl_set_flag_locked(handle, LDL_FLAG_STOPPED);
}
static void add_elements(ldl_session_t *session, iks *tag)
{
apr_hash_index_t *hi;
return;
for (hi = apr_hash_first(session->pool, session->variables); hi; hi = apr_hash_next(hi)) {
void *val = NULL;
const void *key = NULL;
apr_hash_this(hi, &key, NULL, &val);
if (val) {
iks *var = iks_insert(tag, "info_element");
iks_insert_attrib(var, "xmlns", "http://www.freeswitch.org/jie");
iks_insert_attrib(var, "name", (char *) key);
iks_insert_attrib(var, "value", (char *) val);
}
}
}
static iks *ldl_set_jingle_tag(ldl_session_t *session, iks *iq, char *action)
{
iks *jin = iks_insert (iq, "jin:jingle");
iks_insert_attrib(jin, "xmlns:jin", "urn:xmpp:jingle:1");
iks_insert_attrib(jin, "action", action);
iks_insert_attrib(jin, "sid", session->id);
//iks_insert_attrib(jin, "initiator", session->initiator ? session->initiator : session->them);
return jin;
}
static ldl_status new_jingle_iq(ldl_session_t *session, iks **iqp, iks **jinp, unsigned int *id, char *action)
{
iks *iq , *jin;
unsigned int myid;
char idbuf[80];
myid = next_id();
snprintf(idbuf, sizeof(idbuf), "%u", myid);
iq = iks_new("iq");
iks_insert_attrib(iq, "xmlns", "jabber:client");
iks_insert_attrib(iq, "from", session->login);
iks_insert_attrib(iq, "to", session->them);
iks_insert_attrib(iq, "type", "set");
iks_insert_attrib(iq, "id", idbuf);
jin = ldl_set_jingle_tag(session, iq, action);
*jinp = jin;
*iqp = iq;
*id = myid;
return LDL_STATUS_SUCCESS;
}
static ldl_status new_session_iq(ldl_session_t *session, iks **iqp, iks **sessp, unsigned int *id, char *type)
{
iks *iq, *sess;
unsigned int myid;
char idbuf[80];
myid = next_id();
snprintf(idbuf, sizeof(idbuf), "%u", myid);
iq = iks_new("iq");
iks_insert_attrib(iq, "xmlns", "jabber:client");
iks_insert_attrib(iq, "from", session->login);
iks_insert_attrib(iq, "to", session->them);
iks_insert_attrib(iq, "type", "set");
iks_insert_attrib(iq, "id", idbuf);
sess = iks_insert (iq, "ses:session");
iks_insert_attrib(sess, "xmlns:ses", "http://www.google.com/session");
iks_insert_attrib(sess, "type", type);
iks_insert_attrib(sess, "id", session->id);
iks_insert_attrib(sess, "initiator", session->initiator ? session->initiator : session->them);
*sessp = sess;
*iqp = iq;
*id = myid;
return LDL_STATUS_SUCCESS;
}
static void schedule_packet(ldl_handle_t *handle, unsigned int id, iks *xml, unsigned int retries)
{
struct packet_node *packet_node;
apr_thread_mutex_lock(handle->lock);
if ((packet_node = malloc(sizeof(*packet_node)))) {
memset(packet_node, 0, sizeof(*packet_node));
snprintf(packet_node->id, sizeof(packet_node->id), "%u", id);
packet_node->xml = xml;
packet_node->retries = retries;
packet_node->next = apr_time_now();
apr_hash_set(handle->retry_hash, packet_node->id, APR_HASH_KEY_STRING, packet_node);
apr_queue_push(handle->retry_queue, packet_node);
packet_node = NULL;
}
apr_thread_mutex_unlock(handle->lock);
}
char *ldl_session_get_caller(ldl_session_t *session)
{
return session->them;
}
char *ldl_session_get_callee(ldl_session_t *session)
{
return session->login;
}
void ldl_session_set_ip(ldl_session_t *session, char *ip)
{
session->ip = apr_pstrdup(session->pool, ip);
}
char *ldl_session_get_ip(ldl_session_t *session)
{
return session->ip;
}
void ldl_session_set_private(ldl_session_t *session, void *private_data)
{
session->private_data = private_data;
}
void *ldl_session_get_private(ldl_session_t *session)
{
return session->private_data;
}
void ldl_session_accept_candidate(ldl_session_t *session, ldl_candidate_t *candidate)
{
iks *iq, *sess, *tp;
unsigned int myid;
char idbuf[80];
myid = next_id();
snprintf(idbuf, sizeof(idbuf), "%u", myid);
if ((iq = iks_new("iq"))) {
if (!iks_insert_attrib(iq, "type", "set")) goto fail;
if (!iks_insert_attrib(iq, "id", idbuf)) goto fail;
if (!iks_insert_attrib(iq, "from", session->login)) goto fail;
if (!iks_insert_attrib(iq, "to", session->them)) goto fail;
if (!(sess = iks_insert (iq, "ses:session"))) goto fail;
if (!iks_insert_attrib(sess, "xmlns:ses", "http://www.google.com/session")) goto fail;
if (!iks_insert_attrib(sess, "type", "transport-accept")) goto fail;
if (!iks_insert_attrib(sess, "id", candidate->tid)) goto fail;
if (!iks_insert_attrib(sess, "xmlns", "http://www.google.com/session")) goto fail;
if (!iks_insert_attrib(sess, "initiator", session->initiator ? session->initiator : session->them)) goto fail;
if (!(tp = iks_insert (sess, "transport"))) goto fail;
if (!iks_insert_attrib(tp, "xmlns", "http://www.google.com/transport/p2p")) goto fail;
apr_queue_push(session->handle->queue, iq);
iq = NULL;
}
fail:
if (iq) {
iks_delete(iq);
}
}
void *ldl_handle_get_private(ldl_handle_t *handle)
{
return handle->private_info;
}
char *ldl_handle_get_login(ldl_handle_t *handle)
{
return handle->login;
}
void ldl_handle_send_presence(ldl_handle_t *handle, char *from, char *to, char *type, char *rpid, char *message, char *avatar)
{
do_presence(handle, from, to, type, rpid, message, avatar);
}
static void ldl_random_string(char *buf, uint16_t len, char *set)
{
char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
int max;
uint16_t x;
if (!set) {
set = chars;
}
max = (int) strlen(set);
srand((unsigned int) time(NULL));
for (x = 0; x < len; x++) {
int j = (int) (max * 1.0 * rand() / (RAND_MAX + 1.0));
buf[x] = set[j];
}
}
#define TLEN 8192
void ldl_handle_send_vcard(ldl_handle_t *handle, char *from, char *to, char *id, char *vcard)
{
iks *vxml = NULL, *iq = NULL;
int e = 0;
ldl_avatar_t *ap = NULL;
char *text = NULL;
ap = ldl_get_avatar(handle, NULL, from);
if (!vcard) {
char *ext;
if (!ap) {
return;
}
if ((ext = strrchr(ap->path, '.'))) {
ext++;
} else {
ext = "png";
}
text = malloc(TLEN);
snprintf(text, TLEN,
"<vCard xmlns='vcard-temp'><PHOTO><TYPE>image/%s</TYPE><BINVAL>%s</BINVAL></PHOTO></vCard>",
ext,
ap->base64
);
vcard = text;
} else {
if (ap && (strstr(vcard, "photo") || strstr(vcard, "PHOTO"))) {
ldl_random_string(ap->hash, sizeof(ap->hash) -1, NULL);
}
}
if (!(vxml = iks_tree(vcard, 0, &e))) {
globals.logger(DL_LOG_ERR, "Parse returned error [%d]\n", e);
goto fail;
}
if (!(iq = iks_new("iq"))) {
globals.logger(DL_LOG_ERR, "Memory Error\n");
goto fail;
}
if (!iks_insert_attrib(iq, "to", to)) goto fail;
if (!iks_insert_attrib(iq, "xmlns", "jabber:client")) goto fail;
if (!iks_insert_attrib(iq,"from", from)) goto fail;
if (!iks_insert_attrib(iq, "type", "result")) goto fail;
if (!iks_insert_attrib(iq, "id", id)) goto fail;
if (!iks_insert_node(iq, vxml)) goto fail;
apr_queue_push(handle->queue, iq);
iq = NULL;
vxml = NULL;
fail:
if (iq) {
iks_delete(iq);
}
if (vxml) {
iks_delete(vxml);
}
if (text) {
free(text);
}
}
void ldl_handle_send_msg(ldl_handle_t *handle, char *from, char *to, const char *subject, const char *body)
{
iks *msg;
char *t, *e;
char *bdup = NULL;
int on = 0;
int len = 0;
char *my_body = strdup(body);
char *my_body_base = my_body;
assert(handle != NULL);
assert(body != NULL);
if (strchr(my_body, '<')) {
len = (int) strlen(my_body);
if (!(bdup = malloc(len))) {
return;
}
memset(bdup, 0, len);
e = bdup;
for(t = my_body; *t; t++) {
if (*t == '<') {
on = 1;
} else if (*t == '>') {
t++;
on = 0;
}
if (!on) {
*e++ = *t;
}
}
my_body = bdup;
}
msg = iks_make_msg(IKS_TYPE_NONE, to, my_body);
iks_insert_attrib(msg, "type", "chat");
if (!from) {
from = handle->login;
}
iks_insert_attrib(msg, "from", from);
if (subject) {
iks_insert_attrib(msg, "subject", subject);
}
if (bdup) {
free(bdup);
}
if (my_body_base) {
free(my_body_base);
}
apr_queue_push(handle->queue, msg);
msg = NULL;
}
int ldl_global_debug(int on)
{
if (on > -1) {
globals.debug = on ? 1 : 0;
}
return globals.debug ? 1 : 0;
}
void ldl_global_set_logger(ldl_logger_t logger)
{
globals.logger = logger;
}
unsigned int ldl_session_terminate(ldl_session_t *session)
{
iks *iq, *sess;
unsigned int id;
2010-09-07 14:40:39 +00:00
apr_hash_t *hash = session->handle->sessions;
new_session_iq(session, &iq, &sess, &id, "terminate");
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
ldl_set_jingle_tag(session, iq, "session-terminate");
}
schedule_packet(session->handle, id, iq, LDL_RETRY);
2010-09-07 14:40:39 +00:00
if (session->id) {
apr_hash_set(hash, session->id, APR_HASH_KEY_STRING, NULL);
}
if (session->them) {
apr_hash_set(hash, session->them, APR_HASH_KEY_STRING, NULL);
}
return id;
}
unsigned int ldl_session_transport(ldl_session_t *session,
ldl_candidate_t *candidates,
unsigned int clen)
{
iks *iq, *sess, *tag;
unsigned int x, id = 0;
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
return ldl_session_candidates(session, candidates, clen);
}
for (x = 0; x < clen; x++) {
char buf[512];
iq = NULL;
sess = NULL;
id = 0;
new_session_iq(session, &iq, &sess, &id, "transport-info");
tag = sess;
//if (0) add_elements(session, tag);
tag = iks_insert(tag, "transport");
iks_insert_attrib(tag, "xmlns", "http://www.google.com/transport/p2p");
//iks_insert_attrib(tag, "xmlns", "urn:xmpp:jingle:transports:raw-udp:1");
tag = iks_insert(tag, "candidate");
if (candidates[x].name) {
iks_insert_attrib(tag, "name", candidates[x].name);
}
if (candidates[x].address) {
iks_insert_attrib(tag, "address", candidates[x].address);
}
if (candidates[x].port) {
snprintf(buf, sizeof(buf), "%u", candidates[x].port);
iks_insert_attrib(tag, "port", buf);
}
if (candidates[x].username) {
iks_insert_attrib(tag, "username", candidates[x].username);
}
if (candidates[x].password) {
iks_insert_attrib(tag, "password", candidates[x].password);
}
if (candidates[x].pref) {
snprintf(buf, sizeof(buf), "%0.1f", candidates[x].pref);
iks_insert_attrib(tag, "preference", buf);
}
if (candidates[x].protocol) {
iks_insert_attrib(tag, "protocol", candidates[x].protocol);
}
if (candidates[x].type) {
iks_insert_attrib(tag, "type", candidates[x].type);
}
iks_insert_attrib(tag, "network", "0");
iks_insert_attrib(tag, "generation", "0");
schedule_packet(session->handle, id, iq, LDL_RETRY);
}
return id;
}
unsigned int ldl_session_candidates(ldl_session_t *session,
ldl_candidate_t *candidates,
unsigned int clen)
{
iks *iq = NULL, *sess = NULL, *tag = NULL;
unsigned int x = 0, id = 0;
unsigned int pass = 0;
iks *jingle = NULL, *jin_content = NULL, *p_trans = NULL;
const char *tname = "";
const char *type = "";
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
new_jingle_iq(session, &iq, &jingle, &id, "transport-info");
for (pass = 0; pass < 2; pass++) {
if (pass == 0) {
type = "rtp";
tname = "audio";
} else {
type = "video_rtp";
tname = "video";
}
jin_content = iks_insert(jingle, "jin:content");
iks_insert_attrib(jin_content, "name", tname);
iks_insert_attrib(jin_content, "creator", "initiator");
for (x = 0; x < clen; x++) {
char buf[512];
if (strcasecmp(candidates[x].name, type)) {
continue;
}
p_trans = iks_insert(jin_content, "p:transport");
iks_insert_attrib(p_trans, "xmlns:p", "http://www.google.com/transport/p2p");
tag = iks_insert(p_trans, "candidate");
if (candidates[x].name) {
iks_insert_attrib(tag, "name", candidates[x].name);
}
if (candidates[x].address) {
iks_insert_attrib(tag, "address", candidates[x].address);
}
if (candidates[x].port) {
snprintf(buf, sizeof(buf), "%u", candidates[x].port);
iks_insert_attrib(tag, "port", buf);
}
if (candidates[x].username) {
iks_insert_attrib(tag, "username", candidates[x].username);
}
if (candidates[x].password) {
iks_insert_attrib(tag, "password", candidates[x].password);
}
if (candidates[x].pref) {
snprintf(buf, sizeof(buf), "%0.1f", candidates[x].pref);
iks_insert_attrib(tag, "preference", buf);
}
if (candidates[x].protocol) {
iks_insert_attrib(tag, "protocol", candidates[x].protocol);
}
if (candidates[x].type) {
iks_insert_attrib(tag, "type", candidates[x].type);
}
iks_insert_attrib(tag, "network", "0");
iks_insert_attrib(tag, "generation", "0");
}
}
schedule_packet(session->handle, id, iq, LDL_RETRY);
iq = NULL;
sess = NULL;
tag = NULL;
x = 0;
id = 0;
}
new_session_iq(session, &iq, &sess, &id, "candidates");
add_elements(session, sess);
for (x = 0; x < clen; x++) {
char buf[512];
//iq = NULL;
//sess = NULL;
//id = 0;
tag = iks_insert(sess, "ses:candidate");
if (candidates[x].name) {
iks_insert_attrib(tag, "name", candidates[x].name);
}
if (candidates[x].address) {
iks_insert_attrib(tag, "address", candidates[x].address);
}
if (candidates[x].port) {
snprintf(buf, sizeof(buf), "%u", candidates[x].port);
iks_insert_attrib(tag, "port", buf);
}
if (candidates[x].username) {
iks_insert_attrib(tag, "username", candidates[x].username);
}
if (candidates[x].password) {
iks_insert_attrib(tag, "password", candidates[x].password);
}
if (candidates[x].pref) {
snprintf(buf, sizeof(buf), "%0.1f", candidates[x].pref);
iks_insert_attrib(tag, "preference", buf);
}
if (candidates[x].protocol) {
iks_insert_attrib(tag, "protocol", candidates[x].protocol);
}
if (candidates[x].type) {
iks_insert_attrib(tag, "type", candidates[x].type);
}
iks_insert_attrib(tag, "network", "0");
iks_insert_attrib(tag, "generation", "0");
}
schedule_packet(session->handle, id, iq, LDL_RETRY);
return id;
}
char *ldl_handle_probe(ldl_handle_t *handle, char *id, char *from, char *buf, unsigned int len)
{
iks *pres, *msg;
char *lid = NULL, *low_id = NULL;
struct ldl_buffer buffer;
time_t started, elapsed, next = 0;
char *notice = "Call Me!";
buffer.buf = buf;
buffer.len = len;
buffer.hit = 0;
apr_hash_set(handle->probe_hash, id, APR_HASH_KEY_STRING, &buffer);
started = time(NULL);
for(;;) {
elapsed = time(NULL) - started;
if (elapsed == next) {
msg = iks_make_s10n (IKS_TYPE_SUBSCRIBE, id, notice);
iks_insert_attrib(msg, "from", from);
apr_queue_push(handle->queue, msg);
msg = NULL;
pres = iks_new("presence");
iks_insert_attrib(pres, "xmlns", "jabber:client");
iks_insert_attrib(pres, "type", "probe");
iks_insert_attrib(pres, "to", id);
iks_insert_attrib(pres, "from", from);
apr_queue_push(handle->queue, pres);
pres = NULL;
next += 5;
}
if (elapsed >= 17) {
break;
}
if (buffer.hit) {
lid = buffer.buf;
break;
}
ldl_yield(1000);
}
if ((low_id = strdup(id))) {
lowercase(id);
apr_hash_set(handle->probe_hash, low_id, APR_HASH_KEY_STRING, NULL);
free(low_id);
}
return lid;
}
char *ldl_handle_disco(ldl_handle_t *handle, char *id, char *from, char *buf, unsigned int len)
{
iks *iq, *query, *msg;
char *lid = NULL;
struct ldl_buffer buffer;
apr_time_t started;
unsigned int elapsed;
char *notice = "Call Me!";
int again = 0;
unsigned int myid;
char idbuf[80];
myid = next_id();
snprintf(idbuf, sizeof(idbuf), "%u", myid);
buffer.buf = buf;
buffer.len = len;
buffer.hit = 0;
if ((iq = iks_new("iq"))) {
if ((query = iks_insert(iq, "query"))) {
iks_insert_attrib(iq, "type", "get");
iks_insert_attrib(iq, "to", id);
iks_insert_attrib(iq,"from", from);
iks_insert_attrib(iq, "id", idbuf);
iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
} else {
iks_delete(iq);
globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
return NULL;
}
} else {
globals.logger(DL_LOG_CRIT, "Memory ERROR!\n");
return NULL;
}
apr_hash_set(handle->probe_hash, id, APR_HASH_KEY_STRING, &buffer);
msg = iks_make_s10n (IKS_TYPE_SUBSCRIBE, id, notice);
apr_queue_push(handle->queue, msg);
msg = NULL;
msg = iks_make_s10n (IKS_TYPE_SUBSCRIBED, id, notice);
apr_queue_push(handle->queue, msg);
msg = NULL;
apr_queue_push(handle->queue, iq);
iq = NULL;
//schedule_packet(handle, next_id(), pres, LDL_RETRY);
started = apr_time_now();
for(;;) {
elapsed = (unsigned int)((apr_time_now() - started) / 1000);
if (elapsed > 5000 && ! again) {
msg = iks_make_s10n (IKS_TYPE_SUBSCRIBE, id, notice);
apr_queue_push(handle->queue, msg);
msg = NULL;
again++;
}
if (elapsed > 10000) {
break;
}
if (buffer.hit) {
lid = buffer.buf;
break;
}
ldl_yield(1000);
}
apr_hash_set(handle->probe_hash, id, APR_HASH_KEY_STRING, NULL);
return lid;
}
unsigned int ldl_session_describe(ldl_session_t *session,
ldl_payload_t *payloads,
unsigned int plen,
ldl_description_t description, unsigned int *audio_ssrc, unsigned int *video_ssrc,
ldl_crypto_data_t *audio_crypto_data, ldl_crypto_data_t *video_crypto_data)
{
iks *iq;
iks *sess, *payload = NULL, *tag = NULL;//, *u = NULL;
unsigned int x, id;
int video_call = 0;
int compat = 1;
//char *vid_mux = ldl_session_get_value(session, "video:rtcp-mux");
//char *aud_mux = ldl_session_get_value(session, "audio:rtcp-mux");
char tmp[80];
iks *jpayload = NULL, *tp = NULL;
iks *jingle, *jin_audio, *jin_audio_desc = NULL, *jin_video = NULL, *jin_video_desc = NULL, *crypto;
if (!*audio_ssrc) {
*audio_ssrc = (uint32_t) ((intptr_t) session + (uint32_t) time(NULL));
}
if (!*video_ssrc) {
*video_ssrc = (uint32_t) ((intptr_t) payloads + (uint32_t) time(NULL));
}
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
new_jingle_iq(session, &iq, &jingle, &id, description == LDL_DESCRIPTION_ACCEPT ? "session-accept" : "session-initiate");
iks_insert_attrib(jingle, "initiator", session->initiator ? session->initiator : session->them);
if (compat) {
sess = iks_insert (iq, "ses:session");
iks_insert_attrib(sess, "xmlns:ses", "http://www.google.com/session");
iks_insert_attrib(sess, "type", description == LDL_DESCRIPTION_ACCEPT ? "accept" : "initiate");
iks_insert_attrib(sess, "id", session->id);
iks_insert_attrib(sess, "initiator", session->initiator ? session->initiator : session->them);
}
} else {
new_session_iq(session, &iq, &sess, &id, description == LDL_DESCRIPTION_ACCEPT ? "accept" : "initiate");
}
/* Check if this is a video call */
for (x = 0; x < plen; x++) {
if (payloads[x].type == LDL_PAYLOAD_VIDEO) {
video_call = 1;
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
jin_video = iks_insert(jingle, "jin:content");
iks_insert_attrib(jin_video, "name", "video");
iks_insert_attrib(jin_video, "creator", "initiator");
//iks_insert_attrib(jin_video, "senders", "both");
jin_video_desc = iks_insert(jin_video, "rtp:description");
iks_insert_attrib(jin_video_desc, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
iks_insert_attrib(jin_video_desc, "media", "video");
snprintf(tmp, sizeof(tmp), "%u", *video_ssrc);
iks_insert_attrib(jin_video_desc, "ssrc", tmp);
tp = iks_insert(jin_video, "p:transport");
iks_insert_attrib(tp, "xmlns:p", "http://www.google.com/transport/p2p");
}
break;
}
}
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
jin_audio = iks_insert(jingle, "jin:content");
iks_insert_attrib(jin_audio, "name", "audio");
iks_insert_attrib(jin_audio, "creator", "initiator");
//iks_insert_attrib(jin_audio, "senders", "both");
jin_audio_desc = iks_insert(jin_audio, "rtp:description");
iks_insert_attrib(jin_audio_desc, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
iks_insert_attrib(jin_audio_desc, "media", "audio");
snprintf(tmp, sizeof(tmp), "%u", *audio_ssrc);
iks_insert_attrib(jin_audio_desc, "ssrc", tmp);
tp = iks_insert(jin_audio, "p:transport");
iks_insert_attrib(tp, "xmlns:p", "http://www.google.com/transport/p2p");
}
if (compat) {
if (video_call) {
tag = iks_insert(sess, "vid:description");
iks_insert_attrib(tag, "xmlns:vid", "http://www.google.com/session/video");
} else {
tag = iks_insert(sess, "pho:description");
iks_insert_attrib(tag, "xmlns:pho", "http://www.google.com/session/phone");
}
if (video_call) {
for (x = 0; x < plen; x++) {
char idbuf[80];
if (payloads[x].type != LDL_PAYLOAD_VIDEO) {
continue;
}
sprintf(idbuf, "%d", payloads[x].id);
payload = iks_insert(tag, "vid:payload-type");
iks_insert_attrib(payload, "id", idbuf);
iks_insert_attrib(payload, "name", payloads[x].name);
if (payloads[x].type == LDL_PAYLOAD_VIDEO && video_call) {
if (payloads[x].width) {
sprintf(idbuf, "%d", payloads[x].width);
iks_insert_attrib(payload, "width", idbuf);
}
if (payloads[x].height) {
sprintf(idbuf, "%d", payloads[x].height);
iks_insert_attrib(payload, "height", idbuf);
}
if (payloads[x].framerate) {
sprintf(idbuf, "%d", payloads[x].framerate);
iks_insert_attrib(payload, "framerate", idbuf);
}
}
}
//if (vid_mux) {
// iks_insert(tag, "rtcp-mux");
//}
//payload = iks_insert(tag, "vid:src-id");
//iks_insert_cdata(payload, "123456789", 0);
//iks_insert_attrib(payload, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
//iks_insert(payload, "vid:usage");
}
}
for (x = 0; x < plen; x++) {
char idbuf[80];
if (payloads[x].type == LDL_PAYLOAD_VIDEO && !video_call) {
continue;
}
sprintf(idbuf, "%d", payloads[x].id);
if (payloads[x].type == LDL_PAYLOAD_AUDIO) {
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
char ratebuf[80];
char buf[80];
iks *param;
jpayload = iks_insert(jin_audio_desc, "rtp:payload-type");
iks_insert_attrib(jpayload, "id", idbuf);
sprintf(ratebuf, "%d", payloads[x].rate);
iks_insert_attrib(jpayload, "name", payloads[x].name);
iks_insert_attrib(jpayload, "clockrate", ratebuf);
param = iks_insert(jpayload, "rtp:parameter");
iks_insert_attrib(param, "name", "bitrate");
sprintf(buf, "%d", payloads[x].bps);
iks_insert_attrib(param, "value", buf);
sprintf(buf, "%d", payloads[x].ptime);
iks_insert_attrib(jpayload, "ptime", ratebuf);
iks_insert_attrib(jpayload, "maxptime", ratebuf);
}
} else if (payloads[x].type == LDL_PAYLOAD_VIDEO && video_call) {
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
char buf[80];
iks *param;
jpayload = iks_insert(jin_video_desc, "rtp:payload-type");
iks_insert_attrib(jpayload, "id", idbuf);
iks_insert_attrib(jpayload, "name", payloads[x].name);
param = iks_insert(jpayload, "rtp:parameter");
iks_insert_attrib(param, "name", "width");
sprintf(buf, "%d", payloads[x].width);
iks_insert_attrib(param, "value", buf);
param = iks_insert(jpayload, "rtp:parameter");
iks_insert_attrib(param, "name", "height");
sprintf(buf, "%d", payloads[x].height);
iks_insert_attrib(param, "value", buf);
param = iks_insert(jpayload, "rtp:parameter");
iks_insert_attrib(param, "name", "framerate");
sprintf(buf, "%d", payloads[x].framerate);
iks_insert_attrib(param, "value", buf);
}
}
if (compat) {
if (payloads[x].type == LDL_PAYLOAD_AUDIO) {
payload = iks_insert(tag, "pho:payload-type");
iks_insert_attrib(payload, "id", idbuf);
iks_insert_attrib(payload, "name", payloads[x].name);
if (payloads[x].rate) {
sprintf(idbuf, "%d", payloads[x].rate);
iks_insert_attrib(payload, "clockrate", idbuf);
}
if (payloads[x].bps) {
sprintf(idbuf, "%d", payloads[x].bps);
iks_insert_attrib(payload, "bitrate", idbuf);
}
iks_insert_attrib(payload, "xmlns:pho", "http://www.google.com/session/phone");
}
}
//if (payloads[x].id == 34) payloads[x].id = 98; /* XXX */
}
if ((session->handle->flags & LDL_FLAG_JINGLE)) {
if (jin_video_desc && video_crypto_data) {
payload = iks_insert(jin_video_desc, "rtp:encryption");
crypto = iks_insert(payload, "rtp:crypto");
iks_insert_attrib(crypto, "crypto-suite", video_crypto_data->suite);
iks_insert_attrib(crypto, "key-params", video_crypto_data->key);
iks_insert_attrib(crypto, "tag", video_crypto_data->tag);
}
if (jin_audio_desc && audio_crypto_data) {
payload = iks_insert(jin_audio_desc, "rtp:encryption");
crypto = iks_insert(payload, "rtp:crypto");
iks_insert_attrib(crypto, "crypto-suite", audio_crypto_data->suite);
iks_insert_attrib(crypto, "key-params", audio_crypto_data->key);
iks_insert_attrib(crypto, "tag", audio_crypto_data->tag);
}
}
//if (aud_mux) {
// iks_insert(tag, "rtcp-mux");
//}
//payload = iks_insert(tag, "pho:src-id");
//iks_insert_cdata(payload, "987654321", 0);
//iks_insert_attrib(payload, "xmlns:pho", "http://www.google.com/session/phone");
//payload = iks_insert(tag, "rtp:encryption");
//iks_insert_attrib(payload, "xmlns:rtp", "urn:xmpp:jingle:apps:rtp:1");
//u = iks_insert(payload, "pho:usage");
//iks_insert_attrib(u, "xmlns:pho", "http://www.google.com/session/phone");
#if 0
if (description == LDL_DESCRIPTION_INITIATE) {
tp = iks_insert (sess, "transport");
iks_insert_attrib(tp, "xmlns", "http://www.google.com/transport/p2p");
}
#endif
schedule_packet(session->handle, id, iq, LDL_RETRY);
return id;
}
ldl_state_t ldl_session_get_state(ldl_session_t *session)
{
return session->state;
}
ldl_status ldl_session_get_candidates(ldl_session_t *session, ldl_transport_type_t tport, ldl_candidate_t **candidates, unsigned int *len)
{
assert(tport < LDL_TPORT_MAX);
2015-04-23 20:00:27 +00:00
if (session->candidate_len[tport]) {
*candidates = session->candidates[tport];
*len = session->candidate_len[tport];
return LDL_STATUS_SUCCESS;
} else {
*candidates = NULL;
*len = 0;
return LDL_STATUS_FALSE;
}
}
ldl_status ldl_session_get_payloads(ldl_session_t *session, ldl_payload_t **payloads, unsigned int *len)
{
if (session->payload_len) {
*payloads = session->payloads;
*len = session->payload_len;
return LDL_STATUS_SUCCESS;
} else {
*payloads = NULL;
*len = 0;
return LDL_STATUS_FALSE;
}
}
ldl_status ldl_global_terminate(void)
{
if (globals.flag_mutex) {
ldl_clear_flag_locked((&globals), LDL_FLAG_READY);
} else {
ldl_clear_flag((&globals), LDL_FLAG_READY);
}
return LDL_STATUS_SUCCESS;
}
ldl_status ldl_global_init(int debug)
{
if (ldl_test_flag((&globals), LDL_FLAG_INIT)) {
return LDL_STATUS_FALSE;
}
if (apr_initialize() != LDL_STATUS_SUCCESS) {
apr_terminate();
return LDL_STATUS_MEMERR;
}
memset(&globals, 0, sizeof(globals));
if (apr_pool_create(&globals.memory_pool, NULL) != LDL_STATUS_SUCCESS) {
globals.logger(DL_LOG_CRIT, "Could not allocate memory pool\n");
return LDL_STATUS_MEMERR;
}
apr_thread_mutex_create(&globals.flag_mutex, APR_THREAD_MUTEX_NESTED, globals.memory_pool);
globals.log_stream = stdout;
globals.debug = debug;
globals.id = 300;
globals.logger = default_logger;
globals.avatar_hash = apr_hash_make(globals.memory_pool);
ldl_set_flag_locked((&globals), LDL_FLAG_INIT);
ldl_set_flag_locked((&globals), LDL_FLAG_READY);
return LDL_STATUS_SUCCESS;
}
ldl_status ldl_global_destroy(void)
{
if (!ldl_test_flag(&globals, LDL_FLAG_INIT)) {
return LDL_STATUS_FALSE;
}
apr_pool_destroy(globals.memory_pool);
ldl_clear_flag(&globals, LDL_FLAG_INIT);
apr_terminate();
return LDL_STATUS_SUCCESS;
}
void ldl_global_set_log_stream(FILE *log_stream)
{
assert(ldl_test_flag(&globals, LDL_FLAG_INIT));
globals.log_stream = log_stream;
}
int8_t ldl_handle_ready(ldl_handle_t *handle)
{
return (int8_t) (ldl_test_flag(handle, LDL_FLAG_RUNNING) && ldl_test_flag((&globals), LDL_FLAG_READY));
}
ldl_status ldl_handle_init(ldl_handle_t **handle,
char *login,
char *password,
char *server,
ldl_user_flag_t flags,
char *status_msg,
char *priority,
ldl_loop_callback_t loop_callback,
ldl_session_callback_t session_callback,
ldl_response_callback_t response_callback,
void *private_info)
{
apr_pool_t *pool;
assert(ldl_test_flag(&globals, LDL_FLAG_INIT));
*handle = NULL;
if ((apr_pool_create(&pool, globals.memory_pool)) != LDL_STATUS_SUCCESS) {
return LDL_STATUS_MEMERR;
}
if (!login) {
globals.logger(DL_LOG_ERR, "No login supplied!\n");
return LDL_STATUS_FALSE;
}
if (!password) {
globals.logger(DL_LOG_ERR, "No password supplied!\n");
return LDL_STATUS_FALSE;
}
if ((*handle = apr_palloc(pool, sizeof(ldl_handle_t)))) {
ldl_handle_t *new_handle = *handle;
memset(new_handle, 0, sizeof(ldl_handle_t));
new_handle->log_stream = globals.log_stream;
new_handle->login = apr_pstrdup(pool, login);
new_handle->password = apr_pstrdup(pool, password);
if (server) {
char *p;
new_handle->server = apr_pstrdup(pool, server);
if ((p = strchr(new_handle->server, ':'))) {
*p++ = '\0';
new_handle->port = (uint16_t)atoi(p);
}
}
if (status_msg) {
new_handle->status_msg = apr_pstrdup(pool, status_msg);
}
if (priority) {
new_handle->priority = apr_pstrdup(pool, priority);
}
if (loop_callback) {
new_handle->loop_callback = loop_callback;
}
if (session_callback) {
new_handle->session_callback = session_callback;
}
if (response_callback) {
new_handle->response_callback = response_callback;
}
new_handle->private_info = private_info;
new_handle->pool = pool;
new_handle->flags |= flags;
apr_queue_create(&new_handle->queue, LDL_HANDLE_QLEN, new_handle->pool);
apr_queue_create(&new_handle->retry_queue, LDL_HANDLE_QLEN, new_handle->pool);
new_handle->features |= IKS_STREAM_BIND|IKS_STREAM_SESSION;
if (new_handle->flags & LDL_FLAG_SASL_PLAIN) {
new_handle->features |= IKS_STREAM_SASL_PLAIN;
} else if (new_handle->flags & LDL_FLAG_SASL_MD5) {
new_handle->features |= IKS_STREAM_SASL_MD5;
}
new_handle->sessions = apr_hash_make(new_handle->pool);
new_handle->retry_hash = apr_hash_make(new_handle->pool);
new_handle->probe_hash = apr_hash_make(new_handle->pool);
new_handle->sub_hash = apr_hash_make(new_handle->pool);
apr_thread_mutex_create(&new_handle->lock, APR_THREAD_MUTEX_NESTED, new_handle->pool);
apr_thread_mutex_create(&new_handle->flag_mutex, APR_THREAD_MUTEX_NESTED, new_handle->pool);
return LDL_STATUS_SUCCESS;
}
return LDL_STATUS_FALSE;
}
void ldl_handle_run(ldl_handle_t *handle)
{
ldl_clear_flag_locked(handle, LDL_FLAG_STOPPED);
ldl_set_flag_locked(handle, LDL_FLAG_RUNNING);
xmpp_connect(handle, handle->login, handle->password);
ldl_clear_flag_locked(handle, LDL_FLAG_RUNNING);
}
int ldl_handle_running(ldl_handle_t *handle)
{
return ldl_test_flag(handle, LDL_FLAG_RUNNING) ? 1 : 0;
}
void ldl_session_set_gateway(ldl_session_t *session)
{
ldl_set_flag(session, LDL_FLAG_GATEWAY);
}
int ldl_session_gateway(ldl_session_t *session)
{
return ldl_test_flag(session, LDL_FLAG_GATEWAY) ? 1 : 0;
}
int ldl_handle_connected(ldl_handle_t *handle)
{
return ldl_test_flag(handle, LDL_FLAG_CONNECTED) ? 1 : 0;
}
int ldl_handle_authorized(ldl_handle_t *handle)
{
return ldl_test_flag(handle, LDL_FLAG_AUTHORIZED) ? 1 : 0;
}
void ldl_handle_stop(ldl_handle_t *handle)
{
ldl_clear_flag_locked(handle, LDL_FLAG_RUNNING);
#if 0
if (ldl_test_flag(handle, LDL_FLAG_TLS)) {
int fd;
if ((fd = iks_fd(handle->parser)) > -1) {
shutdown(fd, 0x02);
}
}
#endif
while(!ldl_test_flag(handle, LDL_FLAG_STOPPED)) {
microsleep(100);
}
}
ldl_status ldl_handle_destroy(ldl_handle_t **handle)
{
apr_pool_t *pool = (*handle)->pool;
ldl_handle_stop(*handle);
ldl_flush_queue(*handle, 1);
apr_pool_destroy(pool);
*handle = NULL;
return LDL_STATUS_SUCCESS;
}
void ldl_handle_set_log_stream(ldl_handle_t *handle, FILE *log_stream)
{
assert(ldl_test_flag(&globals, LDL_FLAG_INIT));
handle->log_stream = log_stream;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/