blob: 2c5afb32f1c833688925828d84d2f982c55d0b64 [file] [log] [blame]
Harald Welte6eafe912009-10-16 08:32:58 +02001/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
2 * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * (C) 2009 by Mike Haben <michael.haben@btinternet.com>
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +07004 * (C) 2018 by Vadim Yanitskiy <axilirator@gmail.com>
Harald Welte6eafe912009-10-16 08:32:58 +02005 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01009 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
Harald Welte6eafe912009-10-16 08:32:58 +020011 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010016 * GNU Affero General Public License for more details.
Harald Welte6eafe912009-10-16 08:32:58 +020017 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010018 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welte6eafe912009-10-16 08:32:58 +020020 *
21 */
22
Vadim Yanitskiy5b860fa2018-06-12 05:24:52 +070023/**
24 * MSC-specific handling of call independent Supplementary
25 * Services messages (NC_SS) according to GSM TS 09.11
26 * "Signalling interworking for supplementary services".
27 */
Harald Welte6eafe912009-10-16 08:32:58 +020028
29#include <stdio.h>
Harald Welte6eafe912009-10-16 08:32:58 +020030#include <errno.h>
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +070031#include <stdbool.h>
32
Vadim Yanitskiyf2f83b02018-06-17 21:09:28 +070033#include <osmocom/core/linuxlist.h>
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +070034#include <osmocom/core/rate_ctr.h>
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +070035#include <osmocom/core/utils.h>
36#include <osmocom/core/msgb.h>
37#include <osmocom/gsm/tlv.h>
Harald Welte6eafe912009-10-16 08:32:58 +020038
Neels Hofmeyr90843962017-09-04 15:04:35 +020039#include <osmocom/msc/gsm_04_80.h>
40#include <osmocom/msc/gsm_subscriber.h>
41#include <osmocom/msc/debug.h>
42#include <osmocom/msc/osmo_msc.h>
43#include <osmocom/msc/vlr.h>
Max43b01b02017-09-15 11:22:30 +020044#include <osmocom/msc/gsm_04_08.h>
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070045#include <osmocom/msc/transaction.h>
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +070046#include <osmocom/msc/gsup_client.h>
47#include <osmocom/msc/msc_ifaces.h>
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070048
49/* FIXME: choose a proper range */
50static uint32_t new_callref = 0x20000001;
Harald Welte6eafe912009-10-16 08:32:58 +020051
Vadim Yanitskiy5b860fa2018-06-12 05:24:52 +070052/* Entry point for call independent MO SS messages */
53int gsm0911_rcv_nc_ss(struct gsm_subscriber_connection *conn, struct msgb *msg)
Harald Welte6eafe912009-10-16 08:32:58 +020054{
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070055 struct gsm48_hdr *gh = msgb_l3(msg);
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +070056 struct osmo_gsup_message gsup_msg;
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070057 struct gsm_trans *trans;
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +070058 struct msgb *gsup_msgb;
59 uint16_t facility_ie_len;
60 uint8_t *facility_ie;
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070061 uint8_t pdisc, tid;
62 uint8_t msg_type;
63 int rc;
Harald Welte6eafe912009-10-16 08:32:58 +020064
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070065 pdisc = gsm48_hdr_pdisc(gh);
66 msg_type = gsm48_hdr_msg_type(gh);
67 tid = gsm48_hdr_trans_id_flip_ti(gh);
Harald Welte2483f1b2016-06-19 18:06:02 +020068
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070069 /* Associate logging messages with this subscriber */
70 log_set_context(LOG_CTX_VLR_SUBSCR, conn->vsub);
71
72 DEBUGP(DMM, "Received SS/USSD data (trans_id=%x, msg_type=%s)\n",
73 tid, gsm48_pdisc_msgtype_name(pdisc, msg_type));
74
75 /* Reuse existing transaction, or create a new one */
76 trans = trans_find_by_id(conn, pdisc, tid);
77 if (!trans) {
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +070078 /* Count MS-initiated attempts to establish a NC SS/USSD session */
79 rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_NC_SS_MO_REQUESTS]);
80
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070081 /**
82 * According to GSM TS 04.80, section 2.4.2 "Register
83 * (mobile station to network direction)", the REGISTER
84 * message is sent by the mobile station to the network
85 * to assign a new transaction identifier for call independent
86 * supplementary service control and to request or acknowledge
87 * a supplementary service.
88 */
89 if (msg_type != GSM0480_MTYPE_REGISTER) {
90 LOGP(DMM, LOGL_ERROR, "Unexpected message (msg_type=%s), "
91 "transaction is not allocated yet\n",
92 gsm48_pdisc_msgtype_name(pdisc, msg_type));
Vadim Yanitskiy9aec25e2018-06-12 06:26:28 +070093 gsm48_tx_simple(conn,
94 GSM48_PDISC_NC_SS | (tid << 4),
95 GSM0480_MTYPE_RELEASE_COMPLETE);
Vadim Yanitskiy10c64192018-04-17 19:17:11 +070096 return -EINVAL;
97 }
98
99 DEBUGP(DMM, " -> (new transaction)\n");
100 trans = trans_alloc(conn->network, conn->vsub,
101 pdisc, tid, new_callref++);
102 if (!trans) {
103 DEBUGP(DMM, " -> No memory for trans\n");
Vadim Yanitskiy9aec25e2018-06-12 06:26:28 +0700104 gsm48_tx_simple(conn,
105 GSM48_PDISC_NC_SS | (tid << 4),
106 GSM0480_MTYPE_RELEASE_COMPLETE);
Vadim Yanitskiy10c64192018-04-17 19:17:11 +0700107 return -ENOMEM;
108 }
109
110 trans->conn = msc_subscr_conn_get(conn, MSC_CONN_USE_TRANS_NC_SS);
111 trans->dlci = OMSC_LINKID_CB(msg);
112 cm_service_request_concludes(conn, msg);
113 }
Harald Welte2483f1b2016-06-19 18:06:02 +0200114
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700115 /* Attempt to extract Facility IE */
116 rc = gsm0480_extract_ie_by_tag(gh, msgb_l3len(msg),
117 &facility_ie, &facility_ie_len, GSM0480_IE_FACILITY);
118 if (rc) {
119 LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, "
120 "couldn't extract Facility IE\n");
121 goto error;
Tobias Engelea730322013-12-28 17:03:14 +0100122 }
123
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700124 /* Facility IE is optional for RELEASE COMPLETE */
125 if (msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
126 if (!facility_ie || facility_ie_len < 2) {
127 LOGP(DMM, LOGL_ERROR, "GSM 04.80 message parsing error, "
128 "missing mandatory Facility IE\n");
129 rc = -EINVAL;
130 goto error;
Holger Hans Peter Freyther5085e0b2016-07-12 17:53:26 +0200131 }
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700132 }
133
134 /* Compose a mew GSUP message */
135 memset(&gsup_msg, 0x00, sizeof(gsup_msg));
136 gsup_msg.message_type = OSMO_GSUP_MSGT_PROC_SS_REQUEST;
137 gsup_msg.session_id = trans->callref;
138
139 /**
140 * Perform A-interface to GSUP-interface mapping,
141 * according to GSM TS 09.11, table 4.2.
142 */
143 switch (msg_type) {
144 case GSM0480_MTYPE_REGISTER:
145 gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_BEGIN;
146 break;
147 case GSM0480_MTYPE_FACILITY:
148 gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_CONTINUE;
149 break;
150 case GSM0480_MTYPE_RELEASE_COMPLETE:
151 gsup_msg.session_state = OSMO_GSUP_SESSION_STATE_END;
152 break;
153 }
154
155 /* Fill in the (optional) message payload */
156 if (facility_ie) {
157 gsup_msg.ss_info_len = facility_ie_len;
158 gsup_msg.ss_info = facility_ie;
159 }
160
161 /* Fill in subscriber's IMSI */
162 OSMO_STRLCPY_ARRAY(gsup_msg.imsi, conn->vsub->imsi);
163
164 /* Allocate GSUP message buffer */
165 gsup_msgb = gsup_client_msgb_alloc();
166 if (!gsup_msgb) {
167 LOGP(DMM, LOGL_ERROR, "Couldn't allocate GSUP message\n");
168 rc = -ENOMEM;
169 goto error;
170 }
171
172 /* Encode GSUP message */
173 rc = osmo_gsup_encode(gsup_msgb, &gsup_msg);
174 if (rc) {
175 LOGP(DMM, LOGL_ERROR, "Couldn't encode GSUP message\n");
176 goto error;
177 }
178
179 /* Finally send */
180 rc = gsup_client_send(conn->network->vlr->gsup_client, gsup_msgb);
181 if (rc) {
182 LOGP(DMM, LOGL_ERROR, "Couldn't send GSUP message\n");
183 goto error;
184 }
185
Vadim Yanitskiyfcc24ed2018-06-21 17:55:56 +0700186 /* Should we release connection? Or wait for response? */
187 if (msg_type == GSM0480_MTYPE_RELEASE_COMPLETE)
188 trans_free(trans);
189 else
190 msc_subscr_conn_communicating(conn);
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700191
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +0700192 /* Count established MS-initiated NC SS/USSD sessions */
193 if (msg_type == GSM0480_MTYPE_REGISTER)
194 rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_NC_SS_MO_ESTABLISHED]);
195
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700196 return 0;
197
198error:
199 /* Abort transaction on DTAP-interface */
200 gsm0480_send_ussd_reject(conn, tid, -1,
201 GSM_0480_PROBLEM_CODE_TAG_GENERAL,
202 GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
203 if (trans)
204 trans_free(trans);
205
206 /* TODO: abort transaction on GSUP interface if any */
207 return rc;
208}
209
Vadim Yanitskiyf2f83b02018-06-17 21:09:28 +0700210/* Call-back from paging the B-end of the connection */
211static int handle_paging_event(unsigned int hooknum, unsigned int event,
212 struct msgb *msg, void *_conn, void *_transt)
213{
214 struct gsm_subscriber_connection *conn = _conn;
215 enum gsm_paging_event paging_event = event;
216 struct gsm_trans *transt = _transt;
217 struct gsm48_hdr *gh;
218 struct msgb *ss_msg;
219
220 OSMO_ASSERT(!transt->conn);
221 OSMO_ASSERT(transt->ss.msg);
222
223 switch (paging_event) {
224 case GSM_PAGING_SUCCEEDED:
225 DEBUGP(DMM, "Paging subscr %s succeeded!\n",
226 vlr_subscr_msisdn_or_name(transt->vsub));
227
228 /* Assign connection */
229 transt->conn = msc_subscr_conn_get(conn, MSC_CONN_USE_TRANS_NC_SS);
230 transt->paging_request = NULL;
231
232 /* Send stored message */
233 ss_msg = transt->ss.msg;
234 OSMO_ASSERT(ss_msg);
235
236 gh = (struct gsm48_hdr *) msgb_push(ss_msg, sizeof(*gh));
237 gh->proto_discr = GSM48_PDISC_NC_SS;
238 gh->proto_discr |= transt->transaction_id << 4;
239 gh->msg_type = GSM0480_MTYPE_REGISTER;
240
241 /* Sent to the MS, give ownership of ss_msg */
242 msc_tx_dtap(transt->conn, ss_msg);
243 transt->ss.msg = NULL;
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +0700244
245 /* Count established network-initiated NC SS/USSD sessions */
246 rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_NC_SS_MT_ESTABLISHED]);
Vadim Yanitskiyf2f83b02018-06-17 21:09:28 +0700247 break;
248 case GSM_PAGING_EXPIRED:
249 case GSM_PAGING_BUSY:
250 DEBUGP(DMM, "Paging subscr %s %s!\n",
251 vlr_subscr_msisdn_or_name(transt->vsub),
252 paging_event == GSM_PAGING_EXPIRED ? "expired" : "busy");
253
254 /* TODO: inform HLR about this failure */
255
256 msgb_free(transt->ss.msg);
257 transt->ss.msg = NULL;
258
259 transt->callref = 0;
260 transt->paging_request = NULL;
261 trans_free(transt);
262 break;
263 }
264
265 return 0;
266}
267
268static struct gsm_trans *establish_nc_ss_trans(struct gsm_network *net,
269 struct vlr_subscr *vsub, struct osmo_gsup_message *gsup_msg)
270{
271 struct gsm_subscriber_connection *conn;
272 struct gsm_trans *trans, *transt;
273 int tid;
274
275 if (gsup_msg->session_state != OSMO_GSUP_SESSION_STATE_BEGIN) {
276 LOGP(DMM, LOGL_ERROR, "Received non-BEGIN message "
277 "for non-existing transaction\n");
278 return NULL;
279 }
280
281 if (!gsup_msg->ss_info || gsup_msg->ss_info_len < 2) {
282 LOGP(DMM, LOGL_ERROR, "Missing mandatory Facility IE\n");
283 return NULL;
284 }
285
286 /* If subscriber is not "attached" */
287 if (!vsub->lac) {
288 LOGP(DMM, LOGL_ERROR, "Network-originated session "
289 "rejected - subscriber is not attached\n");
290 return NULL;
291 }
292
293 DEBUGP(DMM, "Establishing network-originated session\n");
294
295 /* Allocate a new transaction */
296 trans = trans_alloc(net, vsub, GSM48_PDISC_NC_SS,
297 0xff, gsup_msg->session_id);
298 if (!trans) {
299 DEBUGP(DMM, " -> No memory for trans\n");
300 return NULL;
301 }
302
303 /* Assign transaction ID */
304 tid = trans_assign_trans_id(trans->net,
305 trans->vsub, GSM48_PDISC_NC_SS, 0);
306 if (tid < 0) {
307 LOGP(DMM, LOGL_ERROR, "No free transaction ID\n");
308 /* TODO: inform HLR about this */
309 /* TODO: release connection with subscriber */
310 trans->callref = 0;
311 trans_free(trans);
312 return NULL;
313 }
314 trans->transaction_id = tid;
315
316 /* Attempt to find connection */
317 conn = connection_for_subscr(vsub);
318 if (conn) {
319 /* Assign connection */
320 trans->conn = msc_subscr_conn_get(conn, MSC_CONN_USE_TRANS_NC_SS);
321 trans->dlci = 0x00; /* SAPI=0, not SACCH */
322 return trans;
323 }
324
325 DEBUGP(DMM, "Triggering Paging Request\n");
326
327 /* Find transaction with this subscriber already paging */
328 llist_for_each_entry(transt, &net->trans_list, entry) {
329 /* Transaction of our conn? */
330 if (transt == trans || transt->vsub != vsub)
331 continue;
332
333 LOGP(DMM, LOGL_ERROR, "Paging already started, "
334 "rejecting message...\n");
335 trans_free(trans);
336 return NULL;
337 }
338
339 /* Trigger Paging Request */
340 trans->paging_request = subscr_request_conn(vsub,
341 &handle_paging_event, trans, "GSM 09.11 SS/USSD");
342 if (!trans->paging_request) {
343 LOGP(DMM, LOGL_ERROR, "Failed to allocate paging token\n");
344 trans_free(trans);
345 return NULL;
346 }
347
348 /* Store the Facility IE to be sent */
349 OSMO_ASSERT(trans->ss.msg == NULL);
350 trans->ss.msg = gsm48_msgb_alloc_name("GSM 04.08 SS/USSD");
351 msgb_tlv_put(trans->ss.msg, GSM0480_IE_FACILITY,
352 gsup_msg->ss_info_len, gsup_msg->ss_info);
353
354 return NULL;
355}
356
357/* NC SS specific transaction release.
358 * Gets called by trans_free, DO NOT CALL YOURSELF! */
359void _gsm911_nc_ss_trans_free(struct gsm_trans *trans)
360{
361 /**
362 * TODO: if transaction wasn't properly terminated,
363 * we need to do it here by releasing the subscriber
364 * connection and sending notification via GSUP...
365 */
366 if (trans->ss.msg != NULL)
367 msgb_free(trans->ss.msg);
368}
369
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700370int gsm0911_gsup_handler(struct vlr_subscr *vsub,
371 struct osmo_gsup_message *gsup_msg)
372{
373 struct vlr_instance *vlr;
374 struct gsm_network *net;
375 struct gsm_trans *trans;
376 struct gsm48_hdr *gh;
377 struct msgb *ss_msg;
378 bool trans_end;
379
380 /* Associate logging messages with this subscriber */
381 log_set_context(LOG_CTX_VLR_SUBSCR, vsub);
382
383 /* Obtain pointer to vlr_instance */
384 vlr = vsub->vlr;
385 OSMO_ASSERT(vlr);
386
387 /* Obtain pointer to gsm_network */
388 net = (struct gsm_network *) vlr->user_ctx;
389 OSMO_ASSERT(net);
390
391 /* Handle errors */
392 if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
393 /* FIXME: handle this error somehow! */
Harald Welte6307b852009-10-16 08:41:51 +0200394 return 0;
Holger Hans Peter Freyther5085e0b2016-07-12 17:53:26 +0200395 }
Harald Welte6eafe912009-10-16 08:32:58 +0200396
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700397 /* Attempt to find DTAP-transaction */
398 trans = trans_find_by_callref(net, gsup_msg->session_id);
399 if (!trans) {
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +0700400 /* Count network-initiated attempts to establish a NC SS/USSD session */
401 rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_NC_SS_MT_REQUESTS]);
402
Vadim Yanitskiyf2f83b02018-06-17 21:09:28 +0700403 /* Attempt to establish a new transaction */
404 trans = establish_nc_ss_trans(net, vsub, gsup_msg);
405 if (!trans) {
406 /* FIXME: send ERROR back to the HLR */
407 return -EINVAL;
408 }
409
410 /* Wait for Paging Response */
411 if (trans->paging_request)
412 return 0;
Harald Welte6eafe912009-10-16 08:32:58 +0200413 }
Holger Hans Peter Freyther24866632010-06-30 12:15:19 +0800414
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700415 /* Allocate and prepare a new MT message */
416 ss_msg = gsm48_msgb_alloc_name("GSM 04.08 SS/USSD");
417 gh = (struct gsm48_hdr *) msgb_push(ss_msg, sizeof(*gh));
418 gh->proto_discr = GSM48_PDISC_NC_SS;
419 gh->proto_discr |= trans->transaction_id << 4;
Vadim Yanitskiy10c64192018-04-17 19:17:11 +0700420
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700421 /**
422 * Perform GSUP-interface to A-interface mapping,
423 * according to GSM TS 09.11, table 4.1.
424 *
425 * TODO: see (note 3), both CONTINUE and END may
426 * be also mapped to REGISTER if a new transaction
427 * has to be established.
428 */
429 switch (gsup_msg->session_state) {
430 case OSMO_GSUP_SESSION_STATE_BEGIN:
431 gh->msg_type = GSM0480_MTYPE_REGISTER;
432 break;
433 case OSMO_GSUP_SESSION_STATE_CONTINUE:
434 gh->msg_type = GSM0480_MTYPE_FACILITY;
435 break;
436 case OSMO_GSUP_SESSION_STATE_END:
437 gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
438 break;
439
440 /* Missing or incorrect session state */
441 case OSMO_GSUP_SESSION_STATE_NONE:
442 default:
443 LOGP(DMM, LOGL_ERROR, "Unexpected session state %d\n",
444 gsup_msg->session_state);
445 /* FIXME: send ERROR back to the HLR */
446 msgb_free(ss_msg);
447 return -EINVAL;
448 }
449
450 /* Facility IE is optional only for RELEASE COMPLETE */
451 if (gh->msg_type != GSM0480_MTYPE_RELEASE_COMPLETE) {
452 if (!gsup_msg->ss_info || gsup_msg->ss_info_len < 2) {
453 LOGP(DMM, LOGL_ERROR, "Missing mandatory Facility IE "
454 "for mapped 0x%02x message\n", gh->msg_type);
455 /* FIXME: send ERROR back to the HLR */
456 msgb_free(ss_msg);
457 return -EINVAL;
458 }
459 }
460
461 /* Append Facility IE if preset */
462 if (gsup_msg->ss_info && gsup_msg->ss_info_len > 2) {
463 /* Facility IE carries LV, others carry TLV */
464 if (gh->msg_type == GSM0480_MTYPE_FACILITY)
465 msgb_lv_put(ss_msg, gsup_msg->ss_info_len, gsup_msg->ss_info);
466 else
467 msgb_tlv_put(ss_msg, GSM0480_IE_FACILITY,
468 gsup_msg->ss_info_len, gsup_msg->ss_info);
469 }
470
471 /* Should we release the transaction? */
472 trans_end = (gh->msg_type == GSM0480_MTYPE_RELEASE_COMPLETE);
473
474 /* Sent to the MS, give ownership of ss_msg */
475 msc_tx_dtap(trans->conn, ss_msg);
476
477 /* Release transaction if required */
478 if (trans_end)
479 trans_free(trans);
480
Vadim Yanitskiy8e25cc52018-06-23 03:32:20 +0700481 /* Count established network-initiated NC SS/USSD sessions */
482 if (gsup_msg->session_state == OSMO_GSUP_SESSION_STATE_BEGIN)
483 rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_NC_SS_MT_ESTABLISHED]);
484
Vadim Yanitskiy8a6ef552018-06-12 08:21:20 +0700485 return 0;
Harald Welte6eafe912009-10-16 08:32:58 +0200486}