Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
OpenXG-RAN
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lizhongxiao
OpenXG-RAN
Commits
3ff39449
Commit
3ff39449
authored
Jun 02, 2023
by
Robert Schmidt
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'origin/fix-du-config-update' into integration_2023_w22
parents
ed3c862f
48f82a63
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
44 additions
and
273 deletions
+44
-273
CMakeLists.txt
CMakeLists.txt
+0
-1
doc/Doxyfile
doc/Doxyfile
+0
-2
openair2/F1AP/f1ap_cu_interface_management.c
openair2/F1AP/f1ap_cu_interface_management.c
+0
-1
openair2/F1AP/f1ap_cu_rrc_message_transfer.c
openair2/F1AP/f1ap_cu_rrc_message_transfer.c
+0
-1
openair2/F1AP/f1ap_cu_ue_context_management.c
openair2/F1AP/f1ap_cu_ue_context_management.c
+0
-1
openair2/F1AP/f1ap_decoder.c
openair2/F1AP/f1ap_decoder.c
+0
-205
openair2/F1AP/f1ap_decoder.h
openair2/F1AP/f1ap_decoder.h
+0
-39
openair2/F1AP/f1ap_du_interface_management.c
openair2/F1AP/f1ap_du_interface_management.c
+0
-1
openair2/F1AP/f1ap_du_rrc_message_transfer.c
openair2/F1AP/f1ap_du_rrc_message_transfer.c
+0
-1
openair2/F1AP/f1ap_du_ue_context_management.c
openair2/F1AP/f1ap_du_ue_context_management.c
+0
-1
openair2/F1AP/f1ap_handlers.c
openair2/F1AP/f1ap_handlers.c
+44
-20
No files found.
CMakeLists.txt
View file @
3ff39449
...
...
@@ -470,7 +470,6 @@ add_library(f1ap
${
F1AP_DIR
}
/f1ap_cu_task.c
${
F1AP_DIR
}
/f1ap_cu_ue_context_management.c
${
F1AP_DIR
}
/f1ap_cu_warning_message_transmission.c
${
F1AP_DIR
}
/f1ap_decoder.c
${
F1AP_DIR
}
/f1ap_du_interface_management.c
${
F1AP_DIR
}
/f1ap_du_paging.c
${
F1AP_DIR
}
/f1ap_du_rrc_message_transfer.c
...
...
doc/Doxyfile
View file @
3ff39449
...
...
@@ -1010,12 +1010,10 @@ INPUT = \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_du_rrc_message_transfer.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_default_values.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_du_rrc_message_transfer.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_decoder.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_encoder.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_itti_messaging.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_itti_messaging.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_cu_ue_context_management.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_decoder.h \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_cu_paging.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_du_warning_message_transmission.c \
@CMAKE_CURRENT_SOURCE_DIR@/../openair2/F1AP/f1ap_encoder.h \
...
...
openair2/F1AP/f1ap_cu_interface_management.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_cu_interface_management.h"
...
...
openair2/F1AP/f1ap_cu_rrc_message_transfer.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_cu_rrc_message_transfer.h"
#include "common/ran_context.h"
...
...
openair2/F1AP/f1ap_cu_ue_context_management.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_cu_ue_context_management.h"
#include <string.h>
...
...
openair2/F1AP/f1ap_decoder.c
deleted
100644 → 0
View file @
ed3c862f
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file f1ap_decoder.c
* \brief f1ap pdu decode procedures
* \author EURECOM/NTUST
* \date 2018
* \version 0.1
* \company Eurecom
* \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
* \note
* \warning
*/
#include "f1ap_common.h"
#include "f1ap_decoder.h"
int
asn1_decoder_xer_print
=
0
;
static
int
f1ap_decode_initiating_message
(
F1AP_F1AP_PDU_t
*
pdu
)
{
//MessageDef *message_p;
//MessagesIds message_id;
//asn_encode_to_new_buffer_result_t res = { NULL, {0, NULL, NULL} };
DevAssert
(
pdu
!=
NULL
);
switch
(
pdu
->
choice
.
initiatingMessage
->
procedureCode
)
{
case
F1AP_ProcedureCode_id_F1Setup
:
//res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_F1Setup
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_gNBCUConfigurationUpdate
:
//res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_gNBCUConfigurationUpdate
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_InitialULRRCMessageTransfer
:
//res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_InitialULRRCMessageTransfer
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_DLRRCMessageTransfer
:
//res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_DLRRCMessageTransfer
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_ULRRCMessageTransfer
:
//res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_ULRRCMessageTransfer
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextRelease
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextRelease
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextReleaseRequest
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextReleaseRequest
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextSetup
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextSetup
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextModification
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextModification
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_Paging
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_Paging
\n
"
,
__func__
);
break
;
// case F1AP_ProcedureCode_id_InitialContextSetup:
// res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_F1AP_F1AP_PDU, pdu);
// message_id = F1AP_INITIAL_CONTEXT_SETUP_LOG;
// message_p = itti_alloc_new_message_sized(TASK_F1AP, message_id,
// res.result.encoded + sizeof (IttiMsgText));
// message_p->ittiMsg.f1ap_initial_context_setup_log.size = res.result.encoded;
// memcpy(&message_p->ittiMsg.f1ap_initial_context_setup_log.text, res.buffer, res.result.encoded);
// itti_send_msg_to_task(TASK_UNKNOWN, INSTANCE_DEFAULT, message_p);
// free(res.buffer);
// break;
default:
// F1AP_ERROR("Unknown procedure ID (%d) for initiating message\n",
// (int)pdu->choice.initiatingMessage->procedureCode);
LOG_E
(
F1AP
,
"Unknown procedure ID (%d) for initiating message
\n
"
,
(
int
)
pdu
->
choice
.
initiatingMessage
->
procedureCode
);
AssertFatal
(
0
,
"Unknown procedure ID (%d) for initiating message
\n
"
,
(
int
)
pdu
->
choice
.
initiatingMessage
->
procedureCode
);
return
-
1
;
}
return
0
;
}
static
int
f1ap_decode_successful_outcome
(
F1AP_F1AP_PDU_t
*
pdu
)
{
DevAssert
(
pdu
!=
NULL
);
switch
(
pdu
->
choice
.
successfulOutcome
->
procedureCode
)
{
case
F1AP_ProcedureCode_id_F1Setup
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_F1Setup
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_gNBCUConfigurationUpdate
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_gNBCUConfigurationUpdate
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextRelease
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextRelease
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextSetup
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextSetup
\n
"
,
__func__
);
break
;
case
F1AP_ProcedureCode_id_UEContextModification
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_UEContextModification
\n
"
,
__func__
);
break
;
default:
LOG_E
(
F1AP
,
"Unknown procedure ID (%d) for successfull outcome message
\n
"
,
(
int
)
pdu
->
choice
.
successfulOutcome
->
procedureCode
);
return
-
1
;
}
return
0
;
}
static
int
f1ap_decode_unsuccessful_outcome
(
F1AP_F1AP_PDU_t
*
pdu
)
{
DevAssert
(
pdu
!=
NULL
);
switch
(
pdu
->
choice
.
unsuccessfulOutcome
->
procedureCode
)
{
case
F1AP_ProcedureCode_id_F1Setup
:
LOG_D
(
F1AP
,
"%s(): F1AP_ProcedureCode_id_F1Setup
\n
"
,
__func__
);
break
;
default:
// F1AP_ERROR("Unknown procedure ID (%d) for unsuccessfull outcome message\n",
// (int)pdu->choice.unsuccessfulOutcome->procedureCode);
LOG_E
(
F1AP
,
"Unknown procedure ID (%d) for unsuccessfull outcome message
\n
"
,
(
int
)
pdu
->
choice
.
unsuccessfulOutcome
->
procedureCode
);
return
-
1
;
}
return
0
;
}
int
f1ap_decode_pdu
(
F1AP_F1AP_PDU_t
*
pdu
,
const
uint8_t
*
const
buffer
,
uint32_t
length
)
{
asn_dec_rval_t
dec_ret
;
DevAssert
(
buffer
!=
NULL
);
asn_codec_ctx_t
st
=
{.
max_stack_size
=
100
*
1000
};
dec_ret
=
aper_decode
(
&
st
,
&
asn_DEF_F1AP_F1AP_PDU
,
(
void
**
)
&
pdu
,
buffer
,
length
,
0
,
0
);
if
(
asn1_decoder_xer_print
)
{
LOG_E
(
F1AP
,
"----------------- ASN1 DECODER PRINT START-----------------
\n
"
);
xer_fprint
(
stdout
,
&
asn_DEF_F1AP_F1AP_PDU
,
pdu
);
LOG_E
(
F1AP
,
"----------------- ASN1 DECODER PRINT END -----------------
\n
"
);
}
if
(
dec_ret
.
code
!=
RC_OK
)
{
AssertFatal
(
1
==
0
,
"Failed to decode pdu
\n
"
);
return
-
1
;
}
switch
(
pdu
->
present
)
{
case
F1AP_F1AP_PDU_PR_initiatingMessage
:
return
f1ap_decode_initiating_message
(
pdu
);
case
F1AP_F1AP_PDU_PR_successfulOutcome
:
return
f1ap_decode_successful_outcome
(
pdu
);
case
F1AP_F1AP_PDU_PR_unsuccessfulOutcome
:
return
f1ap_decode_unsuccessful_outcome
(
pdu
);
default:
LOG_E
(
F1AP
,
"Unknown presence (%d) or not implemented
\n
"
,
(
int
)
pdu
->
present
);
break
;
}
return
-
1
;
}
openair2/F1AP/f1ap_decoder.h
deleted
100644 → 0
View file @
ed3c862f
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/*! \file f1ap_decoder.h
* \brief f1ap pdu decode procedures
* \author EURECOM/NTUST
* \date 2018
* \version 0.1
* \company Eurecom
* \email: navid.nikaein@eurecom.fr, bing-kai.hong@eurecom.fr
* \note
* \warning
*/
#ifndef F1AP_ENB_ENCODER_H_
#define F1AP_ENB_ENCODER_H_
int
f1ap_decode_pdu
(
F1AP_F1AP_PDU_t
*
pdu
,
const
uint8_t
*
const
buffer
,
uint32_t
length
)
__attribute__
((
warn_unused_result
));
#endif
/* F1AP_ENB_ENCODER_H_ */
openair2/F1AP/f1ap_du_interface_management.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_du_interface_management.h"
#include "assertions.h"
...
...
openair2/F1AP/f1ap_du_rrc_message_transfer.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_du_rrc_message_transfer.h"
...
...
openair2/F1AP/f1ap_du_ue_context_management.c
View file @
3ff39449
...
...
@@ -32,7 +32,6 @@
#include "f1ap_common.h"
#include "f1ap_encoder.h"
#include "f1ap_decoder.h"
#include "f1ap_itti_messaging.h"
#include "f1ap_du_ue_context_management.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.h"
...
...
openair2/F1AP/f1ap_handlers.c
View file @
3ff39449
...
...
@@ -31,7 +31,6 @@
*/
#include "f1ap_common.h"
#include "f1ap_decoder.h"
#include "f1ap_cu_interface_management.h"
#include "f1ap_du_interface_management.h"
#include "f1ap_cu_rrc_message_transfer.h"
...
...
@@ -81,41 +80,66 @@ const char *f1ap_direction2String(int f1ap_dir) {
return
(
f1ap_direction_String
[
f1ap_dir
]);
}
int
f1ap_handle_message
(
instance_t
instance
,
uint32_t
assoc_id
,
int32_t
stream
,
const
uint8_t
*
const
data
,
const
uint32_t
data_length
)
{
F1AP_F1AP_PDU_t
pdu
=
{
0
};
int
ret
;
static
F1AP_F1AP_PDU_t
*
f1ap_decode_pdu
(
const
uint8_t
*
const
buffer
,
uint32_t
length
)
{
DevAssert
(
buffer
!=
NULL
);
asn_codec_ctx_t
st
=
{.
max_stack_size
=
100
*
1000
};
F1AP_F1AP_PDU_t
*
pdu
=
NULL
;
asn_dec_rval_t
dec_ret
=
aper_decode
(
&
st
,
&
asn_DEF_F1AP_F1AP_PDU
,
(
void
**
)
&
pdu
,
buffer
,
length
,
0
,
0
);
if
(
LOG_DEBUGFLAG
(
DEBUG_ASN1
))
{
LOG_E
(
F1AP
,
"----------------- ASN1 DECODER PRINT START-----------------
\n
"
);
xer_fprint
(
stdout
,
&
asn_DEF_F1AP_F1AP_PDU
,
pdu
);
LOG_E
(
F1AP
,
"----------------- ASN1 DECODER PRINT END -----------------
\n
"
);
}
return
dec_ret
.
code
==
RC_OK
?
pdu
:
NULL
;
}
int
f1ap_handle_message
(
instance_t
instance
,
uint32_t
assoc_id
,
int32_t
stream
,
const
uint8_t
*
const
data
,
const
uint32_t
data_length
)
{
DevAssert
(
data
!=
NULL
);
if
(
f1ap_decode_pdu
(
&
pdu
,
data
,
data_length
)
<
0
)
{
F1AP_F1AP_PDU_t
*
pdu
=
f1ap_decode_pdu
(
data
,
data_length
);
if
(
pdu
==
NULL
)
{
LOG_E
(
F1AP
,
"Failed to decode PDU
\n
"
);
return
-
1
;
}
/* Checking procedure Code and direction of message */
if
(
pdu
.
choice
.
initiatingMessage
->
procedureCode
>=
sizeof
(
f1ap_messages_processing
)
/
(
3
*
sizeof
(
f1ap_message_processing_t
))
||
(
pdu
.
present
>
F1AP_F1AP_PDU_PR_unsuccessfulOutcome
))
{
LOG_E
(
F1AP
,
"[SCTP %d] Either procedureCode %ld or direction %d exceed expected
\n
"
,
assoc_id
,
pdu
.
choice
.
initiatingMessage
->
procedureCode
,
pdu
.
present
);
ASN_STRUCT_FREE_CONTENTS_ONLY
(
asn_DEF_F1AP_F1AP_PDU
,
&
pdu
);
if
(
pdu
->
choice
.
initiatingMessage
->
procedureCode
>=
sizeof
(
f1ap_messages_processing
)
/
(
3
*
sizeof
(
f1ap_message_processing_t
))
||
(
pdu
->
present
>
F1AP_F1AP_PDU_PR_unsuccessfulOutcome
))
{
LOG_E
(
F1AP
,
"[SCTP %d] Either procedureCode %ld or direction %d exceed expected
\n
"
,
assoc_id
,
pdu
->
choice
.
initiatingMessage
->
procedureCode
,
pdu
->
present
);
ASN_STRUCT_FREE
(
asn_DEF_F1AP_F1AP_PDU
,
pdu
);
return
-
1
;
}
if
(
f1ap_messages_processing
[
pdu
.
choice
.
initiatingMessage
->
procedureCode
][
pdu
.
present
-
1
]
==
NULL
)
{
int
ret
;
if
(
f1ap_messages_processing
[
pdu
->
choice
.
initiatingMessage
->
procedureCode
][
pdu
->
present
-
1
]
==
NULL
)
{
// No handler present. This can mean not implemented or no procedure for eNB (wrong direction).
LOG_E
(
F1AP
,
"[SCTP %d] No handler for procedureCode %ld in %s
\n
"
,
assoc_id
,
pdu
.
choice
.
initiatingMessage
->
procedureCode
,
f1ap_direction2String
(
pdu
.
present
-
1
));
LOG_E
(
F1AP
,
"[SCTP %d] No handler for procedureCode %ld in %s
\n
"
,
assoc_id
,
pdu
->
choice
.
initiatingMessage
->
procedureCode
,
f1ap_direction2String
(
pdu
->
present
-
1
));
ret
=-
1
;
}
else
{
/* Calling the right handler */
LOG_D
(
F1AP
,
"Calling handler with instance %ld
\n
"
,
instance
);
ret
=
(
*
f1ap_messages_processing
[
pdu
.
choice
.
initiatingMessage
->
procedureCode
][
pdu
.
present
-
1
])
(
instance
,
assoc_id
,
stream
,
&
pdu
);
ret
=
(
*
f1ap_messages_processing
[
pdu
->
choice
.
initiatingMessage
->
procedureCode
][
pdu
->
present
-
1
])(
instance
,
assoc_id
,
stream
,
pdu
);
}
ASN_STRUCT_FREE
_CONTENTS_ONLY
(
asn_DEF_F1AP_F1AP_PDU
,
&
pdu
);
ASN_STRUCT_FREE
(
asn_DEF_F1AP_F1AP_PDU
,
pdu
);
return
ret
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment