| /** |
| * @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; |
| } |