Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch
Commits
fea30084
提交
fea30084
authored
8月 13, 2010
作者:
Marc Olivier Chouinard
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'master' of
ssh://git.freeswitch.org/freeswitch
上级
3eafca60
45c6c4d3
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
312 行增加
和
75 行删除
+312
-75
freeswitch.spec
freeswitch.spec
+3
-1
ftdm_io.c
libs/freetdm/src/ftdm_io.c
+2
-5
mod_nibblebill.c
src/mod/applications/mod_nibblebill/mod_nibblebill.c
+0
-2
mod_sangoma_codec.c
src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c
+51
-37
mod_skypopen.c
src/mod/endpoints/mod_skypopen/mod_skypopen.c
+176
-17
skypopen.h
src/mod/endpoints/mod_skypopen/skypopen.h
+10
-1
skypopen_protocol.c
src/mod/endpoints/mod_skypopen/skypopen_protocol.c
+70
-12
没有找到文件。
freeswitch.spec
浏览文件 @
fea30084
...
...
@@ -320,7 +320,7 @@ export QA_RPATHS=$[ 0x0001|0x0002 ]
# Application Modules
#
###############################################################################################################################
APPLICATION_MODULES_AE="applications/mod_avmd applications/mod_commands applications/mod_conference applications/mod_db applications/mod_directory applications/mod_distributor applications/mod_dptools applications/mod_easyroute applications/mod_enum applications/mod_esf applications/mod_expr"
APPLICATION_MODULES_AE="applications/mod_avmd applications/mod_commands applications/mod_conference applications/mod_db applications/mod_directory applications/mod_distributor applications/mod_dptools applications/mod_easyroute applications/mod_enum applications/mod_esf applications/mod_expr
applications/mod_callcenter
"
APPLICATION_MODULES_FM="applications/mod_fifo applications/mod_fsv applications/mod_hash applications/mod_lcr applications/mod_limit applications/mod_memcache"
...
...
@@ -595,6 +595,7 @@ fi
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/mime.types
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/acl.conf.xml
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/alsa.conf.xml
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/callcenter.conf.xml
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_csv.conf.xml
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_pg_csv.conf.xml
%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cidlookup.conf.xml
...
...
@@ -741,6 +742,7 @@ fi
%{prefix}/mod/mod_event_multicast.so*
%{prefix}/mod/mod_event_socket.so*
%{prefix}/mod/mod_expr.so*
%{prefix}/mod/mod_callcenter.so*
%{prefix}/mod/mod_fifo.so*
%{prefix}/mod/mod_file_string.so*
%{prefix}/mod/mod_flite.so*
...
...
libs/freetdm/src/ftdm_io.c
浏览文件 @
fea30084
...
...
@@ -2261,11 +2261,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan)
return
FTDM_FAIL
;
}
if
(
!
ftdm_test_flag
(
check
,
FTDM_CHANNEL_INUSE
))
{
ftdm_log
(
FTDM_LOG_WARNING
,
"Called ftdm_channel_close but never ftdm_channel_open in chan %d:%d??
\n
"
,
check
->
span_id
,
check
->
chan_id
);
return
FTDM_FAIL
;
}
if
(
ftdm_test_flag
(
check
,
FTDM_CHANNEL_CONFIGURED
))
{
ftdm_mutex_lock
(
check
->
mutex
);
if
(
ftdm_test_flag
(
check
,
FTDM_CHANNEL_OPEN
))
{
...
...
@@ -2275,6 +2270,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_close(ftdm_channel_t **ftdmchan)
ftdm_channel_reset
(
check
);
*
ftdmchan
=
NULL
;
}
}
else
{
ftdm_log_chan_msg
(
check
,
FTDM_LOG_WARNING
,
"Called ftdm_channel_close but never ftdm_channel_open??
\n
"
);
}
check
->
ring_count
=
0
;
ftdm_mutex_unlock
(
check
->
mutex
);
...
...
src/mod/applications/mod_nibblebill/mod_nibblebill.c
浏览文件 @
fea30084
...
...
@@ -483,8 +483,6 @@ static switch_status_t do_billing(switch_core_session_t *session)
/* Setup new billing data (based on call answer time, in case this module started late with active calls) */
nibble_data
->
lastts
=
profile
->
times
->
answered
;
/* Set the initial answer time to match when the call was really answered */
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_INFO
,
"Beginning new billing on %s
\n
"
,
uuid
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Last successful billing time was %s
\n
"
,
date
);
}
switch_time_exp_lt
(
&
tm
,
nibble_data
->
lastts
);
...
...
src/mod/codecs/mod_sangoma_codec/mod_sangoma_codec.c
浏览文件 @
fea30084
...
...
@@ -126,6 +126,9 @@ struct codec_data {
long
lastrxseqno
;
unsigned
long
rxlost
;
/* discarded silence packets */
unsigned
long
rxdiscarded
;
/* avg Rx time */
switch_time_t
avgrxus
;
switch_time_t
last_rx_time
;
...
...
@@ -400,26 +403,31 @@ static switch_status_t switch_sangoma_encode(switch_codec_t *codec, switch_codec
sess
->
encoder
.
tx
++
;
/* do the reading */
memset
(
&
encoded_frame
,
0
,
sizeof
(
encoded_frame
));
sres
=
switch_rtp_zerocopy_read_frame
(
sess
->
encoder
.
rxrtp
,
&
encoded_frame
,
SWITCH_IO_FLAG_NOBLOCK
);
if
(
sres
==
SWITCH_STATUS_GENERR
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to read on Sangoma encoder RTP session: %d
\n
"
,
sres
);
return
SWITCH_STATUS_FALSE
;
}
for
(
;
;
)
{
sres
=
switch_rtp_zerocopy_read_frame
(
sess
->
encoder
.
rxrtp
,
&
encoded_frame
,
SWITCH_IO_FLAG_NOBLOCK
);
if
(
sres
==
SWITCH_STATUS_GENERR
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to read on Sangoma encoder RTP session: %d
\n
"
,
sres
);
return
SWITCH_STATUS_FALSE
;
}
if
(
0
==
encoded_frame
.
datalen
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"No output on Sangoma encoder RTP session.
\n
"
);
return
SWITCH_STATUS_SUCCESS
;
}
if
(
0
==
encoded_frame
.
datalen
)
{
break
;
}
if
(
encoded_frame
.
payload
!=
codec
->
implementation
->
ianacode
&&
encoded_frame
.
payload
!=
IANACODE_CN
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Read unexpected payload %d in Sangoma encoder RTP session, expecting %d
\n
"
,
encoded_frame
.
payload
,
codec
->
implementation
->
ianacode
);
return
SWITCH_STATUS_FALSE
;
if
(
encoded_frame
.
payload
!=
codec
->
implementation
->
ianacode
&&
encoded_frame
.
payload
!=
IANACODE_CN
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Read unexpected payload %d in Sangoma encoder RTP session, expecting %d
\n
"
,
encoded_frame
.
payload
,
codec
->
implementation
->
ianacode
);
break
;
}
if
(
*
encoded_data_len
)
{
sess
->
encoder
.
rxdiscarded
++
;
}
memcpy
(
encoded_data
,
encoded_frame
.
data
,
encoded_frame
.
datalen
);
*
encoded_data_len
=
encoded_frame
.
datalen
;
}
memcpy
(
encoded_data
,
encoded_frame
.
data
,
encoded_frame
.
datalen
);
*
encoded_data_len
=
encoded_frame
.
datalen
;
/* update encoding stats */
sess
->
encoder
.
rx
++
;
...
...
@@ -513,30 +521,34 @@ static switch_status_t switch_sangoma_decode(switch_codec_t *codec, /* codec ses
sess
->
decoder
.
tx
++
;
/* do the reading */
memset
(
&
ulaw_frame
,
0
,
sizeof
(
ulaw_frame
));
sres
=
switch_rtp_zerocopy_read_frame
(
sess
->
decoder
.
rxrtp
,
&
ulaw_frame
,
SWITCH_IO_FLAG_NOBLOCK
);
if
(
sres
==
SWITCH_STATUS_GENERR
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to read on Sangoma decoder RTP session: %d
\n
"
,
sres
);
return
SWITCH_STATUS_FALSE
;
}
for
(
;
;
)
{
sres
=
switch_rtp_zerocopy_read_frame
(
sess
->
decoder
.
rxrtp
,
&
ulaw_frame
,
SWITCH_IO_FLAG_NOBLOCK
);
if
(
sres
==
SWITCH_STATUS_GENERR
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to read on Sangoma decoder RTP session: %d
\n
"
,
sres
);
return
SWITCH_STATUS_FALSE
;
}
if
(
0
==
ulaw_frame
.
datalen
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"No output on Sangoma decoder RTP session.
\n
"
);
return
SWITCH_STATUS_SUCCESS
;
}
if
(
0
==
ulaw_frame
.
datalen
)
{
break
;
}
if
(
ulaw_frame
.
payload
!=
IANA_ULAW
&&
ulaw_frame
.
payload
!=
IANACODE_CN
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Read unexpected payload %d in Sangoma decoder RTP session, expecting %d
\n
"
,
ulaw_frame
.
payload
,
IANA_ULAW
);
return
SWITCH_STATUS_FALSE
;
}
if
(
ulaw_frame
.
payload
!=
IANA_ULAW
&&
ulaw_frame
.
payload
!=
IANACODE_CN
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Read unexpected payload %d in Sangoma decoder RTP session, expecting %d
\n
"
,
ulaw_frame
.
payload
,
IANA_ULAW
);
break
;
}
/* transcode to linear */
for
(
i
=
0
;
i
<
ulaw_frame
.
datalen
;
i
++
)
{
dbuf_linear
[
i
]
=
ulaw_to_linear
(((
char
*
)
ulaw_frame
.
data
)[
i
]);
if
(
*
decoded_data_len
)
{
sess
->
decoder
.
rxdiscarded
++
;
}
/* transcode to linear */
for
(
i
=
0
;
i
<
ulaw_frame
.
datalen
;
i
++
)
{
dbuf_linear
[
i
]
=
ulaw_to_linear
(((
char
*
)
ulaw_frame
.
data
)[
i
]);
}
*
decoded_data_len
=
i
*
2
;
}
*
decoded_data_len
=
i
*
2
;
/* update decoding stats */
sess
->
decoder
.
rx
++
;
...
...
@@ -708,6 +720,7 @@ SWITCH_STANDARD_API(sangoma_function)
if
(
sess
->
encoder
.
rxrtp
)
{
stats
=
switch_rtp_get_stats
(
sess
->
encoder
.
rxrtp
,
NULL
);
stream
->
write_function
(
stream
,
"-- Encoder Inbound Stats --
\n
"
);
stream
->
write_function
(
stream
,
"Rx Discarded: %lu
\n
"
,
sess
->
encoder
.
rxdiscarded
);
sangoma_print_stats
(
stream
,
&
stats
->
inbound
);
...
...
@@ -719,6 +732,7 @@ SWITCH_STANDARD_API(sangoma_function)
if
(
sess
->
decoder
.
rxrtp
)
{
stats
=
switch_rtp_get_stats
(
sess
->
decoder
.
rxrtp
,
NULL
);
stream
->
write_function
(
stream
,
"-- Decoder Inbound Stats --
\n
"
);
stream
->
write_function
(
stream
,
"Rx Discarded: %lu
\n
"
,
sess
->
decoder
.
rxdiscarded
);
sangoma_print_stats
(
stream
,
&
stats
->
inbound
);
stats
=
switch_rtp_get_stats
(
sess
->
decoder
.
txrtp
,
NULL
);
...
...
src/mod/endpoints/mod_skypopen/mod_skypopen.c
浏览文件 @
fea30084
差异被折叠。
点击展开。
src/mod/endpoints/mod_skypopen/skypopen.h
浏览文件 @
fea30084
...
...
@@ -264,6 +264,7 @@ struct private_object {
char
skype_password
[
256
];
char
destination
[
256
];
struct
timeval
answer_time
;
struct
timeval
ring_time
;
struct
timeval
transfer_time
;
char
transfer_callid_number
[
50
];
...
...
@@ -288,6 +289,10 @@ struct private_object {
int
silent_mode
;
int
write_silence_when_idle
;
int
setsockopt
;
char
answer_id
[
256
];
char
answer_value
[
256
];
char
ring_id
[
256
];
char
ring_value
[
256
];
};
...
...
@@ -328,7 +333,8 @@ int skypopen_close_socket(unsigned int fd);
private_t
*
find_available_skypopen_interface_rr
(
private_t
*
tech_pvt_calling
);
int
remote_party_is_ringing
(
private_t
*
tech_pvt
);
int
remote_party_is_early_media
(
private_t
*
tech_pvt
);
int
skypopen_answer
(
private_t
*
tech_pvt
,
char
*
id
,
char
*
value
);
//int skypopen_answer(private_t * tech_pvt, char *id, char *value);
int
skypopen_answer
(
private_t
*
tech_pvt
);
int
skypopen_transfer
(
private_t
*
tech_pvt
,
char
*
id
,
char
*
value
);
#ifndef WIN32
int
skypopen_socket_create_and_bind
(
private_t
*
tech_pvt
,
int
*
which_port
);
...
...
@@ -337,3 +343,6 @@ int skypopen_socket_create_and_bind(private_t * tech_pvt, unsigned short *which_
#endif //WIN32
int
incoming_chatmessage
(
private_t
*
tech_pvt
,
int
which
);
int
next_port
(
void
);
int
skypopen_partner_handle_ring
(
private_t
*
tech_pvt
);
int
skypopen_answered
(
private_t
*
tech_pvt
);
int
inbound_channel_answered
(
private_t
*
tech_pvt
);
src/mod/endpoints/mod_skypopen/skypopen_protocol.c
浏览文件 @
fea30084
...
...
@@ -214,7 +214,7 @@ int skypopen_signaling_read(private_t * tech_pvt)
ERRORA
(
"Skype got ERROR: |||%s|||, another call is active on this interface
\n\n\n
"
,
SKYPOPEN_P_LOG
,
message
);
tech_pvt
->
interface_state
=
SKYPOPEN_STATE_ERROR_DOUBLE_CALL
;
}
else
if
(
!
strncasecmp
(
message
,
"ERROR 592 ALTER CALL"
,
19
))
{
ERROR
A
(
"Skype got ERROR about TRANSFERRING, no problem: |||%s|||
\n
"
,
SKYPOPEN_P_LOG
,
message
);
NOTIC
A
(
"Skype got ERROR about TRANSFERRING, no problem: |||%s|||
\n
"
,
SKYPOPEN_P_LOG
,
message
);
}
else
if
(
!
strncasecmp
(
message
,
"ERROR 559 CALL"
,
13
))
{
if
(
tech_pvt
->
interface_state
==
SKYPOPEN_STATE_PREANSWER
)
{
DEBUGA_SKYPE
(
"Skype got ERROR about a failed action (probably TRYING to ANSWER A CALL), let's go down: |||%s|||
\n
"
,
SKYPOPEN_P_LOG
,
...
...
@@ -478,8 +478,12 @@ int skypopen_signaling_read(private_t * tech_pvt)
if
(
!
strcasecmp
(
prop
,
"PARTNER_HANDLE"
))
{
if
(
tech_pvt
->
interface_state
!=
SKYPOPEN_STATE_SELECTED
&&
(
!
strlen
(
tech_pvt
->
skype_call_id
)
||
!
strlen
(
tech_pvt
->
session_uuid_str
)))
{
/* we are NOT inside an active call */
DEBUGA_SKYPE
(
"Call %s TRY ANSWER
\n
"
,
SKYPOPEN_P_LOG
,
id
);
skypopen_answer
(
tech_pvt
,
id
,
value
);
DEBUGA_SKYPE
(
"Call %s go to skypopen_partner_handle_ring
\n
"
,
SKYPOPEN_P_LOG
,
id
);
skypopen_strncpy
(
tech_pvt
->
ring_id
,
id
,
sizeof
(
tech_pvt
->
ring_id
));
skypopen_strncpy
(
tech_pvt
->
ring_value
,
value
,
sizeof
(
tech_pvt
->
ring_value
));
skypopen_strncpy
(
tech_pvt
->
answer_id
,
id
,
sizeof
(
tech_pvt
->
answer_id
));
skypopen_strncpy
(
tech_pvt
->
answer_value
,
value
,
sizeof
(
tech_pvt
->
answer_value
));
skypopen_partner_handle_ring
(
tech_pvt
);
}
else
{
/* we are inside an active call */
if
(
!
strcasecmp
(
tech_pvt
->
skype_call_id
,
id
))
{
...
...
@@ -553,7 +557,10 @@ int skypopen_signaling_read(private_t * tech_pvt)
&&
(
!
strlen
(
tech_pvt
->
skype_call_id
)
||
!
strlen
(
tech_pvt
->
session_uuid_str
)))
{
/* we are NOT inside an active call */
DEBUGA_SKYPE
(
"NO ACTIVE calls in this moment, skype_call %s is RINGING, to ask PARTNER_HANDLE
\n
"
,
SKYPOPEN_P_LOG
,
id
);
DEBUGA_SKYPE
(
"NO ACTIVE calls in this moment, skype_call %s is RINGING, to ask PARTNER_DISPNAME and PARTNER_HANDLE
\n
"
,
SKYPOPEN_P_LOG
,
id
);
sprintf
(
msg_to_skype
,
"GET CALL %s PARTNER_DISPNAME"
,
id
);
skypopen_signaling_write
(
tech_pvt
,
msg_to_skype
);
skypopen_sleep
(
100
);
sprintf
(
msg_to_skype
,
"GET CALL %s PARTNER_HANDLE"
,
id
);
skypopen_signaling_write
(
tech_pvt
,
msg_to_skype
);
skypopen_sleep
(
10000
);
...
...
@@ -684,14 +691,7 @@ int skypopen_signaling_read(private_t * tech_pvt)
skypopen_signaling_write
(
tech_pvt
,
msg_to_skype
);
}
tech_pvt
->
skype_callflow
=
CALLFLOW_STATUS_INPROGRESS
;
if
(
!
strlen
(
tech_pvt
->
session_uuid_str
))
{
DEBUGA_SKYPE
(
"New Inbound Channel!
\n\n\n\n
"
,
SKYPOPEN_P_LOG
);
new_inbound_channel
(
tech_pvt
);
}
else
{
tech_pvt
->
interface_state
=
SKYPOPEN_STATE_UP
;
DEBUGA_SKYPE
(
"Outbound Channel Answered! session_uuid_str=%s
\n
"
,
SKYPOPEN_P_LOG
,
tech_pvt
->
session_uuid_str
);
outbound_channel_answered
(
tech_pvt
);
}
skypopen_answered
(
tech_pvt
);
}
else
{
DEBUGA_SKYPE
(
"I'm on %s, skype_call %s is NOT MY call, ignoring
\n
"
,
SKYPOPEN_P_LOG
,
tech_pvt
->
skype_call_id
,
id
);
}
...
...
@@ -1752,3 +1752,61 @@ while(XPending(disp)){
}
#endif // WIN32
int
inbound_channel_answered
(
private_t
*
tech_pvt
)
{
int
res
=
0
;
switch_core_session_t
*
session
=
NULL
;
switch_channel_t
*
channel
=
NULL
;
session
=
switch_core_session_locate
(
tech_pvt
->
session_uuid_str
);
if
(
session
)
{
channel
=
switch_core_session_get_channel
(
session
);
if
(
channel
)
{
switch_set_flag
(
tech_pvt
,
TFLAG_IO
);
}
else
{
ERRORA
(
"no channel
\n
"
,
SKYPOPEN_P_LOG
);
}
switch_core_session_rwunlock
(
session
);
}
else
{
ERRORA
(
"no session
\n
"
,
SKYPOPEN_P_LOG
);
}
return
res
;
}
int
skypopen_answered
(
private_t
*
tech_pvt
)
{
int
res
=
0
;
switch_core_session_t
*
session
=
NULL
;
switch_channel_t
*
channel
=
NULL
;
//WARNINGA("ANSWERED tech_pvt->skype_call_id=%s, tech_pvt->skype_callflow=%d, tech_pvt->interface_state=%d, tech_pvt->skype_user=%s, tech_pvt->callid_number=%s, tech_pvt->ring_value=%s, tech_pvt->ring_id=%s, tech_pvt->answer_value=%s, tech_pvt->answer_id=%s\n", SKYPOPEN_P_LOG, tech_pvt->skype_call_id, tech_pvt->skype_callflow, tech_pvt->interface_state, tech_pvt->skype_user, tech_pvt->callid_number, tech_pvt->ring_value, tech_pvt->ring_id, tech_pvt->answer_value, tech_pvt->answer_id);
session
=
switch_core_session_locate
(
tech_pvt
->
session_uuid_str
);
if
(
session
)
{
channel
=
switch_core_session_get_channel
(
session
);
if
(
channel
)
{
if
(
switch_channel_direction
(
channel
)
==
SWITCH_CALL_DIRECTION_OUTBOUND
)
{
tech_pvt
->
interface_state
=
SKYPOPEN_STATE_UP
;
DEBUGA_SKYPE
(
"Outbound Channel Answered! session_uuid_str=%s
\n
"
,
SKYPOPEN_P_LOG
,
tech_pvt
->
session_uuid_str
);
outbound_channel_answered
(
tech_pvt
);
}
else
{
DEBUGA_SKYPE
(
"answered Inbound Channel!
\n\n\n\n
"
,
SKYPOPEN_P_LOG
);
inbound_channel_answered
(
tech_pvt
);
}
}
else
{
ERRORA
(
"no channel
\n
"
,
SKYPOPEN_P_LOG
);
}
switch_core_session_rwunlock
(
session
);
}
else
{
ERRORA
(
"no session
\n
"
,
SKYPOPEN_P_LOG
);
}
return
res
;
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论