Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 1 | % This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | % License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | % file, You can obtain one at https://mozilla.org/MPL/2.0/. |
| 4 | % (C) 2019 Andrey Velikiy <agreat22@gmail.com> |
Pau Espin Pedrol | 9487d92 | 2024-01-22 14:38:31 +0100 | [diff] [blame] | 5 | % (C) 2019 Fairwaves (edited) |
Harald Welte | b470dfa | 2019-12-01 16:28:48 +0100 | [diff] [blame] | 6 | % (C) 2019 Harald Welte <laforge@gnumonks.org> |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 7 | |
| 8 | -ifndef(GSUP_PROTOCOL). |
| 9 | -define(GSUP_PROTOCOL, true). |
| 10 | |
Pau Espin Pedrol | 1391d07 | 2024-01-24 17:38:35 +0100 | [diff] [blame] | 11 | % TS 24.008 10.5.5.14, GMM Cause used in GSUP: |
| 12 | -define(GSUP_CAUSE_IMSI_UNKNOWN, 16#02). |
| 13 | -define(GSUP_CAUSE_ILLEGAL_MS, 16#03). |
| 14 | -define(GSUP_CAUSE_GPRS_NOTALLOWED, 16#07). |
| 15 | -define(GSUP_CAUSE_PLMN_NOTALLOWED, 16#0b). |
| 16 | -define(GSUP_CAUSE_LA_NOTALLOWED, 16#0c). |
| 17 | -define(GSUP_CAUSE_ROAMING_NOTALLOWED, 16#0d). |
| 18 | -define(GSUP_CAUSE_NO_SUIT_CELL_IN_LA, 16#0f). |
| 19 | -define(GSUP_CAUSE_NET_FAIL, 16#11). |
| 20 | -define(GSUP_CAUSE_CONGESTION, 16#16). |
| 21 | -define(GSUP_CAUSE_GSM_AUTH_UNACCEPT, 16#17). |
| 22 | -define(GSUP_CAUSE_INV_MAND_INFO, 16#60). |
| 23 | -define(GSUP_CAUSE_PROTO_ERR_UNSPEC, 16#6f). |
| 24 | |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 25 | -type 'GSUPMessageType'() :: location_upd_req |
| 26 | | location_upd_err |
| 27 | | location_upd_res |
| 28 | | send_auth_info_req |
| 29 | | send_auth_info_err |
| 30 | | send_auth_info_res |
| 31 | | auth_failure_report |
| 32 | | purge_ms_req |
| 33 | | purge_ms_err |
| 34 | | purge_ms_res |
| 35 | | insert_sub_data_req |
| 36 | | insert_sub_data_err |
| 37 | | insert_sub_data_res |
| 38 | | delete_sub_data_req |
| 39 | | delete_sub_data_err |
| 40 | | delete_sub_data_res |
| 41 | | location_cancellation_req |
| 42 | | location_cancellation_err |
| 43 | | location_cancellation_res |
| 44 | | ss_req |
| 45 | | ss_err |
| 46 | | ss_res |
Andrey Velikiy | fde0fd9 | 2019-08-02 09:42:22 +0300 | [diff] [blame] | 47 | | mo_forward_sm_req |
| 48 | | mo_forward_sm_err |
| 49 | | mo_forward_sm_res |
| 50 | | mt_forward_sm_req |
| 51 | | mt_forward_sm_err |
| 52 | | mt_forward_sm_res |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 53 | | ready_for_sm_req |
| 54 | | ready_for_sm_err |
| 55 | | ready_for_sm_res |
| 56 | | check_imei_req |
| 57 | | check_imei_err |
Andrey Velikiy | bb29966 | 2019-06-19 14:19:10 +0300 | [diff] [blame] | 58 | | check_imei_res |
| 59 | | e_prepare_handover_req |
| 60 | | e_prepare_handover_err |
| 61 | | e_prepare_handover_res |
| 62 | | e_prepare_subseq_handover_req |
| 63 | | e_prepare_subseq_handover_err |
| 64 | | e_prepare_subseq_handover_res |
| 65 | | e_send_end_signal_req |
| 66 | | e_send_end_signal_err |
| 67 | | e_send_end_signal_res |
| 68 | | e_process_access_signalling_req |
| 69 | | e_forward_access_signalling_req |
| 70 | | e_close |
| 71 | | e_abort |
Alexander Couzens | daa4d88 | 2023-07-18 20:18:45 +0200 | [diff] [blame] | 72 | | e_routing_err |
| 73 | | epdg_tunnel_request |
| 74 | | epdg_tunnel_error |
| 75 | | epdg_tunnel_result. |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 76 | |
Harald Welte | b76c7fa | 2019-08-22 13:48:00 +0200 | [diff] [blame] | 77 | -type 'GSUPRatType'() :: rat_unknown | rat_geran_a | rat_utran_iu | rat_eutran_sgs. |
| 78 | |
Harald Welte | 0ba5533 | 2019-08-12 18:53:08 +0200 | [diff] [blame] | 79 | -type 'GSUPAuthTuple'() :: #{ |
| 80 | rand := binary(), |
| 81 | sres := binary(), |
| 82 | kc := binary(), |
| 83 | ik => binary(), |
| 84 | ck => binary(), |
| 85 | autn => binary(), |
| 86 | res => binary() |
| 87 | }. |
| 88 | |
Pau Espin Pedrol | 058a993 | 2024-01-22 14:15:43 +0100 | [diff] [blame] | 89 | -type 'GSUPPdpAddress'() :: #{ |
| 90 | pdp_type_org => integer(), |
| 91 | pdp_type_nr => integer(), |
| 92 | address => binary() |
| 93 | }. |
| 94 | |
Harald Welte | c92ebfa | 2019-08-13 07:37:20 +0200 | [diff] [blame] | 95 | -type 'GSUPPdpInfo'() :: #{ |
| 96 | pdp_context_id => integer(), |
Pau Espin Pedrol | 058a993 | 2024-01-22 14:15:43 +0100 | [diff] [blame] | 97 | pdp_address => 'GSUPPdpAddress'(), |
Pau Espin Pedrol | 1a064fa | 2024-01-22 17:41:30 +0100 | [diff] [blame] | 98 | access_point_name => string(), |
Harald Welte | c92ebfa | 2019-08-13 07:37:20 +0200 | [diff] [blame] | 99 | quality_of_service => binary(), |
| 100 | pdp_charging => integer() |
| 101 | }. |
| 102 | |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 103 | -type 'GSUPMessage'() :: #{ |
| 104 | message_type := 'GSUPMessageType'(), |
| 105 | imsi := binary(), |
| 106 | cause => integer(), |
Harald Welte | 0ba5533 | 2019-08-12 18:53:08 +0200 | [diff] [blame] | 107 | auth_tuples => ['GSUPAuthTuple'()], |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 108 | pdp_info_complete => true, |
Harald Welte | c92ebfa | 2019-08-13 07:37:20 +0200 | [diff] [blame] | 109 | pdp_info_list => ['GSUPPdpInfo'()], |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 110 | cancellation_type => integer(), |
Pau Espin Pedrol | 9487d92 | 2024-01-22 14:38:31 +0100 | [diff] [blame] | 111 | freeze_p_tmsi => true, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 112 | msisdn => binary(), |
| 113 | hlr_number => binary(), |
| 114 | pdp_context_id => [integer()], |
| 115 | pdp_charging => integer(), |
Pau Espin Pedrol | 25ace76 | 2024-02-20 19:55:06 +0100 | [diff] [blame] | 116 | pco => binary(), |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 117 | rand => binary(), |
| 118 | auts => binary(), |
| 119 | cn_domain => integer(), |
Harald Welte | bb32d46 | 2019-12-01 16:20:50 +0100 | [diff] [blame] | 120 | supported_rat_types => ['GSUPRatType'()], |
Harald Welte | b470dfa | 2019-12-01 16:28:48 +0100 | [diff] [blame] | 121 | current_rat_type => 'GSUPRatType'(), |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 122 | session_id => integer(), |
| 123 | session_state => integer(), |
| 124 | ss_info => binary(), |
| 125 | sm_rp_mr => integer(), |
| 126 | sm_rp_da => binary(), |
| 127 | sm_rp_oa => binary(), |
| 128 | sm_rp_ui => binary(), |
| 129 | sm_rp_cause => integer(), |
| 130 | sm_rp_mms => integer(), |
| 131 | sm_alert_reason => integer(), |
| 132 | imei => binary(), |
Andrey Velikiy | c66c8d0 | 2019-06-19 14:16:52 +0300 | [diff] [blame] | 133 | imei_check_result => integer(), |
Alexander Couzens | b656fc1 | 2019-12-27 05:04:40 +0100 | [diff] [blame] | 134 | num_vectors_req => integer(), |
Andrey Velikiy | bb29966 | 2019-06-19 14:19:10 +0300 | [diff] [blame] | 135 | message_class => integer(), |
| 136 | source_name => binary(), |
| 137 | destination_name => binary(), |
| 138 | an_apdu => binary(), |
| 139 | rr_cause => integer(), |
| 140 | session_management_cause => integer(), |
| 141 | bssap_cause => integer() |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 142 | }. |
| 143 | |
| 144 | -define(SESSION_STATE_BEGIN, 1). |
| 145 | -define(SESSION_STATE_CONTINUE, 2). |
| 146 | -define(SESSION_STATE_END, 3). |
| 147 | |
| 148 | -define(IMSI, 16#01). |
| 149 | -define(CAUSE, 16#02). |
| 150 | -define(AUTH_TUPLE, 16#03). |
| 151 | -define(PDP_INFO_COMPLETE, 16#04). |
| 152 | -define(PDP_INFO, 16#05). |
| 153 | -define(CANCELLATION_TYPE, 16#06). |
| 154 | -define(FREEZE_P_TMSI, 16#07). |
| 155 | -define(MSISDN, 16#08). |
| 156 | -define(HLR_NUMBER, 16#09). |
Andrey Velikiy | c66c8d0 | 2019-06-19 14:16:52 +0300 | [diff] [blame] | 157 | -define(MESSAGE_CLASS, 16#0a). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 158 | -define(PDP_CONTEXT_ID, 16#10). |
Pau Espin Pedrol | 058a993 | 2024-01-22 14:15:43 +0100 | [diff] [blame] | 159 | -define(PDP_ADDRESS, 16#11). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 160 | -define(ACCESS_POINT_NAME, 16#12). |
| 161 | -define(QUALITY_OF_SERVICE, 16#13). |
| 162 | -define(PDP_CHARGING, 16#14). |
Pau Espin Pedrol | 25ace76 | 2024-02-20 19:55:06 +0100 | [diff] [blame] | 163 | -define(PCO, 16#15). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 164 | -define(RAND, 16#20). |
| 165 | -define(SRES, 16#21). |
| 166 | -define(KC, 16#22). |
| 167 | -define(IK, 16#23). |
| 168 | -define(CK, 16#24). |
| 169 | -define(AUTN, 16#25). |
| 170 | -define(AUTS, 16#26). |
| 171 | -define(RES, 16#27). |
| 172 | -define(CN_DOMAIN, 16#28). |
Harald Welte | bb32d46 | 2019-12-01 16:20:50 +0100 | [diff] [blame] | 173 | -define(SUPPORTED_RAT_TYPES, 16#29). |
Harald Welte | b470dfa | 2019-12-01 16:28:48 +0100 | [diff] [blame] | 174 | -define(CURRENT_RAT_TYPE, 16#2a). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 175 | -define(SESSION_ID, 16#30). |
| 176 | -define(SESSION_STATE, 16#31). |
| 177 | -define(SS_INFO, 16#35). |
| 178 | -define(SM_RP_MR, 16#40). |
| 179 | -define(SM_RP_DA, 16#41). |
| 180 | -define(SM_RP_OA, 16#42). |
| 181 | -define(SM_RP_UI, 16#43). |
| 182 | -define(SM_RP_CAUSE, 16#44). |
| 183 | -define(SM_RP_MMS, 16#45). |
| 184 | -define(SM_ALERT_REASON, 16#46). |
| 185 | -define(IMEI, 16#50). |
| 186 | -define(IMEI_CHECK_RESULT, 16#51). |
Alexander Couzens | b656fc1 | 2019-12-27 05:04:40 +0100 | [diff] [blame] | 187 | -define(NUM_VECTORS_REQ, 16#52). |
Andrey Velikiy | bb29966 | 2019-06-19 14:19:10 +0300 | [diff] [blame] | 188 | -define(SOURCE_NAME, 16#60). |
| 189 | -define(DESTINATION_NAME, 16#61). |
| 190 | -define(AN_APDU, 16#62). |
| 191 | -define(RR_CAUSE, 16#63). |
| 192 | -define(BSSAP_CAUSE, 16#64). |
| 193 | -define(SESSION_MANAGEMENT_CAUSE, 16#65). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 194 | |
| 195 | -define(MANDATORY_DEFAULT, [imsi, message_type]). |
| 196 | |
Vadim Yanitskiy | e708e54 | 2019-12-26 13:14:35 +0100 | [diff] [blame] | 197 | -define(OPTIONAL_DEFAULT, [message_class, source_name, destination_name]). |
Andrey Velikiy | c66c8d0 | 2019-06-19 14:16:52 +0300 | [diff] [blame] | 198 | |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 199 | -define (GSUP_MESSAGES(), #{ |
| 200 | 16#04 => #{message_type => location_upd_req, mandatory => [], optional => [cn_domain]}, |
| 201 | 16#05 => #{message_type => location_upd_err, mandatory => [cause]}, |
Andrey Velikiy | 1e80178 | 2019-06-24 14:09:57 +0300 | [diff] [blame] | 202 | 16#06 => #{message_type => location_upd_res, mandatory => [], optional => [msisdn, hlr_number, pdp_info_complete, pdp_info_list, pdp_charging]}, |
Alexander Couzens | b656fc1 | 2019-12-27 05:04:40 +0100 | [diff] [blame] | 203 | 16#08 => #{message_type => send_auth_info_req, mandatory => [], optional => [cn_domain, auts, rand, supported_rat_types, current_rat_type, num_vectors_req]}, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 204 | 16#09 => #{message_type => send_auth_info_err, mandatory => [cause]}, |
Andrey Velikiy | 1e80178 | 2019-06-24 14:09:57 +0300 | [diff] [blame] | 205 | 16#0a => #{message_type => send_auth_info_res, mandatory => [], optional => [auth_tuples, auts, rand]}, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 206 | 16#0b => #{message_type => auth_failure_report, mandatory => [], optional => [cn_domain]}, |
Andrey Velikiy | 1e80178 | 2019-06-24 14:09:57 +0300 | [diff] [blame] | 207 | 16#0c => #{message_type => purge_ms_req, mandatory => [], optional => [cn_domain, hlr_number]}, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 208 | 16#0d => #{message_type => purge_ms_err, mandatory => [cause]}, |
| 209 | 16#0e => #{message_type => purge_ms_res, mandatory => [freeze_p_tmsi]}, |
Alexander Couzens | 8c72402 | 2019-12-27 18:34:54 +0100 | [diff] [blame] | 210 | 16#10 => #{message_type => insert_sub_data_req, mandatory => [], optional => [cn_domain, msisdn, hlr_number, pdp_info_complete, pdp_info_list, pdp_charging]}, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 211 | 16#11 => #{message_type => insert_sub_data_err, mandatory => [cause]}, |
| 212 | 16#12 => #{message_type => insert_sub_data_res, mandatory => []}, |
| 213 | 16#14 => #{message_type => delete_sub_data_req, mandatory => [], optional => [cn_domain, pdp_context_id]}, |
| 214 | 16#15 => #{message_type => delete_sub_data_err, mandatory => [cause]}, |
| 215 | 16#16 => #{message_type => delete_sub_data_res, mandatory => []}, |
| 216 | 16#1c => #{message_type => location_cancellation_req, mandatory => [], optional => [cn_domain, cancellation_type]}, |
| 217 | 16#1d => #{message_type => location_cancellation_err, mandatory => [cause]}, |
| 218 | 16#1e => #{message_type => location_cancellation_res, mandatory => [], optional => [cn_domain]}, |
| 219 | 16#20 => #{message_type => ss_req, mandatory => [session_id, session_state], optional => [ss_info]}, |
| 220 | 16#21 => #{message_type => ss_err, mandatory => [session_id, session_state, cause]}, |
| 221 | 16#22 => #{message_type => ss_res, mandatory => [session_id, session_state], optional => [ss_info]}, |
Andrey Velikiy | fde0fd9 | 2019-08-02 09:42:22 +0300 | [diff] [blame] | 222 | 16#24 => #{message_type => mo_forward_sm_req, mandatory => [sm_rp_mr, sm_rp_da, sm_rp_oa, sm_rp_ui]}, |
| 223 | 16#25 => #{message_type => mo_forward_sm_err, mandatory => [sm_rp_mr, sm_rp_cause], optional => [sm_rp_ui]}, |
| 224 | 16#26 => #{message_type => mo_forward_sm_res, mandatory => [sm_rp_mr], optional => [sm_rp_ui]}, |
| 225 | 16#28 => #{message_type => mt_forward_sm_req, mandatory => [sm_rp_mr, sm_rp_da, sm_rp_oa, sm_rp_ui], optional => [sm_rp_mms]}, |
| 226 | 16#29 => #{message_type => mt_forward_sm_err, mandatory => [sm_rp_mr, sm_rp_cause], optional => [sm_rp_ui]}, |
| 227 | 16#2a => #{message_type => mt_forward_sm_res, mandatory => [sm_rp_mr], optional => [sm_rp_ui]}, |
Vadim Yanitskiy | 657fee9 | 2019-12-26 13:18:02 +0100 | [diff] [blame] | 228 | 16#2c => #{message_type => ready_for_sm_req, mandatory => [sm_rp_mr, sm_alert_reason]}, |
| 229 | 16#2d => #{message_type => ready_for_sm_err, mandatory => [sm_rp_mr, sm_rp_cause], optional => [sm_rp_ui]}, |
| 230 | 16#2e => #{message_type => ready_for_sm_res, mandatory => [sm_rp_mr]}, |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 231 | 16#30 => #{message_type => check_imei_req, mandatory => [imei]}, |
| 232 | 16#31 => #{message_type => check_imei_err, mandatory => [cause]}, |
Andrey Velikiy | bb29966 | 2019-06-19 14:19:10 +0300 | [diff] [blame] | 233 | 16#32 => #{message_type => check_imei_res, mandatory => [imei_check_result]}, |
| 234 | 16#34 => #{message_type => e_prepare_handover_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 235 | 16#35 => #{message_type => e_prepare_handover_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, |
| 236 | 16#36 => #{message_type => e_prepare_handover_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 237 | 16#38 => #{message_type => e_prepare_subseq_handover_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 238 | 16#39 => #{message_type => e_prepare_subseq_handover_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, |
| 239 | 16#3a => #{message_type => e_prepare_subseq_handover_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 240 | 16#3c => #{message_type => e_send_end_signal_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 241 | 16#3d => #{message_type => e_send_end_signal_err, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state, bssap_cause]}, |
| 242 | 16#3e => #{message_type => e_send_end_signal_res, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 243 | 16#40 => #{message_type => e_process_access_signalling_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 244 | 16#44 => #{message_type => e_forward_access_signalling_req, mandatory => [message_class, source_name, destination_name, an_apdu, session_id, session_state]}, |
| 245 | 16#47 => #{message_type => e_close, mandatory => [message_class, source_name, destination_name, session_id, session_state]}, |
| 246 | 16#4b => #{message_type => e_abort, mandatory => [message_class, session_id, session_state, bssap_cause]}, |
Alexander Couzens | daa4d88 | 2023-07-18 20:18:45 +0200 | [diff] [blame] | 247 | 16#4e => #{message_type => e_routing_err, mandatory => [message_class, source_name, destination_name], optional => [session_id, session_state]}, |
Pau Espin Pedrol | 25ace76 | 2024-02-20 19:55:06 +0100 | [diff] [blame] | 248 | 16#50 => #{message_type => epdg_tunnel_request, mandatory => [message_class], optional => [pco]}, |
Alexander Couzens | daa4d88 | 2023-07-18 20:18:45 +0200 | [diff] [blame] | 249 | 16#51 => #{message_type => epdg_tunnel_error, mandatory => [message_class, cause], optional => []}, |
Pau Espin Pedrol | 25ace76 | 2024-02-20 19:55:06 +0100 | [diff] [blame] | 250 | 16#52 => #{message_type => epdg_tunnel_result, mandatory => [message_class, pdp_info_complete, pdp_info_list], optional => [pco]} |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 251 | }). |
| 252 | |
Alexander Couzens | d023f75 | 2023-07-19 03:04:03 +0200 | [diff] [blame] | 253 | -define(AUTH_TUPLE_MANDATORY, []). |
| 254 | -define(AUTH_TUPLE_OPTIONAL, [rand, sres, kc, ik, ck, autn, res]). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 255 | -define(PDP_INFO_MANDATORY, []). |
Pau Espin Pedrol | 058a993 | 2024-01-22 14:15:43 +0100 | [diff] [blame] | 256 | -define(PDP_INFO_OPTIONAL, [pdp_context_id, pdp_address, access_point_name, quality_of_service, pdp_charging]). |
Andrey Velikiy | b39df23 | 2019-06-14 21:08:00 +0300 | [diff] [blame] | 257 | |
| 258 | -endif. |