Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/sipsess/accept.c b/src/sipsess/accept.c
new file mode 100644
index 0000000..b8d02f8
--- /dev/null
+++ b/src/sipsess/accept.c
@@ -0,0 +1,246 @@
+/**
+ * @file accept.c SIP Session Accept
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 void cancel_handler(void *arg)
+{
+ struct sipsess *sess = arg;
+
+ (void)sip_treply(&sess->st, sess->sip, sess->msg,
+ 487, "Request Terminated");
+
+ sess->peerterm = true;
+
+ if (sess->terminated)
+ return;
+
+ sipsess_terminate(sess, ECONNRESET, NULL);
+}
+
+
+/**
+ * Accept an incoming SIP Session connection
+ *
+ * @param sessp Pointer to allocated SIP Session
+ * @param sock SIP Session socket
+ * @param msg Incoming SIP message
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param cuser Contact username or URI
+ * @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 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_accept(struct sipsess **sessp, struct sipsess_sock *sock,
+ const struct sip_msg *msg, uint16_t scode,
+ const char *reason, const char *cuser, const char *ctype,
+ struct mbuf *desc,
+ sip_auth_h *authh, void *aarg, bool aref,
+ sipsess_offer_h *offerh, sipsess_answer_h *answerh,
+ sipsess_estab_h *estabh, sipsess_info_h *infoh,
+ sipsess_refer_h *referh, sipsess_close_h *closeh,
+ void *arg, const char *fmt, ...)
+{
+ struct sipsess *sess;
+ va_list ap;
+ int err;
+
+ if (!sessp || !sock || !msg || scode < 101 || scode > 299 ||
+ !cuser || !ctype)
+ return EINVAL;
+
+ err = sipsess_alloc(&sess, sock, cuser, ctype, NULL, authh, aarg, aref,
+ offerh, answerh, NULL, estabh, infoh, referh,
+ closeh, arg);
+ if (err)
+ return err;
+
+ err = sip_dialog_accept(&sess->dlg, msg);
+ if (err)
+ goto out;
+
+ hash_append(sock->ht_sess,
+ hash_joaat_str(sip_dialog_callid(sess->dlg)),
+ &sess->he, sess);
+
+ sess->msg = mem_ref((void *)msg);
+
+ err = sip_strans_alloc(&sess->st, sess->sip, msg, cancel_handler,
+ sess);
+ if (err)
+ goto out;
+
+ va_start(ap, fmt);
+
+ if (scode >= 200)
+ err = sipsess_reply_2xx(sess, msg, scode, reason, desc,
+ fmt, &ap);
+ else {
+ struct sip_contact contact;
+
+ sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp);
+
+ err = sip_treplyf(&sess->st, NULL, sess->sip,
+ msg, true, scode, reason,
+ "%H"
+ "%v"
+ "%s%s%s"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ sip_contact_print, &contact,
+ fmt, &ap,
+ desc ? "Content-Type: " : "",
+ desc ? sess->ctype : "",
+ desc ? "\r\n" : "",
+ desc ? mbuf_get_left(desc) : (size_t)0,
+ desc ? mbuf_buf(desc) : NULL,
+ desc ? mbuf_get_left(desc) : (size_t)0);
+ }
+
+ va_end(ap);
+
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(sess);
+ else
+ *sessp = sess;
+
+ return err;
+}
+
+
+/**
+ * Send progress response
+ *
+ * @param sess SIP Session
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param desc Content description (e.g. SDP)
+ * @param fmt Formatted strings with extra SIP Headers
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_progress(struct sipsess *sess, uint16_t scode, const char *reason,
+ struct mbuf *desc, const char *fmt, ...)
+{
+ struct sip_contact contact;
+ va_list ap;
+ int err;
+
+ if (!sess || !sess->st || !sess->msg || scode < 101 || scode > 199)
+ return EINVAL;
+
+ va_start(ap, fmt);
+
+ sip_contact_set(&contact, sess->cuser, &sess->msg->dst, sess->msg->tp);
+
+ err = sip_treplyf(&sess->st, NULL, sess->sip, sess->msg, true,
+ scode, reason,
+ "%H"
+ "%v"
+ "%s%s%s"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ sip_contact_print, &contact,
+ fmt, &ap,
+ desc ? "Content-Type: " : "",
+ desc ? sess->ctype : "",
+ desc ? "\r\n" : "",
+ desc ? mbuf_get_left(desc) : (size_t)0,
+ desc ? mbuf_buf(desc) : NULL,
+ desc ? mbuf_get_left(desc) : (size_t)0);
+
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Answer an incoming SIP Session connection
+ *
+ * @param sess SIP Session
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param desc Content description (e.g. SDP)
+ * @param fmt Formatted strings with extra SIP Headers
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_answer(struct sipsess *sess, uint16_t scode, const char *reason,
+ struct mbuf *desc, const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ if (!sess || !sess->st || !sess->msg || scode < 200 || scode > 299)
+ return EINVAL;
+
+ va_start(ap, fmt);
+ err = sipsess_reply_2xx(sess, sess->msg, scode, reason, desc,
+ fmt, &ap);
+ va_end(ap);
+
+ return err;
+}
+
+
+/**
+ * Reject an incoming SIP Session connection
+ *
+ * @param sess SIP Session
+ * @param scode Response status code
+ * @param reason Response reason phrase
+ * @param fmt Formatted strings with extra SIP Headers
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_reject(struct sipsess *sess, uint16_t scode, const char *reason,
+ const char *fmt, ...)
+{
+ va_list ap;
+ int err;
+
+ if (!sess || !sess->st || !sess->msg || scode < 300)
+ return EINVAL;
+
+ va_start(ap, fmt);
+ err = sip_treplyf(&sess->st, NULL, sess->sip, sess->msg, false,
+ scode, reason, fmt ? "%v" : NULL, fmt, &ap);
+ va_end(ap);
+
+ return err;
+}
diff --git a/src/sipsess/ack.c b/src/sipsess/ack.c
new file mode 100644
index 0000000..4a9be68
--- /dev/null
+++ b/src/sipsess/ack.c
@@ -0,0 +1,147 @@
+/**
+ * @file ack.c SIP Session ACK
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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"
+
+
+struct sipsess_ack {
+ struct le he;
+ struct tmr tmr;
+ struct sa dst;
+ struct sip_request *req;
+ struct sip_dialog *dlg;
+ struct mbuf *mb;
+ enum sip_transp tp;
+ uint32_t cseq;
+};
+
+
+static void destructor(void *arg)
+{
+ struct sipsess_ack *ack = arg;
+
+ hash_unlink(&ack->he);
+ tmr_cancel(&ack->tmr);
+ mem_deref(ack->req);
+ mem_deref(ack->dlg);
+ mem_deref(ack->mb);
+}
+
+
+static void tmr_handler(void *arg)
+{
+ struct sipsess_ack *ack = arg;
+
+ mem_deref(ack);
+}
+
+
+static int send_handler(enum sip_transp tp, const struct sa *src,
+ const struct sa *dst, struct mbuf *mb, void *arg)
+{
+ struct sipsess_ack *ack = arg;
+ (void)src;
+
+ mem_deref(ack->mb);
+ ack->mb = mem_ref(mb);
+ ack->dst = *dst;
+ ack->tp = tp;
+
+ tmr_start(&ack->tmr, 64 * SIP_T1, tmr_handler, ack);
+
+ return 0;
+}
+
+
+static void resp_handler(int err, const struct sip_msg *msg, void *arg)
+{
+ struct sipsess_ack *ack = arg;
+ (void)err;
+ (void)msg;
+
+ mem_deref(ack);
+}
+
+
+int sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg,
+ uint32_t cseq, struct sip_auth *auth,
+ const char *ctype, struct mbuf *desc)
+{
+ struct sipsess_ack *ack;
+ int err;
+
+ ack = mem_zalloc(sizeof(*ack), destructor);
+ if (!ack)
+ return ENOMEM;
+
+ hash_append(sock->ht_ack,
+ hash_joaat_str(sip_dialog_callid(dlg)),
+ &ack->he, ack);
+
+ ack->dlg = mem_ref(dlg);
+ ack->cseq = cseq;
+
+ err = sip_drequestf(&ack->req, sock->sip, false, "ACK", dlg, cseq,
+ auth, send_handler, resp_handler, ack,
+ "%s%s%s"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ desc ? "Content-Type: " : "",
+ desc ? ctype : "",
+ desc ? "\r\n" : "",
+ desc ? mbuf_get_left(desc) : (size_t)0,
+ desc ? mbuf_buf(desc) : NULL,
+ desc ? mbuf_get_left(desc) : (size_t)0);
+ if (err)
+ goto out;
+
+ out:
+ if (err)
+ mem_deref(ack);
+
+ return err;
+}
+
+
+static bool cmp_handler(struct le *le, void *arg)
+{
+ struct sipsess_ack *ack = le->data;
+ const struct sip_msg *msg = arg;
+
+ if (!sip_dialog_cmp(ack->dlg, msg))
+ return false;
+
+ if (ack->cseq != msg->cseq.num)
+ return false;
+
+ return true;
+}
+
+
+int sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg)
+{
+ struct sipsess_ack *ack;
+
+ ack = list_ledata(hash_lookup(sock->ht_ack,
+ hash_joaat_pl(&msg->callid),
+ cmp_handler, (void *)msg));
+ if (!ack)
+ return ENOENT;
+
+ return sip_send(sock->sip, NULL, ack->tp, &ack->dst, ack->mb);
+}
diff --git a/src/sipsess/close.c b/src/sipsess/close.c
new file mode 100644
index 0000000..b680a9a
--- /dev/null
+++ b/src/sipsess/close.c
@@ -0,0 +1,74 @@
+/**
+ * @file close.c SIP Session Close
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 void bye_resp_handler(int err, const struct sip_msg *msg, void *arg)
+{
+ struct sipsess *sess = arg;
+
+ if (err || sip_request_loops(&sess->ls, msg->scode))
+ goto out;
+
+ if (msg->scode < 200) {
+ return;
+ }
+ else if (msg->scode < 300) {
+ ;
+ }
+ else {
+ if (sess->peerterm)
+ goto out;
+
+ switch (msg->scode) {
+
+ case 401:
+ case 407:
+ err = sip_auth_authenticate(sess->auth, msg);
+ if (err)
+ break;
+
+ err = sipsess_bye(sess, false);
+ if (err)
+ break;
+
+ return;
+ }
+ }
+
+ out:
+ mem_deref(sess);
+}
+
+
+int sipsess_bye(struct sipsess *sess, bool reset_ls)
+{
+ if (sess->req)
+ return EPROTO;
+
+ if (reset_ls)
+ sip_loopstate_reset(&sess->ls);
+
+ return sip_drequestf(&sess->req, sess->sip, true, "BYE",
+ sess->dlg, 0, sess->auth,
+ NULL, bye_resp_handler, sess,
+ "%s"
+ "Content-Length: 0\r\n"
+ "\r\n",
+ sess->close_hdrs);
+}
diff --git a/src/sipsess/connect.c b/src/sipsess/connect.c
new file mode 100644
index 0000000..5f6317e
--- /dev/null
+++ b/src/sipsess/connect.c
@@ -0,0 +1,245 @@
+/**
+ * @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;
+}
diff --git a/src/sipsess/info.c b/src/sipsess/info.c
new file mode 100644
index 0000000..e30bc0d
--- /dev/null
+++ b/src/sipsess/info.c
@@ -0,0 +1,123 @@
+/**
+ * @file info.c SIP Session Info
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 info_request(struct sipsess_request *req);
+
+
+static void info_resp_handler(int err, const struct sip_msg *msg, void *arg)
+{
+ struct sipsess_request *req = arg;
+
+ if (err || sip_request_loops(&req->ls, msg->scode))
+ goto out;
+
+ if (msg->scode < 200) {
+ return;
+ }
+ else if (msg->scode < 300) {
+ ;
+ }
+ else {
+ if (req->sess->terminated)
+ goto out;
+
+ switch (msg->scode) {
+
+ case 401:
+ case 407:
+ err = sip_auth_authenticate(req->sess->auth, msg);
+ if (err) {
+ err = (err == EAUTH) ? 0 : err;
+ break;
+ }
+
+ err = info_request(req);
+ if (err)
+ break;
+
+ return;
+
+ case 408:
+ case 481:
+ sipsess_terminate(req->sess, 0, msg);
+ break;
+ }
+ }
+
+ out:
+ if (!req->sess->terminated) {
+ if (err == ETIMEDOUT)
+ sipsess_terminate(req->sess, err, NULL);
+ else
+ req->resph(err, msg, req->arg);
+ }
+
+ mem_deref(req);
+}
+
+
+static int info_request(struct sipsess_request *req)
+{
+ return sip_drequestf(&req->req, req->sess->sip, true, "INFO",
+ req->sess->dlg, 0, req->sess->auth,
+ NULL, info_resp_handler, req,
+ "Content-Type: %s\r\n"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ req->ctype,
+ mbuf_get_left(req->body),
+ mbuf_buf(req->body), mbuf_get_left(req->body));
+}
+
+
+/**
+ * Send a SIP INFO request in the SIP Session
+ *
+ * @param sess SIP Session
+ * @param ctype Content-type
+ * @param body Content description (e.g. SDP)
+ * @param resph Response handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body,
+ sip_resp_h *resph, void *arg)
+{
+ struct sipsess_request *req;
+ int err;
+
+ if (!sess || sess->terminated || !ctype || !body)
+ return EINVAL;
+
+ if (!sip_dialog_established(sess->dlg))
+ return ENOTCONN;
+
+ err = sipsess_request_alloc(&req, sess, ctype, body, resph, arg);
+ if (err)
+ return err;
+
+ err = info_request(req);
+ if (err)
+ mem_deref(req);
+
+ return err;
+}
diff --git a/src/sipsess/listen.c b/src/sipsess/listen.c
new file mode 100644
index 0000000..86a0ece
--- /dev/null
+++ b/src/sipsess/listen.c
@@ -0,0 +1,367 @@
+/**
+ * @file sipsess/listen.c SIP Session Listen
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 void destructor(void *arg)
+{
+ struct sipsess_sock *sock = arg;
+
+ mem_deref(sock->lsnr_resp);
+ mem_deref(sock->lsnr_req);
+ hash_flush(sock->ht_sess);
+ mem_deref(sock->ht_sess);
+ hash_flush(sock->ht_ack);
+ mem_deref(sock->ht_ack);
+}
+
+
+static void internal_connect_handler(const struct sip_msg *msg, void *arg)
+{
+ struct sipsess_sock *sock = arg;
+
+ (void)sip_treply(NULL, sock->sip, msg, 486, "Busy Here");
+}
+
+
+static bool cmp_handler(struct le *le, void *arg)
+{
+ struct sipsess *sess = le->data;
+ const struct sip_msg *msg = arg;
+
+ return sip_dialog_cmp(sess->dlg, msg);
+}
+
+
+static struct sipsess *sipsess_find(struct sipsess_sock *sock,
+ const struct sip_msg *msg)
+{
+ return list_ledata(hash_lookup(sock->ht_sess,
+ hash_joaat_pl(&msg->callid),
+ cmp_handler, (void *)msg));
+}
+
+
+static void info_handler(struct sipsess_sock *sock, const struct sip_msg *msg)
+{
+ struct sip *sip = sock->sip;
+ struct sipsess *sess;
+
+ sess = sipsess_find(sock, msg);
+ if (!sess || sess->terminated) {
+ (void)sip_reply(sip, msg, 481, "Call Does Not Exist");
+ return;
+ }
+
+ if (!sip_dialog_rseq_valid(sess->dlg, msg)) {
+ (void)sip_reply(sip, msg, 500, "Server Internal Error");
+ return;
+ }
+
+ if (!sess->infoh) {
+ (void)sip_reply(sip, msg, 501, "Not Implemented");
+ return;
+ }
+
+ sess->infoh(sip, msg, sess->arg);
+}
+
+
+static void refer_handler(struct sipsess_sock *sock, const struct sip_msg *msg)
+{
+ struct sip *sip = sock->sip;
+ struct sipsess *sess;
+
+ sess = sipsess_find(sock, msg);
+ if (!sess || sess->terminated) {
+ (void)sip_reply(sip, msg, 481, "Call Does Not Exist");
+ return;
+ }
+
+ if (!sip_dialog_rseq_valid(sess->dlg, msg)) {
+ (void)sip_reply(sip, msg, 500, "Server Internal Error");
+ return;
+ }
+
+ if (!sess->referh) {
+ (void)sip_reply(sip, msg, 501, "Not Implemented");
+ return;
+ }
+
+ sess->referh(sip, msg, sess->arg);
+}
+
+
+static void bye_handler(struct sipsess_sock *sock, const struct sip_msg *msg)
+{
+ struct sip *sip = sock->sip;
+ struct sipsess *sess;
+
+ sess = sipsess_find(sock, msg);
+ if (!sess) {
+ (void)sip_reply(sip, msg, 481, "Call Does Not Exist");
+ return;
+ }
+
+ if (!sip_dialog_rseq_valid(sess->dlg, msg)) {
+ (void)sip_reply(sip, msg, 500, "Server Internal Error");
+ return;
+ }
+
+ (void)sip_treplyf(NULL, NULL, sip, msg, false, 200, "OK",
+ "%s"
+ "Content-Length: 0\r\n"
+ "\r\n",
+ sess->close_hdrs);
+
+ sess->peerterm = true;
+
+ if (sess->terminated)
+ return;
+
+ if (sess->st) {
+ (void)sip_treply(&sess->st, sess->sip, sess->msg,
+ 487, "Request Terminated");
+ }
+
+ sipsess_terminate(sess, ECONNRESET, NULL);
+}
+
+
+static void ack_handler(struct sipsess_sock *sock, const struct sip_msg *msg)
+{
+ struct sipsess *sess;
+ bool awaiting_answer;
+ int err = 0;
+
+ sess = sipsess_find(sock, msg);
+ if (!sess)
+ return;
+
+ if (sipsess_reply_ack(sess, msg, &awaiting_answer))
+ return;
+
+ if (sess->terminated) {
+ if (!sess->replyl.head) {
+ sess->established = true;
+ mem_deref(sess);
+ }
+ return;
+ }
+
+ if (awaiting_answer) {
+ sess->awaiting_answer = false;
+ err = sess->answerh(msg, sess->arg);
+ }
+
+ if (sess->modify_pending && !sess->replyl.head)
+ (void)sipsess_reinvite(sess, true);
+
+ if (sess->established)
+ return;
+
+ sess->msg = mem_deref((void *)sess->msg);
+ sess->established = true;
+
+ if (err)
+ sipsess_terminate(sess, err, NULL);
+ else
+ sess->estabh(msg, sess->arg);
+}
+
+
+static void reinvite_handler(struct sipsess_sock *sock,
+ const struct sip_msg *msg)
+{
+ struct sip *sip = sock->sip;
+ struct sipsess *sess;
+ struct mbuf *desc;
+ char m[256];
+ int err;
+
+ sess = sipsess_find(sock, msg);
+ if (!sess || sess->terminated) {
+ (void)sip_treply(NULL, sip, msg, 481, "Call Does Not Exist");
+ return;
+ }
+
+ if (!sip_dialog_rseq_valid(sess->dlg, msg)) {
+ (void)sip_treply(NULL, sip, msg, 500, "Server Internal Error");
+ return;
+ }
+
+ if (sess->st || sess->awaiting_answer) {
+ (void)sip_treplyf(NULL, NULL, sip, msg, false,
+ 500, "Server Internal Error",
+ "Retry-After: 5\r\n"
+ "Content-Length: 0\r\n"
+ "\r\n");
+ return;
+ }
+
+ if (sess->req) {
+ (void)sip_treply(NULL, sip, msg, 491, "Request Pending");
+ return;
+ }
+
+ err = sess->offerh(&desc, msg, sess->arg);
+ if (err) {
+ (void)sip_reply(sip, msg, 488, str_error(err, m, sizeof(m)));
+ return;
+ }
+
+ (void)sip_dialog_update(sess->dlg, msg);
+ (void)sipsess_reply_2xx(sess, msg, 200, "OK", desc,
+ NULL, NULL);
+
+ /* pending modifications considered outdated;
+ sdp may have changed in above exchange */
+ sess->desc = mem_deref(sess->desc);
+ sess->modify_pending = false;
+ tmr_cancel(&sess->tmr);
+ mem_deref(desc);
+}
+
+
+static void invite_handler(struct sipsess_sock *sock,
+ const struct sip_msg *msg)
+{
+ sock->connh(msg, sock->arg);
+}
+
+
+static bool request_handler(const struct sip_msg *msg, void *arg)
+{
+ struct sipsess_sock *sock = arg;
+
+ if (!pl_strcmp(&msg->met, "INVITE")) {
+
+ if (pl_isset(&msg->to.tag))
+ reinvite_handler(sock, msg);
+ else
+ invite_handler(sock, msg);
+
+ return true;
+ }
+ else if (!pl_strcmp(&msg->met, "ACK")) {
+ ack_handler(sock, msg);
+ return true;
+ }
+ else if (!pl_strcmp(&msg->met, "BYE")) {
+ bye_handler(sock, msg);
+ return true;
+ }
+ else if (!pl_strcmp(&msg->met, "INFO")) {
+ info_handler(sock, msg);
+ return true;
+ }
+ else if (!pl_strcmp(&msg->met, "REFER")) {
+
+ if (!pl_isset(&msg->to.tag))
+ return false;
+
+ refer_handler(sock, msg);
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool response_handler(const struct sip_msg *msg, void *arg)
+{
+ struct sipsess_sock *sock = arg;
+
+ if (pl_strcmp(&msg->cseq.met, "INVITE"))
+ return false;
+
+ if (msg->scode < 200 || msg->scode > 299)
+ return false;
+
+ (void)sipsess_ack_again(sock, msg);
+
+ return true;
+}
+
+
+/**
+ * Listen to a SIP Session socket for incoming connections
+ *
+ * @param sockp Pointer to allocated SIP Session socket
+ * @param sip SIP Stack instance
+ * @param htsize Hashtable size
+ * @param connh Connection handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_listen(struct sipsess_sock **sockp, struct sip *sip,
+ int htsize, sipsess_conn_h *connh, void *arg)
+{
+ struct sipsess_sock *sock;
+ int err;
+
+ if (!sockp || !sip || !htsize)
+ return EINVAL;
+
+ sock = mem_zalloc(sizeof(*sock), destructor);
+ if (!sock)
+ return ENOMEM;
+
+ err = sip_listen(&sock->lsnr_resp, sip, false, response_handler, sock);
+ if (err)
+ goto out;
+
+ err = sip_listen(&sock->lsnr_req, sip, true, request_handler, sock);
+ if (err)
+ goto out;
+
+ err = hash_alloc(&sock->ht_sess, htsize);
+ if (err)
+ goto out;
+
+ err = hash_alloc(&sock->ht_ack, htsize);
+ if (err)
+ goto out;
+
+ sock->sip = sip;
+ sock->connh = connh ? connh : internal_connect_handler;
+ sock->arg = connh ? arg : sock;
+
+ out:
+ if (err)
+ mem_deref(sock);
+ else
+ *sockp = sock;
+
+ return err;
+}
+
+
+/**
+ * Close all SIP Sessions
+ *
+ * @param sock SIP Session socket
+ */
+void sipsess_close_all(struct sipsess_sock *sock)
+{
+ if (!sock)
+ return;
+
+ hash_flush(sock->ht_sess);
+}
diff --git a/src/sipsess/mod.mk b/src/sipsess/mod.mk
new file mode 100644
index 0000000..c2644e7
--- /dev/null
+++ b/src/sipsess/mod.mk
@@ -0,0 +1,16 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+SRCS += sipsess/sess.c
+SRCS += sipsess/accept.c
+SRCS += sipsess/ack.c
+SRCS += sipsess/close.c
+SRCS += sipsess/connect.c
+SRCS += sipsess/info.c
+SRCS += sipsess/listen.c
+SRCS += sipsess/modify.c
+SRCS += sipsess/reply.c
+SRCS += sipsess/request.c
diff --git a/src/sipsess/modify.c b/src/sipsess/modify.c
new file mode 100644
index 0000000..452115f
--- /dev/null
+++ b/src/sipsess/modify.c
@@ -0,0 +1,171 @@
+/**
+ * @file modify.c SIP Session Modify
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 void tmr_handler(void *arg)
+{
+ struct sipsess *sess = arg;
+
+ (void)sipsess_reinvite(sess, true);
+}
+
+
+static void reinvite_resp_handler(int err, const struct sip_msg *msg,
+ void *arg)
+{
+ struct sipsess *sess = arg;
+ const struct sip_hdr *hdr;
+ struct mbuf *desc = NULL;
+
+ if (err || sip_request_loops(&sess->ls, msg->scode))
+ goto out;
+
+ if (msg->scode < 200) {
+ return;
+ }
+ else if (msg->scode < 300) {
+
+ (void)sip_dialog_update(sess->dlg, msg);
+
+ if (sess->sent_offer)
+ (void)sess->answerh(msg, sess->arg);
+ else {
+ sess->modify_pending = false;
+ (void)sess->offerh(&desc, msg, sess->arg);
+ }
+
+ (void)sipsess_ack(sess->sock, sess->dlg, msg->cseq.num,
+ sess->auth, sess->ctype, desc);
+ mem_deref(desc);
+ }
+ 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 = sipsess_reinvite(sess, false);
+ if (err)
+ break;
+
+ return;
+
+ case 408:
+ case 481:
+ sipsess_terminate(sess, 0, msg);
+ return;
+
+ case 491:
+ tmr_start(&sess->tmr, sess->owner ? 3000 : 1000,
+ tmr_handler, sess);
+ return;
+
+ case 500:
+ hdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER);
+ if (!hdr)
+ break;
+
+ tmr_start(&sess->tmr, pl_u32(&hdr->val) * 1000,
+ tmr_handler, sess);
+ return;
+ }
+ }
+ out:
+ if (sess->terminated)
+ mem_deref(sess);
+ else if (err == ETIMEDOUT)
+ sipsess_terminate(sess, err, NULL);
+ else if (sess->modify_pending)
+ (void)sipsess_reinvite(sess, true);
+ else
+ sess->desc = mem_deref(sess->desc);
+}
+
+
+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);
+}
+
+
+int sipsess_reinvite(struct sipsess *sess, bool reset_ls)
+{
+ if (sess->req)
+ return EPROTO;
+
+ sess->sent_offer = sess->desc ? true : false;
+ sess->modify_pending = false;
+
+ if (reset_ls)
+ sip_loopstate_reset(&sess->ls);
+
+ return sip_drequestf(&sess->req, sess->sip, true, "INVITE",
+ sess->dlg, 0, sess->auth,
+ send_handler, reinvite_resp_handler, sess,
+ "%s%s%s"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ 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);
+}
+
+
+/**
+ * Modify an established SIP Session sending Re-INVITE or UPDATE
+ *
+ * @param sess SIP Session
+ * @param desc Content description (e.g. SDP)
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_modify(struct sipsess *sess, struct mbuf *desc)
+{
+ if (!sess || sess->st || sess->terminated)
+ return EINVAL;
+
+ mem_deref(sess->desc);
+ sess->desc = mem_ref(desc);
+
+ if (sess->req || sess->tmr.th || sess->replyl.head) {
+ sess->modify_pending = true;
+ return 0;
+ }
+
+ return sipsess_reinvite(sess, true);
+}
diff --git a/src/sipsess/reply.c b/src/sipsess/reply.c
new file mode 100644
index 0000000..4c3aa6f
--- /dev/null
+++ b/src/sipsess/reply.c
@@ -0,0 +1,161 @@
+/**
+ * @file sipsess/reply.c SIP Session Reply
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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"
+
+
+struct sipsess_reply {
+ struct le le;
+ struct tmr tmr;
+ struct tmr tmrg;
+ const struct sip_msg *msg;
+ struct mbuf *mb;
+ struct sipsess *sess;
+ bool awaiting_answer;
+ uint32_t seq;
+ uint32_t txc;
+};
+
+
+static void destructor(void *arg)
+{
+ struct sipsess_reply *reply = arg;
+
+ list_unlink(&reply->le);
+ tmr_cancel(&reply->tmr);
+ tmr_cancel(&reply->tmrg);
+ mem_deref((void *)reply->msg);
+ mem_deref(reply->mb);
+}
+
+
+static void tmr_handler(void *arg)
+{
+ struct sipsess_reply *reply = arg;
+ struct sipsess *sess = reply->sess;
+
+ mem_deref(reply);
+
+ /* wait for all pending ACKs */
+ if (sess->replyl.head)
+ return;
+
+ /* we want to send bye */
+ sess->established = true;
+
+ if (!sess->terminated)
+ sipsess_terminate(sess, ETIMEDOUT, NULL);
+ else
+ mem_deref(sess);
+}
+
+
+static void retransmit_handler(void *arg)
+{
+ struct sipsess_reply *reply = arg;
+
+ (void)sip_send(reply->sess->sip, reply->msg->sock, reply->msg->tp,
+ &reply->msg->src, reply->mb);
+
+ reply->txc++;
+ tmr_start(&reply->tmrg, MIN(SIP_T1<<reply->txc, SIP_T2),
+ retransmit_handler, reply);
+}
+
+
+int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg,
+ uint16_t scode, const char *reason, struct mbuf *desc,
+ const char *fmt, va_list *ap)
+{
+ struct sipsess_reply *reply;
+ struct sip_contact contact;
+ int err = ENOMEM;
+
+ reply = mem_zalloc(sizeof(*reply), destructor);
+ if (!reply)
+ goto out;
+
+ list_append(&sess->replyl, &reply->le, reply);
+ reply->seq = msg->cseq.num;
+ reply->msg = mem_ref((void *)msg);
+ reply->sess = sess;
+
+ sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp);
+
+ err = sip_treplyf(&sess->st, &reply->mb, sess->sip,
+ msg, true, scode, reason,
+ "%H"
+ "%v"
+ "%s%s%s"
+ "Content-Length: %zu\r\n"
+ "\r\n"
+ "%b",
+ sip_contact_print, &contact,
+ fmt, ap,
+ desc ? "Content-Type: " : "",
+ desc ? sess->ctype : "",
+ desc ? "\r\n" : "",
+ desc ? mbuf_get_left(desc) : (size_t)0,
+ desc ? mbuf_buf(desc) : NULL,
+ desc ? mbuf_get_left(desc) : (size_t)0);
+
+ if (err)
+ goto out;
+
+ tmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply);
+ tmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply);
+
+ if (!mbuf_get_left(msg->mb) && desc) {
+ reply->awaiting_answer = true;
+ sess->awaiting_answer = true;
+ }
+
+ out:
+ if (err) {
+ sess->st = mem_deref(sess->st);
+ mem_deref(reply);
+ }
+
+ return err;
+}
+
+
+static bool cmp_handler(struct le *le, void *arg)
+{
+ struct sipsess_reply *reply = le->data;
+ const struct sip_msg *msg = arg;
+
+ return msg->cseq.num == reply->seq;
+}
+
+
+int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg,
+ bool *awaiting_answer)
+{
+ struct sipsess_reply *reply;
+
+ reply = list_ledata(list_apply(&sess->replyl, false, cmp_handler,
+ (void *)msg));
+ if (!reply)
+ return ENOENT;
+
+ *awaiting_answer = reply->awaiting_answer;
+
+ mem_deref(reply);
+
+ return 0;
+}
diff --git a/src/sipsess/request.c b/src/sipsess/request.c
new file mode 100644
index 0000000..cd7384a
--- /dev/null
+++ b/src/sipsess/request.c
@@ -0,0 +1,79 @@
+/**
+ * @file sipsess/request.c SIP Session Non-INVITE Request
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 void destructor(void *arg)
+{
+ struct sipsess_request *req = arg;
+
+ list_unlink(&req->le);
+ mem_deref(req->ctype);
+ mem_deref(req->body);
+ mem_deref(req->req);
+
+ /* wait for pending requests */
+ if (req->sess->terminated && !req->sess->requestl.head)
+ mem_deref(req->sess);
+}
+
+
+static void internal_resp_handler(int err, const struct sip_msg *msg,
+ void *arg)
+{
+ (void)err;
+ (void)msg;
+ (void)arg;
+}
+
+
+int sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess,
+ const char *ctype, struct mbuf *body,
+ sip_resp_h *resph, void *arg)
+{
+ struct sipsess_request *req;
+ int err = 0;
+
+ if (!reqp || !sess || sess->terminated)
+ return EINVAL;
+
+ req = mem_zalloc(sizeof(*req), destructor);
+ if (!req)
+ return ENOMEM;
+
+ list_append(&sess->requestl, &req->le, req);
+
+ if (ctype) {
+ err = str_dup(&req->ctype, ctype);
+ if (err)
+ goto out;
+ }
+
+ req->sess = sess;
+ req->body = mem_ref(body);
+ req->resph = resph ? resph : internal_resp_handler;
+ req->arg = arg;
+
+ out:
+ if (err)
+ mem_deref(req);
+ else
+ *reqp = req;
+
+ return 0;
+}
diff --git a/src/sipsess/sess.c b/src/sipsess/sess.c
new file mode 100644
index 0000000..ff505eb
--- /dev/null
+++ b/src/sipsess/sess.c
@@ -0,0 +1,264 @@
+/**
+ * @file sipsess/sess.c SIP Session Core
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#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 internal_offer_handler(struct mbuf **descp,
+ const struct sip_msg *msg, void *arg)
+{
+ (void)descp;
+ (void)msg;
+ (void)arg;
+
+ return ENOSYS;
+}
+
+
+static int internal_answer_handler(const struct sip_msg *msg, void *arg)
+{
+ (void)msg;
+ (void)arg;
+
+ return ENOSYS;
+}
+
+
+static void internal_progress_handler(const struct sip_msg *msg, void *arg)
+{
+ (void)msg;
+ (void)arg;
+}
+
+
+static void internal_establish_handler(const struct sip_msg *msg, void *arg)
+{
+ (void)msg;
+ (void)arg;
+}
+
+
+static void internal_close_handler(int err, const struct sip_msg *msg,
+ void *arg)
+{
+ (void)err;
+ (void)msg;
+ (void)arg;
+}
+
+
+static bool termwait(struct sipsess *sess)
+{
+ bool wait = false;
+
+ sess->terminated = 1;
+ sess->offerh = internal_offer_handler;
+ sess->answerh = internal_answer_handler;
+ sess->progrh = internal_progress_handler;
+ sess->estabh = internal_establish_handler;
+ sess->infoh = NULL;
+ sess->referh = NULL;
+ sess->closeh = internal_close_handler;
+ sess->arg = sess;
+
+ tmr_cancel(&sess->tmr);
+
+ if (sess->st) {
+ (void)sip_treply(&sess->st, sess->sip, sess->msg,
+ 486, "Busy Here");
+ }
+
+ if (sess->req) {
+ sip_request_cancel(sess->req);
+ mem_ref(sess);
+ wait = true;
+ }
+
+ if (sess->replyl.head) {
+ mem_ref(sess);
+ wait = true;
+ }
+
+ if (sess->requestl.head) {
+ mem_ref(sess);
+ wait = true;
+ }
+
+ return wait;
+}
+
+
+static bool terminate(struct sipsess *sess)
+{
+ sess->terminated = 2;
+
+ if (!sess->established || sess->peerterm)
+ return false;
+
+ if (sipsess_bye(sess, true))
+ return false;
+
+ mem_ref(sess);
+ return true;
+}
+
+
+static void destructor(void *arg)
+{
+ struct sipsess *sess = arg;
+
+ switch (sess->terminated) {
+
+ case 0:
+ if (termwait(sess))
+ return;
+
+ /*@fallthrough@*/
+
+ case 1:
+ if (terminate(sess))
+ return;
+ break;
+ }
+
+ hash_unlink(&sess->he);
+ tmr_cancel(&sess->tmr);
+ list_flush(&sess->replyl);
+ list_flush(&sess->requestl);
+ mem_deref((void *)sess->msg);
+ mem_deref(sess->req);
+ mem_deref(sess->dlg);
+ mem_deref(sess->auth);
+ mem_deref(sess->cuser);
+ mem_deref(sess->ctype);
+ mem_deref(sess->close_hdrs);
+ mem_deref(sess->hdrs);
+ mem_deref(sess->desc);
+ mem_deref(sess->sock);
+ mem_deref(sess->sip);
+ mem_deref(sess->st);
+}
+
+
+int sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock,
+ const char *cuser, 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)
+{
+ struct sipsess *sess;
+ int err;
+
+ sess = mem_zalloc(sizeof(*sess), destructor);
+ if (!sess)
+ return ENOMEM;
+
+ err = sip_auth_alloc(&sess->auth, authh, aarg, aref);
+ if (err)
+ goto out;
+
+ err = str_dup(&sess->cuser, cuser);
+ if (err)
+ goto out;
+
+ err = str_dup(&sess->ctype, ctype);
+ if (err)
+ goto out;
+
+ sess->sock = mem_ref(sock);
+ sess->desc = mem_ref(desc);
+ sess->sip = mem_ref(sock->sip);
+ sess->offerh = offerh ? offerh : internal_offer_handler;
+ sess->answerh = answerh ? answerh : internal_answer_handler;
+ sess->progrh = progrh ? progrh : internal_progress_handler;
+ sess->estabh = estabh ? estabh : internal_establish_handler;
+ sess->infoh = infoh;
+ sess->referh = referh;
+ sess->closeh = closeh ? closeh : internal_close_handler;
+ sess->arg = arg;
+
+ out:
+ if (err)
+ mem_deref(sess);
+ else
+ *sessp = sess;
+
+ return err;
+}
+
+
+void sipsess_terminate(struct sipsess *sess, int err,
+ const struct sip_msg *msg)
+{
+ sipsess_close_h *closeh;
+ void *arg;
+
+ if (sess->terminated)
+ return;
+
+ closeh = sess->closeh;
+ arg = sess->arg;
+
+ if (!termwait(sess))
+ (void)terminate(sess);
+
+ closeh(err, msg, arg);
+}
+
+
+/**
+ * Get the SIP dialog from a SIP Session
+ *
+ * @param sess SIP Session
+ *
+ * @return SIP Dialog object
+ */
+struct sip_dialog *sipsess_dialog(const struct sipsess *sess)
+{
+ return sess ? sess->dlg : NULL;
+}
+
+
+/**
+ * Set extra SIP headers for inclusion in Session "close" messages
+ * like BYE and 200 OK. Multiple headers can be included.
+ *
+ * @param sess SIP Session
+ * @param hdrs Formatted strings with extra SIP Headers
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...)
+{
+ int err = 0;
+ va_list ap;
+
+ if (!sess)
+ return EINVAL;
+
+ sess->close_hdrs = mem_deref(sess->close_hdrs);
+
+ if (hdrs) {
+ va_start(ap, hdrs);
+ err = re_vsdprintf(&sess->close_hdrs, hdrs, ap);
+ va_end(ap);
+ }
+
+ return err;
+}
diff --git a/src/sipsess/sipsess.h b/src/sipsess/sipsess.h
new file mode 100644
index 0000000..3bdfcc7
--- /dev/null
+++ b/src/sipsess/sipsess.h
@@ -0,0 +1,89 @@
+/**
+ * @file sipsess.h SIP Session Private Interface
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+
+
+struct sipsess {
+ struct le he;
+ struct tmr tmr;
+ struct list replyl;
+ struct list requestl;
+ struct sip_loopstate ls;
+ struct sipsess_sock *sock;
+ const struct sip_msg *msg;
+ struct sip_request *req;
+ struct sip_dialog *dlg;
+ struct sip_strans *st;
+ struct sip_auth *auth;
+ struct sip *sip;
+ char *cuser;
+ char *ctype;
+ char *close_hdrs;
+ struct mbuf *hdrs;
+ struct mbuf *desc;
+ 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;
+ bool owner;
+ bool sent_offer;
+ bool awaiting_answer;
+ bool modify_pending;
+ bool established;
+ bool peerterm;
+ int terminated;
+};
+
+
+struct sipsess_sock {
+ struct sip_lsnr *lsnr_resp;
+ struct sip_lsnr *lsnr_req;
+ struct hash *ht_sess;
+ struct hash *ht_ack;
+ struct sip *sip;
+ sipsess_conn_h *connh;
+ void *arg;
+};
+
+
+struct sipsess_request {
+ struct le le;
+ struct sip_loopstate ls;
+ struct sipsess *sess;
+ struct sip_request *req;
+ char *ctype;
+ struct mbuf *body;
+ sip_resp_h *resph;
+ void *arg;
+};
+
+
+int sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock,
+ const char *cuser, 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);
+void sipsess_terminate(struct sipsess *sess, int err,
+ const struct sip_msg *msg);
+int sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg,
+ uint32_t cseq, struct sip_auth *auth,
+ const char *ctype, struct mbuf *desc);
+int sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg);
+int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg,
+ uint16_t scode, const char *reason, struct mbuf *desc,
+ const char *fmt, va_list *ap);
+int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg,
+ bool *awaiting_answer);
+int sipsess_reinvite(struct sipsess *sess, bool reset_ls);
+int sipsess_bye(struct sipsess *sess, bool reset_ls);
+int sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess,
+ const char *ctype, struct mbuf *body,
+ sip_resp_h *resph, void *arg);