/* The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-) Copyright (C) 2001,2002,2003 Aymeric MOIZARD jack@atosc.org This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include /* internal include */ /** * Initialize negotiation facility.. * @param config The element to work on. */ int osip_rfc3264_init (struct osip_rfc3264 **config) { osip_rfc3264_t *cnf; *config = NULL; cnf = (osip_rfc3264_t *) osip_malloc (sizeof(osip_rfc3264_t)); if (cnf==NULL) return -1; memset(cnf, 0, sizeof(osip_rfc3264_t)); *config = cnf; return 0; } /** * Free negotiation facility. * @param config The element to work on. */ void osip_rfc3264_free (struct osip_rfc3264 *config) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int i; if (config==NULL) return; for (i=0;iaudio_medias[i]!=NULL) { sdp_media_free(cnf->audio_medias[i]); cnf->audio_medias[i] = NULL; } } for (i=0;ivideo_medias[i]!=NULL) { sdp_media_free(cnf->video_medias[i]); cnf->video_medias[i] = NULL; } } for (i=0;it38_medias[i]!=NULL) { sdp_media_free(cnf->t38_medias[i]); cnf->t38_medias[i] = NULL; } } for (i=0;iapp_medias[i]!=NULL) { sdp_media_free(cnf->app_medias[i]); cnf->app_medias[i] = NULL; } } osip_free(cnf); } /** * Test if a media exist in the configuration. * @param config The element to work on. * @param pos The index of the media element. */ int osip_rfc3264_endof_media (struct osip_rfc3264 *config, int pos) { if (config==NULL) return -1; return 0; } /** * Get a media from the configuration. * @param config The element to work on. * @param pos The index of the media element to get. */ sdp_media_t *osip_rfc3264_get (struct osip_rfc3264 *config, int pos) { if (config==NULL) return NULL; return NULL; } /** * Remove a media from the configuration. * @param config The element to work on. * @param pos The index of the media element to remove. */ int osip_rfc3264_remove (struct osip_rfc3264 *config, int pos) { if (config==NULL) return -1; return 0; } /** * Remove all medias from the configuration. * @param config The element to work on. */ int osip_rfc3264_reset_media (struct osip_rfc3264 *config) { if (config==NULL) return -1; return 0; } /** * Add a media (for audio) in the configuration. * @param config The element to work on. * @param pos The index of the media element to add. */ int osip_rfc3264_add_audio_media (struct osip_rfc3264 *config, sdp_media_t *med, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_AUDIO_CODECS) return -1; if (pos==-1) { for (pos=0;posaudio_medias[pos]!=NULL; pos++) { } } if (pos>=MAX_AUDIO_CODECS) return -1; /* no space left */ cnf->audio_medias[pos] = med; return 0; } /** * Remove a media in the configuration. * @param config The element to work on. * @param pos The index of the media element to remove. */ int osip_rfc3264_del_audio_media (struct osip_rfc3264 *config, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_AUDIO_CODECS) return -1; sdp_media_free(cnf->audio_medias[pos]); cnf->audio_medias[pos] = NULL; return 0; } /** * Add a media (for video) in the configuration. * @param config The element to work on. * @param pos The index of the media element to add. */ int osip_rfc3264_add_video_media (struct osip_rfc3264 *config, sdp_media_t *med, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_VIDEO_CODECS) return -1; if (pos==-1) { for (pos=0;posvideo_medias[pos]!=NULL; pos++) { } } if (pos>=MAX_VIDEO_CODECS) return -1; /* no space left */ cnf->video_medias[pos] = med; return 0; } /** * Remove a media in the configuration. * @param config The element to work on. * @param pos The index of the media element to remove. */ int osip_rfc3264_del_video_media (struct osip_rfc3264 *config, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_VIDEO_CODECS) return -1; sdp_media_free(cnf->video_medias[pos]); cnf->video_medias[pos] = NULL; return 0; } /** * Add a media (for t38) in the configuration. * @param config The element to work on. * @param pos The index of the media element to add. */ int osip_rfc3264_add_t38_media (struct osip_rfc3264 *config, sdp_media_t *med, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_T38_CODECS) return -1; if (pos==-1) { for (pos=0;post38_medias[pos]!=NULL; pos++) { } } if (pos>=MAX_T38_CODECS) return -1; /* no space left */ cnf->t38_medias[pos] = med; return 0; } /** * Remove a media in the configuration. * @param config The element to work on. * @param pos The index of the media element to remove. */ int osip_rfc3264_del_t38_media (struct osip_rfc3264 *config, int pos) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return -1; if (pos>=MAX_T38_CODECS) return -1; sdp_media_free(cnf->t38_medias[pos]); cnf->t38_medias[pos] = NULL; return 0; } /** * Search for support of a special codec. * @param config The element to work on. */ sdp_media_t *osip_rfc3264_find_audio (struct osip_rfc3264 *config, char *payload, char *rtpmap) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int i; if (config==NULL) return NULL; if (rtpmap==NULL) { for (i=0;iaudio_medias[i]!=NULL) { sdp_media_t *med = cnf->audio_medias[i]; char *str = (char *) osip_list_get (med->m_payloads, 0); /* static payload?: only compare payload number */ if (strlen(str)==strlen(payload) && 0==osip_strcasecmp(str, payload)) return med; } } return NULL; } for (i=0;iaudio_medias[i]!=NULL) { sdp_media_t *med = cnf->audio_medias[i]; int pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); if (0==osip_strcasecmp("rtpmap", attr->a_att_field) && attr->a_att_value!=NULL) { char *tmp = strchr(attr->a_att_value, ' '); char *tmp2 = strchr(rtpmap, ' '); if (tmp!=NULL && tmp2!=NULL) { if (0==osip_strcasecmp(tmp,tmp2)) return med; } } pos++; } } } return NULL; } /** * Search for support of a special codec. * @param config The element to work on. */ sdp_media_t *osip_rfc3264_find_video (struct osip_rfc3264 *config, char *payload, char *rtpmap) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int i; if (config==NULL) return NULL; if (rtpmap==NULL) { for (i=0;ivideo_medias[i]!=NULL) { sdp_media_t *med = cnf->video_medias[i]; char *str = (char *) osip_list_get (med->m_payloads, 0); /* static payload?: only compare payload number */ if (strlen(str)==strlen(payload) && 0==osip_strcasecmp(str, payload)) return med; } } return NULL; } for (i=0;ivideo_medias[i]!=NULL) { sdp_media_t *med = cnf->video_medias[i]; int pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); if (0==osip_strcasecmp("rtpmap", attr->a_att_field) && attr->a_att_value!=NULL) { char *tmp = strchr(attr->a_att_value, ' '); char *tmp2 = strchr(rtpmap, ' '); if (tmp!=NULL && tmp2!=NULL) { if (0==osip_strcasecmp(tmp,tmp2)) return med; } } pos++; } } } return NULL; } /** * Search for support of a special codec. * @param config The element to work on. */ sdp_media_t *osip_rfc3264_find_t38 (struct osip_rfc3264 *config, char *payload) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return NULL; return NULL; } /** * Search for support of a special codec. * @param config The element to work on. */ sdp_media_t *osip_rfc3264_find_app (struct osip_rfc3264 *config, char *payload) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; if (config==NULL) return NULL; return NULL; } /** * Compare remote sdp packet against local supported media. * Only one media line is checked. * * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param audio_tab The local list of media supported by both side. * @param video_tab The local list of media supported by both side. * @param t38_tab The local list of media supported by both side. * @param app_tab The local list of media supported by both side. * @param pos_media The position of the media line to match. */ int osip_rfc3264_match(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, sdp_media_t *audio_tab[], sdp_media_t *video_tab[], sdp_media_t *t38_tab[], sdp_media_t *app_tab[], int pos_media) { sdp_media_t *remote_med; int pos; audio_tab[0] = NULL; video_tab[0] = NULL; t38_tab[0] = NULL; app_tab[0] = NULL; if (config==NULL) return -1; pos=0; while (!sdp_message_endof_media (remote_sdp, pos)) { if (pos_media==0) { remote_med = osip_list_get (remote_sdp->m_medias, pos); if (remote_med->m_media!=NULL && 0 == osip_strcasecmp (remote_med->m_media, "audio")) { osip_rfc3264_match_audio(config, remote_sdp, remote_med, audio_tab); } else if (remote_med->m_media!=NULL && 0 == osip_strcasecmp (remote_med->m_media, "video")) { osip_rfc3264_match_video(config, remote_sdp, remote_med, video_tab); } else if (remote_med->m_media!=NULL && 0 == osip_strcasecmp (remote_med->m_media, "image")) { osip_rfc3264_match_t38(config, remote_sdp, remote_med, t38_tab); } else if (remote_med->m_media!=NULL && 0 == osip_strcasecmp (remote_med->m_media, "application")) { osip_rfc3264_match_app(config, remote_sdp, remote_med, app_tab); } return 0; } remote_med=NULL; pos++; pos_media--; } return -1; } /** * Compare remote sdp packet against local supported media for audio. * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param remote_med The remote Media SDP line. * @param audio_tab The local list of media supported by both side. */ int osip_rfc3264_match_audio(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, sdp_media_t *remote_med, sdp_media_t *audio_tab[]) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int num=0; int pos; audio_tab[0] = NULL; if (cnf==NULL) return -1; /* search for the audio media line */ pos=0; while (!osip_list_eol (remote_med->m_payloads, pos)) { char *payload = (char *) osip_list_get (remote_med->m_payloads, pos); sdp_media_t *local_med; char *rtpmap = NULL; int posattr = 0; /* search for the rtpmap associated to the payload */ while (!osip_list_eol (remote_med->a_attributes, posattr)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (remote_med->a_attributes, posattr); if (0==osip_strncasecmp(attr->a_att_field, "rtpmap", 6)) { if (attr->a_att_value!=NULL && 0==osip_strncasecmp(attr->a_att_value, payload, strlen(payload))) { /* TODO check if it was not like 101: == 10 */ rtpmap = attr->a_att_value; break; } } posattr++; } local_med = osip_rfc3264_find_audio(config, payload, rtpmap); if (local_med!=NULL) { /* found a supported codec? */ audio_tab[num] = local_med; num++; } /* search for support of this codec in local media list */ pos++; } audio_tab[num] = NULL; return 0; } /** * Compare remote sdp packet against local supported media for video. * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param remote_med The remote Media SDP line. * @param video_tab The local list of media supported by both side. */ int osip_rfc3264_match_video(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, sdp_media_t *remote_med, sdp_media_t *video_tab[]) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int num=0; int pos; video_tab[0] = NULL; if (cnf==NULL) return -1; /* search for the video media line */ pos=0; while (!osip_list_eol (remote_med->m_payloads, pos)) { char *payload = (char *) osip_list_get (remote_med->m_payloads, pos); sdp_media_t *local_med; char *rtpmap = NULL; int posattr = 0; /* search for the rtpmap associated to the payload */ while (!osip_list_eol (remote_med->a_attributes, posattr)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (remote_med->a_attributes, posattr); if (0==osip_strncasecmp(attr->a_att_field, "rtpmap", 6)) { if (attr->a_att_value!=NULL && 0==osip_strncasecmp(attr->a_att_value, payload, strlen(payload))) { /* TODO check if it was not like 101: == 10 */ rtpmap = attr->a_att_value; break; } } posattr++; } local_med = osip_rfc3264_find_video(config, payload, rtpmap); if (local_med!=NULL) { /* found a supported codec? */ video_tab[num] = local_med; num++; } /* search for support of this codec in local media list */ pos++; } video_tab[num] = NULL; return 0; } /** * Compare remote sdp packet against local supported media for t38. * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param remote_med The remote Media SDP line. * @param t38_tab The local list of media supported by both side. */ int osip_rfc3264_match_t38(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, sdp_media_t *remote_med, sdp_media_t *t38_tab[]) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; t38_tab[0] = NULL; if (cnf==NULL) return -1; return 0; } /** * Compare remote sdp packet against local supported media for application. * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param remote_med The remote Media SDP line. * @param app_tab The local list of media supported by both side. */ int osip_rfc3264_match_app(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, sdp_media_t *remote_med, sdp_media_t *app_tab[]) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; app_tab[0] = NULL; if (cnf==NULL) return -1; return 0; } /** * Prepare an uncomplete answer. * @param config The element to work on. * @param remote_sdp The remote SDP packet. * @param local_sdp The local SDP packet to prepare. * @param length The local SDP packet's length. */ int osip_rfc3264_prepare_answer(struct osip_rfc3264 *config, sdp_message_t *remote_sdp, char *local_sdp, int length) { int pos, pos2; if (config==NULL) return -1; if (remote_sdp==NULL) return -1; if (osip_list_size(remote_sdp->t_descrs)>0) #if (defined WIN32 || defined _WIN32_WCE) _snprintf(local_sdp, 4096, "v=0\r\n\ o=userX 20000001 20000001 IN IP4 TOREPLACE\r\n\ s=-\r\n\ c=IN IP4 TOREPLACE\r\n"); #else snprintf(local_sdp, 4096, "v=0\r\n\ o=userX 20000001 20000001 IN IP4 TOREPLACE\r\n\ s=-\r\n\ c=IN IP4 TOREPLACE\r\n"); #endif /* Fill t= (and r=) fields */ pos=0; while (!osip_list_eol(remote_sdp->t_descrs, pos)) { char tmp[100]; sdp_time_descr_t *td; td = (sdp_time_descr_t *) osip_list_get(remote_sdp->t_descrs, pos); if (td->t_start_time!=NULL && td->t_stop_time!=NULL) #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp, 100, "t=%s %s\r\n", td->t_start_time, td->t_stop_time); #else snprintf(tmp, 100, "t=%s %s\r\n", td->t_start_time, td->t_stop_time); #endif else #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp, 100, "t=0 0\r\n"); #else snprintf(tmp, 100, "t=0 0\r\n"); #endif if ((int)(strlen(local_sdp)+strlen(tmp)+1)r_repeats, pos2)) { char *str = (char *) osip_list_get (td->r_repeats, pos2); if ((int)(strlen(local_sdp)+strlen(str)+5+1)m_medias, pos)) { int posattr = 0; char tmp[200]; char tmp2[200]; char inactive='X'; sdp_media_t *med; #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp2, 199, "\r\n"); #else snprintf(tmp2, 199, "\r\n"); #endif med = (sdp_media_t *) osip_list_get(remote_sdp->m_medias, pos); /* search for the rtpmap associated to the payload */ while (!osip_list_eol (med->a_attributes, posattr)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, posattr); if (strlen(attr->a_att_field)==8 && attr->a_att_value==NULL) { if (0==osip_strncasecmp(attr->a_att_field, "sendonly", 8)) { #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp2, 199, "\r\na=recvonly\r\n"); #else snprintf(tmp2, 199, "\r\na=recvonly\r\n"); #endif break; } else if (0==osip_strncasecmp(attr->a_att_field, "recvonly", 8)) { #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp2, 199, "\r\na=sendonly\r\n"); #else snprintf(tmp2, 199, "\r\na=sendonly\r\n"); #endif break; } else if (0==osip_strncasecmp(attr->a_att_field, "sendrecv", 8)) { break; } else if (0==osip_strncasecmp(attr->a_att_field, "inactive", 8)) { #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp2, 199, "\r\na=inactive\r\n"); #else snprintf(tmp2, 199, "\r\na=inactive\r\n"); #endif inactive='0'; break; } } posattr++; } if (med->m_media!=NULL && med->m_proto!=NULL && med->m_number_of_port==NULL) { #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp, 199, "m=%s %c %s ", med->m_media, inactive, med->m_proto); #else snprintf(tmp, 199, "m=%s %c %s ", med->m_media, inactive, med->m_proto); #endif } else if (med->m_media!=NULL && med->m_proto!=NULL && med->m_number_of_port==NULL) { #if (defined WIN32 || defined _WIN32_WCE) _snprintf(tmp, 199, "m=%s %c %s/%s ", med->m_media, inactive, med->m_proto, med->m_number_of_port); #else snprintf(tmp, 199, "m=%s %c %s/%s ", med->m_media, inactive, med->m_proto, med->m_number_of_port); #endif } else return -1; if ((int)(strlen(local_sdp)+strlen(tmp)+1)m_medias, pos)) { remote_med = (sdp_media_t *) osip_list_get(remote_sdp->m_medias, pos); local_med = (sdp_media_t *) osip_list_get(local_sdp->m_medias, pos); if (pos==mline) break; remote_med = NULL; local_med = NULL; pos++; } if (remote_med==NULL) return -1; pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); if (0==osip_strcasecmp("rtpmap", attr->a_att_field) && attr->a_att_value!=NULL) { sdp_attribute_t *mattr; char *tmp; /* fill the m= line */ tmp = (char*) osip_list_get(med->m_payloads, 0); if (tmp!=NULL) osip_list_add(local_med->m_payloads, osip_strdup(tmp), -1); else return -1; sdp_attribute_init(&mattr); mattr->a_att_field = osip_strdup(attr->a_att_field); mattr->a_att_value = osip_strdup(attr->a_att_value); /* fill the a= line */ osip_list_add (local_med->a_attributes, mattr, -1); return 0; } } return -1; /* no rtpmap found? It is mandatory in audio and video media */ } /** * Agree to support a specific codec. * This method should be called for each codec returned by * osip_rfc3264_match(...) * * @param config The element to work on. * @param med One of the media returned by osip_rfc3264_match * @param remote_sdp The remote SDP packet. * @param local_sdp The local SDP packet to prepare. */ int osip_rfc3264_accept_codec(struct osip_rfc3264 *config, sdp_media_t *med, sdp_message_t *remote_sdp, sdp_message_t *local_sdp) { if (config==NULL) return -1; return 0; } /* #ifdef RFC3264_DEBUG */ /** * List supported codecs. (for debugging purpose only) * * @param config The element to work on. */ int __osip_rfc3264_print_codecs(struct osip_rfc3264 *config) { osip_rfc3264_t *cnf = (osip_rfc3264_t*) config; int i,pos; if (config==NULL) return -1; fprintf(stdout, "Audio codecs Supported:\n"); for (i=0;iaudio_medias[i]!=NULL) { sdp_media_t *med = cnf->audio_medias[i]; char *str = (char *) osip_list_get (med->m_payloads, 0); fprintf(stdout, "\tm=%s %s %s %s\n", med->m_media, med->m_port, med->m_proto, str); pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); fprintf(stdout, "\ta=%s:%s\n", attr->a_att_field, attr->a_att_value); pos++; } fprintf(stdout, "\n"); } } fprintf(stdout, "Video codecs Supported:\n"); for (i=0;ivideo_medias[i]!=NULL) { sdp_media_t *med = cnf->video_medias[i]; char *str = (char *) osip_list_get (med->m_payloads, 0); fprintf(stdout, "\tm=%s %s %s %s\n", med->m_media, med->m_port, med->m_proto, str); pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); fprintf(stdout, "\ta=%s:%s\n", attr->a_att_field, attr->a_att_value); pos++; } fprintf(stdout, "\n"); } } fprintf(stdout, "t38 configs Supported:\n"); for (i=0;it38_medias[i]!=NULL) { sdp_media_t *med = cnf->t38_medias[i]; fprintf(stdout, "m=%s %s %s X\n", med->m_media, med->m_port, med->m_proto); pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); fprintf(stdout, "\ta=%s:%s\n", attr->a_att_field, attr->a_att_value); pos++; } fprintf(stdout, "\n"); } } fprintf(stdout, "Application config Supported:\n"); for (i=0;iapp_medias[i]!=NULL) { sdp_media_t *med = cnf->app_medias[i]; fprintf(stdout, "m=%s %s %s X\n", med->m_media, med->m_port, med->m_proto); pos = 0; while (!osip_list_eol (med->a_attributes, pos)) { sdp_attribute_t *attr = (sdp_attribute_t *) osip_list_get (med->a_attributes, pos); fprintf(stdout, "\ta=%s:%s\n", attr->a_att_field, attr->a_att_value); pos++; } fprintf(stdout, "\n"); } } return 0; } /* #endif */