Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia / sdp_cmp.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program 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.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20 #include <pjmedia/sdp.h>
21 #include <pjmedia/errno.h>
22 #include <pj/assert.h>
23 #include <pj/string.h>
24
25
26 /* Compare connection line. */
27 static pj_status_t compare_conn(const pjmedia_sdp_conn *c1,
28                                 const pjmedia_sdp_conn *c2)
29 {
30     /* Compare network type. */
31     if (pj_strcmp(&c1->net_type, &c2->net_type) != 0)
32         return PJMEDIA_SDP_ECONNNOTEQUAL;
33
34     /* Compare address type. */
35     if (pj_strcmp(&c1->addr_type, &c2->addr_type) != 0)
36         return PJMEDIA_SDP_ECONNNOTEQUAL;
37
38     /* Compare address. */
39     if (pj_strcmp(&c1->addr, &c2->addr) != 0)
40         return PJMEDIA_SDP_ECONNNOTEQUAL;
41
42     return PJ_SUCCESS;
43 }
44
45 /* Compare attributes array. */
46 static pj_status_t compare_attr_imp(unsigned count1,
47                                     pjmedia_sdp_attr *const attr1[],
48                                     unsigned count2,
49                                     pjmedia_sdp_attr *const attr2[])
50 {
51     pj_status_t status;
52     unsigned i;
53     const pj_str_t inactive = { "inactive", 8 };
54     const pj_str_t sendrecv = { "sendrecv", 8 };
55     const pj_str_t sendonly = { "sendonly", 8 };
56     const pj_str_t recvonly = { "recvonly", 8 };
57     const pj_str_t fmtp = { "fmtp", 4 };
58     const pj_str_t rtpmap = { "rtpmap", 6 };
59
60     /* For simplicity, we only compare the following attributes, and ignore
61      * the others:
62      *  - direction, eg. inactive, sendonly, recvonly, sendrecv
63      *  - fmtp for each payload.
64      *  - rtpmap for each payload.
65      */
66     for (i=0; i<count1; ++i) {
67         const pjmedia_sdp_attr *a1 = attr1[i];
68
69         if (pj_strcmp(&a1->name, &inactive) == 0 || 
70             pj_strcmp(&a1->name, &sendrecv) == 0 ||
71             pj_strcmp(&a1->name, &sendonly) == 0 ||
72             pj_strcmp(&a1->name, &recvonly) == 0)
73         {
74             /* For inactive, sendrecv, sendonly, and recvonly attributes,
75              * the same attribute must be present on the other SDP.
76              */
77             const pjmedia_sdp_attr *a2;
78             a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, NULL);
79             if (!a2)
80                 return PJMEDIA_SDP_EDIRNOTEQUAL;
81
82         } else if (pj_strcmp(&a1->name, &fmtp) == 0) {
83             /* For fmtp attribute, find the fmtp attribute in the other SDP
84              * for the same payload type, and compare the fmtp param/value.
85              */
86             pjmedia_sdp_fmtp fmtp1, fmtp2;
87             const pjmedia_sdp_attr *a2;
88
89             status = pjmedia_sdp_attr_get_fmtp(a1, &fmtp1);
90             if (status != PJ_SUCCESS)
91                 return PJMEDIA_SDP_EFMTPNOTEQUAL;
92
93             a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &fmtp1.fmt);
94             if (!a2)
95                 return PJMEDIA_SDP_EFMTPNOTEQUAL;
96
97             status = pjmedia_sdp_attr_get_fmtp(a2, &fmtp2);
98             if (status != PJ_SUCCESS)
99                 return PJMEDIA_SDP_EFMTPNOTEQUAL;
100
101             if (pj_strcmp(&fmtp1.fmt_param, &fmtp2.fmt_param) != 0)
102                 return PJMEDIA_SDP_EFMTPNOTEQUAL;
103
104         } else if (pj_strcmp(&a1->name, &rtpmap) == 0) {
105             /* For rtpmap attribute, find rtpmap attribute on the other SDP
106              * for the same payload type, and compare both rtpmap atribute
107              * values.
108              */
109             pjmedia_sdp_rtpmap r1, r2;
110             const pjmedia_sdp_attr *a2;
111
112             status = pjmedia_sdp_attr_get_rtpmap(a1, &r1);
113             if (status != PJ_SUCCESS)
114                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
115
116             a2 = pjmedia_sdp_attr_find(count2, attr2, &a1->name, &r1.pt);
117             if (!a2)
118                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
119
120             status = pjmedia_sdp_attr_get_rtpmap(a2, &r2);
121             if (status != PJ_SUCCESS)
122                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
123
124             if (pj_strcmp(&r1.pt, &r2.pt) != 0)
125                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
126             if (pj_strcmp(&r1.enc_name, &r2.enc_name) != 0)
127                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
128             if (r1.clock_rate != r2.clock_rate)
129                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
130             if (pj_strcmp(&r1.param, &r2.param) != 0)
131                 return PJMEDIA_SDP_ERTPMAPNOTEQUAL;
132         }
133     }
134
135     return PJ_SUCCESS;
136 }
137
138
139 /* Compare attributes array. */
140 static pj_status_t compare_attr(unsigned count1,
141                                 pjmedia_sdp_attr *const attr1[],
142                                 unsigned count2,
143                                 pjmedia_sdp_attr *const attr2[])
144 {
145     pj_status_t status;
146
147     status = compare_attr_imp(count1, attr1, count2, attr2);
148     if (status != PJ_SUCCESS)
149         return status;
150
151     status = compare_attr_imp(count2, attr2, count1, attr1);
152     if (status != PJ_SUCCESS)
153         return status;
154
155     return PJ_SUCCESS;
156 }
157
158 /* Compare media descriptor */
159 PJ_DEF(pj_status_t) pjmedia_sdp_media_cmp( const pjmedia_sdp_media *sd1,
160                                            const pjmedia_sdp_media *sd2,
161                                            unsigned option)
162 {
163     unsigned i;
164     pj_status_t status;
165
166     PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL);
167
168     PJ_UNUSED_ARG(option);
169
170     /* Compare media type. */
171     if (pj_strcmp(&sd1->desc.media, &sd2->desc.media) != 0)
172         return PJMEDIA_SDP_EMEDIANOTEQUAL;
173
174     /* Compare port number. */
175     if (sd1->desc.port != sd2->desc.port)
176         return PJMEDIA_SDP_EPORTNOTEQUAL;
177
178     /* Compare port count. */
179     if (sd1->desc.port_count != sd2->desc.port_count)
180         return PJMEDIA_SDP_EPORTNOTEQUAL;
181
182     /* Compare transports. */
183     if (pj_strcmp(&sd1->desc.transport, &sd2->desc.transport) != 0)
184         return PJMEDIA_SDP_ETPORTNOTEQUAL;
185
186     /* For zeroed port media, stop comparing here */
187     if (sd1->desc.port == 0)
188         return PJ_SUCCESS;
189
190     /* Compare number of formats. */
191     if (sd1->desc.fmt_count != sd2->desc.fmt_count)
192         return PJMEDIA_SDP_EFORMATNOTEQUAL;
193
194     /* Compare formats, in order. */
195     for (i=0; i<sd1->desc.fmt_count; ++i) {
196         if (pj_strcmp(&sd1->desc.fmt[i], &sd2->desc.fmt[i]) != 0)
197             return PJMEDIA_SDP_EFORMATNOTEQUAL;
198     }
199
200     /* Compare connection line, if they exist. */
201     if (sd1->conn) {
202         if (!sd2->conn)
203             return PJMEDIA_SDP_EMEDIANOTEQUAL;
204         status = compare_conn(sd1->conn, sd2->conn);
205     } else {
206         if (sd2->conn)
207             return PJMEDIA_SDP_EMEDIANOTEQUAL;
208     }
209
210     /* Compare attributes. */
211     status = compare_attr(sd1->attr_count, sd1->attr, 
212                           sd2->attr_count, sd2->attr);
213     if (status != PJ_SUCCESS)
214         return status;
215
216     /* Looks equal */
217     return PJ_SUCCESS;
218 }
219
220 /*
221  * Compare two SDP session for equality.
222  */
223 PJ_DEF(pj_status_t) pjmedia_sdp_session_cmp( const pjmedia_sdp_session *sd1,
224                                              const pjmedia_sdp_session *sd2,
225                                              unsigned option)
226 {
227     unsigned i;
228     pj_status_t status;
229
230     PJ_ASSERT_RETURN(sd1 && sd2 && option==0, PJ_EINVAL);
231
232     PJ_UNUSED_ARG(option);
233
234     /* Compare the origin line. */
235     if (pj_strcmp(&sd1->origin.user, &sd2->origin.user) != 0)
236         return PJMEDIA_SDP_EORIGINNOTEQUAL;
237
238     if (sd1->origin.id != sd2->origin.id)
239         return PJMEDIA_SDP_EORIGINNOTEQUAL;
240
241     if (sd1->origin.version != sd2->origin.version)
242         return PJMEDIA_SDP_EORIGINNOTEQUAL;
243
244     if (pj_strcmp(&sd1->origin.net_type, &sd2->origin.net_type) != 0)
245         return PJMEDIA_SDP_EORIGINNOTEQUAL;
246
247     if (pj_strcmp(&sd1->origin.addr_type, &sd2->origin.addr_type) != 0)
248         return PJMEDIA_SDP_EORIGINNOTEQUAL;
249
250     if (pj_strcmp(&sd1->origin.addr, &sd2->origin.addr) != 0)
251         return PJMEDIA_SDP_EORIGINNOTEQUAL;
252
253     
254     /* Compare the subject line. */
255     if (pj_strcmp(&sd1->name, &sd2->name) != 0)
256         return PJMEDIA_SDP_ENAMENOTEQUAL;
257
258     /* Compare connection line, when they exist */
259     if (sd1->conn) {
260         if (!sd2->conn)
261             return PJMEDIA_SDP_ECONNNOTEQUAL;
262         status = compare_conn(sd1->conn, sd2->conn);
263         if (status != PJ_SUCCESS)
264             return status;
265     } else {
266         if (sd2->conn)
267             return PJMEDIA_SDP_ECONNNOTEQUAL;
268     }
269
270     /* Compare time line. */
271     if (sd1->time.start != sd2->time.start)
272         return PJMEDIA_SDP_ETIMENOTEQUAL;
273
274     if (sd1->time.stop != sd2->time.stop)
275         return PJMEDIA_SDP_ETIMENOTEQUAL;
276
277     /* Compare attributes. */
278     status = compare_attr(sd1->attr_count, sd1->attr, 
279                           sd2->attr_count, sd2->attr);
280     if (status != PJ_SUCCESS)
281         return status;
282
283     /* Compare media lines. */
284     if (sd1->media_count != sd2->media_count)
285         return PJMEDIA_SDP_EMEDIANOTEQUAL;
286
287     for (i=0; i<sd1->media_count; ++i) {
288         status = pjmedia_sdp_media_cmp(sd1->media[i], sd2->media[i], 0);
289         if (status != PJ_SUCCESS)
290             return status;
291     }
292
293     /* Looks equal. */
294     return PJ_SUCCESS;
295 }
296
297
298 PJ_DEF(pj_status_t) pjmedia_sdp_conn_cmp(const pjmedia_sdp_conn *conn1, 
299                                          const pjmedia_sdp_conn *conn2,
300                                          unsigned option)
301 {
302     PJ_UNUSED_ARG(option);
303     return compare_conn(conn1, conn2);
304 }