all: updated FSF address in GPL text
[sip-router] / modules / qos / qos_ctx_helpers.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2007 SOMA Networks, Inc.
5  * Written by Ovidiu Sas (osas)
6  *
7  * This file is part of Kamailio, a free SIP server.
8  *
9  * Kamailio is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version
13  *
14  * Kamailio is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License 
20  * along with this program; if not, write to the Free Software 
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  * History:
24  * -------
25  * 2007-07-16 initial version (osas)
26  */
27
28 #include "../../mem/mem.h"
29 #include "../../mem/shm_mem.h"
30 #include "../../parser/sdp/sdp_cloner.h"
31 #include "../dialog/dlg_hash.h"
32 #include "qos_ctx_helpers.h"
33
34 #define ERROR_MATCH           -1
35 #define NO_INVITE_REQ_MATCH    0
36 #define NO_INVITE_RESP_MATCH   1
37 #define PENDING_MATCH          2
38 #define NEGOTIATED_MATCH       3
39 #define NO_ACK_REQ_MATCH       4
40 #define NO_UPDATE_REQ_MATCH    7
41 #define NO_UPDATE_RESP_MATCH   8
42
43
44 #define N_UNKNOWN       0
45 /* INVITE/200_ok */
46 #define N_INVITE_200OK  1
47 /* 200_ok/ACK */
48 #define N_200OK_ACK     2
49 /* early media (http://www.ietf.org/rfc/rfc3959.txt) */
50 /* 183_early_media/PRACK */
51 #define N_183_PRACK     3
52
53 qos_ctx_t *build_new_qos_ctx(void) {
54         qos_ctx_t *ctx = NULL;
55
56         ctx = (qos_ctx_t *)shm_malloc(sizeof(qos_ctx_t));
57         if (ctx!=NULL) {
58                 memset(ctx, 0, sizeof(qos_ctx_t));
59         } else {
60                 LM_ERR("No enough shared memory\n");
61                 return NULL;
62         }
63         if (!lock_init(&ctx->lock)) {
64                 shm_free(ctx);
65                 return NULL;
66         }
67         return ctx;
68 }
69
70 void destroy_qos(qos_sdp_t *qos_sdp)
71 {
72         free_cloned_sdp_session(qos_sdp->sdp_session[0]);
73         free_cloned_sdp_session(qos_sdp->sdp_session[1]);
74         shm_free(qos_sdp);
75
76         return;
77 }
78
79
80 void print_qos_sdp(qos_sdp_t *qos_sdp)
81 {
82         if (qos_sdp == NULL) {
83                 return;
84         }
85         LM_DBG("[%p] prev->%p next->%p method_dir=%d method_id=%d method='%.*s' cseq='%.*s' negotiation=%d sdp[0:QOS_CALLER]=%p sdp[1:QOS_CALLEE]=%p\n",
86                 qos_sdp, qos_sdp->prev, qos_sdp->next, qos_sdp->method_dir, qos_sdp->method_id,
87                 qos_sdp->method.len ,qos_sdp->method.s, qos_sdp->cseq.len, qos_sdp->cseq.s,
88                 qos_sdp->negotiation, qos_sdp->sdp_session[0], qos_sdp->sdp_session[1]);
89         /* print_sdp_session(qos_sdp->sdp_session[0]); */
90         /* print_sdp_session(qos_sdp->sdp_session[1]); */
91 }
92
93
94
95 /*
96  * Find a matching sdp inside the local qos_ctx
97  * for the given session received via message _m with the given direction, cseq and method
98  * and return the type of the match and a pointer to the matched qos_sdp so we can properly insert the given session into the qos_ctx->qos_sdp.
99  */
100 int find_qos_sdp(qos_ctx_t *qos_ctx, unsigned int dir, unsigned int other_role, str *cseq_number, int cseq_method_id, sdp_session_cell_t *session, struct sip_msg *_m, qos_sdp_t **_qos_sdp)
101 {
102         qos_sdp_t *qos_sdp;
103         str *received_cnt_disp, *local_cnt_disp;
104
105         LM_DBG("received session: %p and other_role: %s\n", session, (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
106
107         switch (_m->first_line.type) {
108                 case SIP_REQUEST:
109                         switch (cseq_method_id) {
110                                 case METHOD_INVITE:
111                                         return NO_INVITE_REQ_MATCH;
112                                         break;
113                                 case METHOD_ACK:
114                                         /* searching into the pending_sdp list */
115                                         qos_sdp = qos_ctx->pending_sdp;
116                                         LM_DBG("searching the negotiated_sdp: %p\n", qos_sdp);
117                                         while (qos_sdp) {
118                                                 if (METHOD_INVITE == qos_sdp->method_id && dir != qos_sdp->method_dir && qos_sdp->negotiation == N_200OK_ACK &&
119                                                         cseq_number->len == qos_sdp->cseq.len && 0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) {
120                                                         LM_DBG("method_id, dir and cseq match with previous session %p->%p\n",
121                                                                 qos_sdp, qos_sdp->sdp_session[other_role]);
122                                                         /* print_sdp_session(qos_sdp->sdp_session[other_role]); */
123                                                         if (qos_sdp->sdp_session[other_role] != NULL) {
124                                                                 local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp);
125                                                                 received_cnt_disp = &(session->cnt_disp);
126                                                                 if (local_cnt_disp->len == received_cnt_disp->len) {
127                                                                         if (local_cnt_disp->len == 0) {
128                                                                                 LM_DBG("no cnt disp header ... => %p\n", qos_sdp);
129                                                                                 *_qos_sdp = qos_sdp;
130                                                                                 return PENDING_MATCH;
131                                                                         } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) {
132                                                                                 LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp);
133                                                                                 *_qos_sdp = qos_sdp;
134                                                                                 return PENDING_MATCH;
135                                                                         }
136                                                                 } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 &&
137                                                                         0==strncmp(local_cnt_disp->s, "session", 7)) {
138                                                                         /* We may have an offer with cnt_disp='session' and an answer with cnt_disp='' */
139                                                                         *_qos_sdp = qos_sdp;
140                                                                         return PENDING_MATCH;
141                                                                 }
142                                                         } else {
143                                                                 LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
144                                                         }
145                                                 }
146                                                 qos_sdp = qos_sdp->next;
147                                         }
148                                         return NO_ACK_REQ_MATCH;
149                                         break;
150                                 case METHOD_UPDATE:
151                                         return NO_UPDATE_REQ_MATCH;
152                                         break;
153                                 case METHOD_PRACK:
154                                         LM_ERR("PRACK not implemented yet\n");
155                                         return ERROR_MATCH;
156                                         break;
157                                 default:
158                                         LM_ERR("Unexpected method id %d\n", cseq_method_id);
159                                         return ERROR_MATCH;
160                         }
161                         break;
162                 case SIP_REPLY:
163                         switch (cseq_method_id) {
164                                 case METHOD_INVITE:
165                                         /* searching into the pending_sdp list */
166                                         qos_sdp = qos_ctx->pending_sdp;
167                                         while (qos_sdp) {
168                                                 //print_qos_sdp(qos_sdp);
169                                                 if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir &&
170                                                         qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len &&
171                                                         0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) {
172                                                         LM_DBG("method_id, dir and cseq match with previous session %p->%p\n",
173                                                                 qos_sdp, qos_sdp->sdp_session[other_role]);
174                                                         /* print_sdp_session(qos_sdp->sdp_session[other_role]); */
175                                                         if (qos_sdp->sdp_session[other_role] != NULL) {
176                                                                 local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp);
177                                                                 received_cnt_disp = &(session->cnt_disp);
178                                                                 if (local_cnt_disp->len == received_cnt_disp->len) {
179                                                                         if (local_cnt_disp->len == 0) {
180                                                                                 LM_DBG("no cnt disp header ... => %p\n", qos_sdp);
181                                                                                 *_qos_sdp = qos_sdp;
182                                                                                 return PENDING_MATCH;
183                                                                         } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) {
184                                                                                 LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp);
185                                                                                 *_qos_sdp = qos_sdp;
186                                                                                 return PENDING_MATCH;
187                                                                         }
188                                                                 } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 &&
189                                                                         0==strncmp(local_cnt_disp->s, "session", 7)) {
190                                                                         /* We have an offer with cnt_disp='session' and an answer with cnt_disp='' */
191                                                                         *_qos_sdp = qos_sdp;
192                                                                         return PENDING_MATCH;
193                                                                 }
194                                                         } else {
195                                                                 LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
196                                                         }
197                                                 }
198                                                 qos_sdp = qos_sdp->next;
199                                         }
200                                         /* searching into the negotiated_sdp list */
201                                         qos_sdp = qos_ctx->negotiated_sdp;
202                                         LM_DBG("searching the negotiated_sdp: %p\n", qos_sdp);
203                                         while (qos_sdp) {
204                                                 //print_qos_sdp(qos_sdp);
205                                                 if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir &&
206                                                         qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len &&
207                                                         0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) {
208                                                         LM_DBG("method_id, dir and cseq match with previous session %p\n", qos_sdp->sdp_session[other_role]);
209                                                         if (qos_sdp->sdp_session[other_role] != NULL) {
210                                                                 local_cnt_disp = &(qos_sdp->sdp_session[other_role]->cnt_disp);
211                                                                 received_cnt_disp = &(session->cnt_disp);
212                                                                 if (local_cnt_disp->len == received_cnt_disp->len) {
213                                                                         if (local_cnt_disp->len == 0) {
214                                                                                 LM_DBG("no cnt disp header ... => %p\n", qos_sdp);
215                                                                                 *_qos_sdp = qos_sdp;
216                                                                                 return NEGOTIATED_MATCH;
217                                                                         } else if (0==strncmp(local_cnt_disp->s, received_cnt_disp->s, local_cnt_disp->len)) {
218                                                                                 LM_DBG("'%.*s' => %p\n", local_cnt_disp->len, local_cnt_disp->s, qos_sdp);
219                                                                                 *_qos_sdp = qos_sdp;
220                                                                                 return NEGOTIATED_MATCH;
221                                                                         }
222                                                                 } else if (received_cnt_disp->len == 0 && local_cnt_disp->len == 7 &&
223                                                                         0==strncasecmp(local_cnt_disp->s, "session", 7)) {
224                                                                         /* We have an offer with cnt_disp='session' and an answer with cnt_disp='' */
225                                                                         *_qos_sdp = qos_sdp;
226                                                                         return NEGOTIATED_MATCH;
227                                                                 }
228                                                         } else {
229                                                                 LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
230                                                         }
231                                                 }
232                                                 qos_sdp = qos_sdp->next;
233                                         }
234                                         return NO_INVITE_RESP_MATCH;
235                                         break;
236                                 case METHOD_UPDATE:
237                                         LM_ERR("FIXME\n");
238                                         return NO_UPDATE_RESP_MATCH;
239                                         break;
240                                 default:
241                                         LM_ERR("Unexpected reply for method id %d\n", cseq_method_id);
242                                         return ERROR_MATCH;
243                         }
244                         break;
245                 default:
246                         LM_ERR("Unknown SIP message type: %d\n", _m->first_line.type);
247                         return ERROR_MATCH;
248         }
249         LM_ERR("FIXME: out of case\n");
250         return ERROR_MATCH;
251 }
252
253 void link_pending_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp)
254 {
255         if (qos_sdp->prev != NULL) LM_ERR("got qos_sdp->prev=%p\n", qos_sdp->prev);
256         if (qos_sdp->next != NULL) LM_ERR("got qos_sdp->next=%p\n", qos_sdp->next);
257
258         if (qos_ctx->pending_sdp) {
259                 LM_DBG("Adding pending qos_sdp: %p\n", qos_sdp);
260                 if (qos_ctx->pending_sdp->prev != NULL) LM_ERR("got qos_ctx->pending_sdp->prev=%p\n", qos_ctx->pending_sdp->prev);
261                 qos_sdp->next = qos_ctx->pending_sdp;
262                 qos_ctx->pending_sdp->prev = qos_sdp;
263                 qos_ctx->pending_sdp = qos_sdp;
264         } else {
265                 LM_DBG("Inserting pending qos_sdp: %p\n", qos_sdp);
266                 qos_ctx->pending_sdp = qos_sdp;
267         }
268 }
269
270 void unlink_pending_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp)
271 {
272         if (qos_sdp->next)
273                 qos_sdp->next->prev = qos_sdp->prev;
274
275         if (qos_sdp->prev)
276                 qos_sdp->prev->next = qos_sdp->next;
277         else
278                 qos_ctx->pending_sdp = qos_sdp->next;
279
280         qos_sdp->next = qos_sdp->prev = NULL;
281 }
282 void unlink_negotiated_qos_sdp(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp)
283 {
284         if (qos_sdp->next)
285                 qos_sdp->next->prev = qos_sdp->prev;
286
287         if (qos_sdp->prev)
288                 qos_sdp->prev->next = qos_sdp->next;
289         else
290                 qos_ctx->negotiated_sdp = qos_sdp->next;
291
292         qos_sdp->next = qos_sdp->prev = NULL;
293 }
294
295
296 void link_negotiated_qos_sdp_and_run_cb(qos_ctx_t *qos_ctx, qos_sdp_t *qos_sdp, unsigned int role, struct sip_msg *_m)
297 {
298         qos_sdp_t *next_qos_sdp;
299         qos_sdp_t *temp_qos_sdp = qos_ctx->negotiated_sdp;
300
301         if (qos_sdp->prev != NULL) LM_ERR("got qos_sdp->prev=%p\n", qos_sdp->prev);
302         if (qos_sdp->next != NULL) LM_ERR("got qos_sdp->next=%p\n", qos_sdp->next);
303
304         if (temp_qos_sdp) {
305                 while (temp_qos_sdp) {
306                         next_qos_sdp = temp_qos_sdp->next;
307                         if (qos_sdp->negotiation == temp_qos_sdp->negotiation) {
308                                 LM_DBG("run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx=%p, temp_qos_sdp=%p, role=%d, _m=%p)\n",
309                                         qos_ctx, temp_qos_sdp, role, _m);
310                                 run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx, temp_qos_sdp, role, _m);
311
312                                 unlink_negotiated_qos_sdp(qos_ctx, temp_qos_sdp);
313                                 destroy_qos(temp_qos_sdp);
314                                 break;
315                         }
316                         temp_qos_sdp = next_qos_sdp;
317                 }
318                 if (qos_ctx->negotiated_sdp) {
319                         LM_DBG("Adding negotiated qos_sdp: %p\n", qos_sdp);
320                         if (qos_ctx->negotiated_sdp->prev != NULL) LM_ERR("got qos_ctx->negotiated_sdp->prev=%p\n", qos_ctx->negotiated_sdp->prev);
321                         qos_sdp->next = qos_ctx->negotiated_sdp;
322                         qos_ctx->negotiated_sdp->prev = qos_sdp;
323                         qos_ctx->negotiated_sdp = qos_sdp;
324                 } else {
325                         LM_DBG("Inserting negotiated qos_sdp: %p\n", qos_sdp);
326                         qos_ctx->negotiated_sdp = qos_sdp;
327                 }
328         } else {
329                 LM_DBG("Inserting first negotiated qos_sdp: %p\n", qos_sdp);
330                 qos_ctx->negotiated_sdp = qos_sdp;
331         }
332
333         LM_DBG("run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n",
334                 qos_ctx, qos_sdp, role, _m);
335         run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx, qos_sdp, role, _m);
336 }
337
338 int add_pending_sdp_session(qos_ctx_t *qos_ctx, unsigned int dir, str *cseq_number, str *cseq_method, int cseq_method_id,
339                                 unsigned int role, unsigned int negotiation, sdp_session_cell_t *session, struct sip_msg *_m)
340 {
341         unsigned int len;
342         sdp_session_cell_t *cloned_session;
343         qos_sdp_t *qos_sdp;
344         char *p;
345
346         len = sizeof(qos_sdp_t) + cseq_method->len + cseq_number->len;
347         qos_sdp = (qos_sdp_t *)shm_malloc(len);
348         LM_DBG("alloc qos_sdp: %p\n", qos_sdp);
349         if (qos_sdp==NULL) {
350                 LM_ERR("oom %d\n", len);
351                 return -1;
352         } else {
353                 memset(qos_sdp, 0, len);
354                 LM_DBG("Allocated memory for qos_sdp: %p\n", qos_sdp);
355
356                 cloned_session = clone_sdp_session_cell(session);
357                 if (cloned_session==NULL) {
358                         shm_free(qos_sdp);
359                         LM_DBG("free qos_sdp: %p\n", qos_sdp);
360                         return -1;
361                 }
362                 qos_sdp->sdp_session[role] = cloned_session;
363
364                 LM_DBG("qos_sdp->sdp_session[%d]=%p\n", role, qos_sdp->sdp_session[role]);
365
366                 if (_m->first_line.type == SIP_REQUEST) {
367                         qos_sdp->method_dir = dir;
368                 } else {
369                         /* This is a SIP_REPLY and we need to set
370                          * the direction for the SIP_REQUEST */
371                         if (dir==DLG_DIR_UPSTREAM)
372                                 qos_sdp->method_dir = DLG_DIR_DOWNSTREAM;
373                         else
374                                 qos_sdp->method_dir = DLG_DIR_UPSTREAM;
375                 }
376                 qos_sdp->method_id = cseq_method_id;
377                 qos_sdp->negotiation = negotiation;
378                 p = (char*)(qos_sdp+1);
379
380                 qos_sdp->method.s = p;
381                 qos_sdp->method.len = cseq_method->len;
382                 memcpy( p, cseq_method->s, cseq_method->len);
383                 p += cseq_method->len;
384
385                 qos_sdp->cseq.s = p;
386                 qos_sdp->cseq.len = cseq_number->len;
387                 memcpy( p,cseq_number->s, cseq_number->len);
388                 /* p += cseq_number->len; */
389
390                 link_pending_qos_sdp(qos_ctx, qos_sdp);
391
392                 LM_DBG("run_qos_callbacks(QOSCB_ADD_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n",
393                         qos_ctx, qos_sdp, role, _m);
394                 run_qos_callbacks(QOSCB_ADD_SDP, qos_ctx, qos_sdp, role, _m);
395         }
396         return 0;
397 }
398
399
400
401
402 /*
403  * Add the sdp carried by the given SIP message into the qos context.
404  */
405 void add_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role)
406 {
407         qos_sdp_t *qos_sdp;
408         sdp_session_cell_t *recv_session;
409         str *cseq_number, *cseq_method;
410         int cseq_method_id, sdp_match;
411
412         if ( (!_m->cseq && parse_headers(_m,HDR_CSEQ_F,0)<0) || !_m->cseq || !_m->cseq->parsed) {
413                 LM_ERR("bad sip message or missing CSeq hdr\n");
414                 return;
415         }
416
417         cseq_number = &((get_cseq(_m))->number);
418         cseq_method = &((get_cseq(_m))->method);
419         cseq_method_id = (get_cseq(_m))->method_id;
420
421         LM_DBG("cseq=`%.*s' `%.*s' and dir=%d\n",
422                 cseq_number->len, cseq_number->s,
423                 cseq_method->len, cseq_method->s, dir);
424
425         /* Let's iterate through all the received sessions */
426         recv_session = ((sdp_info_t*)_m->body)->sessions;
427         while(recv_session) {
428                 qos_sdp = NULL;
429                 sdp_match = find_qos_sdp(qos_ctx, dir, other_role, cseq_number, cseq_method_id, recv_session, _m, &qos_sdp);
430
431                 switch (sdp_match) {
432                         case NO_INVITE_REQ_MATCH:
433                                 if (0!=add_pending_sdp_session( qos_ctx, dir, cseq_number, cseq_method, cseq_method_id, role, N_INVITE_200OK, recv_session, _m)) {
434                                         LM_ERR("Unable to add new sdp session\n");
435                                         goto error;
436                                 }
437
438                                 break;
439                         case NO_INVITE_RESP_MATCH:
440                                 if (0!=add_pending_sdp_session( qos_ctx, dir, cseq_number, cseq_method, cseq_method_id, role, N_200OK_ACK, recv_session, _m)) {
441                                         LM_ERR("Unable to add new sdp session\n");
442                                         goto error;
443                                 }
444
445                                 break;
446                         case ERROR_MATCH:
447                         case NO_ACK_REQ_MATCH:
448                         case NO_UPDATE_REQ_MATCH:
449                         case NO_UPDATE_RESP_MATCH:
450                                 LM_ERR("error match: %d\n", sdp_match);
451                                 break;
452                         case PENDING_MATCH:
453                                 LM_DBG("we have a pending match: %p\n", qos_sdp);
454                                 /* Let's save the received session */
455                                 qos_sdp->sdp_session[role] = clone_sdp_session_cell(recv_session);
456                                 if (qos_sdp->sdp_session[role] == NULL) {
457                                         LM_ERR("PENDING_MATCH:oom: Unable to add new sdp session\n");
458                                         return;
459                                 }
460
461                                 /* Negotiation completed, need to move the established SDP into the negotiated_sdp */
462
463                                 /* removing qos_sdp from qos_ctx->pending_sdp list */
464                                 unlink_pending_qos_sdp(qos_ctx, qos_sdp);
465                                 /* inserting qos_sdp into the qos_ctx->negotiated_sdp list */
466                                 link_negotiated_qos_sdp_and_run_cb(qos_ctx, qos_sdp, role, _m);
467
468                                 break;
469                         case NEGOTIATED_MATCH:
470                                 LM_DBG("we have a negotiated match: %p\n", qos_sdp);
471                                 /* some sanity checks */
472                                 if (qos_sdp->sdp_session[role]) {
473                                         free_cloned_sdp_session(qos_sdp->sdp_session[role]);
474                                 } else {
475                                         LM_ERR("missing sdp_session for %s\n", (role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
476                                 }
477                                 /* Let's save the received session */
478                                 qos_sdp->sdp_session[role] = clone_sdp_session_cell(recv_session);
479                                 if (qos_sdp->sdp_session[role]  == NULL) {
480                                         LM_ERR("NEGOTIATED_MATCH:oom: Unable to add new sdp session\n");
481                                         return;
482                                 }
483
484                                 LM_DBG("run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n",
485                                         qos_ctx, qos_sdp, role, _m);
486                                 run_qos_callbacks(QOSCB_UPDATE_SDP, qos_ctx, qos_sdp, role, _m);
487
488                                 break;
489                         default:
490                                 LM_CRIT("Undefined return code from find_qos_sdp(): %d\n", sdp_match);
491                 }
492                 recv_session = recv_session->next;
493         }
494
495         return;
496 error:
497         shm_free(qos_sdp);
498         LM_DBG("free qos_sdp: %p\n", qos_sdp);
499         return;
500 }
501
502 /*
503  * Remove the sdp previously added.
504  */
505 void remove_sdp(qos_ctx_t *qos_ctx, unsigned int dir, struct sip_msg *_m, unsigned int role, unsigned int other_role)
506 {
507         str *cseq_number;
508         int cseq_method_id;
509         qos_sdp_t *qos_sdp;
510
511         if ( (!_m->cseq && parse_headers(_m,HDR_CSEQ_F,0)<0) || !_m->cseq || !_m->cseq->parsed) {
512                 LM_ERR("bad sip message or missing CSeq hdr\n");
513                 return;
514         }
515
516         cseq_number = &((get_cseq(_m))->number);
517         cseq_method_id = (get_cseq(_m))->method_id;
518
519         if (_m->first_line.type == SIP_REPLY) {
520                 switch (cseq_method_id) {
521                         case METHOD_INVITE:
522                         case METHOD_UPDATE:
523                                         /* searching into the pending_sdp list only */
524                                         qos_sdp = qos_ctx->pending_sdp;
525                                         while (qos_sdp) {
526                                                 qos_sdp = qos_sdp->next;
527                                                 if (cseq_method_id == qos_sdp->method_id && dir != qos_sdp->method_dir &&
528                                                         qos_sdp->negotiation == N_INVITE_200OK && cseq_number->len == qos_sdp->cseq.len &&
529                                                         0 == strncmp(cseq_number->s, qos_sdp->cseq.s, cseq_number->len)) {
530                                                         LM_DBG("method_id, dir and cseq match with previous session %p->%p\n",
531                                                                 qos_sdp, qos_sdp->sdp_session[other_role]);
532                                                         /* print_sdp_session(qos_sdp->sdp_session[other_role]); */
533                                                         if (qos_sdp->sdp_session[other_role] != NULL) {
534                                                                 LM_DBG("run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx=%p, qos_sdp=%p, role=%d, _m=%p)\n",
535                                                                         qos_ctx, qos_sdp, role, _m);
536                                                                 run_qos_callbacks(QOSCB_REMOVE_SDP, qos_ctx, qos_sdp, role, _m);
537                                                                 unlink_negotiated_qos_sdp(qos_ctx, qos_sdp);
538                                                                 /* Here we free up the pending qos_sdp */
539                                                                 destroy_qos(qos_sdp);
540                                                                 continue;
541                                                         } else {
542                                                                 LM_ERR("skipping search for null sdp for %s\n", (other_role==QOS_CALLER)?"QOS_CALLER":"QOS_CALLEE");
543                                                         }
544                                                 }
545                                         } /* end while (qos_sdp) */
546                                 break;
547                         default:
548                                 LM_ERR("Unexpected method id %d\n", cseq_method_id);
549                 }
550         } else {
551                 LM_ERR("we remove sdp only for a SIP_REPLY, not for a %d\n",
552                         _m->first_line.type);
553         }
554
555         return;
556 }
557
558 void destroy_qos_ctx(qos_ctx_t *qos_ctx)
559 {
560         qos_sdp_t * qos_sdp, * next_qos_sdp;
561
562         lock_get(&qos_ctx->lock);
563
564         qos_sdp = qos_ctx->pending_sdp;
565         while (qos_sdp) {
566                 next_qos_sdp = qos_sdp->next;
567                 destroy_qos(qos_sdp);
568                 qos_sdp = next_qos_sdp;
569         }
570         qos_sdp = qos_ctx->negotiated_sdp;
571         while (qos_sdp) {
572                 next_qos_sdp = qos_sdp->next;
573                 destroy_qos(qos_sdp);
574                 qos_sdp = next_qos_sdp;
575         }
576
577         lock_release(&qos_ctx->lock);
578
579         lock_destroy(&qos_ctx->lock);
580
581         LM_DBG("free qos_ctx: %p\n", qos_ctx);
582         shm_free(qos_ctx);
583
584         return;
585 }
586