void janus_sdp_free(janus_sdp *sdp) { if(!sdp) return; g_free(sdp->o_name); g_free(sdp->o_addr); g_free(sdp->s_name); g_free(sdp->c_addr); GList *temp = sdp->attributes; while(temp) { janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data; janus_sdp_attribute_destroy(a); temp = temp->next; } g_list_free(sdp->attributes); sdp->attributes = NULL; temp = sdp->m_lines; while(temp) { janus_sdp_mline *m = (janus_sdp_mline *)temp->data; janus_sdp_mline_destroy(m); temp = temp->next; } g_list_free(sdp->m_lines); sdp->m_lines = NULL; g_free(sdp); }
/* Internal frees */ static void janus_sdp_free(const janus_refcount *sdp_ref) { janus_sdp *sdp = janus_refcount_containerof(sdp_ref, janus_sdp, ref); /* This SDP instance can be destroyed, free all the resources */ g_free(sdp->o_name); g_free(sdp->o_addr); g_free(sdp->s_name); g_free(sdp->c_addr); GList *temp = sdp->attributes; while(temp) { janus_sdp_attribute *a = (janus_sdp_attribute *)temp->data; janus_sdp_attribute_destroy(a); temp = temp->next; } g_list_free(sdp->attributes); sdp->attributes = NULL; temp = sdp->m_lines; while(temp) { janus_sdp_mline *m = (janus_sdp_mline *)temp->data; janus_sdp_mline_destroy(m); temp = temp->next; } g_list_free(sdp->m_lines); sdp->m_lines = NULL; g_free(sdp); }
int janus_sdp_mline_remove(janus_sdp *sdp, janus_sdp_mtype type) { if(sdp == NULL) return -1; GList *ml = sdp->m_lines; while(ml) { janus_sdp_mline *m = (janus_sdp_mline *)ml->data; if(m->type == type) { /* Found! */ sdp->m_lines = g_list_remove(sdp->m_lines, m); janus_sdp_mline_destroy(m); return 0; } ml = ml->next; } /* If we got here, we couldn't the m-line */ return -2; }
janus_sdp *janus_sdp_parse(const char *sdp, char *error, size_t errlen) { if(!sdp) return NULL; if(strstr(sdp, "v=") != sdp) { if(error) g_snprintf(error, errlen, "Invalid SDP (doesn't start with v=)"); return NULL; } janus_sdp *imported = g_malloc0(sizeof(janus_sdp)); g_atomic_int_set(&imported->destroyed, 0); janus_refcount_init(&imported->ref, janus_sdp_free); imported->o_ipv4 = TRUE; imported->c_ipv4 = TRUE; gboolean success = TRUE; janus_sdp_mline *mline = NULL; gchar **parts = g_strsplit(sdp, strstr(sdp, "\r\n") ? "\r\n" : "\n", -1); if(parts) { int index = 0; char *line = NULL; while(success && (line = parts[index]) != NULL) { if(*line == '\0') { index++; continue; } if(strlen(line) < 3) { if(error) g_snprintf(error, errlen, "Invalid line (%zu bytes): %s", strlen(line), line); success = FALSE; break; } if(*(line+1) != '=') { if(error) g_snprintf(error, errlen, "Invalid line (2nd char is not '='): %s", line); success = FALSE; break; } char c = *line; if(mline == NULL) { /* Global stuff */ switch(c) { case 'v': { if(sscanf(line, "v=%d", &imported->version) != 1) { if(error) g_snprintf(error, errlen, "Invalid v= line: %s", line); success = FALSE; break; } break; } case 'o': { if(imported->o_name || imported->o_addr) { if(error) g_snprintf(error, errlen, "Multiple o= lines: %s", line); success = FALSE; break; } char name[256], addrtype[6], addr[256]; if(sscanf(line, "o=%255s %"SCNu64" %"SCNu64" IN %5s %255s", name, &imported->o_sessid, &imported->o_version, addrtype, addr) != 5) { if(error) g_snprintf(error, errlen, "Invalid o= line: %s", line); success = FALSE; break; } if(!strcasecmp(addrtype, "IP4")) imported->o_ipv4 = TRUE; else if(!strcasecmp(addrtype, "IP6")) imported->o_ipv4 = FALSE; else { if(error) g_snprintf(error, errlen, "Invalid o= line (unsupported protocol %s): %s", addrtype, line); success = FALSE; break; } imported->o_name = g_strdup(name); imported->o_addr = g_strdup(addr); break; } case 's': { if(imported->s_name) { if(error) g_snprintf(error, errlen, "Multiple s= lines: %s", line); success = FALSE; break; } imported->s_name = g_strdup(line+2); break; } case 't': { if(sscanf(line, "t=%"SCNu64" %"SCNu64, &imported->t_start, &imported->t_stop) != 2) { if(error) g_snprintf(error, errlen, "Invalid t= line: %s", line); success = FALSE; break; } break; } case 'c': { if(imported->c_addr) { if(error) g_snprintf(error, errlen, "Multiple global c= lines: %s", line); success = FALSE; break; } char addrtype[6], addr[256]; if(sscanf(line, "c=IN %5s %255s", addrtype, addr) != 2) { if(error) g_snprintf(error, errlen, "Invalid c= line: %s", line); success = FALSE; break; } if(!strcasecmp(addrtype, "IP4")) imported->c_ipv4 = TRUE; else if(!strcasecmp(addrtype, "IP6")) imported->c_ipv4 = FALSE; else { if(error) g_snprintf(error, errlen, "Invalid c= line (unsupported protocol %s): %s", addrtype, line); success = FALSE; break; } imported->c_addr = g_strdup(addr); break; } case 'a': { janus_sdp_attribute *a = g_malloc0(sizeof(janus_sdp_attribute)); janus_refcount_init(&a->ref, janus_sdp_attribute_free); line += 2; char *semicolon = strchr(line, ':'); if(semicolon == NULL) { a->name = g_strdup(line); a->value = NULL; } else { if(*(semicolon+1) == '\0') { janus_sdp_attribute_destroy(a); if(error) g_snprintf(error, errlen, "Invalid a= line: %s", line); success = FALSE; break; } *semicolon = '\0'; a->name = g_strdup(line); a->value = g_strdup(semicolon+1); a->direction = JANUS_SDP_DEFAULT; *semicolon = ':'; if(strstr(line, "/sendonly")) a->direction = JANUS_SDP_SENDONLY; else if(strstr(line, "/recvonly")) a->direction = JANUS_SDP_RECVONLY; if(strstr(line, "/inactive")) a->direction = JANUS_SDP_INACTIVE; } imported->attributes = g_list_append(imported->attributes, a); break; } case 'm': { janus_sdp_mline *m = g_malloc0(sizeof(janus_sdp_mline)); g_atomic_int_set(&m->destroyed, 0); janus_refcount_init(&m->ref, janus_sdp_mline_free); /* Start with media type, port and protocol */ char type[32]; char proto[64]; if(strlen(line) > 200) { janus_sdp_mline_destroy(m); if(error) g_snprintf(error, errlen, "Invalid m= line (too long): %zu", strlen(line)); success = FALSE; break; } if(sscanf(line, "m=%31s %"SCNu16" %63s %*s", type, &m->port, proto) != 3) { janus_sdp_mline_destroy(m); if(error) g_snprintf(error, errlen, "Invalid m= line: %s", line); success = FALSE; break; } m->type = janus_sdp_parse_mtype(type); m->type_str = g_strdup(type); m->proto = g_strdup(proto); m->direction = JANUS_SDP_SENDRECV; m->c_ipv4 = TRUE; if(m->port > 0) { /* Now let's check the payload types/formats */ gchar **mline_parts = g_strsplit(line+2, " ", -1); if(!mline_parts) { janus_sdp_mline_destroy(m); if(error) g_snprintf(error, errlen, "Invalid m= line (no payload types/formats): %s", line); success = FALSE; break; } int mindex = 0; while(mline_parts[mindex]) { if(mindex < 3) { /* We've parsed these before */ mindex++; continue; } /* Add string fmt */ m->fmts = g_list_append(m->fmts, g_strdup(mline_parts[mindex])); /* Add numeric payload type */ int ptype = atoi(mline_parts[mindex]); m->ptypes = g_list_append(m->ptypes, GINT_TO_POINTER(ptype)); mindex++; } g_strfreev(mline_parts); if(m->fmts == NULL || m->ptypes == NULL) { janus_sdp_mline_destroy(m); if(error) g_snprintf(error, errlen, "Invalid m= line (no payload types/formats): %s", line); success = FALSE; break; } } /* Append to the list of m-lines */ imported->m_lines = g_list_append(imported->m_lines, m); /* From now on, we parse this m-line */ mline = m; break; } default: JANUS_LOG(LOG_WARN, "Ignoring '%c' property\n", c); break; } } else { /* m-line stuff */ switch(c) { case 'c': { if(mline->c_addr) { if(error) g_snprintf(error, errlen, "Multiple m-line c= lines: %s", line); success = FALSE; break; } char addrtype[6], addr[256]; if(sscanf(line, "c=IN %5s %255s", addrtype, addr) != 2) { if(error) g_snprintf(error, errlen, "Invalid c= line: %s", line); success = FALSE; break; } if(!strcasecmp(addrtype, "IP4")) mline->c_ipv4 = TRUE; else if(!strcasecmp(addrtype, "IP6")) mline->c_ipv4 = FALSE; else { if(error) g_snprintf(error, errlen, "Invalid c= line (unsupported protocol %s): %s", addrtype, line); success = FALSE; break; } mline->c_addr = g_strdup(addr); break; } case 'b': { if(mline->b_name) { if(error) g_snprintf(error, errlen, "Multiple m-line b= lines: %s", line); success = FALSE; break; } line += 2; char *semicolon = strchr(line, ':'); if(semicolon == NULL || (*(semicolon+1) == '\0')) { if(error) g_snprintf(error, errlen, "Invalid b= line: %s", line); success = FALSE; break; } *semicolon = '\0'; mline->b_name = g_strdup(line); mline->b_value = atol(semicolon+1); *semicolon = ':'; break; } case 'a': { janus_sdp_attribute *a = g_malloc0(sizeof(janus_sdp_attribute)); janus_refcount_init(&a->ref, janus_sdp_attribute_free); line += 2; char *semicolon = strchr(line, ':'); if(semicolon == NULL) { /* Is this a media direction attribute? */ janus_sdp_mdirection direction = janus_sdp_parse_mdirection(line); if(direction != JANUS_SDP_INVALID) { janus_sdp_attribute_destroy(a); mline->direction = direction; break; } a->name = g_strdup(line); a->value = NULL; } else { if(*(semicolon+1) == '\0') { janus_sdp_attribute_destroy(a); if(error) g_snprintf(error, errlen, "Invalid a= line: %s", line); success = FALSE; break; } *semicolon = '\0'; a->name = g_strdup(line); a->value = g_strdup(semicolon+1); a->direction = JANUS_SDP_DEFAULT; *semicolon = ':'; if(strstr(line, "/sendonly")) a->direction = JANUS_SDP_SENDONLY; else if(strstr(line, "/recvonly")) a->direction = JANUS_SDP_RECVONLY; if(strstr(line, "/inactive")) a->direction = JANUS_SDP_INACTIVE; } mline->attributes = g_list_append(mline->attributes, a); break; } case 'm': { /* Current m-line ended, back to global parsing */ mline = NULL; continue; } default: JANUS_LOG(LOG_WARN, "Ignoring '%c' property (m-line)\n", c); break; } } index++; } g_strfreev(parts); } /* FIXME Do a last check: is all the stuff that's supposed to be there available? */ if(success && (imported->o_name == NULL || imported->o_addr == NULL || imported->s_name == NULL || imported->m_lines == NULL)) { success = FALSE; if(error) g_snprintf(error, errlen, "Missing mandatory lines (o=, s= or m=)"); } /* If something wrong happened, free and return a failure */ if(!success) { if(error) JANUS_LOG(LOG_ERR, "%s\n", error); janus_sdp_destroy(imported); imported = NULL; } return imported; }