1 /**
2  * @file modify.c  SIP Session Modify
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <re_types.h>
7 #include <re_mem.h>
8 #include <re_mbuf.h>
9 #include <re_sa.h>
10 #include <re_list.h>
11 #include <re_hash.h>
12 #include <re_fmt.h>
13 #include <re_uri.h>
14 #include <re_tmr.h>
15 #include <re_msg.h>
16 #include <re_sip.h>
17 #include <re_sipsess.h>
18 #include "sipsess.h"
19 
20 
tmr_handler(void * arg)21 static void tmr_handler(void *arg)
22 {
23 	struct sipsess *sess = arg;
24 
25 	(void)sipsess_reinvite(sess, true);
26 }
27 
28 
reinvite_resp_handler(int err,const struct sip_msg * msg,void * arg)29 static void reinvite_resp_handler(int err, const struct sip_msg *msg,
30 				  void *arg)
31 {
32 	struct sipsess *sess = arg;
33 	const struct sip_hdr *hdr;
34 	struct mbuf *desc = NULL;
35 
36 	if (err || sip_request_loops(&sess->ls, msg->scode))
37 		goto out;
38 
39 	if (msg->scode < 200) {
40 		return;
41 	}
42 	else if (msg->scode < 300) {
43 
44 		(void)sip_dialog_update(sess->dlg, msg);
45 
46 		if (sess->sent_offer)
47 			(void)sess->answerh(msg, sess->arg);
48 		else {
49 			sess->modify_pending = false;
50 			(void)sess->offerh(&desc, msg, sess->arg);
51 		}
52 
53 		(void)sipsess_ack(sess->sock, sess->dlg, msg->cseq.num,
54 				  sess->auth, sess->ctype, desc);
55 		mem_deref(desc);
56 	}
57 	else {
58 		if (sess->terminated)
59 			goto out;
60 
61 		switch (msg->scode) {
62 
63 		case 401:
64 		case 407:
65 			err = sip_auth_authenticate(sess->auth, msg);
66 			if (err) {
67 				err = (err == EAUTH) ? 0 : err;
68 				break;
69 			}
70 
71 			err = sipsess_reinvite(sess, false);
72 			if (err)
73 				break;
74 
75 			return;
76 
77 		case 408:
78 		case 481:
79 			sipsess_terminate(sess, 0, msg);
80 			return;
81 
82 		case 491:
83 			tmr_start(&sess->tmr, sess->owner ? 3000 : 1000,
84 				  tmr_handler, sess);
85 			return;
86 
87 		case 500:
88 			hdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER);
89 			if (!hdr)
90 				break;
91 
92 			tmr_start(&sess->tmr, pl_u32(&hdr->val) * 1000,
93 				  tmr_handler, sess);
94 			return;
95 		}
96 	}
97  out:
98 	if (sess->terminated)
99 		mem_deref(sess);
100 	else if (err == ETIMEDOUT)
101 		sipsess_terminate(sess, err, NULL);
102 	else if (sess->modify_pending)
103 		(void)sipsess_reinvite(sess, true);
104 	else
105 		sess->desc = mem_deref(sess->desc);
106 }
107 
108 
send_handler(enum sip_transp tp,const struct sa * src,const struct sa * dst,struct mbuf * mb,void * arg)109 static int send_handler(enum sip_transp tp, const struct sa *src,
110 			const struct sa *dst, struct mbuf *mb, void *arg)
111 {
112 	struct sip_contact contact;
113 	struct sipsess *sess = arg;
114 	(void)dst;
115 
116 	sip_contact_set(&contact, sess->cuser, src, tp);
117 
118 	return mbuf_printf(mb, "%H", sip_contact_print, &contact);
119 }
120 
121 
sipsess_reinvite(struct sipsess * sess,bool reset_ls)122 int sipsess_reinvite(struct sipsess *sess, bool reset_ls)
123 {
124 	if (sess->req)
125 		return EPROTO;
126 
127 	sess->sent_offer = sess->desc ? true : false;
128 	sess->modify_pending = false;
129 
130 	if (reset_ls)
131 		sip_loopstate_reset(&sess->ls);
132 
133 	return sip_drequestf(&sess->req, sess->sip, true, "INVITE",
134 			     sess->dlg, 0, sess->auth,
135 			     send_handler, reinvite_resp_handler, sess,
136 			     "%s%s%s"
137 			     "Content-Length: %zu\r\n"
138 			     "\r\n"
139 			     "%b",
140 			     sess->desc ? "Content-Type: " : "",
141 			     sess->desc ? sess->ctype : "",
142 			     sess->desc ? "\r\n" : "",
143 			     sess->desc ? mbuf_get_left(sess->desc) :(size_t)0,
144 			     sess->desc ? mbuf_buf(sess->desc) : NULL,
145 			     sess->desc ? mbuf_get_left(sess->desc):(size_t)0);
146 }
147 
148 
149 /**
150  * Modify an established SIP Session sending Re-INVITE or UPDATE
151  *
152  * @param sess      SIP Session
153  * @param desc      Content description (e.g. SDP)
154  *
155  * @return 0 if success, otherwise errorcode
156  */
sipsess_modify(struct sipsess * sess,struct mbuf * desc)157 int sipsess_modify(struct sipsess *sess, struct mbuf *desc)
158 {
159 	if (!sess || sess->st || sess->terminated)
160 		return EINVAL;
161 
162 	mem_deref(sess->desc);
163 	sess->desc = mem_ref(desc);
164 
165 	if (sess->req || sess->tmr.th || sess->replyl.head) {
166 		sess->modify_pending = true;
167 		return 0;
168 	}
169 
170 	return sipsess_reinvite(sess, true);
171 }
172