Desktop

/* $Id: sofia.c,v 1.3 2011/10/31 14:51:25 khorben Exp $ */
/* Copyright (c) 2011 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Phone */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <System.h>
#include <Phone/modem.h>
#include <sofia-sip/nua.h>
#include <sofia-sip/sip_header.h>
#include <sofia-sip/su_glib.h>
#include <sofia-sip/url.h>
/* Sofia */
/* private */
/* types */
typedef struct _Sofia
{
su_home_t home[1];
su_root_t * root;
guint source;
nua_t * nua;
nua_handle_t * handle;
} Sofia;
typedef enum _SofiaConfig
{
SOFIA_CONFIG_REGISTRAR = 0,
SOFIA_CONFIG_USERNAME,
SOFIA_CONFIG_PROXY
} SofiaConfig;
#define SOFIA_CONFIG_LAST SOFIA_CONFIG_PROXY
#define SOFIA_CONFIG_COUNT (SOFIA_CONFIG_LAST + 1)
/* variables */
static ModemConfig _sofia_config[SOFIA_CONFIG_COUNT + 1] =
{
{ "registrar", "Registrar", MCT_STRING, NULL },
{ "username", "Username", MCT_STRING, NULL },
{ "proxy", "Proxy", MCT_STRING, NULL },
{ NULL, NULL, MCT_NONE, NULL },
};
/* prototypes */
static int _sofia_init(ModemPlugin * modem);
static int _sofia_destroy(ModemPlugin * modem);
static int _sofia_start(ModemPlugin * modem, unsigned int retry);
static int _sofia_stop(ModemPlugin * modem);
static int _sofia_request(ModemPlugin * modem, ModemRequest * request);
/* callbacks */
static void _sofia_callback(nua_event_t event, int status, char const * phrase,
nua_t * nua, nua_magic_t * magic, nua_handle_t * nh,
nua_hmagic_t * hmagic, sip_t const * sip, tagi_t tags[]);
/* public */
/* variables */
ModemPlugin plugin =
{
NULL,
"Sofia",
NULL,
_sofia_config,
_sofia_init,
_sofia_destroy,
_sofia_start,
_sofia_stop,
_sofia_request,
NULL,
NULL
};
/* private */
/* functions */
/* sofia_init */
static int _sofia_init(ModemPlugin * modem)
{
Sofia * sofia;
GSource * gsource;
if((sofia = object_new(sizeof(*sofia))) == NULL)
return -1;
memset(sofia, 0, sizeof(*sofia));
modem->priv = sofia;
su_init();
su_home_init(sofia->home);
if((sofia->root = su_glib_root_create(NULL)) == NULL)
{
_sofia_destroy(modem);
return -1;
}
gsource = su_glib_root_gsource(sofia->root);
sofia->source = g_source_attach(gsource, g_main_context_default());
return 0;
}
/* sofia_destroy */
static int _sofia_destroy(ModemPlugin * modem)
{
Sofia * sofia = modem->priv;
_sofia_stop(modem);
if(sofia->source != 0)
g_source_remove(sofia->source);
sofia->source = 0;
su_root_destroy(sofia->root);
su_home_deinit(sofia->home);
su_deinit();
object_delete(sofia);
return 0;
}
/* sofia_start */
static int _sofia_start(ModemPlugin * modem, unsigned int retry)
{
Sofia * sofia = modem->priv;
char const * registrar = modem->config[SOFIA_CONFIG_REGISTRAR].value;
char const * proxy = modem->config[SOFIA_CONFIG_PROXY].value;
char const * username = modem->config[SOFIA_CONFIG_USERNAME].value;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(sofia->nua != NULL) /* already started */
return 0;
if((sofia->nua = nua_create(sofia->root, _sofia_callback, modem,
TAG_NULL())) == NULL)
return -1;
nua_set_params(sofia->nua, NUTAG_REGISTRAR(registrar),
NUTAG_PROXY(proxy), TAG_NULL());
sofia->handle = nua_handle(sofia->nua, modem, TAG_NULL());
nua_register(sofia->handle, NUTAG_M_USERNAME(username), TAG_NULL());
return 0;
}
/* sofia_stop */
static int _sofia_stop(ModemPlugin * modem)
{
Sofia * sofia = modem->priv;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(sofia->handle != NULL)
nua_handle_destroy(sofia->handle);
sofia->handle = NULL;
if(sofia->nua != NULL)
{
nua_shutdown(sofia->nua);
su_root_run(sofia->root);
nua_destroy(sofia->nua);
}
sofia->nua = NULL;
return 0;
}
/* sofia_request */
static int _request_call(ModemPlugin * modem, ModemRequest * request);
static int _request_message_send(ModemPlugin * modem, ModemRequest * request);
static int _sofia_request(ModemPlugin * modem, ModemRequest * request)
{
switch(request->type)
{
case MODEM_REQUEST_CALL:
return _request_call(modem, request);
case MODEM_REQUEST_MESSAGE_SEND:
return _request_message_send(modem, request);
#ifndef DEBUG
default:
break;
#endif
}
return 0;
}
static int _request_call(ModemPlugin * modem, ModemRequest * request)
{
Sofia * sofia = modem->priv;
url_t * url;
struct { nua_handle_t * handle; } * op;
sip_to_t * to;
if((url = url_make(sofia->home, request->call.number)) == NULL)
return -1;
if((op = su_zalloc(sofia->home, sizeof(*op))) == NULL)
return -1; /* XXX free url? */
if((to = sip_to_create(NULL, url)) == NULL)
return -1; /* XXX free url and op? */
to->a_display = "Private"; /* XXX look it up */
if((op->handle = nua_handle(sofia->nua, op, SIPTAG_TO(to), TAG_END()))
== NULL)
return -modem->helper->error(modem->helper->modem,
"Cannot create operation handle", 1);
nua_invite(op->handle, /* other tags as needed ... */ TAG_END());
return 0;
}
static int _request_message_send(ModemPlugin * modem, ModemRequest * request)
{
Sofia * sofia = modem->priv;
struct { nua_handle_t * handle; } * op;
if((op = su_zalloc(sofia->home, sizeof(*op))) == NULL)
return -1; /* XXX free url? */
if((op->handle = nua_handle(sofia->nua, op, NUTAG_URL(
request->message_send.number),
TAG_END())) == NULL)
return -1;
nua_message(op->handle, SIPTAG_CONTENT_TYPE_STR("text/plain"),
SIPTAG_PAYLOAD_STR(request->message_send.content),
TAG_END());
return 0;
}
/* callbacks */
/* sofia_callback */
static void _sofia_callback(nua_event_t event, int status, char const * phrase,
nua_t * nua, nua_magic_t * magic, nua_handle_t * nh,
nua_hmagic_t * hmagic, sip_t const * sip, tagi_t tags[])
{
ModemPlugin * modem = magic;
Sofia * sofia = modem->priv;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
switch(event)
{
case nua_i_error:
/* FIXME report error */
fprintf(stderr, "%03d %s\n", status, phrase);
break;
case nua_i_notify:
/* FIXME report event */
fprintf(stderr, "%03d %s\n", status, phrase);
break;
case nua_i_state:
/* FIXME report event */
fprintf(stderr, "%03d %s\n", status, phrase);
break;
case nua_r_invite:
if(status == 200)
nua_ack(nh, TAG_END());
else
/* FIXME report error */
fprintf(stderr, "%03d %s\n", status, phrase);
break;
case nua_r_register:
/* FIXME implement */
fprintf(stderr, "register: %03d %s\n", status, phrase);
break;
case nua_r_set_params:
/* FIXME implement */
break;
case nua_r_shutdown:
/* exit the background loop when ready */
if(status == 200)
su_root_break(sofia->root);
break;
default:
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() %s%d%s: %03d \"%s\"\n",
__func__, "event ", event,
" not handled: ", status, phrase);
#endif
break;
}
}