1 /*
2  * Copyright (C) 2010-2011 Raphael Coeffic
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /** @file AmOfferAnswer.cpp */
28 
29 #include "AmOfferAnswer.h"
30 #include "AmSipDialog.h"
31 #include "AmSipHeaders.h"
32 #include "log.h"
33 
34 #include <assert.h>
35 
36 const char* __dlg_oa_status2str[AmOfferAnswer::__max_OA]  = {
37     "None",
38     "OfferRecved",
39     "OfferSent",
40     "Completed"
41 };
42 
getOAStateStr(AmOfferAnswer::OAState st)43 static const char* getOAStateStr(AmOfferAnswer::OAState st) {
44   if((st < 0) || (st >= AmOfferAnswer::__max_OA))
45     return "Invalid";
46   else
47     return __dlg_oa_status2str[st];
48 }
49 
50 
AmOfferAnswer(AmSipDialog * dlg)51 AmOfferAnswer::AmOfferAnswer(AmSipDialog* dlg)
52   : state(OA_None),
53     cseq(0),
54     sdp_remote(),
55     sdp_local(),
56     dlg(dlg)
57 {
58 
59 }
60 
getState()61 AmOfferAnswer::OAState AmOfferAnswer::getState()
62 {
63   return state;
64 }
65 
setState(AmOfferAnswer::OAState n_st)66 void AmOfferAnswer::setState(AmOfferAnswer::OAState n_st)
67 {
68   DBG("setting SIP dialog O/A status: %s->%s\n",
69       getOAStateStr(state), getOAStateStr(n_st));
70   state = n_st;
71 }
72 
getLocalSdp()73 const AmSdp& AmOfferAnswer::getLocalSdp()
74 {
75   return sdp_local;
76 }
77 
getRemoteSdp()78 const AmSdp& AmOfferAnswer::getRemoteSdp()
79 {
80   return sdp_remote;
81 }
82 
83 /** State maintenance */
saveState()84 void AmOfferAnswer::saveState()
85 {
86   saved_state = state;
87 }
88 
checkStateChange()89 int AmOfferAnswer::checkStateChange()
90 {
91   int ret = 0;
92 
93   if((saved_state != state) &&
94      (state == OA_Completed)) {
95 
96     ret = dlg->onSdpCompleted();
97   }
98 
99   return ret;
100 }
101 
clear()102 void AmOfferAnswer::clear()
103 {
104   setState(OA_None);
105   cseq  = 0;
106   sdp_remote.clear();
107   sdp_local.clear();
108 }
109 
clearTransitionalState()110 void AmOfferAnswer::clearTransitionalState()
111 {
112   if(state != OA_Completed){
113     clear();
114   }
115 }
116 
onRequestIn(const AmSipRequest & req)117 int AmOfferAnswer::onRequestIn(const AmSipRequest& req)
118 {
119   saveState();
120 
121   const char* err_txt  = NULL;
122   int         err_code = 0;
123 
124   if((req.method == SIP_METH_INVITE ||
125       req.method == SIP_METH_UPDATE ||
126       req.method == SIP_METH_ACK ||
127       req.method == SIP_METH_PRACK) &&
128      !req.body.empty() ) {
129 
130     const AmMimeBody* sdp_body = req.body.hasContentType(SIP_APPLICATION_SDP);
131     if(sdp_body)
132       err_code = onRxSdp(req.cseq,*sdp_body,&err_txt);
133   }
134 
135   if(checkStateChange()){
136     err_code = 500;
137     err_txt = "internal error";
138   }
139 
140   if(err_code){
141     if( req.method != SIP_METH_ACK ){ // INVITE || UPDATE || PRACK
142       dlg->reply(req,err_code,err_txt);
143     }
144     else { // ACK
145       // TODO: only if reply to initial INVITE (if re-INV, app should decide)
146       DBG("error %i with SDP received in ACK request: sending BYE\n",err_code);
147       dlg->bye();
148     }
149   }
150 
151   if((req.method == SIP_METH_ACK) &&
152      (req.cseq == cseq)){
153     // 200 ACK received:
154     //  -> reset OA state
155     DBG("200 ACK received: resetting OA state");
156     clearTransitionalState();
157   }
158 
159   return err_code ? -1 : 0;
160 }
161 
onReplyIn(const AmSipReply & reply)162 int AmOfferAnswer::onReplyIn(const AmSipReply& reply)
163 {
164   const char* err_txt  = NULL;
165   int         err_code = 0;
166 
167   if((reply.cseq_method == SIP_METH_INVITE ||
168       reply.cseq_method == SIP_METH_UPDATE ||
169       reply.cseq_method == SIP_METH_PRACK) &&
170      !reply.body.empty() ) {
171 
172     const AmMimeBody* sdp_body = reply.body.hasContentType(SIP_APPLICATION_SDP);
173     if(sdp_body) {
174 
175       if(((state == OA_Completed) ||
176 	  (state == OA_OfferRecved)) &&
177 	 (reply.cseq == cseq)) {
178 
179 	DBG("ignoring subsequent SDP reply within the same transaction\n");
180 	DBG("this usually happens when 183 and 200 have SDP\n");
181 
182 	/* Make sure that session is started when 200 OK is received */
183 	if (reply.code == 200) dlg->onSdpCompleted();
184 
185       }
186       else {
187 	saveState();
188 	err_code = onRxSdp(reply.cseq,reply.body,&err_txt);
189 	checkStateChange();
190       }
191     }
192   }
193 
194   if( (reply.code >= 300) &&
195       (reply.cseq == cseq) ) {
196     // final error reply -> cleanup OA state
197     DBG("after %u reply to %s: resetting OA state\n",
198 	reply.code, reply.cseq_method.c_str());
199     clearTransitionalState();
200   }
201 
202 
203   if(err_code){
204     // TODO: only if initial INVITE (if re-INV, app should decide)
205     DBG("error %i (%s) with SDP received in %i reply: sending ACK+BYE\n",
206 	err_code,err_txt?err_txt:"none",reply.code);
207     dlg->bye();
208   }
209 
210   return 0;
211 }
212 
onRxSdp(unsigned int m_cseq,const AmMimeBody & body,const char ** err_txt)213 int AmOfferAnswer::onRxSdp(unsigned int m_cseq, const AmMimeBody& body, const char** err_txt)
214 {
215   DBG("entering onRxSdp(), oa_state=%s\n", getOAStateStr(state));
216   OAState old_oa_state = state;
217 
218   int err_code = 0;
219   assert(err_txt);
220 
221   const AmMimeBody *sdp = body.hasContentType("application/sdp");
222 
223   if (sdp == NULL) {
224     err_code = 400;
225     *err_txt = "sdp body part not found";
226   } else if (sdp_remote.parse((const char*)sdp->getPayload())) {
227     err_code = 400;
228     *err_txt = "session description parsing failed";
229   }
230   else if(sdp_remote.media.empty()){
231     err_code = 400;
232     *err_txt = "no media line found in SDP message";
233   }
234 
235   if(err_code != 0) {
236     sdp_remote.clear();
237   }
238 
239   if(err_code == 0) {
240 
241     switch(state) {
242     case OA_None:
243     case OA_Completed:
244       setState(OA_OfferRecved);
245       cseq = m_cseq;
246       break;
247 
248     case OA_OfferSent:
249       setState(OA_Completed);
250       break;
251 
252     case OA_OfferRecved:
253       err_code = 400;// TODO: check correct error code
254       *err_txt = "pending SDP offer";
255       break;
256 
257     default:
258       assert(0);
259       break;
260     }
261   }
262 
263   DBG("oa_state: %s -> %s\n", getOAStateStr(old_oa_state), getOAStateStr(state));
264 
265   return err_code;
266 }
267 
onTxSdp(unsigned int m_cseq,const AmMimeBody & body)268 int AmOfferAnswer::onTxSdp(unsigned int m_cseq, const AmMimeBody& body)
269 {
270   // assume that the payload is ok if it is not empty.
271   // (do not parse again self-generated SDP)
272   if(body.empty()){
273     return -1;
274   }
275 
276   switch(state) {
277 
278   case OA_None:
279   case OA_Completed:
280     setState(OA_OfferSent);
281     cseq = m_cseq;
282     break;
283 
284   case OA_OfferRecved:
285     setState(OA_Completed);
286     break;
287 
288   case OA_OfferSent:
289     // There is already a pending offer!!!
290     DBG("There is already a pending offer, onTxSdp fails\n");
291     return -1;
292 
293   default:
294     break;
295   }
296 
297   return 0;
298 }
299 
onRequestOut(AmSipRequest & req)300 int AmOfferAnswer::onRequestOut(AmSipRequest& req)
301 {
302   AmMimeBody* sdp_body = req.body.hasContentType(SIP_APPLICATION_SDP);
303   bool generate_sdp = sdp_body && !sdp_body->getLen();
304   bool has_sdp = sdp_body && sdp_body->getLen();
305 
306   if (!sdp_body &&
307       ((req.method == SIP_METH_PRACK) ||
308        (req.method == SIP_METH_ACK))
309       && (state == OA_OfferRecved)) {
310     generate_sdp = true;
311     sdp_body = req.body.addPart(SIP_APPLICATION_SDP);
312   }
313 
314   saveState();
315 
316   if (generate_sdp) {
317     string sdp_buf;
318     if (!getSdpBody(sdp_buf)){
319       sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(),
320 			   sdp_buf.length());
321       has_sdp = true;
322     }
323     else {
324       return -1;
325     }
326   } else if (sdp_body && has_sdp) {
327     // update local SDP copy
328     if (sdp_local.parse((const char*)sdp_body->getPayload())) {
329       ERROR("parser failed on Tx SDP: '%s'\n", (const char*)sdp_body->getPayload());
330     }
331   }
332 
333   if(has_sdp && (onTxSdp(req.cseq,req.body) != 0)){
334     DBG("onTxSdp() failed\n");
335     return -1;
336   }
337 
338   return 0;
339 }
340 
onReplyOut(AmSipReply & reply)341 int AmOfferAnswer::onReplyOut(AmSipReply& reply)
342 {
343   AmMimeBody* sdp_body = reply.body.hasContentType(SIP_APPLICATION_SDP);
344   bool generate_sdp = sdp_body && !sdp_body->getLen();
345   bool has_sdp = sdp_body && sdp_body->getLen();
346 
347   if (!has_sdp && !generate_sdp) {
348     // let's see whether we should force SDP or not.
349 
350     if (reply.cseq_method == SIP_METH_INVITE){
351 
352       if ((reply.code == 183)
353 	  || ((reply.code >= 200) && (reply.code < 300))) {
354 
355 	// either offer received or no offer at all:
356 	//  -> force SDP
357 	generate_sdp = (state == OA_OfferRecved)
358 	  || (state == OA_None)
359 	  || (state == OA_Completed);
360       }
361     }
362     else if (reply.cseq_method == SIP_METH_UPDATE) {
363 
364       if ((reply.code >= 200) &&
365 	  (reply.code < 300)) {
366 
367 	// offer received:
368 	//  -> force SDP
369 	generate_sdp = (state == OA_OfferRecved);
370       }
371     }
372   }
373 
374   if (reply.cseq_method == SIP_METH_INVITE && reply.code < 300) {
375     // ignore SDP repeated in 1xx and 2xx replies (183, 180, ... 2xx)
376     if (has_sdp &&
377         (state == OA_Completed || state == OA_OfferSent) &&
378         reply.cseq == cseq)
379     {
380       has_sdp = false;
381     }
382   }
383 
384   saveState();
385 
386   if (generate_sdp) {
387 
388     string sdp_buf;
389     if(getSdpBody(sdp_buf)) {
390       if ((reply.code == 183 && reply.cseq_method == SIP_METH_INVITE) ||
391           (reply.code == 200 && reply.cseq_method == SIP_METH_INVITE && state == OA_Completed)) {
392         // just ignore if no SDP is generated (required for B2B)
393       }
394       else return -1;
395     }
396     else {
397       if(!sdp_body){
398         if( (sdp_body =
399              reply.body.addPart(SIP_APPLICATION_SDP))
400             == NULL ) {
401           DBG("AmMimeBody::addPart() failed\n");
402           return -1;
403         }
404       }
405 
406       sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(),
407                            sdp_buf.length());
408       has_sdp = true;
409     }
410   } else if (sdp_body && has_sdp) {
411     // update local SDP copy
412     if (sdp_local.parse((const char*)sdp_body->getPayload())) {
413       ERROR("parser failed on Tx SDP: '%s'\n", (const char*)sdp_body->getPayload());
414     }
415   }
416 
417   if (has_sdp && (onTxSdp(reply.cseq,reply.body) != 0)) {
418 
419     DBG("onTxSdp() failed\n");
420     return -1;
421   }
422 
423   if( (reply.code >= 300) &&
424       (reply.cseq == cseq) ) {
425     // final error reply -> cleanup OA state
426     DBG("after %u reply to %s: resetting OA state\n",
427 	reply.code, reply.cseq_method.c_str());
428     clearTransitionalState();
429   }
430 
431   return 0;
432 }
433 
onRequestSent(const AmSipRequest & req)434 int AmOfferAnswer::onRequestSent(const AmSipRequest& req)
435 {
436   int ret = checkStateChange();
437 
438   if((req.method == SIP_METH_ACK) &&
439      (req.cseq == cseq)) {
440 
441     // transaction has been removed:
442     //  -> cleanup OA state
443     DBG("200 ACK sent: resetting OA state\n");
444     clearTransitionalState();
445   }
446 
447   return ret;
448 }
449 
onReplySent(const AmSipReply & reply)450 int AmOfferAnswer::onReplySent(const AmSipReply& reply)
451 {
452   int ret = checkStateChange();
453 
454   // final answer to non-invite req that triggered O/A transaction
455   if( (reply.code >= 200) &&
456       (reply.cseq_method != SIP_METH_CANCEL) &&
457       (reply.cseq == cseq) &&
458       (reply.cseq_method != SIP_METH_INVITE) ) {
459 
460     // transaction has been removed:
461     //  -> cleanup OA state
462     DBG("transaction finished by final reply %u: resetting OA state\n", reply.cseq);
463     clearTransitionalState();
464   }
465 
466   return ret;
467 }
468 
getSdpBody(string & sdp_body)469 int AmOfferAnswer::getSdpBody(string& sdp_body)
470 {
471     switch(state){
472     case OA_None:
473     case OA_Completed:
474       if(dlg->getSdpOffer(sdp_local)){
475 	sdp_local.print(sdp_body);
476       }
477       else {
478 	DBG("No SDP Offer.\n");
479 	return -1;
480       }
481       break;
482     case OA_OfferRecved:
483       if(dlg->getSdpAnswer(sdp_remote,sdp_local)){
484 	sdp_local.print(sdp_body);
485       }
486       else {
487 	DBG("No SDP Answer.\n");
488 	return -1;
489       }
490       break;
491 
492     case OA_OfferSent:
493       DBG("Still waiting for a reply\n");
494       return -1;
495 
496     default:
497       break;
498     }
499 
500     return 0;
501 }
502 
onNoAck(unsigned int ack_cseq)503 void AmOfferAnswer::onNoAck(unsigned int ack_cseq)
504 {
505   if(ack_cseq == cseq){
506     DBG("ACK timeout: resetting OA state\n");
507     clearTransitionalState();
508   }
509 }
510