re/src/sipsess/connect.c
Alfred E. Heggestad 9ff9a24c48 sip: add sip contact, needed for GRUU
- in sipsess and sipevent, the cuser parameter can now be
  a contact-username or a uri (e.g. GRUU)

- Tested by Juha Heinanen (with baresip+gruu)
2014-09-25 07:51:19 +00:00

245 lines
5.4 KiB
C

/**
* @file connect.c SIP Session Connect
*
* Copyright (C) 2010 Creytiv.com
*/
#include <string.h>
#include <re_types.h>
#include <re_mem.h>
#include <re_mbuf.h>
#include <re_sa.h>
#include <re_list.h>
#include <re_hash.h>
#include <re_fmt.h>
#include <re_uri.h>
#include <re_tmr.h>
#include <re_msg.h>
#include <re_sip.h>
#include <re_sipsess.h>
#include "sipsess.h"
static int invite(struct sipsess *sess);
static int send_handler(enum sip_transp tp, const struct sa *src,
const struct sa *dst, struct mbuf *mb, void *arg)
{
struct sip_contact contact;
struct sipsess *sess = arg;
(void)dst;
sip_contact_set(&contact, sess->cuser, src, tp);
return mbuf_printf(mb, "%H", sip_contact_print, &contact);
}
static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg)
{
struct sipsess *sess = arg;
struct mbuf *desc = NULL;
if (err || sip_request_loops(&sess->ls, msg->scode))
goto out;
if (msg->scode < 200) {
sess->progrh(msg, sess->arg);
return;
}
else if (msg->scode < 300) {
sess->hdrs = mem_deref(sess->hdrs);
err = sip_dialog_create(sess->dlg, msg);
if (err)
goto out;
if (sess->sent_offer)
err = sess->answerh(msg, sess->arg);
else {
sess->modify_pending = false;
err = sess->offerh(&desc, msg, sess->arg);
}
err |= sipsess_ack(sess->sock, sess->dlg, msg->cseq.num,
sess->auth, sess->ctype, desc);
sess->established = true;
mem_deref(desc);
if (err || sess->terminated)
goto out;
if (sess->modify_pending)
(void)sipsess_reinvite(sess, true);
else
sess->desc = mem_deref(sess->desc);
sess->estabh(msg, sess->arg);
return;
}
else if (msg->scode < 400) {
/* Redirect to first Contact */
if (sess->terminated)
goto out;
err = sip_dialog_update(sess->dlg, msg);
if (err)
goto out;
err = invite(sess);
if (err)
goto out;
return;
}
else {
if (sess->terminated)
goto out;
switch (msg->scode) {
case 401:
case 407:
err = sip_auth_authenticate(sess->auth, msg);
if (err) {
err = (err == EAUTH) ? 0 : err;
break;
}
err = invite(sess);
if (err)
break;
return;
}
}
out:
if (!sess->terminated)
sipsess_terminate(sess, err, msg);
else
mem_deref(sess);
}
static int invite(struct sipsess *sess)
{
sess->sent_offer = sess->desc ? true : false;
sess->modify_pending = false;
return sip_drequestf(&sess->req, sess->sip, true, "INVITE",
sess->dlg, 0, sess->auth,
send_handler, invite_resp_handler, sess,
"%b"
"%s%s%s"
"Content-Length: %zu\r\n"
"\r\n"
"%b",
sess->hdrs ? mbuf_buf(sess->hdrs) : NULL,
sess->hdrs ? mbuf_get_left(sess->hdrs) :(size_t)0,
sess->desc ? "Content-Type: " : "",
sess->desc ? sess->ctype : "",
sess->desc ? "\r\n" : "",
sess->desc ? mbuf_get_left(sess->desc) :(size_t)0,
sess->desc ? mbuf_buf(sess->desc) : NULL,
sess->desc ? mbuf_get_left(sess->desc):(size_t)0);
}
/**
* Connect to a remote SIP useragent
*
* @param sessp Pointer to allocated SIP Session
* @param sock SIP Session socket
* @param to_uri To SIP uri
* @param from_name From display name
* @param from_uri From SIP uri
* @param cuser Contact username or URI
* @param routev Outbound route vector
* @param routec Outbound route vector count
* @param ctype Session content-type
* @param desc Content description (e.g. SDP)
* @param authh SIP Authentication handler
* @param aarg Authentication handler argument
* @param aref True to mem_ref() aarg
* @param offerh Session offer handler
* @param answerh Session answer handler
* @param progrh Session progress handler
* @param estabh Session established handler
* @param infoh Session info handler
* @param referh Session refer handler
* @param closeh Session close handler
* @param arg Handler argument
* @param fmt Formatted strings with extra SIP Headers
*
* @return 0 if success, otherwise errorcode
*/
int sipsess_connect(struct sipsess **sessp, struct sipsess_sock *sock,
const char *to_uri, const char *from_name,
const char *from_uri, const char *cuser,
const char *routev[], uint32_t routec,
const char *ctype, struct mbuf *desc,
sip_auth_h *authh, void *aarg, bool aref,
sipsess_offer_h *offerh, sipsess_answer_h *answerh,
sipsess_progr_h *progrh, sipsess_estab_h *estabh,
sipsess_info_h *infoh, sipsess_refer_h *referh,
sipsess_close_h *closeh, void *arg, const char *fmt, ...)
{
struct sipsess *sess;
int err;
if (!sessp || !sock || !to_uri || !from_uri || !cuser || !ctype)
return EINVAL;
err = sipsess_alloc(&sess, sock, cuser, ctype, desc, authh, aarg, aref,
offerh, answerh, progrh, estabh, infoh, referh,
closeh, arg);
if (err)
return err;
/* Custom SIP headers */
if (fmt) {
va_list ap;
sess->hdrs = mbuf_alloc(256);
if (!sess->hdrs) {
err = ENOMEM;
goto out;
}
va_start(ap, fmt);
err = mbuf_vprintf(sess->hdrs, fmt, ap);
sess->hdrs->pos = 0;
va_end(ap);
if (err)
goto out;
}
sess->owner = true;
err = sip_dialog_alloc(&sess->dlg, to_uri, to_uri, from_name,
from_uri, routev, routec);
if (err)
goto out;
hash_append(sock->ht_sess,
hash_joaat_str(sip_dialog_callid(sess->dlg)),
&sess->he, sess);
err = invite(sess);
if (err)
goto out;
out:
if (err)
mem_deref(sess);
else
*sessp = sess;
return err;
}