/** * @file connect.c SIP Session Connect * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }