提交 0e76b635 authored 作者: Anthony Minessale's avatar Anthony Minessale

64 bit goodies

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@897 d0543943-73ff-0310-b7d9-9358b9ac24b2
上级 ce72c5d1
#CFLAGS += -I/usr/src/common/src
CFLAGS += -I.
LDFLAGS += -leXosip2 -ljrtp
OBJS=osip_rfc3264.o
ifeq ($(OSARCH),Darwin)
LINKER=g++
else
LINKER=$(CC)
endif
all: depends $(MODNAME).$(DYNAMIC_LIB_EXTEN)
all: depends $(OBJS) $(MODNAME).$(DYNAMIC_LIB_EXTEN)
depends:
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install jrtplib --prefix=$(PREFIX) --disable-gst
......@@ -15,9 +15,12 @@ depends:
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install libeXosip2-2.2.2.tar.gz --disable-josua --prefix=$(PREFIX)
%.o: %.c
$(CC) -fPIC $(CFLAGS) -c -o $@ $<
$(MODNAME).$(DYNAMIC_LIB_EXTEN): $(MODNAME).c
$(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o
$(LINKER) $(SOLINK) -o $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(MODNAME).o $(LDFLAGS)
$(LINKER) $(SOLINK) -o $(MODNAME).$(DYNAMIC_LIB_EXTEN) $(OBJS) $(MODNAME).o $(LDFLAGS)
clean:
rm -fr *.$(DYNAMIC_LIB_EXTEN) *.o *~
......
......@@ -35,8 +35,9 @@
#include <jrtplib3/jrtp4c.h>
#include <eXosip2/eXosip.h>
#include <osip2/osip_mt.h>
#include <osipparser2/osip_rfc3264.h>
#include <osip_rfc3264.h>
#include <osipparser2/osip_port.h>
#define ENABLE_TRACE
static const char modname[] = "mod_exosip";
#define STRLEN 15
......@@ -951,6 +952,9 @@ SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_modul
{
/* NOTE: **interface is **_interface because the common lib redefines interface to struct in some situations */
osip_trace_initialize(10, stdout);
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OH OH no pool\n");
return SWITCH_STATUS_TERM;
......
/*
The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
Copyright (C) 2001,2002,2003,2004,2005 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 <stdlib.h>
#include <osipparser2/osip_port.h>
#include <osipparser2/osip_rfc3264.h>
#include <osip_rfc3264i.h> /* 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; i < MAX_AUDIO_CODECS; i++)
{
if (cnf->audio_medias[i] != NULL)
{
sdp_media_free (cnf->audio_medias[i]);
cnf->audio_medias[i] = NULL;
}
}
for (i = 0; i < MAX_VIDEO_CODECS; i++)
{
if (cnf->video_medias[i] != NULL)
{
sdp_media_free (cnf->video_medias[i]);
cnf->video_medias[i] = NULL;
}
}
for (i = 0; i < MAX_T38_CODECS; i++)
{
if (cnf->t38_medias[i] != NULL)
{
sdp_media_free (cnf->t38_medias[i]);
cnf->t38_medias[i] = NULL;
}
}
for (i = 0; i < MAX_APP_CODECS; i++)
{
if (cnf->app_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 med The media element to add.
* @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; pos < MAX_AUDIO_CODECS && cnf->audio_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 med The media element to add.
* @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; pos < MAX_VIDEO_CODECS && cnf->video_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 med The media element to add.
* @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; pos < MAX_T38_CODECS && cnf->t38_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.
* @param payload The payload to find.
* @param rtpmap The rtpmap for the payload.
*/
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; i < MAX_AUDIO_CODECS; i++)
{
if (cnf->audio_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; i < MAX_AUDIO_CODECS; i++)
{
if (cnf->audio_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.
* @param payload The payload to find.
* @param rtpmap The rtpmap for the payload.
*/
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; i < MAX_VIDEO_CODECS; i++)
{
if (cnf->video_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; i < MAX_VIDEO_CODECS; i++)
{
if (cnf->video_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.
* @param payload The payload to find.
*/
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.
* @param payload The payload to find.
*/
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 __PALMOS__ && (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 __PALMOS__ && (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 __PALMOS__ && (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) < length)
{
strcat (local_sdp, tmp);
pos2 = 0;
while (!osip_list_eol (td->r_repeats, pos2))
{
char *str = (char *) osip_list_get (td->r_repeats, pos2);
if ((int) (strlen (local_sdp) + strlen (str) + 5 + 1) < length)
{
strcat (local_sdp, "r=");
strcat (local_sdp, str);
strcat (local_sdp, "\r\n");
} else
return -1;
pos2++;
}
} else
return -1;
pos++;
}
pos = 0;
while (!osip_list_eol (remote_sdp->m_medias, pos))
{
int posattr = 0;
char tmp[200];
char tmp2[200];
char inactive = 'X';
sdp_media_t *med;
#if !defined __PALMOS__ && (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 __PALMOS__ && (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 __PALMOS__ && (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 __PALMOS__ && (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 __PALMOS__ && (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 __PALMOS__ && (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) < length)
strcat (local_sdp, tmp);
else
return -1;
if ((int) (strlen (local_sdp) + strlen (tmp2) + 1) < length)
strcat (local_sdp, tmp2);
else
return -1;
pos++;
}
return 0;
}
/**
* Agree to support a specific codec.
* This method should be called for each codec returned by
* osip_rfc3264_match(...) that the calle agree to support.
*
* @param config The element to work on.
* @param remote_sdp The remote SDP packet.
* @param local_sdp The local SDP packet to complete.
* @param med One of the media returned by osip_rfc3264_match.
* @param mline The position of the media line to complete.
*/
int
osip_rfc3264_complete_answer (struct osip_rfc3264 *config,
sdp_message_t * remote_sdp,
sdp_message_t * local_sdp,
sdp_media_t * med, int mline)
{
osip_rfc3264_t *cnf = (osip_rfc3264_t *) config;
sdp_media_t *remote_med = NULL;
sdp_media_t *local_med = NULL;
int pos;
if (cnf == NULL)
return -1;
if (remote_sdp == NULL)
return -1;
if (med == NULL)
return -1;
if (mline < 0)
return -1;
if (local_sdp == NULL)
return -1;
pos = 0;
while (!osip_list_eol (remote_sdp->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; i < MAX_AUDIO_CODECS; i++)
{
if (cnf->audio_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; i < MAX_VIDEO_CODECS; i++)
{
if (cnf->video_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; i < MAX_T38_CODECS; i++)
{
if (cnf->t38_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; i < MAX_APP_CODECS; i++)
{
if (cnf->app_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 */
/*
The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
Copyright (C) 2001,2002,2003,2004 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
*/
#ifndef __SDP_RFC3264_H__
#define __SDP_RFC3264_H__
#include <osipparser2/osip_list.h>
#include <osipparser2/sdp_message.h>
/**
* @file osip_rfc3264.h
* @brief oSIP sdp negotiation facility.
*/
/**
* @defgroup oSIP_rfc3264 oSIP sdp negotiation facility.
* @ingroup osip2_sdp
* @{
*/
#ifdef __cplusplus
extern "C"
{
#endif
/**
* Structure to hold support for codecs.
* @struct osip_rfc3264
*/
struct osip_rfc3264;
/**
* Maximum number of supported audio payload.
* @def MAX_AUDIO_CODECS
*/
#define MAX_AUDIO_CODECS 100
/**
* Maximum number of supported video payload.
* @def MAX_VIDEO_CODECS
*/
#define MAX_VIDEO_CODECS 100
/**
* Maximum number of supported t38 config.
* @def MAX_T38_CODECS
*/
#define MAX_T38_CODECS 2
/**
* Maximum number of supported application config.
* @def MAX_APP_CODECS
*/
#define MAX_APP_CODECS 100
/**
* Initialize negotiation facility..
* @param config The element to work on.
*/
int osip_rfc3264_init (struct osip_rfc3264 **config);
/**
* Free negotiation facility.
* @param config The element to work on.
*/
void osip_rfc3264_free (struct osip_rfc3264 *config);
/**
* 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);
/**
* 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);
/**
* 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);
/**
* Remove all medias from the configuration.
* @param config The element to work on.
*/
int osip_rfc3264_reset_media (struct osip_rfc3264 *config);
/**
* Add a media in the configuration.
* @param config The element to work on.
* @param med The media element to add.
* @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);
/**
* 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);
/**
* Add a media (for T.38) in the configuration.
* @param config The element to work on.
* @param med The media element to add.
* @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);
/**
* Remove a media (for T.38) 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);
/**
* Add a media (for video) in the configuration.
* @param config The element to work on.
* @param med The media element to add.
* @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);
/**
* 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);
/**
* 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);
/**
* Search for support of a special codec.
* @param config The element to work on.
* @param payload The payload to find.
* @param rtpmap The rtpmap for the payload.
*/
sdp_media_t *osip_rfc3264_find_video (struct osip_rfc3264 *config,
char *payload, char *rtpmap);
/**
* Search for support of a special codec.
* @param config The element to work on.
* @param payload The payload to find.
*/
sdp_media_t *osip_rfc3264_find_t38 (struct osip_rfc3264 *config, char *payload);
/**
* Search for support of a special codec.
* @param config The element to work on.
* @param payload The payload to find.
*/
sdp_media_t *osip_rfc3264_find_app (struct osip_rfc3264 *config, char *payload);
/**
* 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);
/**
* 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[]);
/**
* 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[]);
/**
* 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[]);
/**
* 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[]);
/**
* 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);
/**
* Agree to support a specific codec.
* This method should be called for each codec returned by
* osip_rfc3264_match(...) that the calle agree to support.
*
* @param config The element to work on.
* @param remote_sdp The remote SDP packet.
* @param local_sdp The local SDP packet to complete.
* @param med One of the media returned by osip_rfc3264_match.
* @param mline The position of the media line to complete.
*/
int
osip_rfc3264_complete_answer (struct osip_rfc3264 *config,
sdp_message_t * remote_sdp,
sdp_message_t * local_sdp,
sdp_media_t * med, int mline);
/**
* 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);
/**
* List supported codecs. (for debugging purpose only)
*
* @param config The element to work on.
*/
int __osip_rfc3264_print_codecs (struct osip_rfc3264 *config);
#ifdef __cplusplus
}
#endif
#endif
/*
The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
Copyright (C) 2001,2002,2003,2004,2005 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
*/
#ifndef __SDP_RFC3264I_H__
#define __SDP_RFC3264I_H__
#include <osipparser2/osip_list.h>
#include <osipparser2/sdp_message.h>
#include <osipparser2/osip_rfc3264.h>
#ifndef DOXYGEN
typedef struct osip_rfc3264 osip_rfc3264_t;
struct osip_rfc3264
{
sdp_media_t *audio_medias[MAX_AUDIO_CODECS];
sdp_media_t *video_medias[MAX_VIDEO_CODECS];
sdp_media_t *t38_medias[MAX_T38_CODECS];
sdp_media_t *app_medias[MAX_APP_CODECS];
};
#endif
#endif
CFLAGS +=-I/usr/local/include -I/usr/src/libpri -I/usr/src/linux/include -I. -I/usr/include
CFLAGS +=-D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -O6 -march=i686 -DAFT_A104
CFLAGS +=-D__LINUX__ -D_REENTRANT -D_GNU_SOURCE -DAFT_A104
LDFLAGS += -lsangoma
......@@ -12,6 +12,7 @@ endif
all: depends $(MODNAME).$(DYNAMIC_LIB_EXTEN)
depends:
MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install libsangoma --prefix=$(PREFIX)
$(MODNAME).$(DYNAMIC_LIB_EXTEN): $(MODNAME).c
$(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o
......
......@@ -395,7 +395,9 @@ SWITCH_DECLARE(switch_channel_state) switch_channel_set_state(switch_channel *ch
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s State Change %s -> %s\n", channel->name,
state_names[last_state], state_names[state]);
channel->state = state;
switch_core_session_signal_state_change(channel->session);
if (state < CS_DONE) {
switch_core_session_signal_state_change(channel->session);
}
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s Invalid State Change %s -> %s\n", channel->name,
state_names[last_state], state_names[state]);
......@@ -560,6 +562,7 @@ SWITCH_DECLARE(switch_status) switch_channel_hangup(switch_channel *channel)
if (channel->state < CS_HANGUP) {
channel->times.hungup = switch_time_now();
channel->state = CS_HANGUP;
switch_core_session_kill_channel(channel->session, SWITCH_SIG_KILL);
switch_core_session_signal_state_change(channel->session);
}
return channel->state;
......
......@@ -1723,13 +1723,13 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
switch (state) {
case CS_NEW: /* Just created, Waiting for first instructions */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State NEW\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State NEW\n", switch_channel_get_name(session->channel));
break;
case CS_DONE:
continue;
break;
case CS_HANGUP: /* Deactivate and end the thread */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State HANGUP\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State HANGUP\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_hangup ||
(driver_state_handler->on_hangup &&
driver_state_handler->on_hangup(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1768,7 +1768,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
midstate = switch_channel_get_state(session->channel);
break;
case CS_INIT: /* Basic setup tasks */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State INIT\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State INIT\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_init ||
(driver_state_handler->on_init &&
driver_state_handler->on_init(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1804,7 +1804,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
}
break;
case CS_RING: /* Look for a dialplan and find something to do */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State RING\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State RING\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_ring ||
(driver_state_handler->on_ring &&
driver_state_handler->on_ring(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1840,7 +1840,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
}
break;
case CS_EXECUTE: /* Execute an Operation */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State EXECUTE\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State EXECUTE\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_execute ||
(driver_state_handler->on_execute &&
driver_state_handler->on_execute(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1876,7 +1876,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
}
break;
case CS_LOOPBACK: /* loop all data back to source */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State LOOPBACK\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State LOOPBACK\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_loopback ||
(driver_state_handler->on_loopback &&
driver_state_handler->on_loopback(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1912,7 +1912,7 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
}
break;
case CS_TRANSMIT: /* send/recieve data to/from another channel */
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "State TRANSMIT\n");
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "(%s) State TRANSMIT\n", switch_channel_get_name(session->channel));
if (!driver_state_handler->on_transmit ||
(driver_state_handler->on_transmit &&
driver_state_handler->on_transmit(session) == SWITCH_STATUS_SUCCESS &&
......@@ -1957,12 +1957,11 @@ SWITCH_DECLARE(void) switch_core_session_run(switch_core_session *session)
laststate = midstate;
}
if (state < CS_DONE && midstate == switch_channel_get_state(session->channel)) {
switch_thread_cond_wait(session->cond, session->mutex);
}
}
session->thread_running = 0;
}
......@@ -2071,9 +2070,8 @@ static void *SWITCH_THREAD_FUNC switch_core_session_thread(switch_thread *thread
switch_core_hash_insert(runtime.session_table, session->uuid_str, session);
switch_core_session_run(session);
switch_core_hash_delete(runtime.session_table, session->uuid_str);
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Session %ld (%s) Ended\n", id, switch_channel_get_name(session->channel));
switch_core_session_destroy(&session);
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Session %ld Ended\n", id);
return NULL;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论