3 Copyright (C) 2010 Simon MORLAT (simon.morlat@free.fr)
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 This header files defines the Signaling Abstraction Layer.
22 The purpose of this layer is too allow experiment different call signaling
23 protocols and implementations under linphone, for example SIP, JINGLE...
27 const char* sal_transport_to_string(SalTransport transport) {
29 case SalTransportUDP:return "udp";
30 case SalTransportTCP: return "tcp";
31 case SalTransportTLS:return "tls";
32 case SalTransportDTLS:return "dtls";
34 ms_fatal("Unexpected transport [%i]",transport);
40 SalTransport sal_transport_parse(const char* param) {
41 if (strcasecmp("udp",param)==0) return SalTransportUDP;
42 if (strcasecmp("tcp",param)==0) return SalTransportTCP;
43 if (strcasecmp("tls",param)==0) return SalTransportTLS;
44 if (strcasecmp("dtls",param)==0) return SalTransportDTLS;
45 ms_error("Unknown transport type[%s], returning UDP", param);
46 return SalTransportUDP;
49 SalMediaDescription *sal_media_description_new(){
50 SalMediaDescription *md=ms_new0(SalMediaDescription,1);
55 static void sal_media_description_destroy(SalMediaDescription *md){
57 for(i=0;i<SAL_MEDIA_DESCRIPTION_MAX_STREAMS;i++){
58 ms_list_for_each(md->streams[i].payloads,(void (*)(void *))payload_type_destroy);
59 ms_list_free(md->streams[i].payloads);
60 md->streams[i].payloads=NULL;
65 void sal_media_description_ref(SalMediaDescription *md){
69 void sal_media_description_unref(SalMediaDescription *md){
72 sal_media_description_destroy (md);
76 SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md,
77 SalMediaProto proto, SalStreamType type){
79 for(i=0;i<md->n_active_streams;++i){
80 SalStreamDescription *ss=&md->streams[i];
81 if (ss->proto==proto && ss->type==type) return ss;
86 bool_t sal_media_description_empty(const SalMediaDescription *md){
87 if (md->n_active_streams > 0) return FALSE;
91 void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir){
93 for(i=0;i<md->n_active_streams;++i){
94 SalStreamDescription *ss=&md->streams[i];
100 static bool_t is_null_address(const char *addr){
101 return strcmp(addr,"0.0.0.0")==0 || strcmp(addr,"::0")==0;
104 /*check for the presence of at least one stream with requested direction */
105 static bool_t has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){
108 /* we are looking for at least one stream with requested direction, inactive streams are ignored*/
109 for(i=0;i<md->n_active_streams;++i){
110 const SalStreamDescription *ss=&md->streams[i];
111 if (ss->dir==stream_dir) return TRUE;
112 /*compatibility check for phones that only used the null address and no attributes */
113 if (ss->dir==SalStreamSendRecv && stream_dir==SalStreamSendOnly && (is_null_address(md->addr) || is_null_address(ss->rtp_addr)))
119 bool_t sal_media_description_has_dir(const SalMediaDescription *md, SalStreamDir stream_dir){
120 if (stream_dir==SalStreamRecvOnly){
121 if (has_dir(md,SalStreamSendOnly) || has_dir(md,SalStreamSendRecv)) return FALSE;
123 }else if (stream_dir==SalStreamSendOnly){
124 if (has_dir(md,SalStreamRecvOnly) || has_dir(md,SalStreamSendRecv)) return FALSE;
126 }else if (stream_dir==SalStreamSendRecv){
127 return has_dir(md,SalStreamSendRecv);
129 /*SalStreamInactive*/
130 if (has_dir(md,SalStreamSendOnly) || has_dir(md,SalStreamSendRecv) || has_dir(md,SalStreamRecvOnly))
138 static bool_t fmtp_equals(const char *p1, const char *p2){
139 if (p1 && p2 && strcmp(p1,p2)==0) return TRUE;
140 if (p1==NULL && p2==NULL) return TRUE;
145 static bool_t payload_type_equals(const PayloadType *p1, const PayloadType *p2){
146 if (p1->type!=p2->type) return FALSE;
147 if (strcmp(p1->mime_type,p2->mime_type)!=0) return FALSE;
148 if (p1->clock_rate!=p2->clock_rate) return FALSE;
149 if (p1->channels!=p2->channels) return FALSE;
150 if (payload_type_get_number(p1) != payload_type_get_number(p2)) return FALSE;
152 Do not compare fmtp right now: they are modified internally when the call is started
155 if (!fmtp_equals(p1->recv_fmtp,p2->recv_fmtp) ||
156 !fmtp_equals(p1->send_fmtp,p2->send_fmtp))
162 static bool_t is_recv_only(PayloadType *p){
163 return (p->flags & PAYLOAD_TYPE_FLAG_CAN_RECV) && ! (p->flags & PAYLOAD_TYPE_FLAG_CAN_SEND);
166 static bool_t payload_list_equals(const MSList *l1, const MSList *l2){
167 const MSList *e1,*e2;
168 for(e1=l1,e2=l2;e1!=NULL && e2!=NULL; e1=e1->next,e2=e2->next){
169 PayloadType *p1=(PayloadType*)e1->data;
170 PayloadType *p2=(PayloadType*)e2->data;
171 if (!payload_type_equals(p1,p2))
175 /*skip possible recv-only payloads*/
176 for(;e1!=NULL && is_recv_only((PayloadType*)e1->data);e1=e1->next){
177 ms_message("Skipping recv-only payload type...");
180 if (e1!=NULL || e2!=NULL){
181 /*means one list is longer than the other*/
187 int sal_stream_description_equals(const SalStreamDescription *sd1, const SalStreamDescription *sd2) {
188 int result = SAL_MEDIA_DESCRIPTION_UNCHANGED;
191 /* A different proto should result in SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED but the encryption change
192 needs a stream restart for now, so use SAL_MEDIA_DESCRIPTION_CODEC_CHANGED */
193 if (sd1->proto != sd2->proto) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
194 for (i = 0; i < SAL_CRYPTO_ALGO_MAX; i++) {
195 if ((sd1->crypto[i].tag != sd2->crypto[i].tag)
196 || (sd1->crypto[i].algo != sd2->crypto[i].algo)
197 || (strncmp(sd1->crypto[i].master_key, sd2->crypto[i].master_key, sizeof(sd1->crypto[i].master_key) - 1))) {
198 result |= SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED;
202 if (sd1->type != sd2->type) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
203 if (strcmp(sd1->rtp_addr, sd2->rtp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
204 if (sd1->rtp_port != sd2->rtp_port) {
205 if ((sd1->rtp_port == 0) || (sd2->rtp_port == 0)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
206 else result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
208 if (strcmp(sd1->rtcp_addr, sd2->rtcp_addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
209 if (sd1->rtcp_port != sd2->rtcp_port) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
210 if (!payload_list_equals(sd1->payloads, sd2->payloads)) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
211 if (sd1->bandwidth != sd2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
212 if (sd1->ptime != sd2->ptime) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
213 if (sd1->dir != sd2->dir) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
218 int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaDescription *md2) {
219 int result = SAL_MEDIA_DESCRIPTION_UNCHANGED;
222 if (strcmp(md1->addr, md2->addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
223 if (md1->n_total_streams != md2->n_total_streams) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
224 if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
225 for(i = 0; i < md1->n_total_streams; ++i){
226 result |= sal_stream_description_equals(&md1->streams[i], &md2->streams[i]);
231 static void assign_string(char **str, const char *arg){
240 void sal_op_set_contact(SalOp *op, const char *contact){
241 assign_string(&((SalOpBase*)op)->contact,contact);
244 void sal_op_set_route(SalOp *op, const char *route){
245 assign_string(&((SalOpBase*)op)->route,route);
248 void sal_op_set_from(SalOp *op, const char *from){
249 assign_string(&((SalOpBase*)op)->from,from);
252 void sal_op_set_to(SalOp *op, const char *to){
253 assign_string(&((SalOpBase*)op)->to,to);
256 void sal_op_set_user_pointer(SalOp *op, void *up){
257 ((SalOpBase*)op)->user_pointer=up;
260 Sal *sal_op_get_sal(const SalOp *op){
261 return ((SalOpBase*)op)->root;
264 const char *sal_op_get_from(const SalOp *op){
265 return ((SalOpBase*)op)->from;
268 const char *sal_op_get_to(const SalOp *op){
269 return ((SalOpBase*)op)->to;
272 const char *sal_op_get_contact(const SalOp *op){
273 return ((SalOpBase*)op)->contact;
276 const char *sal_op_get_remote_contact(const SalOp *op){
277 return ((SalOpBase*)op)->remote_contact;
280 const char *sal_op_get_route(const SalOp *op){
281 return ((SalOpBase*)op)->route;
284 const char *sal_op_get_remote_ua(const SalOp *op){
285 return ((SalOpBase*)op)->remote_ua;
288 void *sal_op_get_user_pointer(const SalOp *op){
289 return ((SalOpBase*)op)->user_pointer;
292 const char *sal_op_get_proxy(const SalOp *op){
293 return ((SalOpBase*)op)->route;
296 const char *sal_op_get_network_origin(const SalOp *op){
297 return ((SalOpBase*)op)->origin;
299 const char* sal_op_get_call_id(const SalOp *op) {
300 return ((SalOpBase*)op)->call_id;
302 void __sal_op_init(SalOp *b, Sal *sal){
303 memset(b,0,sizeof(SalOpBase));
304 ((SalOpBase*)b)->root=sal;
307 void __sal_op_set_network_origin(SalOp *op, const char *origin){
308 assign_string(&((SalOpBase*)op)->origin,origin);
311 void __sal_op_set_remote_contact(SalOp *op, const char *ct){
312 assign_string(&((SalOpBase*)op)->remote_contact,ct);
315 void __sal_op_free(SalOp *op){
316 SalOpBase *b=(SalOpBase *)op;
338 ms_free(b->remote_ua);
341 if (b->remote_contact){
342 ms_free(b->remote_contact);
343 b->remote_contact=NULL;
346 sal_media_description_unref(b->local_media);
348 sal_media_description_unref(b->remote_media);
351 if (b->custom_headers)
352 sal_custom_header_free(b->custom_headers);
356 SalAuthInfo* sal_auth_info_new() {
357 return ms_new0(SalAuthInfo,1);
360 SalAuthInfo* sal_auth_info_clone(const SalAuthInfo* auth_info) {
361 SalAuthInfo* new_auth_info=sal_auth_info_new();
362 new_auth_info->username=auth_info->username?ms_strdup(auth_info->username):NULL;
363 new_auth_info->userid=auth_info->userid?ms_strdup(auth_info->userid):NULL;
364 new_auth_info->realm=auth_info->realm?ms_strdup(auth_info->realm):NULL;
365 new_auth_info->password=auth_info->password?ms_strdup(auth_info->password):NULL;
366 return new_auth_info;
369 void sal_auth_info_delete(const SalAuthInfo* auth_info) {
370 if (auth_info->username) ms_free(auth_info->username);
371 if (auth_info->userid) ms_free(auth_info->userid);
372 if (auth_info->realm) ms_free(auth_info->realm);
373 if (auth_info->password) ms_free(auth_info->password);
374 ms_free((void*)auth_info);
377 SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){
378 SalCustomHeader *h=ms_new0(SalCustomHeader,1);
379 h->header_name=ms_strdup(name);
380 h->header_value=ms_strdup(value);
382 return (SalCustomHeader*)ms_list_append_link((MSList*)ch,(MSList*)h);
385 const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name){
387 for (it=(const MSList*)ch;it!=NULL;it=it->next){
388 const SalCustomHeader *itch=(const SalCustomHeader *)it;
389 if (strcasecmp(itch->header_name,name)==0)
390 return itch->header_value;
395 static void sal_custom_header_uninit(SalCustomHeader *ch){
396 ms_free(ch->header_name);
397 ms_free(ch->header_value);
400 void sal_custom_header_free(SalCustomHeader *ch){
401 ms_list_for_each((MSList*)ch,(void (*)(void*))sal_custom_header_uninit);
402 ms_list_free((MSList *)ch);
405 SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch){
407 SalCustomHeader *ret=NULL;
408 for (it=(const MSList*)ch;it!=NULL;it=it->next){
409 const SalCustomHeader *itch=(const SalCustomHeader *)it;
410 ret=sal_custom_header_append(ret,itch->header_name,itch->header_value);
415 const SalCustomHeader *sal_op_get_custom_header(SalOp *op){
416 SalOpBase *b=(SalOpBase *)op;
417 return b->custom_headers;
421 * Warning: this function takes owneship of the custom headers
423 void sal_op_set_custom_header(SalOp *op, SalCustomHeader* ch){
424 SalOpBase *b=(SalOpBase *)op;
425 if (b->custom_headers){
426 sal_custom_header_free(b->custom_headers);
427 b->custom_headers=NULL;
429 b->custom_headers=ch;